Persistence #1 featuring quill (#508)

* Add .scalafmt.conf

* Adopt quill for database access

* Removed postgresql-async
* Refactored all instances of database access
* Creating duplicate characters of the same account is no longer possible
* Rewrote large parts of LoginSessionActor

* Implement migrations

* Move overrides into subdirectory

* Make usernames case insensitive

* Use LOWER(?) comparison instead of storing lowercased username

* import scala.util.{Success, Failure}

* Add config and joda-time dependencies

* Add sbt-scalafmt

* Use defaultWithAlign scalafmt preset

* Format all

* Add scalafix

* Remove unused imports

* Don't lowercase username when inserting

* Update readme

* Listen on worldserver.Hostname address

* Remove database test on startup

It could fail when the global thread pool is busy loading zone
maps. Migrations run on the main thread and also serve the
purpose of verifying the database configuration so it's fine to
remove the test altogether.

* Refactor chat message handlers, zones

What started as a small change to how zones are stored turned
into a pretty big effort of refactoring the chat message handler.
The !hack command was removed, the /capturebase commandwas added.

* Expose db ports in docker-compose.yml

* Silence property override log

* Rework configuration

* Unify configuration using the typesafe.config library
* Add configuration option for public address
* Configuration is now loaded from application.conf rather than worldserver.ini
* Refactor PsLogin and remove unnecessary logging
* Move pslogin into net.psforever.pslogin namespace

* Fix coverage
This commit is contained in:
Jakob Gillich 2020-07-14 05:54:05 +02:00 committed by GitHub
parent 88b194fde2
commit e0defe8240
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
850 changed files with 144487 additions and 47476 deletions

3
.gitignore vendored
View file

@ -13,8 +13,7 @@ out/
project/metals.sbt project/metals.sbt
# User configs # User configs
config/worldserver.ini config/psforever.conf
pslogin/src/main/resources/user.conf
# Log files # Log files
*.log *.log

6
.scalafix.conf Normal file
View file

@ -0,0 +1,6 @@
rules = [
RemoveUnused
]
RemoveUnused.imports = true
RemoveUnused.privates = false
RemoveUnused.locals = false

3
.scalafmt.conf Normal file
View file

@ -0,0 +1,3 @@
version = 2.6.1
preset = defaultWithAlign
maxColumn = 120

View file

@ -78,7 +78,11 @@ The Login and World servers require PostgreSQL for persistence.
- Linux - [Debian](https://www.postgresql.org/download/linux/debian/) or [Ubuntu](https://www.postgresql.org/download/linux/ubuntu/) - Linux - [Debian](https://www.postgresql.org/download/linux/debian/) or [Ubuntu](https://www.postgresql.org/download/linux/ubuntu/)
- macOS - Application https://www.postgresql.org/download/ (or `brew install postgresql && brew services start postgresql`) - macOS - Application https://www.postgresql.org/download/ (or `brew install postgresql && brew services start postgresql`)
The default database is named `psforever` and the credentials are `psforever:psforever`. To change these, make a copy of [`config/worldserver.ini.dist`](config/worldserver.ini.dist) to `config/worldserver.ini` and change the corresponding fields in the database section. This database user will need ALL access to tables, sequences, and functions. The default database is named `psforever` and the credentials are
`psforever:psforever`. To change these, create a configuration file at
`config/psforever.conf`. For configuration options and their defaults, see
[`pslogin/src/main/resources/application.conf`]. This database user will need
ALL access to tables, sequences, and functions.
The permissions required can be summarized by the SQL below. The permissions required can be summarized by the SQL below.
Loading this in requires access to a graphical tool such as [pgAdmin](https://www.pgadmin.org/download/) (highly recommended) or a PostgreSQL terminal (`psql`) for advanced users. Loading this in requires access to a graphical tool such as [pgAdmin](https://www.pgadmin.org/download/) (highly recommended) or a PostgreSQL terminal (`psql`) for advanced users.
@ -95,9 +99,9 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA PUBLIC GRANT ALL ON FUNCTIONS TO psforever;
**NOTE:** applying default privileges _after_ importing the schema will not apply them to existing objects. To fix this, you must drop all objects and try again or apply permissions manually using the Query Tool / `psql`. **NOTE:** applying default privileges _after_ importing the schema will not apply them to existing objects. To fix this, you must drop all objects and try again or apply permissions manually using the Query Tool / `psql`.
Now you need to synchronize the schema. This is currently available in [`schema.sql`](schema.sql). The server will automatically apply the latest schema. Migrations can also be applied manually using
To do this right click on the psforever database -> Query Tool... -> Copy and paste / Open the `schema.sql` file into the editor -> Hit the "Play/Run" button. The schema should be loaded into the database. the [Flyway CLI](https://flywaydb.org/documentation/commandline/). Existing databases before the
Once you have the schema loaded in, the LoginServer will automatically create accounts on first login. If you'd like a nice account management interface, check out the [PSFPortal](https://github.com/psforever/PSFPortal) web interface. introduction of migrations must be baselined using the `flyway baseline` command.
### Becoming a GM ### Becoming a GM
@ -161,33 +165,6 @@ Using SBT, you can generate documentation for both the common and pslogin projec
Current documentation is available at [https://psforever.github.io/docs/master/index.html](https://psforever.github.io/docs/master/index.html) Current documentation is available at [https://psforever.github.io/docs/master/index.html](https://psforever.github.io/docs/master/index.html)
## Troubleshooting
#### Unable to initialize pscrypto
If you get an error like below
```
12:17:28.037 [main] ERROR PsLogin - Unable to initialize pscrypto
java.lang.UnsatisfiedLinkError: Unable to load library 'pscrypto': Native library (win32-x86-64/pscrypto.dll) not found in resource path
```
Then you are missing the native library required to provide cryptographic functions to the login server. To fix this, you need a binary build of [PSCrypto](#downloading-pscrypto).
If you are still having trouble on Linux, try putting the library in `root directory/pscrypto-lib/libpscrypto.so`.
## Contributing
Please fork the project and provide a pull request to contribute code. Coding guidelines and contribution checklists coming soon.
## Get in touch
- Website: http://psforever.net
- Discord (chat with us): https://discord.gg/0nRe5TNbTYoUruA4
- Join the #code channel and ask any questions you have there
Chord is the lead developer and you can contact him on Discord as Chord or by email [chord@tuta.io](mailto:chord@tuta.io). Discord is preferred.
## Tools ## Tools
### decodePackets ### decodePackets
@ -217,6 +194,33 @@ psf-decode-packets -o ./output-directory foo.gcap bar.gcap
By default, decodePackets takes in `.gcap` files, but it can also take gcapy ascii files with the By default, decodePackets takes in `.gcap` files, but it can also take gcapy ascii files with the
`-p` option. Run `psf-decode-packets --help` to get usage info. `-p` option. Run `psf-decode-packets --help` to get usage info.
## Troubleshooting
#### Unable to initialize pscrypto
If you get an error like below
```
12:17:28.037 [main] ERROR PsLogin - Unable to initialize pscrypto
java.lang.UnsatisfiedLinkError: Unable to load library 'pscrypto': Native library (win32-x86-64/pscrypto.dll) not found in resource path
```
Then you are missing the native library required to provide cryptographic functions to the login server. To fix this, you need a binary build of [PSCrypto](#downloading-pscrypto).
If you are still having trouble on Linux, try putting the library in `root directory/pscrypto-lib/libpscrypto.so`.
## Contributing
Please fork the project and provide a pull request to contribute code. Coding guidelines and contribution checklists coming soon.
## Get in touch
- Website: http://psforever.net
- Discord (chat with us): https://discord.gg/0nRe5TNbTYoUruA4
- Join the #code channel and ask any questions you have there
Chord is the lead developer and you can contact him on Discord as Chord or by email [chord@tuta.io](mailto:chord@tuta.io). Discord is preferred.
## License ## License
GNU GPLv3. See LICENSE.md for the full copy. GNU GPLv3. See LICENSE.md for the full copy.

122
build.sbt
View file

@ -4,8 +4,18 @@ 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.2",
scalacOptions := Seq("-unchecked", "-feature", "-deprecation", "-encoding", "utf8", "-language:postfixOps"), semanticdbEnabled := true,
semanticdbVersion := scalafixSemanticdb.revision,
scalacOptions := Seq(
"-unchecked",
"-feature",
"-deprecation",
"-encoding",
"utf8",
"-language:postfixOps",
"-Wunused:imports",
"-Xmacro-settings:materialize-derivations"
),
// Quiet test options // Quiet test options
// https://github.com/etorreborre/specs2/blob/8305db76c5084e4b3ce5827ce23117f6fb6beee4/common/shared/src/main/scala/org/specs2/main/Report.scala#L94 // https://github.com/etorreborre/specs2/blob/8305db76c5084e4b3ce5827ce23117f6fb6beee4/common/shared/src/main/scala/org/specs2/main/Report.scala#L94
// https://etorreborre.github.io/specs2/guide/SPECS2-2.4.17/org.specs2.guide.Runners.html // https://etorreborre.github.io/specs2/guide/SPECS2-2.4.17/org.specs2.guide.Runners.html
@ -14,22 +24,31 @@ lazy val commonSettings = Seq(
testOptions in QuietTest += Tests.Argument(TestFrameworks.ScalaTest, "-oCEHILMNOPQRX"), testOptions in QuietTest += Tests.Argument(TestFrameworks.ScalaTest, "-oCEHILMNOPQRX"),
// Trick taken from https://groups.google.com/d/msg/scala-user/mxV9ok7J_Eg/kt-LnsrD0bkJ // Trick taken from https://groups.google.com/d/msg/scala-user/mxV9ok7J_Eg/kt-LnsrD0bkJ
// scaladoc flags: https://github.com/scala/scala/blob/2.11.x/src/scaladoc/scala/tools/nsc/doc/Settings.scala // scaladoc flags: https://github.com/scala/scala/blob/2.11.x/src/scaladoc/scala/tools/nsc/doc/Settings.scala
scalacOptions in (Compile,doc) := { Seq( scalacOptions in (Compile, doc) ++= Seq(
"-groups", "-groups",
"-implicits", "-implicits",
"-doc-title", "PSF-LoginServer - ", "-doc-title",
"-doc-version", "master", "PSF-LoginServer - ",
"-doc-footer", "Copyright PSForever", "-doc-version",
"master",
"-doc-footer",
"Copyright PSForever",
// For non unidoc builds, you may need bd.getName before the template parameter // For non unidoc builds, you may need bd.getName before the template parameter
"-doc-source-url", "https://github.com/psforever/PSF-LoginServer/blob/master/€{FILE_PATH}.scala", "-doc-source-url",
"-sourcepath", baseDirectory.value.getAbsolutePath // needed for scaladoc relative source paths "https://github.com/psforever/PSF-LoginServer/blob/master/€{FILE_PATH}.scala",
) "-sourcepath",
}, baseDirectory.value.getAbsolutePath // needed for scaladoc relative source paths
),
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.5", "com.typesafe.akka" %% "akka-actor" % "2.6.6",
"com.typesafe.akka" %% "akka-testkit" % "2.6.5" % "test", "com.typesafe.akka" %% "akka-slf4j" % "2.6.6",
"com.typesafe.akka" %% "akka-protobuf-v3" % "2.6.6",
"com.typesafe.akka" %% "akka-stream" % "2.6.6",
"com.typesafe.akka" %% "akka-testkit" % "2.6.6" % "test",
"com.typesafe.akka" %% "akka-actor-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",
@ -41,14 +60,20 @@ lazy val commonSettings = Seq(
"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.postgresql-async" %% "postgresql-async" % "0.3.0",
"com.github.t3hnar" %% "scala-bcrypt" % "4.1", "com.github.t3hnar" %% "scala-bcrypt" % "4.1",
"org.ini4j" % "ini4j" % "0.5.4",
"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" "com.typesafe.akka" %% "akka-stream" % "2.6.5",
"io.getquill" %% "quill-jasync-postgres" % "3.5.2",
"org.flywaydb" % "flyway-core" % "6.5.0",
"org.postgresql" % "postgresql" % "42.2.14",
"com.typesafe" % "config" % "1.4.0",
"com.github.pureconfig" %% "pureconfig" % "0.13.0",
"com.beachape" %% "enumeratum" % "1.6.1",
"joda-time" % "joda-time" % "2.10.6",
"commons-io" % "commons-io" % "2.6"
) )
) )
@ -61,63 +86,58 @@ lazy val pscryptoSettings = Seq(
lazy val psloginPackSettings = Seq( lazy val psloginPackSettings = Seq(
packMain := Map("ps-login" -> "PsLogin"), packMain := Map("ps-login" -> "PsLogin"),
packArchivePrefix := "pslogin", packArchivePrefix := "pslogin",
packExtraClasspath := Map("ps-login" -> Seq("${PROG_HOME}/pscrypto-lib", packExtraClasspath := Map("ps-login" -> Seq("${PROG_HOME}/pscrypto-lib", "${PROG_HOME}/config")),
"${PROG_HOME}/config")),
packResourceDir += (baseDirectory.value / "pscrypto-lib" -> "pscrypto-lib"), packResourceDir += (baseDirectory.value / "pscrypto-lib" -> "pscrypto-lib"),
packResourceDir += (baseDirectory.value / "config" -> "config"), packResourceDir += (baseDirectory.value / "config" -> "config"),
packResourceDir += (baseDirectory.value / "pslogin/src/main/resources" -> "config") packResourceDir += (baseDirectory.value / "pslogin/src/main/resources" -> "config")
) )
lazy val root = (project in file(".")). lazy val root = (project in file("."))
configs(QuietTest). .configs(QuietTest)
enablePlugins(PackPlugin). .enablePlugins(PackPlugin)
settings(commonSettings: _*). .settings(commonSettings: _*)
settings(psloginPackSettings: _*). .settings(psloginPackSettings: _*)
enablePlugins(ScalaUnidocPlugin). .enablePlugins(ScalaUnidocPlugin)
aggregate(pslogin, common). .aggregate(pslogin, common)
dependsOn(pslogin, common)
lazy val pslogin = (project in file("pslogin")). lazy val pslogin = (project in file("pslogin"))
configs(QuietTest). .configs(QuietTest)
settings(commonSettings: _*). .settings(commonSettings: _*)
settings( .settings(
name := "pslogin", name := "pslogin",
// ActorTests have specific timing requirements and will be flaky if run in parallel // ActorTests have specific timing requirements and will be flaky if run in parallel
parallelExecution in Test := false, parallelExecution in Test := false,
// TODO(chord): remove exclusion when WorldSessionActor is refactored: https://github.com/psforever/PSF-LoginServer/issues/279 // TODO(chord): remove exclusion when WorldSessionActor is refactored: https://github.com/psforever/PSF-LoginServer/issues/279
coverageExcludedPackages := "WorldSessionActor.*;zonemaps.*", coverageExcludedPackages := "net.psforever.pslogin.WorldSessionActor.*;net.psforever.pslogin.zonemaps.*",
// Copy all tests from Test -> QuietTest (we're only changing the run options) // Copy all tests from Test -> QuietTest (we're only changing the run options)
inConfig(QuietTest)(Defaults.testTasks) inConfig(QuietTest)(Defaults.testTasks)
). )
settings(pscryptoSettings: _*). .settings(pscryptoSettings: _*)
dependsOn(common) .dependsOn(common)
lazy val common = (project in file("common")). lazy val common = (project in file("common"))
configs(QuietTest). .configs(QuietTest)
settings(commonSettings: _*). .settings(commonSettings: _*)
settings( .settings(
name := "common", name := "common",
// Copy all tests from Test -> QuietTest (we're only changing the run options) // Copy all tests from Test -> QuietTest (we're only changing the run options)
inConfig(QuietTest)(Defaults.testTasks) inConfig(QuietTest)(Defaults.testTasks)
). )
settings(pscryptoSettings: _*) .settings(pscryptoSettings: _*)
lazy val decodePackets = (project in file("tools/decode-packets")). lazy val decodePackets = (project in file("tools/decode-packets"))
enablePlugins(PackPlugin). .enablePlugins(PackPlugin)
settings(commonSettings: _*). .settings(commonSettings: _*)
settings(decodePacketsPackSettings: _*). .settings(decodePacketsPackSettings: _*)
settings( .settings(
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"org.scala-lang.modules" %% "scala-parallel-collections" % "0.2.0", "org.scala-lang.modules" %% "scala-parallel-collections" % "0.2.0",
"com.github.scopt" %% "scopt" % "4.0.0-RC2", "com.github.scopt" %% "scopt" % "4.0.0-RC2"
"commons-io" % "commons-io" % "2.6"
) )
). )
dependsOn(common) .dependsOn(common)
lazy val decodePacketsPackSettings = Seq( lazy val decodePacketsPackSettings = Seq(packMain := Map("psf-decode-packets" -> "DecodePackets"))
packMain := Map("psf-decode-packets" -> "DecodePackets"),
)
// Special test configuration for really quiet tests (used in CI) // Special test configuration for really quiet tests (used in CI)
lazy val QuietTest = config("quiet") extend (Test) lazy val QuietTest = config("quiet") extend (Test)

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
// Taken from http://code.hootsuite.com/logging-contextual-info-in-an-asynchronous-scala-application/
package akka.actor package akka.actor
// Taken from https://medium.com/hootsuite-engineering/logging-contextual-info-in-an-asynchronous-scala-application-8ea33bfec9b3
import akka.util.Timeout import akka.util.Timeout
import org.slf4j.MDC import org.slf4j.MDC

View file

@ -0,0 +1,69 @@
package akka.actor
// Taken from https://medium.com/hootsuite-engineering/logging-contextual-info-in-an-asynchronous-scala-application-8ea33bfec9b3
import org.slf4j.MDC
import scala.concurrent.ExecutionContext
trait MDCPropagatingExecutionContext extends ExecutionContext {
// name the self-type "self" so we can refer to it inside the nested class
self =>
override def prepare(): ExecutionContext =
new ExecutionContext {
// Save the call-site MDC state
val context = MDC.getCopyOfContextMap
def execute(r: Runnable): Unit =
self.execute(new Runnable {
def run(): Unit = {
// Save the existing execution-site MDC state
val oldContext = MDC.getCopyOfContextMap
try {
// Set the call-site MDC state into the execution-site MDC
if (context != null)
MDC.setContextMap(context)
else
MDC.clear()
r.run()
} finally {
// Restore the existing execution-site MDC state
if (oldContext != null)
MDC.setContextMap(oldContext)
else
MDC.clear()
}
}
})
def reportFailure(t: Throwable): Unit = self.reportFailure(t)
}
}
object MDCPropagatingExecutionContext {
object Implicits {
// Convenience wrapper around the Scala global ExecutionContext so you can just do:
// import MDCPropagatingExecutionContext.Implicits.global
implicit lazy val global = MDCPropagatingExecutionContextWrapper(ExecutionContext.Implicits.global)
}
}
/**
* Wrapper around an existing ExecutionContext that makes it propagate MDC information.
*/
class MDCPropagatingExecutionContextWrapper(wrapped: ExecutionContext)
extends ExecutionContext
with MDCPropagatingExecutionContext {
override def execute(r: Runnable): Unit = wrapped.execute(r)
override def reportFailure(t: Throwable): Unit = wrapped.reportFailure(t)
}
object MDCPropagatingExecutionContextWrapper {
def apply(wrapped: ExecutionContext): MDCPropagatingExecutionContextWrapper = {
new MDCPropagatingExecutionContextWrapper(wrapped)
}
}

View file

@ -12,7 +12,9 @@ trait IFinalizable {
def assertNotClosed = { def assertNotClosed = {
if (closed) if (closed)
throw new ObjectFinalizedException(this.getClass.getCanonicalName + ": already finalized. Cannot interact with object") throw new ObjectFinalizedException(
this.getClass.getCanonicalName + ": already finalized. Cannot interact with object"
)
} }
override def finalize() = { override def finalize() = {

View file

@ -1,240 +0,0 @@
// Copyright (c) 2019 PSForever
package net.psforever.config
import org.ini4j
import scala.reflect.ClassTag
import scala.reflect.runtime.universe.TypeTag
import scala.annotation.implicitNotFound
import scala.concurrent.duration._
import scala.collection.mutable.Map
case class ConfigValueMapper[T](name: String)(f: (String => Option[T])) {
def apply(t: String): Option[T] = f(t)
}
object ConfigValueMapper {
import scala.language.implicitConversions
implicit val toInt : ConfigValueMapper[Int] = ConfigValueMapper[Int]("toInt") { e =>
try {
Some(e.toInt)
} catch {
case e: Exception => None
}
}
// TypeTag is necessary to be able to retrieve an instance of the Enum class
// at runtime as it is usually erased at runtime. This is low cost as its only
// used during the config parsing
implicit def toEnum[E <: Enumeration#Value](implicit m: TypeTag[E]) : ConfigValueMapper[E] = ConfigValueMapper[E]("toEnum") { e =>
try {
Some(EnumReflector.withName[E](e))
} catch {
case e: Exception => None
}
}
implicit val toBool : ConfigValueMapper[Boolean] = ConfigValueMapper[Boolean]("toBool") { e =>
if (e == "yes") {
Some(true)
} else if (e == "no") {
Some(false)
} else {
None
}
}
implicit val toFloat : ConfigValueMapper[Float] = ConfigValueMapper[Float]("toFloat") { e =>
try {
Some(e.toFloat)
} catch {
case e: Exception => None
}
}
implicit val toDuration : ConfigValueMapper[Duration] = ConfigValueMapper[Duration]("toDuration") { e =>
try {
Some(Duration(e))
} catch {
case e: Exception => None
}
}
implicit val toStr : ConfigValueMapper[String] = ConfigValueMapper[String]("toString") { e =>
Some(e)
}
}
sealed trait ConfigEntry {
type Value
val key : String
val default : Value
def getType : String
val constraints : Seq[Constraint[Value]]
def read(v: String): Option[Value]
}
final case class ConfigEntryString(key: String, default : String, constraints : Constraint[String]*) extends ConfigEntry {
type Value = String
def getType = "String"
def read(v : String) = ConfigValueMapper.toStr(v)
}
final case class ConfigEntryInt(key: String, default : Int, constraints : Constraint[Int]*) extends ConfigEntry {
type Value = Int
def getType = "Int"
def read(v : String) = ConfigValueMapper.toInt(v)
}
final case class ConfigEntryBool(key: String, default : Boolean, constraints : Constraint[Boolean]*) extends ConfigEntry {
type Value = Boolean
def getType = "Bool"
def read(v : String) = ConfigValueMapper.toBool(v)
}
final case class ConfigEntryEnum[E <: Enumeration](key: String, default : E#Value)(implicit m : TypeTag[E#Value], implicit val m2 : TypeTag[E#ValueSet]) extends ConfigEntry {
type Value = E#Value
val constraints : Seq[Constraint[E#Value]] = Seq()
def getType = EnumReflector.values[E#ValueSet](m2).mkString(", ")
def read(v : String) = ConfigValueMapper.toEnum[E#Value](m)(v)
}
final case class ConfigEntryFloat(key: String, default : Float, constraints : Constraint[Float]*) extends ConfigEntry {
type Value = Float
def getType = "Float"
def read(v : String) = ConfigValueMapper.toFloat(v)
}
final case class ConfigEntryTime(key: String, default : Duration, constraints : Constraint[Duration]*) extends ConfigEntry {
type Value = Duration
def getType = "Time"
def read(v : String) = ConfigValueMapper.toDuration(v)
}
case class ConfigSection(name: String, entries: ConfigEntry*)
@implicitNotFound("Nothing was inferred")
sealed trait ConfigTypeRequired[-T]
object ConfigTypeRequired {
implicit object cfgTypeRequired extends ConfigTypeRequired[Any]
//We do not want Nothing to be inferred, so make an ambiguous implicit
implicit object `\n The Get[T] call needs a type T matching the corresponding ConfigEntry` extends ConfigTypeRequired[Nothing]
}
trait ConfigParser {
protected var config_map : Map[String, Any] = Map()
protected val config_template : Seq[ConfigSection]
// Misuse of this function can lead to run time exceptions when the types don't match
// ClassTag is needed due to type erasure on T
// https://dzone.com/articles/scala-classtag-a-simple-use-case
def Get[T : ConfigTypeRequired](key : String)(implicit m: ClassTag[T]) : T = {
config_map.get(key) match {
case Some(value : T) => value
case None =>
throw new NoSuchElementException(s"Config key '${key}' not found")
case Some(value : Any) =>
throw new ClassCastException(s"Incorrect type T = ${m.runtimeClass.getSimpleName} passed to Get[T]: needed ${value.getClass.getSimpleName}")
}
}
def Load(filename : String) : ValidationResult = {
var map : Map[String, Any] = scala.collection.mutable.Map()
LoadMap(filename, map) match {
case i : Invalid =>
i
case Valid =>
ReplaceConfig(map)
// run post-parse validation only if we successfully parsed
postParseChecks match {
case i : Invalid =>
i
case Valid =>
Valid
}
}
}
def LoadMap(filename : String, map : Map[String, Any]) : ValidationResult = {
val ini = new org.ini4j.Ini()
try {
ini.load(new java.io.File(filename))
} catch {
case e : org.ini4j.InvalidFileFormatException =>
return Invalid(e.getMessage)
case e : java.io.FileNotFoundException =>
return Invalid(e.getMessage)
}
val result : Seq[ValidationResult] = config_template.map { section =>
val sectionIni = ini.get(section.name)
if (sectionIni == null)
Seq(Invalid("section.missing", section.name))
else
section.entries.map(parseSection(sectionIni, _, map))
}.reduceLeft((x, y) => x ++ y)
val errors : Seq[Invalid] = result.collect { case iv : Invalid => iv }
if (errors.length > 0)
errors.reduceLeft((x, y) => x ++ y)
else
Valid
}
def ReplaceConfig(map : Map[String, Any]) : Unit = {
config_map = map
}
def GetRawConfig : Map[String, Any] = {
config_map
}
def FormatErrors(invalidResult : Invalid) : Seq[String] = {
var count = 0;
invalidResult.errors.map((error) => {
var message = error.message;
if (error.args.length > 0)
message += " ("+error.args(0)+")"
count += 1;
s"Error ${count}: ${message}"
});
}
protected def postParseChecks : ValidationResult = {
Valid
}
protected def parseSection(sectionIni : org.ini4j.Profile.Section, entry : ConfigEntry, map : Map[String, Any]) : ValidationResult = {
var rawValue = sectionIni.get(entry.key, 0)
val full_key : String = sectionIni.getName + "." + entry.key
val value = if (rawValue == null) {
println(s"config warning: missing key '${entry.key}', using default value '${entry.default}'")
entry.default
} else {
rawValue = rawValue.stripPrefix("\"").stripSuffix("\"")
entry.read(rawValue) match {
case Some(v) => v
case None => return Invalid(ValidationError(String.format("%s: value format error (expected: %s)", full_key, entry.getType)))
}
}
map += (full_key -> value)
ParameterValidator(entry.constraints, Some(value)) match {
case v @ Valid => v
case i @ Invalid(errors) => {
Invalid(errors.map(x => ValidationError(x.messages.map(full_key + ": " + _), x.args: _*)))
}
}
}
}

View file

@ -1,290 +0,0 @@
// Copyright (c) 2019 PSForever
// Lifted from https://raw.githubusercontent.com/playframework/playframework/2.7.x/core/play/src/main/scala/play/api/data/validation/Validation.scala
package net.psforever.config
/**
* A form constraint.
*
* @tparam T type of values handled by this constraint
* @param name the constraint name, to be displayed to final user
* @param args the message arguments, to format the constraint name
* @param f the validation function
*/
case class Constraint[-T](name: Option[String], args: Seq[Any])(f: (T => ValidationResult)) {
/**
* Run the constraint validation.
*
* @param t the value to validate
* @return the validation result
*/
def apply(t: T): ValidationResult = f(t)
}
/**
* This object provides helpers for creating `Constraint` values.
*
* For example:
* {{{
* val negative = Constraint[Int] {
* case i if i < 0 => Valid
* case _ => Invalid("Must be a negative number.")
* }
* }}}
*/
object Constraint {
/**
* Creates a new anonymous constraint from a validation function.
*
* @param f the validation function
* @return a constraint
*/
def apply[T](f: (T => ValidationResult)): Constraint[T] = apply(None, Nil)(f)
/**
* Creates a new named constraint from a validation function.
*
* @param name the constraint name
* @param args the constraint arguments, used to format the constraint name
* @param f the validation function
* @return a constraint
*/
def apply[T](name: String, args: Any*)(f: (T => ValidationResult)): Constraint[T] = apply(Some(name), args.toSeq)(f)
}
/**
* Defines a set of built-in constraints.
*/
object Constraints extends Constraints
/**
* Defines a set of built-in constraints.
*
* @define emailAddressDoc Defines an emailAddress constraint for `String` values which will validate email addresses.
*
* '''name'''[constraint.email]
* '''error'''[error.email]
*
* @define nonEmptyDoc Defines a required constraint for `String` values, i.e. one in which empty strings are invalid.
*
* '''name'''[constraint.required]
* '''error'''[error.required]
*/
trait Constraints {
private val emailRegex =
"""^[a-zA-Z0-9\.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$""".r
/**
* $emailAddressDoc
*/
def emailAddress(errorMessage: String = "error.email"): Constraint[String] = Constraint[String]("constraint.email") {
e =>
if (e == null) Invalid(ValidationError(errorMessage))
else if (e.trim.isEmpty) Invalid(ValidationError(errorMessage))
else
emailRegex
.findFirstMatchIn(e)
.map(_ => Valid)
.getOrElse(Invalid(ValidationError(errorMessage)))
}
/**
* $emailAddressDoc
*
*/
def emailAddress: Constraint[String] = emailAddress()
/**
* $nonEmptyDoc
*/
def nonEmpty(errorMessage: String = "error.required"): Constraint[String] =
Constraint[String]("constraint.required") { o =>
if (o == null) Invalid(ValidationError(errorMessage))
else if (o.trim.isEmpty) Invalid(ValidationError(errorMessage))
else Valid
}
/**
* $nonEmptyDoc
*
*/
def nonEmpty: Constraint[String] = nonEmpty()
/**
* Defines a minimum value for `Ordered` values, by default the value must be greater than or equal to the constraint parameter
*
* '''name'''[constraint.min(minValue)]
* '''error'''[error.min(minValue)] or [error.min.strict(minValue)]
*/
def min[T](
minValue: T,
strict: Boolean = false,
errorMessage: String = "error.min",
strictErrorMessage: String = "error.min.strict"
)(implicit ordering: scala.math.Ordering[T]): Constraint[T] = Constraint[T]("constraint.min", minValue) { o =>
(ordering.compare(o, minValue).sign, strict) match {
case (1, _) | (0, false) => Valid
case (_, false) => Invalid(ValidationError(errorMessage, minValue))
case (_, true) => Invalid(ValidationError(strictErrorMessage, minValue))
}
}
/**
* Defines a maximum value for `Ordered` values, by default the value must be less than or equal to the constraint parameter
*
* '''name'''[constraint.max(maxValue)]
* '''error'''[error.max(maxValue)] or [error.max.strict(maxValue)]
*/
def max[T](
maxValue: T,
strict: Boolean = false,
errorMessage: String = "error.max",
strictErrorMessage: String = "error.max.strict"
)(implicit ordering: scala.math.Ordering[T]): Constraint[T] = Constraint[T]("constraint.max", maxValue) { o =>
(ordering.compare(o, maxValue).sign, strict) match {
case (-1, _) | (0, false) => Valid
case (_, false) => Invalid(ValidationError(errorMessage, maxValue))
case (_, true) => Invalid(ValidationError(strictErrorMessage, maxValue))
}
}
/**
* Defines a minimum length constraint for `String` values, i.e. the strings length must be greater than or equal to the constraint parameter
*
* '''name'''[constraint.minLength(length)]
* '''error'''[error.minLength(length)]
*/
def minLength(length: Int, errorMessage: String = "error.minLength"): Constraint[String] =
Constraint[String]("constraint.minLength", length) { o =>
require(length >= 0, "string minLength must not be negative")
if (o == null) Invalid(ValidationError(errorMessage, length))
else if (o.size >= length) Valid
else Invalid(ValidationError(errorMessage, length))
}
/**
* Defines a maximum length constraint for `String` values, i.e. the strings length must be less than or equal to the constraint parameter
*
* '''name'''[constraint.maxLength(length)]
* '''error'''[error.maxLength(length)]
*/
def maxLength(length: Int, errorMessage: String = "error.maxLength"): Constraint[String] =
Constraint[String]("constraint.maxLength", length) { o =>
require(length >= 0, "string maxLength must not be negative")
if (o == null) Invalid(ValidationError(errorMessage, length))
else if (o.size <= length) Valid
else Invalid(ValidationError(errorMessage, length))
}
/**
* Defines a regular expression constraint for `String` values, i.e. the string must match the regular expression pattern
*
* '''name'''[constraint.pattern(regex)] or defined by the name parameter.
* '''error'''[error.pattern(regex)] or defined by the error parameter.
*/
def pattern(
regex: => scala.util.matching.Regex,
name: String = "constraint.pattern",
error: String = "error.pattern"
): Constraint[String] = Constraint[String](name, () => regex) { o =>
require(regex != null, "regex must not be null")
require(name != null, "name must not be null")
require(error != null, "error must not be null")
if (o == null) Invalid(ValidationError(error, regex))
else regex.unapplySeq(o).map(_ => Valid).getOrElse(Invalid(ValidationError(error, name)))
}
}
/**
* A validation result.
*/
sealed trait ValidationResult
/**
* Validation was a success.
*/
case object Valid extends ValidationResult
/**
* Validation was a failure.
*
* @param errors the resulting errors
*/
case class Invalid(errors: Seq[ValidationError]) extends ValidationResult {
/**
* Combines these validation errors with another validation failure.
*
* @param other validation failure
* @return a new merged `Invalid`
*/
def ++(other: Invalid): Invalid = Invalid(this.errors ++ other.errors)
}
/**
* This object provides helper methods to construct `Invalid` values.
*/
object Invalid {
/**
* Creates an `Invalid` value with a single error.
*
* @param error the validation error
* @return an `Invalid` value
*/
def apply(error: ValidationError): Invalid = Invalid(Seq(error))
/**
* Creates an `Invalid` value with a single error.
*
* @param error the validation error message
* @param args the validation error message arguments
* @return an `Invalid` value
*/
def apply(error: String, args: Any*): Invalid = Invalid(Seq(ValidationError(error, args: _*)))
}
object ParameterValidator {
def apply[T](constraints: Iterable[Constraint[T]], optionalParam: Option[T]*) =
optionalParam.flatMap {
_.map { param =>
constraints.flatMap {
_(param) match {
case i: Invalid => Some(i)
case _ => None
}
}
}
}.flatten match {
case Nil => Valid
case invalids =>
invalids.reduceLeft { (a, b) =>
a ++ b
}
}
}
/**
* A validation error.
*
* @param messages the error message, if more then one message is passed it will use the last one
* @param args the error message arguments
*/
case class ValidationError(messages: Seq[String], args: Any*) {
lazy val message = messages.last
}
object ValidationError {
def apply(message: String, args: Any*) = new ValidationError(Seq(message), args: _*)
}

View file

@ -1,54 +0,0 @@
// Copyright (c) 2019 PSForever
package net.psforever.config
import scala.reflect.runtime.universe._
/**
* Scala [[Enumeration]] helpers implementing Scala versions of
* Java's [[java.lang.Enum.valueOf(Class[Enum], String)]].
* @author Dmitriy Yefremov (http://yefremov.net/blog/scala-enum-by-name/)
*/
object EnumReflector {
private val mirror: Mirror = runtimeMirror(getClass.getClassLoader)
/**
* Returns a value of the specified enumeration with the given name.
* @param name value name
* @tparam T enumeration type
* @return enumeration value, see [[scala.Enumeration.withName(String)]]
*/
def withName[T <: Enumeration#Value: TypeTag](name: String): T = {
typeOf[T] match {
case valueType @ TypeRef(enumType, _, _) =>
val methodSymbol = factoryMethodSymbol(enumType, "withName")
val moduleSymbol = enumType.termSymbol.asModule
reflect(moduleSymbol, methodSymbol)(name).asInstanceOf[T]
}
}
/**
* Returns the set of values of an enumeration
* @tparam T enumeration type
* @return possible enumeration values, see [[scala.Enumeration.values()]]
*/
def values[T <: Enumeration#ValueSet: TypeTag]: T = {
typeOf[T] match {
case valueType @ TypeRef(enumType, _, _) =>
val methodSymbol = factoryMethodSymbol(enumType, "values")
val moduleSymbol = enumType.termSymbol.asModule
reflect(moduleSymbol, methodSymbol)().asInstanceOf[T]
}
}
private def factoryMethodSymbol(enumType: Type, name : String): MethodSymbol = {
enumType.member(TermName(name)).asMethod
}
private def reflect(module: ModuleSymbol, method: MethodSymbol)(args: Any*): Any = {
val moduleMirror = mirror.reflectModule(module)
val instanceMirror = mirror.reflect(moduleMirror.instance)
instanceMirror.reflectMethod(method)(args:_*)
}
}

View file

@ -4,7 +4,7 @@ package net.psforever.crypto
import com.sun.jna.ptr.IntByReference import com.sun.jna.ptr.IntByReference
import net.psforever.IFinalizable import net.psforever.IFinalizable
import sna.Library import sna.Library
import com.sun.jna.{NativeLibrary, Pointer} import com.sun.jna.Pointer
import scodec.bits.ByteVector import scodec.bits.ByteVector
object CryptoInterface { object CryptoInterface {
@ -14,18 +14,18 @@ object CryptoInterface {
final val PSCRYPTO_VERSION_MINOR = 1 final val PSCRYPTO_VERSION_MINOR = 1
/** /**
NOTE: this is a single, global shared library for the entire server's crypto needs * NOTE: this is a single, global shared library for the entire server's crypto needs
*
Unfortunately, access to this object didn't used to be synchronized. I noticed that * Unfortunately, access to this object didn't used to be synchronized. I noticed that
tests for this module were hanging ("arrive at a shared secret" & "must fail to agree on * tests for this module were hanging ("arrive at a shared secret" & "must fail to agree on
a secret..."). This heisenbug was responsible for failed Travis test runs and developer * a secret..."). This heisenbug was responsible for failed Travis test runs and developer
issues as well. Using Windows minidumps, I tracked the issue to a single thread deep in * issues as well. Using Windows minidumps, I tracked the issue to a single thread deep in
pscrypto.dll. It appeared to be executing an EB FE instruction (on Intel x86 this is * pscrypto.dll. It appeared to be executing an EB FE instruction (on Intel x86 this is
`jmp $-2` or jump to self), which is an infinite loop. The stack trace made little to no * `jmp $-2` or jump to self), which is an infinite loop. The stack trace made little to no
sense and after banging my head on the wall for many hours, I assumed that something deep * sense and after banging my head on the wall for many hours, I assumed that something deep
in CryptoPP, the libgcc libraries, or MSVC++ was the cause (or myself). Now all access to * in CryptoPP, the libgcc libraries, or MSVC++ was the cause (or myself). Now all access to
pscrypto functions that allocate and deallocate memory (DH_Start, RC5_Init) are synchronized. * pscrypto functions that allocate and deallocate memory (DH_Start, RC5_Init) are synchronized.
This *appears* to have fixed the problem. * This *appears* to have fixed the problem.
*/ */
final val psLib = new Library(libName) final val psLib = new Library(libName)
@ -61,8 +61,10 @@ object CryptoInterface {
psLib.PSCrypto_Get_Version(libraryMajor, libraryMinor)[Unit] psLib.PSCrypto_Get_Version(libraryMajor, libraryMinor)[Unit]
if (!psLib.PSCrypto_Init(PSCRYPTO_VERSION_MAJOR, PSCRYPTO_VERSION_MINOR)[Boolean]) { if (!psLib.PSCrypto_Init(PSCRYPTO_VERSION_MAJOR, PSCRYPTO_VERSION_MINOR)[Boolean]) {
throw new IllegalArgumentException(s"Invalid PSCrypto library version ${libraryMajor.getValue}.${libraryMinor.getValue}. Expected " + throw new IllegalArgumentException(
s"$PSCRYPTO_VERSION_MAJOR.$PSCRYPTO_VERSION_MINOR") s"Invalid PSCrypto library version ${libraryMajor.getValue}.${libraryMinor.getValue}. Expected " +
s"$PSCRYPTO_VERSION_MAJOR.$PSCRYPTO_VERSION_MINOR"
)
} }
} }
@ -213,8 +215,7 @@ object CryptoInterface {
} }
} }
class CryptoState(val decryptionKey : ByteVector, class CryptoState(val decryptionKey: ByteVector, val encryptionKey: ByteVector) extends IFinalizable {
val encryptionKey : ByteVector) extends IFinalizable {
// Note that the keys must be returned as primitive Arrays for JNA to work // Note that the keys must be returned as primitive Arrays for JNA to work
var encCryptoHandle: Pointer = Pointer.NULL var encCryptoHandle: Pointer = Pointer.NULL
var decCryptoHandle: Pointer = Pointer.NULL var decCryptoHandle: Pointer = Pointer.NULL
@ -267,10 +268,13 @@ object CryptoInterface {
} }
} }
class CryptoStateWithMAC(decryptionKey : ByteVector, class CryptoStateWithMAC(
decryptionKey: ByteVector,
encryptionKey: ByteVector, encryptionKey: ByteVector,
val decryptionMACKey: ByteVector, val decryptionMACKey: ByteVector,
val encryptionMACKey : ByteVector) extends CryptoState(decryptionKey, encryptionKey) { val encryptionMACKey: ByteVector
) extends CryptoState(decryptionKey, encryptionKey) {
/** /**
* Performs a MAC operation over the message. Used when encrypting packets * Performs a MAC operation over the message. Used when encrypting packets
* *

View file

@ -1,10 +1,11 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.newcodecs package net.psforever.newcodecs
import scodec.{ Codec, SizeBound } import scodec.Codec
import scodec.bits.BitVector import scodec.bits.BitVector
private[newcodecs] final class BinaryChoiceCodec[A](choice: Boolean, codec_true: => Codec[A], codec_false: => Codec[A]) extends Codec[A] { private[newcodecs] final class BinaryChoiceCodec[A](choice: Boolean, codec_true: => Codec[A], codec_false: => Codec[A])
extends Codec[A] {
private lazy val evaluatedCodec_true = codec_true private lazy val evaluatedCodec_true = codec_true
@ -26,6 +27,7 @@ private[newcodecs] final class BinaryChoiceCodec[A](choice: Boolean, codec_true:
evaluatedCodec_false.decode(buffer) evaluatedCodec_false.decode(buffer)
} }
override def toString = if(choice) s"binarychoice(true, $evaluatedCodec_true, ?)" else "binarychoice(false, ?, $evaluatedCodec_false)" override def toString =
if (choice) s"binarychoice(true, $evaluatedCodec_true, ?)" else "binarychoice(false, ?, $evaluatedCodec_false)"
} }

View file

@ -4,14 +4,17 @@ package net.psforever.newcodecs
import scodec._ import scodec._
import scodec.bits.BitVector import scodec.bits.BitVector
final class PrefixedVectorCodec[A](firstCodec: Codec[A], codec: Codec[A], limit: Option[Int] = None) extends Codec[Vector[A]] { final class PrefixedVectorCodec[A](firstCodec: Codec[A], codec: Codec[A], limit: Option[Int] = None)
extends Codec[Vector[A]] {
def sizeBound = limit match { def sizeBound =
limit match {
case None => SizeBound.unknown case None => SizeBound.unknown
case Some(lim) => codec.sizeBound * lim.toLong case Some(lim) => codec.sizeBound * lim.toLong
} }
def encode(vector: Vector[A]) = Encoder.encodeSeq(firstCodec)(vector.slice(0,1)).map { bits => def encode(vector: Vector[A]) =
Encoder.encodeSeq(firstCodec)(vector.slice(0, 1)).map { bits =>
if (vector.length > 1) if (vector.length > 1)
bits ++ (Encoder.encodeSeq(codec)(vector.tail) getOrElse BitVector.empty) bits ++ (Encoder.encodeSeq(codec)(vector.tail) getOrElse BitVector.empty)
else else

View file

@ -42,7 +42,9 @@ final class QuantizedDoubleCodec(min: Double, max: Double, bits: Int) extends Co
override def decode(buffer: BitVector) = { override def decode(buffer: BitVector) = {
if (buffer.sizeGreaterThanOrEqual(bitsL)) if (buffer.sizeGreaterThanOrEqual(bitsL))
Attempt.successful(DecodeResult(UnquantizeDouble(buffer.take(bitsL).toInt(false, ByteOrdering.LittleEndian)), buffer.drop(bitsL))) Attempt.successful(
DecodeResult(UnquantizeDouble(buffer.take(bitsL).toInt(false, ByteOrdering.LittleEndian)), buffer.drop(bitsL))
)
else else
Attempt.failure(Err.insufficientBits(bitsL, buffer.size)) Attempt.failure(Err.insufficientBits(bitsL, buffer.size))
} }

View file

@ -2,23 +2,28 @@
package net.psforever.newcodecs package net.psforever.newcodecs
import scodec.Attempt import scodec.Attempt
import scodec.Attempt.{Failure, Successful}
import scodec._ import scodec._
import scodec.bits.BitVector
package object newcodecs { package object newcodecs {
def q_double(min: Double, max: Double, bits: Int): Codec[Double] = new QuantizedDoubleCodec(min, max, bits) def q_double(min: Double, max: Double, bits: Int): Codec[Double] = new QuantizedDoubleCodec(min, max, bits)
def q_float(min : Double, max : Double, bits : Int): Codec[Float] = q_double(min, max, bits).narrow(v => Attempt.successful(v.toFloat), _.toDouble) def q_float(min: Double, max: Double, bits: Int): Codec[Float] =
q_double(min, max, bits).narrow(v => Attempt.successful(v.toFloat), _.toDouble)
def binary_choice[A](choice: Boolean, codec_true: => Codec[A], codec_false: => Codec[A]): Codec[A] = new BinaryChoiceCodec(choice, codec_true, codec_false) def binary_choice[A](choice: Boolean, codec_true: => Codec[A], codec_false: => Codec[A]): Codec[A] =
new BinaryChoiceCodec(choice, codec_true, codec_false)
def prefixedVectorOfN[A](countCodec: Codec[Int], firstValueCodec: Codec[A], valueCodec: Codec[A]): Codec[Vector[A]] = def prefixedVectorOfN[A](countCodec: Codec[Int], firstValueCodec: Codec[A], valueCodec: Codec[A]): Codec[Vector[A]] =
countCodec. countCodec
flatZip { count => new PrefixedVectorCodec(firstValueCodec, valueCodec, Some(count)) }. .flatZip { count => new PrefixedVectorCodec(firstValueCodec, valueCodec, Some(count)) }
narrow[Vector[A]]({ case (cnt, xs) => .narrow[Vector[A]](
{
case (cnt, xs) =>
if (xs.size == cnt) Attempt.successful(xs) if (xs.size == cnt) Attempt.successful(xs)
else Attempt.failure(Err(s"Insufficient number of elements: decoded ${xs.size} but should have decoded $cnt")) else
}, xs => (xs.size, xs)). Attempt.failure(Err(s"Insufficient number of elements: decoded ${xs.size} but should have decoded $cnt"))
withToString(s"vectorOfN($countCodec, $valueCodec)") },
xs => (xs.size, xs)
)
.withToString(s"vectorOfN($countCodec, $valueCodec)")
} }

View file

@ -5,10 +5,9 @@ import net.psforever.objects.definition.AmmoBoxDefinition
import net.psforever.objects.equipment.{Ammo, Equipment} import net.psforever.objects.equipment.{Ammo, Equipment}
import net.psforever.types.PlanetSideEmpire import net.psforever.types.PlanetSideEmpire
class AmmoBox(private val ammoDef : AmmoBoxDefinition, class AmmoBox(private val ammoDef: AmmoBoxDefinition, cap: Option[Int] = None) extends Equipment {
cap : Option[Int] = None private var capacity = if (cap.isDefined) { AmmoBox.limitCapacity(cap.get, 1) }
) extends Equipment { else { FullCapacity }
private var capacity = if(cap.isDefined) { AmmoBox.limitCapacity(cap.get, 1) } else { FullCapacity }
def AmmoType: Ammo.Value = ammoDef.AmmoType def AmmoType: Ammo.Value = ammoDef.AmmoType
@ -54,12 +53,12 @@ object AmmoBox {
val maxCap: Int = ammoDef.Capacity val maxCap: Int = ammoDef.Capacity
val splitCap: Int = boxCap / maxCap val splitCap: Int = boxCap / maxCap
box.Capacity = math.min(box.Capacity, maxCap) box.Capacity = math.min(box.Capacity, maxCap)
val list : List[AmmoBox] = if(splitCap == 0) { Nil } else { box +: List.fill(splitCap - 1)(new AmmoBox(ammoDef)) } val list: List[AmmoBox] = if (splitCap == 0) { Nil }
else { box +: List.fill(splitCap - 1)(new AmmoBox(ammoDef)) }
val leftover = boxCap - maxCap * splitCap val leftover = boxCap - maxCap * splitCap
if (leftover > 0) { if (leftover > 0) {
list :+ AmmoBox(ammoDef, leftover) list :+ AmmoBox(ammoDef, leftover)
} } else {
else {
list list
} }
} }

View file

@ -10,16 +10,28 @@ import net.psforever.types._
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.collection.mutable 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) { 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 */ /** char_id, Character ID; a unique identifier corresponding to a database table row index */
/** Battle Experience Points */ /** Battle Experience Points */
private var bep: Long = 0 private var bep: Long = 0
/** Command Experience Points */ /** Command Experience Points */
private var cep: Long = 0 private var cep: Long = 0
/** Cosmetics * */ /** Cosmetics * */
private var pStyle: Option[Cosmetics] = None private var pStyle: Option[Cosmetics] = None
/** Certifications */ /** Certifications */
private val certs: mutable.Set[CertificationType.Value] = mutable.Set[CertificationType.Value]() private val certs: mutable.Set[CertificationType.Value] = mutable.Set[CertificationType.Value]()
/** Implants<br> /** Implants<br>
* Unlike other objects, all `ImplantSlot` objects are already built into the `Avatar`. * 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. * Additionally, implants do not have tightly-coupled "`Definition` objects" that explain a formal implant object.
@ -29,15 +41,18 @@ class Avatar(private val char_id : Long, val name : String, val faction : Planet
* @see `DetailedCharacterData.implants` * @see `DetailedCharacterData.implants`
*/ */
private val implants: Array[ImplantSlot] = Array.fill[ImplantSlot](3)(new ImplantSlot) private val implants: Array[ImplantSlot] = Array.fill[ImplantSlot](3)(new ImplantSlot)
/** Equipment Loadouts<br> /** Equipment Loadouts<br>
* 0-9 are Infantry loadouts<br> * 0-9 are Infantry loadouts<br>
* 10-14 are Vehicle loadouts * 10-14 are Vehicle loadouts
*/ */
private val equipmentLoadouts: LoadoutManager = new LoadoutManager(15) private val equipmentLoadouts: LoadoutManager = new LoadoutManager(15)
/** /**
* Squad Loadouts * Squad Loadouts
*/ */
private val squadLoadouts: LoadoutManager = new LoadoutManager(10) private val squadLoadouts: LoadoutManager = new LoadoutManager(10)
/** Locker */ /** Locker */
private val locker: LockerContainer = new LockerContainer() { private val locker: LockerContainer = new LockerContainer() {
override def toString: String = { override def toString: String = {
@ -57,24 +72,28 @@ class Avatar(private val char_id : Long, val name : String, val faction : Planet
private var lfs: Boolean = false private var lfs: Boolean = false
private var vehicleOwned: Option[PlanetSideGUID] = None private var vehicleOwned: Option[PlanetSideGUID] = None
/** key - object id<br> /** key - object id<br>
* value - time last used (ms) * value - time last used (ms)
* */ */
private var lastUsedEquipmentTimes: mutable.LongMap[Long] = mutable.LongMap[Long]() private var lastUsedEquipmentTimes: mutable.LongMap[Long] = mutable.LongMap[Long]()
/** exo-suit times are sorted by `Enumeration` order, which was determined by packet process<br> /** exo-suit times are sorted by `Enumeration` order, which was determined by packet process<br>
* key - exo-suit id<br> * key - exo-suit id<br>
* value - time last used (ms) * value - time last used (ms)
* */ */
private val lastUsedExoSuitTimes: Array[Long] = Array.fill[Long](ExoSuitType.values.size)(0L) 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> /** mechanized exo-suit times are sorted by subtype distinction, which was determined by packet process<br>
* key - subtype id<br> * key - subtype id<br>
* value - time last used (ms) * value - time last used (ms)
* */ */
private val lastUsedMaxExoSuitTimes: Array[Long] = Array.fill[Long](4)(0L) //invalid, ai, av, aa private val lastUsedMaxExoSuitTimes: Array[Long] = Array.fill[Long](4)(0L) //invalid, ai, av, aa
/** key - object id<br> /** key - object id<br>
* value - time last acquired (from a terminal) (ms) * value - time last acquired (from a terminal) (ms)
* */ */
private var lastPurchaseTimes: mutable.LongMap[Long] = mutable.LongMap[Long]() private var lastPurchaseTimes: mutable.LongMap[Long] = mutable.LongMap[Long]()
/** /**
* To reload purchase and use timers, a string representing the item must be produced. * 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. * Point directly from the object id to the object definition and get the `Name` from that definition.
@ -123,7 +142,8 @@ class Avatar(private val char_id : Long, val name : String, val faction : Planet
* @return the tye of implant * @return the tye of implant
*/ */
def Implant(slot: Int): ImplantType.Value = { def Implant(slot: Int): ImplantType.Value = {
if(-1 < slot && slot < implants.length) { implants(slot).Implant } else { ImplantType.None } if (-1 < slot && slot < implants.length) { implants(slot).Implant }
else { ImplantType.None }
} }
/** /**
@ -137,7 +157,8 @@ class Avatar(private val char_id : Long, val name : String, val faction : Planet
* @return the index of the `ImplantSlot` where the implant was installed * @return the index of the `ImplantSlot` where the implant was installed
*/ */
def InstallImplant(implant: ImplantDefinition): Option[Int] = { def InstallImplant(implant: ImplantDefinition): Option[Int] = {
implants.find({p => p.Installed.contains(implant) || p.Implant == implant.Type}) match { //try to find the installed implant implants
.find({ p => p.Installed.contains(implant) || p.Implant == implant.Type }) match { //try to find the installed implant
case None => case None =>
recursiveFindImplantInSlot(implants.iterator, ImplantType.None) match { //install in a free slot recursiveFindImplantInSlot(implants.iterator, ImplantType.None) match { //install in a free slot
case Some(slot) => case Some(slot) =>
@ -183,16 +204,18 @@ class Avatar(private val char_id : Long, val name : String, val faction : Planet
* @param index a defaulted index value representing the structure underlying the `Iterator` param * @param index a defaulted index value representing the structure underlying the `Iterator` param
* @return the index where the target implant is installed * @return the index where the target implant is installed
*/ */
@tailrec private def recursiveFindImplantInSlot(iter : Iterator[ImplantSlot], implantType : ImplantType.Value, index : Int = 0) : Option[Int] = { @tailrec private def recursiveFindImplantInSlot(
iter: Iterator[ImplantSlot],
implantType: ImplantType.Value,
index: Int = 0
): Option[Int] = {
if (!iter.hasNext) { if (!iter.hasNext) {
None None
} } else {
else {
val slot = iter.next val slot = iter.next
if (slot.Unlocked && slot.Implant == implantType) { if (slot.Unlocked && slot.Implant == implantType) {
Some(index) Some(index)
} } else {
else {
recursiveFindImplantInSlot(iter, implantType, index + 1) recursiveFindImplantInSlot(iter, implantType, index + 1)
} }
} }
@ -264,8 +287,7 @@ class Avatar(private val char_id : Long, val name : String, val faction : Planet
def GetLastUsedTime(code: ExoSuitType.Value, subtype: Int): Long = { def GetLastUsedTime(code: ExoSuitType.Value, subtype: Int): Long = {
if (code == ExoSuitType.MAX) { if (code == ExoSuitType.MAX) {
lastUsedMaxExoSuitTimes(subtype) lastUsedMaxExoSuitTimes(subtype)
} } else {
else {
GetLastUsedTime(code) GetLastUsedTime(code)
} }
} }
@ -326,7 +348,8 @@ class Avatar(private val char_id : Long, val name : String, val faction : Planet
def canEqual(other: Any): Boolean = other.isInstanceOf[Avatar] def canEqual(other: Any): Boolean = other.isInstanceOf[Avatar]
override def equals(other : Any) : Boolean = other match { override def equals(other: Any): Boolean =
other match {
case that: Avatar => case that: Avatar =>
(that canEqual this) && (that canEqual this) &&
name == that.name && name == that.name &&
@ -347,7 +370,13 @@ class Avatar(private val char_id : Long, val name : String, val faction : Planet
} }
object Avatar { object Avatar {
def apply(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : CharacterVoice.Value) : 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) new Avatar(0L, name, faction, sex, head, voice)
} }

View file

@ -20,7 +20,8 @@ import net.psforever.types.CertificationType
* indicate whether the current output product is something the player is permitted to utilize. * indicate whether the current output product is something the player is permitted to utilize.
* @param cItemDef the `ObjectDefinition` that constructs this item and maintains some of its immutable fields * @param cItemDef the `ObjectDefinition` that constructs this item and maintains some of its immutable fields
*/ */
class ConstructionItem(private val cItemDef : ConstructionItemDefinition) extends Equipment class ConstructionItem(private val cItemDef: ConstructionItemDefinition)
extends Equipment
with FireModeSwitch[ConstructionFireMode] { with FireModeSwitch[ConstructionFireMode] {
private var fireModeIndex: Int = 0 private var fireModeIndex: Int = 0
private var ammoTypeIndex: Int = 0 private var ammoTypeIndex: Int = 0

View file

@ -19,6 +19,7 @@ object Default {
//actor //actor
import akka.actor.{Actor => AkkaActor, ActorRef, ActorSystem, DeadLetter, Props} import akka.actor.{Actor => AkkaActor, ActorRef, ActorSystem, DeadLetter, Props}
/** /**
* An actor designed to wrap around `deadLetters` and redirect all normal messages to it. * An actor designed to wrap around `deadLetters` and redirect all normal messages to it.
* This measure is more to "protect" `deadLetters` than anything else. * This measure is more to "protect" `deadLetters` than anything else.
@ -31,6 +32,7 @@ object Default {
} }
} }
private var defaultRef: ActorRef = ActorRef.noSender private var defaultRef: ActorRef = ActorRef.noSender
/** /**
* Instigate the default actor. * Instigate the default actor.
* @param sys the actor universe under which this default actor will exist * @param sys the actor universe under which this default actor will exist

View file

@ -30,11 +30,21 @@ object Deployables {
DeployedItem.tank_traps -> { () => new TrapDeployable(GlobalDefinitions.tank_traps) }, DeployedItem.tank_traps -> { () => new TrapDeployable(GlobalDefinitions.tank_traps) },
DeployedItem.portable_manned_turret -> { () => new TurretDeployable(GlobalDefinitions.portable_manned_turret) }, DeployedItem.portable_manned_turret -> { () => new TurretDeployable(GlobalDefinitions.portable_manned_turret) },
DeployedItem.portable_manned_turret -> { () => new TurretDeployable(GlobalDefinitions.portable_manned_turret) }, DeployedItem.portable_manned_turret -> { () => new TurretDeployable(GlobalDefinitions.portable_manned_turret) },
DeployedItem.portable_manned_turret_nc -> { ()=> new TurretDeployable(GlobalDefinitions.portable_manned_turret_nc) }, DeployedItem.portable_manned_turret_nc -> { () =>
DeployedItem.portable_manned_turret_tr -> { ()=> new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) }, new TurretDeployable(GlobalDefinitions.portable_manned_turret_nc)
DeployedItem.portable_manned_turret_vs -> { ()=> new TurretDeployable(GlobalDefinitions.portable_manned_turret_vs) }, },
DeployedItem.deployable_shield_generator -> { ()=> new ShieldGeneratorDeployable(GlobalDefinitions.deployable_shield_generator) }, DeployedItem.portable_manned_turret_tr -> { () =>
DeployedItem.router_telepad_deployable -> { () => new TelepadDeployable(GlobalDefinitions.router_telepad_deployable) } new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr)
},
DeployedItem.portable_manned_turret_vs -> { () =>
new TurretDeployable(GlobalDefinitions.portable_manned_turret_vs)
},
DeployedItem.deployable_shield_generator -> { () =>
new ShieldGeneratorDeployable(GlobalDefinitions.deployable_shield_generator)
},
DeployedItem.router_telepad_deployable -> { () =>
new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
}
).withDefaultValue({ () => new ExplosiveDeployable(GlobalDefinitions.boomer) }) ).withDefaultValue({ () => new ExplosiveDeployable(GlobalDefinitions.boomer) })
} }
@ -68,10 +78,13 @@ object Deployables {
zone.LocalEvents ! LocalServiceMessage(owner, LocalAction.AlertDestroyDeployable(PlanetSideGUID(0), target)) zone.LocalEvents ! LocalServiceMessage(owner, LocalAction.AlertDestroyDeployable(PlanetSideGUID(0), target))
case None => ; case None => ;
} }
zone.LocalEvents ! LocalServiceMessage(s"${target.Faction}", LocalAction.DeployableMapIcon( zone.LocalEvents ! LocalServiceMessage(
s"${target.Faction}",
LocalAction.DeployableMapIcon(
PlanetSideGUID(0), PlanetSideGUID(0),
DeploymentAction.Dismiss, DeploymentAction.Dismiss,
DeployableInfo(target.GUID, Deployable.Icon(target.Definition.Item), target.Position, PlanetSideGUID(0))) DeployableInfo(target.GUID, Deployable.Icon(target.Definition.Item), target.Position, PlanetSideGUID(0))
)
) )
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(target), zone)) zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(target), zone))
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(target, zone, time)) zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(target, zone, time))
@ -88,13 +101,18 @@ 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.Clear() avatar.Deployables
.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] }
.partition(_.isInstanceOf[BoomerDeployable]) .partition(_.isInstanceOf[BoomerDeployable])
//do not change the OwnerName field at this time //do not change the OwnerName field at this time
boomers.collect({ case obj : BoomerDeployable => boomers.collect({
zone.LocalEvents.tell(LocalServiceMessage.Deployables(RemoverActor.AddTask(obj, zone, Some(0 seconds))), replyTo) //near-instant case obj: BoomerDeployable =>
zone.LocalEvents.tell(
LocalServiceMessage.Deployables(RemoverActor.AddTask(obj, zone, Some(0 seconds))),
replyTo
) //near-instant
obj.Owner = None obj.Owner = None
obj.Trigger = None obj.Trigger = None
}) })
@ -124,8 +142,6 @@ object Deployables {
} }
} }
/** /**
* Initialize the deployables backend information. * Initialize the deployables backend information.
* @param avatar the player's core * @param avatar the player's core
@ -151,7 +167,11 @@ object Deployables {
* @param certification the certification that was added * @param certification the certification that was added
* @param certificationSet all applicable certifications * @param certificationSet all applicable certifications
*/ */
def AddToDeployableQuantities(avatar : Avatar, certification : CertificationType.Value, certificationSet : Set[CertificationType.Value]) : List[(Int,Int,Int,Int)] = { def AddToDeployableQuantities(
avatar: Avatar,
certification: CertificationType.Value,
certificationSet: Set[CertificationType.Value]
): List[(Int, Int, Int, Int)] = {
avatar.Deployables.AddToDeployableQuantities(certification, certificationSet) avatar.Deployables.AddToDeployableQuantities(certification, certificationSet)
avatar.Deployables.UpdateUI(certification) avatar.Deployables.UpdateUI(certification)
} }
@ -163,7 +183,11 @@ object Deployables {
* @param certification the certification that was added * @param certification the certification that was added
* @param certificationSet all applicable certifications * @param certificationSet all applicable certifications
*/ */
def RemoveFromDeployableQuantities(avatar : Avatar, certification : CertificationType.Value, certificationSet : Set[CertificationType.Value]) : List[(Int,Int,Int,Int)] = { def RemoveFromDeployableQuantities(
avatar: Avatar,
certification: CertificationType.Value,
certificationSet: Set[CertificationType.Value]
): List[(Int, Int, Int, Int)] = {
avatar.Deployables.RemoveFromDeployableQuantities(certification, certificationSet) avatar.Deployables.RemoveFromDeployableQuantities(certification, certificationSet)
avatar.Deployables.UpdateUI(certification) avatar.Deployables.UpdateUI(certification)
} }

View file

@ -18,8 +18,7 @@ import services.local.{LocalAction, LocalServiceMessage}
import scala.concurrent.duration._ import scala.concurrent.duration._
class ExplosiveDeployable(cdef : ExplosiveDeployableDefinition) extends ComplexDeployable(cdef) class ExplosiveDeployable(cdef: ExplosiveDeployableDefinition) extends ComplexDeployable(cdef) with JammableUnit {
with JammableUnit {
override def Definition: ExplosiveDeployableDefinition = cdef override def Definition: ExplosiveDeployableDefinition = cdef
} }
@ -40,7 +39,8 @@ class ExplosiveDeployableDefinition(private val objectId : Int) extends ComplexD
} }
override def Initialize(obj: PlanetSideServerObject with Deployable, context: ActorContext) = { override def Initialize(obj: PlanetSideServerObject with Deployable, context: ActorContext) = {
obj.Actor = context.actorOf(Props(classOf[ExplosiveDeployableControl], obj), PlanetSideServerObject.UniqueActorName(obj)) obj.Actor =
context.actorOf(Props(classOf[ExplosiveDeployableControl], obj), PlanetSideServerObject.UniqueActorName(obj))
} }
override def Uninitialize(obj: PlanetSideServerObject with Deployable, context: ActorContext) = { override def Uninitialize(obj: PlanetSideServerObject with Deployable, context: ActorContext) = {
@ -54,11 +54,11 @@ object ExplosiveDeployableDefinition {
} }
} }
class ExplosiveDeployableControl(mine : ExplosiveDeployable) extends Actor class ExplosiveDeployableControl(mine: ExplosiveDeployable) extends Actor with Damageable {
with Damageable {
def DamageableObject = mine def DamageableObject = mine
def receive : Receive = takesDamage def receive: Receive =
takesDamage
.orElse { .orElse {
case _ => ; case _ => ;
} }
@ -71,8 +71,7 @@ class ExplosiveDeployableControl(mine : ExplosiveDeployable) extends Actor
val damage = originalHealth - mine.Health val damage = originalHealth - mine.Health
if (Damageable.CanDamageOrJammer(mine, damage, cause)) { if (Damageable.CanDamageOrJammer(mine, damage, cause)) {
ExplosiveDeployableControl.DamageResolution(mine, cause, damage) ExplosiveDeployableControl.DamageResolution(mine, cause, damage)
} } else {
else {
mine.Health = originalHealth mine.Health = originalHealth
} }
} }
@ -84,12 +83,13 @@ object ExplosiveDeployableControl {
target.History(cause) target.History(cause)
if (target.Health == 0) { if (target.Health == 0) {
DestructionAwareness(target, cause) DestructionAwareness(target, cause)
} } else if (!target.Jammed && Damageable.CanJammer(target, cause)) {
else if(!target.Jammed && Damageable.CanJammer(target, cause)) { if (
if(target.Jammed = { target.Jammed = {
val radius = cause.projectile.profile.DamageRadius val radius = cause.projectile.profile.DamageRadius
Vector3.DistanceSquared(cause.hit_pos, cause.target.Position) < radius * radius Vector3.DistanceSquared(cause.hit_pos, cause.target.Position) < radius * radius
}) { }
) {
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)
@ -113,9 +113,15 @@ 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.Id, AvatarAction.Destroy(target.GUID, attribution, Service.defaultPlayerGUID, target.Position)) zone.AvatarEvents ! AvatarServiceMessage(
zone.Id,
AvatarAction.Destroy(target.GUID, attribution, Service.defaultPlayerGUID, target.Position)
)
if (target.Health == 0) { if (target.Health == 0) {
zone.LocalEvents ! LocalServiceMessage(zone.Id, LocalAction.TriggerEffect(Service.defaultPlayerGUID, "detonate_damaged_mine", target.GUID)) zone.LocalEvents ! LocalServiceMessage(
zone.Id,
LocalAction.TriggerEffect(Service.defaultPlayerGUID, "detonate_damaged_mine", target.GUID)
)
} }
} }
} }

View file

@ -242,7 +242,9 @@ object GlobalDefinitions {
val lasher_projectile_ap = ProjectileDefinition(Projectiles.lasher_projectile_ap) val lasher_projectile_ap = ProjectileDefinition(Projectiles.lasher_projectile_ap)
val liberator_bomb_cluster_bomblet_projectile = ProjectileDefinition(Projectiles.liberator_bomb_cluster_bomblet_projectile) val liberator_bomb_cluster_bomblet_projectile = ProjectileDefinition(
Projectiles.liberator_bomb_cluster_bomblet_projectile
)
val liberator_bomb_cluster_projectile = ProjectileDefinition(Projectiles.liberator_bomb_cluster_projectile) val liberator_bomb_cluster_projectile = ProjectileDefinition(Projectiles.liberator_bomb_cluster_projectile)
@ -653,20 +655,16 @@ object GlobalDefinitions {
if (index == 0 || index == 3) { if (index == 0 || index == 3) {
if (index == 0) { if (index == 0) {
3 //3-second fuse 3 //3-second fuse
} } else {
else {
0 //explode on contact 0 //explode on contact
} }
} } else if (index == 1 || index == 4) {
else if(index == 1 || index == 4) {
if (index == 1) { if (index == 1) {
4 //3-second fuse, anchored 4 //3-second fuse, anchored
} } else {
else {
1 //explode on contact, anchored 1 //explode on contact, anchored
} }
} } else {
else {
index index
} }
} }
@ -1157,7 +1155,6 @@ object GlobalDefinitions {
val vt_spawn = new BuildingDefinition(984) { Name = "vt_spawn" } val vt_spawn = new BuildingDefinition(984) { Name = "vt_spawn" }
val vt_vehicle = new BuildingDefinition(985) { Name = "vt_vehicle" } val vt_vehicle = new BuildingDefinition(985) { Name = "vt_vehicle" }
/** /**
* Given a faction, provide the standard assault melee weapon. * Given a faction, provide the standard assault melee weapon.
* @param faction the faction * @param faction the faction
@ -1247,6 +1244,7 @@ object GlobalDefinitions {
case PlanetSideEmpire.NEUTRAL => bullet_9mm case PlanetSideEmpire.NEUTRAL => bullet_9mm
} }
} }
/** /**
* For a given faction, provide the AP ammunition for the medium assault rifle. * For a given faction, provide the AP ammunition for the medium assault rifle.
* The ammunition value here must work with the result of obtaining the rifle using the faction. * The ammunition value here must work with the result of obtaining the rifle using the faction.
@ -1343,23 +1341,19 @@ object GlobalDefinitions {
def MAXArms(subtype: Int, faction: PlanetSideEmpire.Value): ToolDefinition = { def MAXArms(subtype: Int, faction: PlanetSideEmpire.Value): ToolDefinition = {
if (subtype == 1) { if (subtype == 1) {
AI_MAX(faction) AI_MAX(faction)
} } else if (subtype == 2) {
else if(subtype == 2) {
AV_MAX(faction) AV_MAX(faction)
} } else if (subtype == 3) {
else if(subtype == 3) {
AA_MAX(faction) AA_MAX(faction)
} } else {
else {
suppressor //there are no common pool MAX arms suppressor //there are no common pool MAX arms
} }
} }
def isMaxArms(tdef: ToolDefinition): Boolean = { def isMaxArms(tdef: ToolDefinition): Boolean = {
tdef match { tdef match {
case `trhev_dualcycler` | `nchev_scattercannon` | `vshev_quasar` case `trhev_dualcycler` | `nchev_scattercannon` | `vshev_quasar` | `trhev_pounder` | `nchev_falcon` |
| `trhev_pounder` | `nchev_falcon` | `vshev_comet` `vshev_comet` | `trhev_burster` | `nchev_sparrow` | `vshev_starfire` =>
| `trhev_burster` | `nchev_sparrow` | `vshev_starfire` =>
true true
case _ => case _ =>
false false
@ -1468,11 +1462,14 @@ object GlobalDefinitions {
*/ */
def isFactionWeapon(edef: EquipmentDefinition): PlanetSideEmpire.Value = { def isFactionWeapon(edef: EquipmentDefinition): PlanetSideEmpire.Value = {
edef match { edef match {
case `chainblade` | `repeater` | `anniversary_guna` | `cycler` | `mini_chaingun` | `striker` | `trhev_dualcycler` | `trhev_pounder` | `trhev_burster` => case `chainblade` | `repeater` | `anniversary_guna` | `cycler` | `mini_chaingun` | `striker` |
`trhev_dualcycler` | `trhev_pounder` | `trhev_burster` =>
PlanetSideEmpire.TR PlanetSideEmpire.TR
case `magcutter` | `isp` | `anniversary_gun` | `gauss` | `r_shotgun` | `hunterseeker` | `nchev_scattercannon` | `nchev_falcon` | `nchev_sparrow` => case `magcutter` | `isp` | `anniversary_gun` | `gauss` | `r_shotgun` | `hunterseeker` | `nchev_scattercannon` |
`nchev_falcon` | `nchev_sparrow` =>
PlanetSideEmpire.NC PlanetSideEmpire.NC
case `forceblade` | `beamer` | `anniversary_gunb` | `pulsar` | `lasher` | `lancer` | `vshev_quasar` | `vshev_comet` | `vshev_starfire` => case `forceblade` | `beamer` | `anniversary_gunb` | `pulsar` | `lasher` | `lancer` | `vshev_quasar` |
`vshev_comet` | `vshev_starfire` =>
PlanetSideEmpire.VS PlanetSideEmpire.VS
case _ => case _ =>
PlanetSideEmpire.NEUTRAL PlanetSideEmpire.NEUTRAL
@ -1487,11 +1484,17 @@ object GlobalDefinitions {
*/ */
def isFactionEquipment(edef: EquipmentDefinition): PlanetSideEmpire.Value = { def isFactionEquipment(edef: EquipmentDefinition): PlanetSideEmpire.Value = {
edef match { edef match {
case `chainblade` | `repeater` | `anniversary_guna` | `cycler` | `mini_chaingun` | `striker` | `striker_missile_ammo` | `trhev_dualcycler` | `trhev_pounder` | `trhev_burster` | `dualcycler_ammo` | `pounder_ammo` | `burster_ammo` => case `chainblade` | `repeater` | `anniversary_guna` | `cycler` | `mini_chaingun` | `striker` |
`striker_missile_ammo` | `trhev_dualcycler` | `trhev_pounder` | `trhev_burster` | `dualcycler_ammo` |
`pounder_ammo` | `burster_ammo` =>
PlanetSideEmpire.TR PlanetSideEmpire.TR
case `magcutter` | `isp` | `anniversary_gun` | `gauss` | `r_shotgun` | `hunterseeker` | `hunter_seeker_missile` | `nchev_scattercannon` | `nchev_falcon` | `nchev_sparrow` | `scattercannon_ammo` | `falcon_ammo` | `sparrow_ammo` => case `magcutter` | `isp` | `anniversary_gun` | `gauss` | `r_shotgun` | `hunterseeker` | `hunter_seeker_missile` |
`nchev_scattercannon` | `nchev_falcon` | `nchev_sparrow` | `scattercannon_ammo` | `falcon_ammo` |
`sparrow_ammo` =>
PlanetSideEmpire.NC PlanetSideEmpire.NC
case `forceblade` | `beamer` | `anniversary_gunb` | `pulsar` | `lasher` | `lancer` | `energy_cell` | `lancer_cartridge` | `vshev_quasar` | `vshev_comet` | `vshev_starfire` | `quasar_ammo` | `comet_ammo` | `starfire_ammo` => case `forceblade` | `beamer` | `anniversary_gunb` | `pulsar` | `lasher` | `lancer` | `energy_cell` |
`lancer_cartridge` | `vshev_quasar` | `vshev_comet` | `vshev_starfire` | `quasar_ammo` | `comet_ammo` |
`starfire_ammo` =>
PlanetSideEmpire.VS PlanetSideEmpire.VS
case _ => case _ =>
PlanetSideEmpire.NEUTRAL PlanetSideEmpire.NEUTRAL
@ -1546,7 +1549,8 @@ object GlobalDefinitions {
*/ */
def isFlightVehicle(vdef: VehicleDefinition): Boolean = { def isFlightVehicle(vdef: VehicleDefinition): Boolean = {
vdef match { vdef match {
case `mosquito` | `lightgunship` | `wasp` | `liberator` | `vulture` | `phantasm` | `lodestar` | `dropship` | `galaxy_gunship` => case `mosquito` | `lightgunship` | `wasp` | `liberator` | `vulture` | `phantasm` | `lodestar` | `dropship` |
`galaxy_gunship` =>
true true
case _ => case _ =>
false false
@ -1575,8 +1579,7 @@ object GlobalDefinitions {
def canStationaryRotate(vdef: VehicleDefinition): Boolean = { def canStationaryRotate(vdef: VehicleDefinition): Boolean = {
if (isFlightVehicle(vdef) || isHoverVehicle(vdef)) { if (isFlightVehicle(vdef) || isHoverVehicle(vdef)) {
true true
} } else {
else {
vdef match { vdef match {
case `lightning` | `prowler` | `vanguard` => case `lightning` | `prowler` | `vanguard` =>
true true
@ -1651,7 +1654,8 @@ object GlobalDefinitions {
Infiltration.Holster(4, EquipmentSize.Melee) Infiltration.Holster(4, EquipmentSize.Melee)
def CommonMaxConfig(max: SpecialExoSuitDefinition): Unit = { def CommonMaxConfig(max: SpecialExoSuitDefinition): Unit = {
max.Permissions = List(CertificationType.AIMAX,CertificationType.AVMAX, CertificationType.AAMAX, CertificationType.UniMAX) max.Permissions =
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
@ -1687,6 +1691,7 @@ object GlobalDefinitions {
NCMAX.CapacitorDrainPerSecond = 4 NCMAX.CapacitorDrainPerSecond = 4
} }
/** /**
* Initialize `AmmoBoxDefinition` globals. * Initialize `AmmoBoxDefinition` globals.
*/ */
@ -2772,12 +2777,30 @@ object GlobalDefinitions {
jammer_cartridge_projectile.Lifespan = 15f jammer_cartridge_projectile.Lifespan = 15f
jammer_cartridge_projectile.AdditionalEffect = true jammer_cartridge_projectile.AdditionalEffect = true
jammer_cartridge_projectile.JammerProjectile = true jammer_cartridge_projectile.JammerProjectile = true
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player) -> 1000 jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.AMS) -> 5000 EffectTarget.Category.Player,
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.MotionSensor) -> 30000 EffectTarget.Validation.Player
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.Spitfire) -> 30000 ) -> 1000
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Turret, EffectTarget.Validation.Turret) -> 30000 jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.VehicleNotAMS) -> 10000 EffectTarget.Category.Vehicle,
EffectTarget.Validation.AMS
) -> 5000
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Deployable,
EffectTarget.Validation.MotionSensor
) -> 30000
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Deployable,
EffectTarget.Validation.Spitfire
) -> 30000
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Turret,
EffectTarget.Validation.Turret
) -> 30000
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Vehicle,
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile) ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile)
jammer_cartridge_projectile_b.Name = "jammer_cartridge_projectile_b" jammer_cartridge_projectile_b.Name = "jammer_cartridge_projectile_b"
@ -2791,12 +2814,30 @@ object GlobalDefinitions {
jammer_cartridge_projectile_b.Lifespan = 2f jammer_cartridge_projectile_b.Lifespan = 2f
jammer_cartridge_projectile_b.AdditionalEffect = true jammer_cartridge_projectile_b.AdditionalEffect = true
jammer_cartridge_projectile_b.JammerProjectile = true jammer_cartridge_projectile_b.JammerProjectile = true
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player) -> 1000 jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.AMS) -> 5000 EffectTarget.Category.Player,
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.MotionSensor) -> 30000 EffectTarget.Validation.Player
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.Spitfire) -> 30000 ) -> 1000
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(EffectTarget.Category.Turret, EffectTarget.Validation.Turret) -> 30000 jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.VehicleNotAMS) -> 10000 EffectTarget.Category.Vehicle,
EffectTarget.Validation.AMS
) -> 5000
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Deployable,
EffectTarget.Validation.MotionSensor
) -> 30000
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Deployable,
EffectTarget.Validation.Spitfire
) -> 30000
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Turret,
EffectTarget.Validation.Turret
) -> 30000
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Vehicle,
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile_b) ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile_b)
jammer_grenade_projectile.Name = "jammer_grenade_projectile" jammer_grenade_projectile.Name = "jammer_grenade_projectile"
@ -2809,12 +2850,30 @@ object GlobalDefinitions {
jammer_grenade_projectile.Lifespan = 15f jammer_grenade_projectile.Lifespan = 15f
jammer_grenade_projectile.AdditionalEffect = true jammer_grenade_projectile.AdditionalEffect = true
jammer_grenade_projectile.JammerProjectile = true jammer_grenade_projectile.JammerProjectile = true
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player) -> 1000 jammer_grenade_projectile.JammedEffectDuration += TargetValidation(
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.AMS) -> 5000 EffectTarget.Category.Player,
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.MotionSensor) -> 30000 EffectTarget.Validation.Player
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.Spitfire) -> 30000 ) -> 1000
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Turret, EffectTarget.Validation.Turret) -> 30000 jammer_grenade_projectile.JammedEffectDuration += TargetValidation(
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.VehicleNotAMS) -> 10000 EffectTarget.Category.Vehicle,
EffectTarget.Validation.AMS
) -> 5000
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Deployable,
EffectTarget.Validation.MotionSensor
) -> 30000
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Deployable,
EffectTarget.Validation.Spitfire
) -> 30000
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Turret,
EffectTarget.Validation.Turret
) -> 30000
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Vehicle,
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile) ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile)
jammer_grenade_projectile_enh.Name = "jammer_grenade_projectile_enh" jammer_grenade_projectile_enh.Name = "jammer_grenade_projectile_enh"
@ -2828,12 +2887,30 @@ object GlobalDefinitions {
jammer_grenade_projectile_enh.Lifespan = 3f jammer_grenade_projectile_enh.Lifespan = 3f
jammer_grenade_projectile_enh.AdditionalEffect = true jammer_grenade_projectile_enh.AdditionalEffect = true
jammer_grenade_projectile_enh.JammerProjectile = true jammer_grenade_projectile_enh.JammerProjectile = true
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player) -> 1000 jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.AMS) -> 5000 EffectTarget.Category.Player,
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.MotionSensor) -> 30000 EffectTarget.Validation.Player
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.Spitfire) -> 30000 ) -> 1000
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(EffectTarget.Category.Turret, EffectTarget.Validation.Turret) -> 30000 jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.VehicleNotAMS) -> 10000 EffectTarget.Category.Vehicle,
EffectTarget.Validation.AMS
) -> 5000
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Deployable,
EffectTarget.Validation.MotionSensor
) -> 30000
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Deployable,
EffectTarget.Validation.Spitfire
) -> 30000
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Turret,
EffectTarget.Validation.Turret
) -> 30000
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(
EffectTarget.Category.Vehicle,
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile_enh) ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile_enh)
katana_projectile.Name = "katana_projectile" katana_projectile.Name = "katana_projectile"
@ -4569,7 +4646,9 @@ object GlobalDefinitions {
ace.Modes(2).Item(DeployedItem.spitfire_aa -> Set(CertificationType.FortificationEngineering)) ace.Modes(2).Item(DeployedItem.spitfire_aa -> Set(CertificationType.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(CertificationType.CombatEngineering))
ace.Modes(3).Item(DeployedItem.sensor_shield -> Set(CertificationType.AdvancedHacking, CertificationType.CombatEngineering)) ace
.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"
@ -4619,7 +4698,8 @@ object GlobalDefinitions {
scythe.FireModes += new FireModeDefinition scythe.FireModes += new FireModeDefinition
scythe.FireModes(1).AmmoTypeIndices += 0 scythe.FireModes(1).AmmoTypeIndices += 0
scythe.FireModes(1).ProjectileTypeIndices += 0 scythe.FireModes(1).ProjectileTypeIndices += 0
scythe.FireModes(1).AmmoSlotIndex = 1 //note: the scythe has two magazines using a single pool; however, it can not ammo-switch or mode-switch scythe.FireModes(1).AmmoSlotIndex =
1 //note: the scythe has two magazines using a single pool; however, it can not ammo-switch or mode-switch
scythe.FireModes(1).Magazine = 250 scythe.FireModes(1).Magazine = 250
chaingun_p.Name = "chaingun_p" chaingun_p.Name = "chaingun_p"
@ -5957,7 +6037,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)
@ -6058,7 +6139,8 @@ object GlobalDefinitions {
galaxy_gunship.TrunkLocation = Vector3(-9.85f, 0f, 0f) galaxy_gunship.TrunkLocation = Vector3(-9.85f, 0f, 0f)
galaxy_gunship.AutoPilotSpeeds = (0, 4) galaxy_gunship.AutoPilotSpeeds = (0, 4)
galaxy_gunship.Packet = variantConverter galaxy_gunship.Packet = variantConverter
galaxy_gunship.DestroyedModel = Some(DestroyedVehicle.Dropship) //the adb calls out a galaxy_gunship_destroyed but no such asset exists galaxy_gunship.DestroyedModel =
Some(DestroyedVehicle.Dropship) //the adb calls out a galaxy_gunship_destroyed but no such asset exists
galaxy_gunship.Subtract.Damage1 = 7 galaxy_gunship.Subtract.Damage1 = 7
galaxy_gunship.JackingDuration = Array(0, 60, 20, 10) galaxy_gunship.JackingDuration = Array(0, 60, 20, 10)
@ -6351,9 +6433,16 @@ object GlobalDefinitions {
spawn_terminal.Repairable = false spawn_terminal.Repairable = false
order_terminal.Name = "order_terminal" order_terminal.Name = "order_terminal"
order_terminal.Tab += 0 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons) order_terminal.Tab += 0 -> OrderTerminalDefinition.EquipmentPage(
order_terminal.Tab += 1 -> OrderTerminalDefinition.ArmorWithAmmoPage(EquipmentTerminalDefinition.suits ++ EquipmentTerminalDefinition.maxSuits, EquipmentTerminalDefinition.maxAmmo) EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons
order_terminal.Tab += 2 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons) )
order_terminal.Tab += 1 -> OrderTerminalDefinition.ArmorWithAmmoPage(
EquipmentTerminalDefinition.suits ++ EquipmentTerminalDefinition.maxSuits,
EquipmentTerminalDefinition.maxAmmo
)
order_terminal.Tab += 2 -> OrderTerminalDefinition.EquipmentPage(
EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons
)
order_terminal.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition) order_terminal.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
order_terminal.Tab += 4 -> OrderTerminalDefinition.InfantryLoadoutPage() order_terminal.Tab += 4 -> OrderTerminalDefinition.InfantryLoadoutPage()
order_terminal.SellEquipmentByDefault = true order_terminal.SellEquipmentByDefault = true
@ -6364,9 +6453,16 @@ object GlobalDefinitions {
order_terminal.Subtract.Damage1 = 8 order_terminal.Subtract.Damage1 = 8
order_terminala.Name = "order_terminala" order_terminala.Name = "order_terminala"
order_terminala.Tab += 0 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons) order_terminala.Tab += 0 -> OrderTerminalDefinition.EquipmentPage(
order_terminala.Tab += 1 -> OrderTerminalDefinition.ArmorWithAmmoPage(EquipmentTerminalDefinition.suits, EquipmentTerminalDefinition.maxAmmo) EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons
order_terminala.Tab += 2 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons) )
order_terminala.Tab += 1 -> OrderTerminalDefinition.ArmorWithAmmoPage(
EquipmentTerminalDefinition.suits,
EquipmentTerminalDefinition.maxAmmo
)
order_terminala.Tab += 2 -> OrderTerminalDefinition.EquipmentPage(
EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons
)
order_terminala.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition) order_terminala.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
order_terminala.Tab += 4 -> OrderTerminalDefinition.InfantryLoadoutPage() order_terminala.Tab += 4 -> OrderTerminalDefinition.InfantryLoadoutPage()
order_terminala.Tab(4).asInstanceOf[OrderTerminalDefinition.InfantryLoadoutPage].Exclude = ExoSuitType.MAX order_terminala.Tab(4).asInstanceOf[OrderTerminalDefinition.InfantryLoadoutPage].Exclude = ExoSuitType.MAX
@ -6375,9 +6471,16 @@ object GlobalDefinitions {
order_terminala.Repairable = false order_terminala.Repairable = false
order_terminalb.Name = "order_terminalb" order_terminalb.Name = "order_terminalb"
order_terminalb.Tab += 0 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons) order_terminalb.Tab += 0 -> OrderTerminalDefinition.EquipmentPage(
order_terminalb.Tab += 1 -> OrderTerminalDefinition.ArmorWithAmmoPage(EquipmentTerminalDefinition.suits, EquipmentTerminalDefinition.maxAmmo) EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons
order_terminalb.Tab += 2 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons) )
order_terminalb.Tab += 1 -> OrderTerminalDefinition.ArmorWithAmmoPage(
EquipmentTerminalDefinition.suits,
EquipmentTerminalDefinition.maxAmmo
)
order_terminalb.Tab += 2 -> OrderTerminalDefinition.EquipmentPage(
EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons
)
order_terminalb.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition) order_terminalb.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
order_terminalb.Tab += 4 -> OrderTerminalDefinition.InfantryLoadoutPage() order_terminalb.Tab += 4 -> OrderTerminalDefinition.InfantryLoadoutPage()
order_terminalb.Tab(4).asInstanceOf[OrderTerminalDefinition.InfantryLoadoutPage].Exclude = ExoSuitType.MAX order_terminalb.Tab(4).asInstanceOf[OrderTerminalDefinition.InfantryLoadoutPage].Exclude = ExoSuitType.MAX
@ -6386,9 +6489,16 @@ object GlobalDefinitions {
order_terminalb.Repairable = false order_terminalb.Repairable = false
vanu_equipment_term.Name = "vanu_equipment_term" vanu_equipment_term.Name = "vanu_equipment_term"
vanu_equipment_term.Tab += 0 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons) vanu_equipment_term.Tab += 0 -> OrderTerminalDefinition.EquipmentPage(
vanu_equipment_term.Tab += 1 -> OrderTerminalDefinition.ArmorWithAmmoPage(EquipmentTerminalDefinition.suits ++ EquipmentTerminalDefinition.maxSuits, EquipmentTerminalDefinition.maxAmmo) EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons
vanu_equipment_term.Tab += 2 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons) )
vanu_equipment_term.Tab += 1 -> OrderTerminalDefinition.ArmorWithAmmoPage(
EquipmentTerminalDefinition.suits ++ EquipmentTerminalDefinition.maxSuits,
EquipmentTerminalDefinition.maxAmmo
)
vanu_equipment_term.Tab += 2 -> OrderTerminalDefinition.EquipmentPage(
EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons
)
vanu_equipment_term.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition) vanu_equipment_term.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
vanu_equipment_term.Tab += 4 -> OrderTerminalDefinition.InfantryLoadoutPage() vanu_equipment_term.Tab += 4 -> OrderTerminalDefinition.InfantryLoadoutPage()
vanu_equipment_term.SellEquipmentByDefault = true vanu_equipment_term.SellEquipmentByDefault = true
@ -6417,7 +6527,10 @@ object GlobalDefinitions {
implant_terminal_interface.RepairIfDestroyed = true implant_terminal_interface.RepairIfDestroyed = true
ground_vehicle_terminal.Name = "ground_vehicle_terminal" ground_vehicle_terminal.Name = "ground_vehicle_terminal"
ground_vehicle_terminal.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(VehicleTerminalDefinition.groundVehicles, VehicleTerminalDefinition.trunk) ground_vehicle_terminal.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(
VehicleTerminalDefinition.groundVehicles,
VehicleTerminalDefinition.trunk
)
ground_vehicle_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage() ground_vehicle_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
ground_vehicle_terminal.MaxHealth = 500 ground_vehicle_terminal.MaxHealth = 500
ground_vehicle_terminal.Damageable = true ground_vehicle_terminal.Damageable = true
@ -6426,7 +6539,10 @@ object GlobalDefinitions {
ground_vehicle_terminal.Subtract.Damage1 = 8 ground_vehicle_terminal.Subtract.Damage1 = 8
air_vehicle_terminal.Name = "air_vehicle_terminal" air_vehicle_terminal.Name = "air_vehicle_terminal"
air_vehicle_terminal.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(VehicleTerminalDefinition.flight1Vehicles, VehicleTerminalDefinition.trunk) air_vehicle_terminal.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(
VehicleTerminalDefinition.flight1Vehicles,
VehicleTerminalDefinition.trunk
)
air_vehicle_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage() air_vehicle_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
air_vehicle_terminal.MaxHealth = 500 air_vehicle_terminal.MaxHealth = 500
air_vehicle_terminal.Damageable = true air_vehicle_terminal.Damageable = true
@ -6435,7 +6551,10 @@ object GlobalDefinitions {
air_vehicle_terminal.Subtract.Damage1 = 8 air_vehicle_terminal.Subtract.Damage1 = 8
dropship_vehicle_terminal.Name = "dropship_vehicle_terminal" dropship_vehicle_terminal.Name = "dropship_vehicle_terminal"
dropship_vehicle_terminal.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(VehicleTerminalDefinition.flight1Vehicles ++ VehicleTerminalDefinition.flight2Vehicles, VehicleTerminalDefinition.trunk) dropship_vehicle_terminal.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(
VehicleTerminalDefinition.flight1Vehicles ++ VehicleTerminalDefinition.flight2Vehicles,
VehicleTerminalDefinition.trunk
)
dropship_vehicle_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage() dropship_vehicle_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
dropship_vehicle_terminal.MaxHealth = 500 dropship_vehicle_terminal.MaxHealth = 500
dropship_vehicle_terminal.Damageable = true dropship_vehicle_terminal.Damageable = true
@ -6444,7 +6563,10 @@ object GlobalDefinitions {
dropship_vehicle_terminal.Subtract.Damage1 = 8 dropship_vehicle_terminal.Subtract.Damage1 = 8
vehicle_terminal_combined.Name = "vehicle_terminal_combined" vehicle_terminal_combined.Name = "vehicle_terminal_combined"
vehicle_terminal_combined.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(VehicleTerminalDefinition.flight1Vehicles ++ VehicleTerminalDefinition.groundVehicles, VehicleTerminalDefinition.trunk) vehicle_terminal_combined.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(
VehicleTerminalDefinition.flight1Vehicles ++ VehicleTerminalDefinition.groundVehicles,
VehicleTerminalDefinition.trunk
)
vehicle_terminal_combined.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage() vehicle_terminal_combined.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
vehicle_terminal_combined.MaxHealth = 500 vehicle_terminal_combined.MaxHealth = 500
vehicle_terminal_combined.Damageable = true vehicle_terminal_combined.Damageable = true
@ -6453,7 +6575,10 @@ object GlobalDefinitions {
vehicle_terminal_combined.Subtract.Damage1 = 8 vehicle_terminal_combined.Subtract.Damage1 = 8
vanu_air_vehicle_term.Name = "vanu_air_vehicle_term" vanu_air_vehicle_term.Name = "vanu_air_vehicle_term"
vanu_air_vehicle_term.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(VehicleTerminalDefinition.flight1Vehicles, VehicleTerminalDefinition.trunk) vanu_air_vehicle_term.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(
VehicleTerminalDefinition.flight1Vehicles,
VehicleTerminalDefinition.trunk
)
vanu_air_vehicle_term.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage() vanu_air_vehicle_term.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
vanu_air_vehicle_term.MaxHealth = 500 vanu_air_vehicle_term.MaxHealth = 500
vanu_air_vehicle_term.Damageable = true vanu_air_vehicle_term.Damageable = true
@ -6462,7 +6587,10 @@ object GlobalDefinitions {
vanu_air_vehicle_term.Subtract.Damage1 = 8 vanu_air_vehicle_term.Subtract.Damage1 = 8
vanu_vehicle_term.Name = "vanu_vehicle_term" vanu_vehicle_term.Name = "vanu_vehicle_term"
vanu_vehicle_term.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(VehicleTerminalDefinition.groundVehicles, VehicleTerminalDefinition.trunk) vanu_vehicle_term.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(
VehicleTerminalDefinition.groundVehicles,
VehicleTerminalDefinition.trunk
)
vanu_vehicle_term.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage() vanu_vehicle_term.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
vanu_vehicle_term.MaxHealth = 500 vanu_vehicle_term.MaxHealth = 500
vanu_vehicle_term.Damageable = true vanu_vehicle_term.Damageable = true
@ -6471,7 +6599,10 @@ object GlobalDefinitions {
vanu_vehicle_term.Subtract.Damage1 = 8 vanu_vehicle_term.Subtract.Damage1 = 8
bfr_terminal.Name = "bfr_terminal" bfr_terminal.Name = "bfr_terminal"
bfr_terminal.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(VehicleTerminalDefinition.bfrVehicles, VehicleTerminalDefinition.trunk) bfr_terminal.Tab += 46769 -> OrderTerminalDefinition.VehiclePage(
VehicleTerminalDefinition.bfrVehicles,
VehicleTerminalDefinition.trunk
)
bfr_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage() bfr_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
bfr_terminal.MaxHealth = 500 bfr_terminal.MaxHealth = 500
bfr_terminal.Damageable = true bfr_terminal.Damageable = true
@ -6632,14 +6763,18 @@ object GlobalDefinitions {
lodestar_repair_terminal.Repairable = false lodestar_repair_terminal.Repairable = false
multivehicle_rearm_terminal.Name = "multivehicle_rearm_terminal" multivehicle_rearm_terminal.Name = "multivehicle_rearm_terminal"
multivehicle_rearm_terminal.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition) multivehicle_rearm_terminal.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(
EquipmentTerminalDefinition.vehicleAmmunition
)
multivehicle_rearm_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage() multivehicle_rearm_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
multivehicle_rearm_terminal.SellEquipmentByDefault = true //TODO ? multivehicle_rearm_terminal.SellEquipmentByDefault = true //TODO ?
multivehicle_rearm_terminal.Damageable = false multivehicle_rearm_terminal.Damageable = false
multivehicle_rearm_terminal.Repairable = false multivehicle_rearm_terminal.Repairable = false
bfr_rearm_terminal.Name = "bfr_rearm_terminal" bfr_rearm_terminal.Name = "bfr_rearm_terminal"
bfr_rearm_terminal.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(Map.empty[String, ()=>Equipment]) //TODO add stock to page bfr_rearm_terminal.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(
Map.empty[String, () => Equipment]
) //TODO add stock to page
bfr_rearm_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage() bfr_rearm_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
bfr_rearm_terminal.SellEquipmentByDefault = true //TODO ? bfr_rearm_terminal.SellEquipmentByDefault = true //TODO ?
bfr_rearm_terminal.Damageable = false bfr_rearm_terminal.Damageable = false
@ -6653,7 +6788,9 @@ object GlobalDefinitions {
air_rearm_terminal.Repairable = false air_rearm_terminal.Repairable = false
ground_rearm_terminal.Name = "ground_rearm_terminal" ground_rearm_terminal.Name = "ground_rearm_terminal"
ground_rearm_terminal.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition) ground_rearm_terminal.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(
EquipmentTerminalDefinition.vehicleAmmunition
)
ground_rearm_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage() ground_rearm_terminal.Tab += 4 -> OrderTerminalDefinition.VehicleLoadoutPage()
ground_rearm_terminal.SellEquipmentByDefault = true //TODO ? ground_rearm_terminal.SellEquipmentByDefault = true //TODO ?
ground_rearm_terminal.Damageable = false ground_rearm_terminal.Damageable = false

View file

@ -14,14 +14,20 @@ import net.psforever.types.{ExoSuitType, ImplantType}
* Being jammed de-activates the implant, put it into a state of "not being ready," and causes the initialization to repeat. * Being jammed de-activates the implant, put it into a state of "not being ready," and causes the initialization to repeat.
*/ */
class ImplantSlot { class ImplantSlot {
/** is this slot available for holding an implant */ /** is this slot available for holding an implant */
private var unlocked: Boolean = false private var unlocked: Boolean = false
/** whether this implant is ready for use */ /** whether this implant is ready for use */
private var initialized: Boolean = false private var initialized: Boolean = false
/** */
/**
*/
private var initializeTime: Long = 0L private var initializeTime: Long = 0L
/** is this implant active */ /** is this implant active */
private var active: Boolean = false private var active: Boolean = false
/** what implant is currently installed in this slot; None if there is no implant currently installed */ /** what implant is currently installed in this slot; None if there is no implant currently installed */
private var implant: Option[ImplantDefinition] = None private var implant: Option[ImplantDefinition] = None
@ -54,7 +60,8 @@ class ImplantSlot {
Active Active
} }
def Implant : ImplantType.Value = Installed match { def Implant: ImplantType.Value =
Installed match {
case Some(idef) => case Some(idef) =>
idef.Type idef.Type
case None => case None =>
@ -83,7 +90,8 @@ class ImplantSlot {
def Installed: Option[ImplantDefinition] = implant def Installed: Option[ImplantDefinition] = implant
def MaxTimer : Long = Implant match { def MaxTimer: Long =
Implant match {
case ImplantType.None => case ImplantType.None =>
-1L -1L
case _ => case _ =>
@ -93,8 +101,7 @@ class ImplantSlot {
def ActivationCharge: Int = { def ActivationCharge: Int = {
if (Active) { if (Active) {
Installed.get.ActivationStaminaCost Installed.get.ActivationStaminaCost
} } else {
else {
0 0
} }
} }
@ -108,8 +115,7 @@ class ImplantSlot {
if (Active) { if (Active) {
val inst = Installed.get val inst = Installed.get
inst.StaminaCost inst.StaminaCost
} } else {
else {
0 0
} }
} }

View file

@ -8,6 +8,7 @@ import scala.collection.concurrent.{Map, TrieMap}
* `LivePlayerList` is a singleton and this private class lacks exposure. * `LivePlayerList` is a singleton and this private class lacks exposure.
*/ */
private class LivePlayerList { private class LivePlayerList {
/** key - the session id; value - a `Player` object */ /** key - the session id; value - a `Player` object */
private val sessionMap: Map[Long, Avatar] = new TrieMap[Long, Avatar] private val sessionMap: Map[Long, Avatar] = new TrieMap[Long, Avatar]
@ -48,6 +49,7 @@ private class LivePlayerList {
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.Remove(session)` * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.Remove(session)`
*/ */
object LivePlayerList { object LivePlayerList {
/** As `LivePlayerList` is a singleton, an object of `LivePlayerList` is automatically instantiated. */ /** As `LivePlayerList` is a singleton, an object of `LivePlayerList` is automatically instantiated. */
private val Instance: LivePlayerList = new LivePlayerList private val Instance: LivePlayerList = new LivePlayerList

View file

@ -19,8 +19,7 @@ import services.avatar.{AvatarAction, AvatarServiceMessage}
* The `Player` class refers to it as the "fifth slot" as its permanent slot number is encoded as `0x85`. * The `Player` class refers to it as the "fifth slot" as its permanent slot number is encoded as `0x85`.
* The inventory of this object is accessed using a game world `Locker` object (`mb_locker`). * The inventory of this object is accessed using a game world `Locker` object (`mb_locker`).
*/ */
class LockerContainer extends PlanetSideServerObject class LockerContainer extends PlanetSideServerObject with Container {
with Container {
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private val inventory = GridInventory(30, 20) private val inventory = GridInventory(30, 20)
@ -44,8 +43,7 @@ object LockerContainer {
} }
} }
class LockerEquipment(locker : LockerContainer) extends Equipment class LockerEquipment(locker: LockerContainer) extends Equipment with Container {
with Container {
private val obj = locker private val obj = locker
override def GUID: PlanetSideGUID = obj.GUID override def GUID: PlanetSideGUID = obj.GUID
@ -65,11 +63,11 @@ class LockerEquipment(locker : LockerContainer) extends Equipment
def Definition: EquipmentDefinition = obj.Definition def Definition: EquipmentDefinition = obj.Definition
} }
class LockerContainerControl(locker : LockerContainer, toChannel : String) extends Actor class LockerContainerControl(locker: LockerContainer, toChannel: String) extends Actor with ContainableBehavior {
with ContainableBehavior {
def ContainerObject = locker def ContainerObject = locker
def receive : Receive = containerBehavior def receive: Receive =
containerBehavior
.orElse { .orElse {
case _ => ; case _ => ;
} }
@ -116,6 +114,12 @@ class LockerContainerControl(locker : LockerContainer, toChannel : String) exten
def SwapItemCallback(item: Equipment): Unit = { def SwapItemCallback(item: Equipment): Unit = {
val zone = locker.Zone val zone = locker.Zone
zone.AvatarEvents ! AvatarServiceMessage(toChannel, AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectDetachMessage(locker.GUID, item.GUID, Vector3.Zero, 0f))) zone.AvatarEvents ! AvatarServiceMessage(
toChannel,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
ObjectDetachMessage(locker.GUID, item.GUID, Vector3.Zero, 0f)
)
)
} }
} }

View file

@ -20,6 +20,7 @@ class OffhandEquipmentSlot(size : EquipmentSize.Value) extends EquipmentSlot {
} }
object OffhandEquipmentSlot { object OffhandEquipmentSlot {
/** /**
* An `EquipmentSlot` that can not be manipulated because its size is `Blocked` permanently. * An `EquipmentSlot` that can not be manipulated because its size is `Blocked` permanently.
*/ */

View file

@ -48,7 +48,8 @@ abstract class PlanetSideGameObject extends IdentifiableEntity with WorldEntity
object PlanetSideGameObject { object PlanetSideGameObject {
def toString(obj: PlanetSideGameObject): String = { def toString(obj: PlanetSideGameObject): String = {
val guid : String = if(obj.HasGUID) { obj.GUID.toString } else { "NOGUID" } val guid: String = if (obj.HasGUID) { obj.GUID.toString }
else { "NOGUID" }
val P = obj.Position val P = obj.Position
s"[$guid](x,y,z=${P.x % .3f},${P.y % .3f},${P.z % .3f})" s"[$guid](x,y,z=${P.x % .3f},${P.y % .3f},${P.z % .3f})"
} }

View file

@ -2,7 +2,12 @@
package net.psforever.objects package net.psforever.objects
import net.psforever.objects.avatar.LoadoutManager import net.psforever.objects.avatar.LoadoutManager
import net.psforever.objects.definition.{AvatarDefinition, ExoSuitDefinition, ImplantDefinition, SpecialExoSuitDefinition} import net.psforever.objects.definition.{
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
@ -16,7 +21,8 @@ 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) extends PlanetSideServerObject class Player(private val core: Avatar)
extends PlanetSideServerObject
with FactionAffinity with FactionAffinity
with Vitality with Vitality
with ResistanceProfile with ResistanceProfile
@ -48,7 +54,8 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
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 = false // If stamina drops to 0, player is fatigued until regenerating at least 20 stamina private var fatigued: Boolean =
false // If stamina drops to 0, player is fatigued until regenerating at least 20 stamina
private var afk: Boolean = false private var afk: Boolean = false
private var vehicleSeated: Option[PlanetSideGUID] = None private var vehicleSeated: Option[PlanetSideGUID] = None
@ -60,6 +67,7 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
var death_by: Int = 0 var death_by: Int = 0
var lastSeenStreamMessage: Array[Long] = Array.fill[Long](65535)(0L) var lastSeenStreamMessage: Array[Long] = Array.fill[Long](65535)(0L)
var lastShotSeq_time: Int = -1 var lastShotSeq_time: Int = -1
/** From PlanetsideAttributeMessage */ /** From PlanetsideAttributeMessage */
var PlanetsideAttribute: Array[Long] = Array.ofDim(120) var PlanetsideAttribute: Array[Long] = Array.ofDim(120)
var skipStaminaRegenForTurns: Int = 0 var skipStaminaRegenForTurns: Int = 0
@ -113,8 +121,7 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
if (!isAlive) { if (!isAlive) {
backpack = true backpack = true
true true
} } else {
else {
false false
} }
} }
@ -122,7 +129,8 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
def Stamina: Int = stamina def Stamina: Int = stamina
def Stamina_=(assignStamina: Int): Int = { def Stamina_=(assignStamina: Int): Int = {
stamina = if(isAlive) { math.min(math.max(0, assignStamina), MaxStamina) } else { 0 } stamina = if (isAlive) { math.min(math.max(0, assignStamina), MaxStamina) }
else { 0 }
Stamina Stamina
} }
@ -150,12 +158,10 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
if (newValue < capacitor) { if (newValue < capacitor) {
capacitorLastUsedMillis = System.currentTimeMillis() capacitorLastUsedMillis = System.currentTimeMillis()
capacitorLastChargedMillis = 0 capacitorLastChargedMillis = 0
} } else if (newValue > capacitor && newValue < ExoSuitDef.MaxCapacitor) {
else if(newValue > capacitor && newValue < ExoSuitDef.MaxCapacitor) {
capacitorLastChargedMillis = System.currentTimeMillis() capacitorLastChargedMillis = System.currentTimeMillis()
capacitorLastUsedMillis = 0 capacitorLastUsedMillis = 0
} } else if (newValue > capacitor && newValue == ExoSuitDef.MaxCapacitor) {
else if(newValue > capacitor && newValue == ExoSuitDef.MaxCapacitor) {
capacitorLastChargedMillis = 0 capacitorLastChargedMillis = 0
capacitorLastUsedMillis = 0 capacitorLastUsedMillis = 0
capacitorState = CapacitorStateType.Idle capacitorState = CapacitorStateType.Idle
@ -180,27 +186,23 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
def CapacitorLastUsedMillis = capacitorLastUsedMillis def CapacitorLastUsedMillis = capacitorLastUsedMillis
def CapacitorLastChargedMillis = capacitorLastChargedMillis def CapacitorLastChargedMillis = capacitorLastChargedMillis
def VisibleSlots : Set[Int] = if(exosuit.SuitType == ExoSuitType.MAX) { def VisibleSlots: Set[Int] =
if (exosuit.SuitType == ExoSuitType.MAX) {
Set(0) Set(0)
} } else {
else {
(0 to 4).filterNot(index => holsters(index).Size == EquipmentSize.Blocked).toSet (0 to 4).filterNot(index => holsters(index).Size == EquipmentSize.Blocked).toSet
} }
override def Slot(slot: Int): EquipmentSlot = { override def Slot(slot: Int): EquipmentSlot = {
if (inventory.Offset <= slot && slot <= inventory.LastIndex) { if (inventory.Offset <= slot && slot <= inventory.LastIndex) {
inventory.Slot(slot) inventory.Slot(slot)
} } 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 core.FifthSlot
} } else if (slot == Player.FreeHandSlot) {
else if(slot == Player.FreeHandSlot) {
freeHand freeHand
} } else {
else {
OffhandEquipmentSlot.BlockedSlot OffhandEquipmentSlot.BlockedSlot
} }
} }
@ -222,21 +224,24 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
case Some(index) => case Some(index) =>
Some(index) Some(index)
case None => case None =>
if(freeHand.Equipment.isDefined) { None } else { Some(Player.FreeHandSlot) } if (freeHand.Equipment.isDefined) { None }
else { Some(Player.FreeHandSlot) }
} }
} }
} }
@tailrec private def recursiveHolsterFit(iter : Iterator[EquipmentSlot], objSize : EquipmentSize.Value, index : Int = 0) : Option[Int] = { @tailrec private def recursiveHolsterFit(
iter: Iterator[EquipmentSlot],
objSize: EquipmentSize.Value,
index: Int = 0
): Option[Int] = {
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 {
recursiveHolsterFit(iter, objSize, index + 1) recursiveHolsterFit(iter, objSize, index + 1)
} }
} }
@ -259,23 +264,24 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
case None => case None =>
if (freeHand.Equipment.isDefined && freeHand.Equipment.get.GUID == guid) { if (freeHand.Equipment.isDefined && freeHand.Equipment.get.GUID == guid) {
Some(Player.FreeHandSlot) Some(Player.FreeHandSlot)
} } else {
else {
None None
} }
} }
} }
@tailrec private def findInHolsters(iter : Iterator[EquipmentSlot], guid : PlanetSideGUID, index : Int = 0) : Option[Int] = { @tailrec private def findInHolsters(
iter: Iterator[EquipmentSlot],
guid: PlanetSideGUID,
index: Int = 0
): Option[Int] = {
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 {
findInHolsters(iter, guid, index + 1) findInHolsters(iter, guid, index + 1)
} }
} }
@ -289,16 +295,14 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
case None => case None =>
Success(List()) Success(List())
} }
} } else if (dest == Player.FreeHandSlot) {
else if(dest == Player.FreeHandSlot) {
freeHand.Equipment match { freeHand.Equipment match {
case Some(item) => case Some(item) =>
Success(List(InventoryItem(item, dest))) Success(List(InventoryItem(item, dest)))
case None => case None =>
Success(List()) Success(List())
} }
} } else {
else {
super.Collisions(dest, width, height) super.Collisions(dest, width, height)
} }
} }
@ -309,8 +313,7 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
if (slot != drawnSlot) { if (slot != drawnSlot) {
if (slot == Player.HandsDownSlot) { if (slot == Player.HandsDownSlot) {
drawnSlot = slot drawnSlot = slot
} } else if (VisibleSlots.contains(slot) && holsters(slot).Equipment.isDefined) {
else if(VisibleSlots.contains(slot) && holsters(slot).Equipment.isDefined) {
drawnSlot = slot drawnSlot = slot
lastDrawnSlot = slot lastDrawnSlot = slot
} }
@ -428,8 +431,7 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
Cosmetics(value) Cosmetics(value)
} }
(original, core.PersonalStyleFeatures) (original, core.PersonalStyleFeatures)
} } else {
else {
(None, None) (None, None)
} }
} }
@ -444,12 +446,12 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
} }
} }
private def BasicFeatureToggle(feature : PersonalStyle.Value) : (Option[Cosmetics], Option[Cosmetics]) = core.PersonalStyleFeatures match { private def BasicFeatureToggle(feature: PersonalStyle.Value): (Option[Cosmetics], Option[Cosmetics]) =
core.PersonalStyleFeatures match {
case Some(c: Cosmetics) => case Some(c: Cosmetics) =>
if (c.Styles.contains(feature)) { if (c.Styles.contains(feature)) {
RemoveFromPersonalStyle(feature) RemoveFromPersonalStyle(feature)
} } else {
else {
AddToPersonalStyle(feature) AddToPersonalStyle(feature)
} }
case None => case None =>
@ -466,13 +468,10 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
core.PersonalStyleFeatures match { core.PersonalStyleFeatures match {
case Some(c: Cosmetics) => case Some(c: Cosmetics) =>
if (c.Styles.contains(PersonalStyle.BrimmedCap)) { if (c.Styles.contains(PersonalStyle.BrimmedCap)) {
(RemoveFromPersonalStyle(PersonalStyle.BrimmedCap)._1, (RemoveFromPersonalStyle(PersonalStyle.BrimmedCap)._1, AddToPersonalStyle(PersonalStyle.Beret)._2)
AddToPersonalStyle(PersonalStyle.Beret)._2) } else if (c.Styles.contains(PersonalStyle.Beret)) {
}
else if(c.Styles.contains(PersonalStyle.Beret)) {
RemoveFromPersonalStyle(PersonalStyle.Beret) RemoveFromPersonalStyle(PersonalStyle.Beret)
} } else {
else {
AddToPersonalStyle(PersonalStyle.BrimmedCap) AddToPersonalStyle(PersonalStyle.BrimmedCap)
} }
case None => case None =>
@ -480,7 +479,8 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
} }
} }
private var usingSpecial : SpecialExoSuitDefinition.Mode.Value=>SpecialExoSuitDefinition.Mode.Value = DefaultUsingSpecial private var usingSpecial: SpecialExoSuitDefinition.Mode.Value => SpecialExoSuitDefinition.Mode.Value =
DefaultUsingSpecial
private var gettingSpecial: () => SpecialExoSuitDefinition.Mode.Value = DefaultGettingSpecial private var gettingSpecial: () => SpecialExoSuitDefinition.Mode.Value = DefaultGettingSpecial
@ -492,8 +492,7 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
case PlanetSideEmpire.NC => UsingShield case PlanetSideEmpire.NC => UsingShield
case _ => DefaultUsingSpecial case _ => DefaultUsingSpecial
} }
} } else {
else {
usingSpecial = DefaultUsingSpecial usingSpecial = DefaultUsingSpecial
gettingSpecial = DefaultGettingSpecial gettingSpecial = DefaultGettingSpecial
} }
@ -501,25 +500,26 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
def UsingSpecial: SpecialExoSuitDefinition.Mode.Value = { gettingSpecial() } def UsingSpecial: SpecialExoSuitDefinition.Mode.Value = { gettingSpecial() }
def UsingSpecial_=(state : SpecialExoSuitDefinition.Mode.Value) : SpecialExoSuitDefinition.Mode.Value = usingSpecial(state) def UsingSpecial_=(state: SpecialExoSuitDefinition.Mode.Value): SpecialExoSuitDefinition.Mode.Value =
usingSpecial(state)
private def DefaultUsingSpecial(state : SpecialExoSuitDefinition.Mode.Value) : SpecialExoSuitDefinition.Mode.Value = SpecialExoSuitDefinition.Mode.Normal private def DefaultUsingSpecial(state: SpecialExoSuitDefinition.Mode.Value): SpecialExoSuitDefinition.Mode.Value =
SpecialExoSuitDefinition.Mode.Normal
private def UsingAnchorsOrOverdrive(state : SpecialExoSuitDefinition.Mode.Value) : SpecialExoSuitDefinition.Mode.Value = { private def UsingAnchorsOrOverdrive(
state: SpecialExoSuitDefinition.Mode.Value
): SpecialExoSuitDefinition.Mode.Value = {
import SpecialExoSuitDefinition.Mode._ import SpecialExoSuitDefinition.Mode._
val curr = UsingSpecial val curr = UsingSpecial
val next = if (curr == Normal) { val next = if (curr == Normal) {
if (state == Anchored || state == Overdrive) { if (state == Anchored || state == Overdrive) {
state state
} } else {
else {
Normal Normal
} }
} } else if (state == Normal) {
else if(state == Normal) {
Normal Normal
} } else {
else {
curr curr
} }
MAXUsingSpecial(next) MAXUsingSpecial(next)
@ -531,15 +531,12 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
val next = if (curr == Normal) { val next = if (curr == Normal) {
if (state == Shielded) { if (state == Shielded) {
state state
} } else {
else {
Normal Normal
} }
} } else if (state == Normal) {
else if(state == Normal) {
Normal Normal
} } else {
else {
curr curr
} }
MAXUsingSpecial(next) MAXUsingSpecial(next)
@ -547,25 +544,30 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
private def DefaultGettingSpecial(): SpecialExoSuitDefinition.Mode.Value = SpecialExoSuitDefinition.Mode.Normal private def DefaultGettingSpecial(): SpecialExoSuitDefinition.Mode.Value = SpecialExoSuitDefinition.Mode.Normal
private def MAXUsingSpecial(state : SpecialExoSuitDefinition.Mode.Value) : SpecialExoSuitDefinition.Mode.Value = exosuit match { private def MAXUsingSpecial(state: SpecialExoSuitDefinition.Mode.Value): SpecialExoSuitDefinition.Mode.Value =
exosuit match {
case obj: SpecialExoSuitDefinition => case obj: SpecialExoSuitDefinition =>
obj.UsingSpecial = state obj.UsingSpecial = state
case _ => case _ =>
SpecialExoSuitDefinition.Mode.Normal SpecialExoSuitDefinition.Mode.Normal
} }
private def MAXGettingSpecial() : SpecialExoSuitDefinition.Mode.Value = exosuit match { private def MAXGettingSpecial(): SpecialExoSuitDefinition.Mode.Value =
exosuit match {
case obj: SpecialExoSuitDefinition => case obj: SpecialExoSuitDefinition =>
obj.UsingSpecial obj.UsingSpecial
case _ => case _ =>
SpecialExoSuitDefinition.Mode.Normal SpecialExoSuitDefinition.Mode.Normal
} }
def isAnchored : Boolean = ExoSuit == ExoSuitType.MAX && Faction == PlanetSideEmpire.TR && UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored def isAnchored: Boolean =
ExoSuit == ExoSuitType.MAX && Faction == PlanetSideEmpire.TR && UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored
def isOverdrived : Boolean = ExoSuit == ExoSuitType.MAX && Faction == PlanetSideEmpire.TR && UsingSpecial == SpecialExoSuitDefinition.Mode.Overdrive def isOverdrived: Boolean =
ExoSuit == ExoSuitType.MAX && Faction == PlanetSideEmpire.TR && UsingSpecial == SpecialExoSuitDefinition.Mode.Overdrive
def isShielded : Boolean = ExoSuit == ExoSuitType.MAX && Faction == PlanetSideEmpire.NC && UsingSpecial == SpecialExoSuitDefinition.Mode.Shielded def isShielded: Boolean =
ExoSuit == ExoSuitType.MAX && Faction == PlanetSideEmpire.NC && UsingSpecial == SpecialExoSuitDefinition.Mode.Shielded
def AccessingBackpack: Option[PlanetSideGUID] = backpackAccess def AccessingBackpack: Option[PlanetSideGUID] = backpackAccess
@ -631,7 +633,8 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
def SetLastUsedTime(code: ExoSuitType.Value, subtype: Int): Unit = core.SetLastUsedTime(code, subtype) 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 SetLastUsedTime(code: ExoSuitType.Value, subtype: Int, time: Long): Unit =
core.SetLastUsedTime(code, subtype, time)
def GetLastPurchaseTime(code: Int): Long = core.GetLastPurchaseTime(code) def GetLastPurchaseTime(code: Int): Long = core.GetLastPurchaseTime(code)
@ -647,7 +650,8 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
def canEqual(other: Any): Boolean = other.isInstanceOf[Player] def canEqual(other: Any): Boolean = other.isInstanceOf[Player]
override def equals(other : Any) : Boolean = other match { override def equals(other: Any): Boolean =
other match {
case that: Player => case that: Player =>
(that canEqual this) && (that canEqual this) &&
core == that.core core == that.core
@ -697,29 +701,30 @@ object Player {
val obj = new Player(player.core) val obj = new Player(player.core)
obj.Continent = player.Continent obj.Continent = player.Continent
obj obj
} } else {
else {
player player
} }
} }
def GetHackLevel(player: Player): Int = { def GetHackLevel(player: Player): Int = {
if(player.Certifications.contains(CertificationType.ExpertHacking) || player.Certifications.contains(CertificationType.ElectronicsExpert)) { if (
player.Certifications.contains(CertificationType.ExpertHacking) || player.Certifications.contains(
CertificationType.ElectronicsExpert
)
) {
3 3
} } else if (player.Certifications.contains(CertificationType.AdvancedHacking)) {
else if(player.Certifications.contains(CertificationType.AdvancedHacking)) {
2 2
} } else if (player.Certifications.contains(CertificationType.Hacking)) {
else if (player.Certifications.contains(CertificationType.Hacking)) {
1 1
} } else {
else {
0 0
} }
} }
def toString(obj: Player): String = { def toString(obj: Player): String = {
val guid = if(obj.HasGUID) { s" ${obj.Continent}-${obj.GUID.guid}" } else { "" } val guid = if (obj.HasGUID) { s" ${obj.Continent}-${obj.GUID.guid}" }
else { "" }
s"${obj.core}$guid ${obj.Health}/${obj.MaxHealth} ${obj.Armor}/${obj.MaxArmor}" s"${obj.core}$guid ${obj.Health}/${obj.MaxHealth} ${obj.Armor}/${obj.MaxArmor}"
} }
} }

View file

@ -26,16 +26,20 @@ object Players {
* `false`, otherwise * `false`, otherwise
*/ */
def RevivingTickAction(target: Player, user: Player, item: Tool)(progress: Float): Boolean = { def RevivingTickAction(target: Player, user: Player, item: Tool)(progress: Float): Boolean = {
if(!target.isAlive && !target.isBackpack && if (
!target.isAlive && !target.isBackpack &&
user.isAlive && !user.isMoving && user.isAlive && !user.isMoving &&
user.Slot(user.DrawnSlot).Equipment.contains(item) && item.Magazine >= 25 && user.Slot(user.DrawnSlot).Equipment.contains(item) && item.Magazine >= 25 &&
Vector3.Distance(target.Position, user.Position) < target.Definition.RepairDistance) { Vector3.Distance(target.Position, user.Position) < target.Definition.RepairDistance
) {
val events = target.Zone.AvatarEvents val events = target.Zone.AvatarEvents
val uname = user.Name val uname = user.Name
events ! AvatarServiceMessage(uname, AvatarAction.SendResponse(Service.defaultPlayerGUID, RepairMessage(target.GUID, progress.toInt))) events ! AvatarServiceMessage(
uname,
AvatarAction.SendResponse(Service.defaultPlayerGUID, RepairMessage(target.GUID, progress.toInt))
)
true true
} } else {
else {
false false
} }
} }
@ -52,7 +56,13 @@ object Players {
val name = target.Name val name = target.Name
log.info(s"$medic had revived $name") log.info(s"$medic had revived $name")
val magazine = item.Discharge(Some(25)) val magazine = item.Discharge(Some(25))
target.Zone.AvatarEvents ! AvatarServiceMessage(medic, AvatarAction.SendResponse(Service.defaultPlayerGUID, InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine))) target.Zone.AvatarEvents ! AvatarServiceMessage(
medic,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine)
)
)
target.Zone.AvatarEvents ! AvatarServiceMessage(name, AvatarAction.Revive(target.GUID)) target.Zone.AvatarEvents ! AvatarServiceMessage(name, AvatarAction.Revive(target.GUID))
} }
@ -64,11 +74,14 @@ object Players {
* @param list a persistent `List` of `Equipment` in the holster slots * @param list a persistent `List` of `Equipment` in the holster slots
* @return a `List` of `Equipment` in the holster slots * @return a `List` of `Equipment` in the holster slots
*/ */
@tailrec def clearHolsters(iter : Iterator[EquipmentSlot], index : Int = 0, list : List[InventoryItem] = Nil) : List[InventoryItem] = { @tailrec def clearHolsters(
iter: Iterator[EquipmentSlot],
index: Int = 0,
list: List[InventoryItem] = Nil
): List[InventoryItem] = {
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) =>
@ -91,8 +104,7 @@ object Players {
@tailrec def fillEmptyHolsters(iter: Iterator[EquipmentSlot], list: List[InventoryItem]): List[InventoryItem] = { @tailrec def fillEmptyHolsters(iter: Iterator[EquipmentSlot], list: List[InventoryItem]): List[InventoryItem] = {
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 {
@ -103,8 +115,7 @@ object Players {
case None => case None =>
fillEmptyHolsters(iter, list) fillEmptyHolsters(iter, list)
} }
} } else {
else {
fillEmptyHolsters(iter, list) fillEmptyHolsters(iter, list)
} }
} }

View file

@ -19,9 +19,7 @@ import services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.duration._ import scala.concurrent.duration._
class SensorDeployable(cdef : SensorDeployableDefinition) extends ComplexDeployable(cdef) class SensorDeployable(cdef: SensorDeployableDefinition) extends ComplexDeployable(cdef) with Hackable with JammableUnit
with Hackable
with JammableUnit
class SensorDeployableDefinition(private val objectId: Int) extends ComplexDeployableDefinition(objectId) { class SensorDeployableDefinition(private val objectId: Int) extends ComplexDeployableDefinition(objectId) {
Name = "sensor_deployable" Name = "sensor_deployable"
@ -30,7 +28,8 @@ class SensorDeployableDefinition(private val objectId : Int) extends ComplexDepl
Packet = new SmallDeployableConverter Packet = new SmallDeployableConverter
override def Initialize(obj: PlanetSideServerObject with Deployable, context: ActorContext) = { override def Initialize(obj: PlanetSideServerObject with Deployable, context: ActorContext) = {
obj.Actor = context.actorOf(Props(classOf[SensorDeployableControl], obj), PlanetSideServerObject.UniqueActorName(obj)) obj.Actor =
context.actorOf(Props(classOf[SensorDeployableControl], obj), PlanetSideServerObject.UniqueActorName(obj))
} }
override def Uninitialize(obj: PlanetSideServerObject with Deployable, context: ActorContext) = { override def Uninitialize(obj: PlanetSideServerObject with Deployable, context: ActorContext) = {
@ -44,7 +43,8 @@ object SensorDeployableDefinition {
} }
} }
class SensorDeployableControl(sensor : SensorDeployable) extends Actor class SensorDeployableControl(sensor: SensorDeployable)
extends Actor
with JammableBehavior with JammableBehavior
with DamageableEntity with DamageableEntity
with RepairableEntity { with RepairableEntity {
@ -52,7 +52,8 @@ class SensorDeployableControl(sensor : SensorDeployable) extends Actor
def DamageableObject = sensor def DamageableObject = sensor
def RepairableObject = sensor def RepairableObject = sensor
def receive : Receive = jammableBehavior def receive: Receive =
jammableBehavior
.orElse(takesDamage) .orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser) .orElse(canBeRepairedByNanoDispenser)
.orElse { .orElse {
@ -66,17 +67,25 @@ class SensorDeployableControl(sensor : SensorDeployable) extends Actor
SensorDeployableControl.DestructionAwareness(sensor, PlanetSideGUID(0)) SensorDeployableControl.DestructionAwareness(sensor, PlanetSideGUID(0))
} }
override def StartJammeredSound(target : Any, dur : Int) : Unit = target match { override def StartJammeredSound(target: Any, dur: Int): Unit =
target match {
case obj: PlanetSideServerObject if !jammedSound => case obj: PlanetSideServerObject if !jammedSound =>
obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 1)) obj.Zone.VehicleEvents ! VehicleServiceMessage(
obj.Zone.Id,
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 1)
)
super.StartJammeredSound(obj, dur) super.StartJammeredSound(obj, dur)
case _ => ; case _ => ;
} }
override def StartJammeredStatus(target : Any, dur : Int) : Unit = target match { override def StartJammeredStatus(target: Any, dur: Int): Unit =
target match {
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.Id, LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, false, 1000)) zone.LocalEvents ! LocalServiceMessage(
zone.Id,
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, false, 1000)
)
super.StartJammeredStatus(obj, dur) super.StartJammeredStatus(obj, dur)
case _ => ; case _ => ;
} }
@ -85,7 +94,10 @@ class SensorDeployableControl(sensor : SensorDeployable) extends Actor
target match { target match {
case obj: PlanetSideServerObject if jammedSound => case obj: PlanetSideServerObject if jammedSound =>
val zone = obj.Zone val zone = obj.Zone
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 0)) zone.VehicleEvents ! VehicleServiceMessage(
zone.Id,
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 0)
)
case _ => ; case _ => ;
} }
super.CancelJammeredSound(target) super.CancelJammeredSound(target)
@ -94,7 +106,10 @@ class SensorDeployableControl(sensor : SensorDeployable) extends Actor
override def CancelJammeredStatus(target: Any): Unit = { override def CancelJammeredStatus(target: Any): Unit = {
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.Id, LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, true, 1000)) sensor.Zone.LocalEvents ! LocalServiceMessage(
sensor.Zone.Id,
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, true, 1000)
)
case _ => ; case _ => ;
} }
super.CancelJammeredStatus(target) super.CancelJammeredStatus(target)
@ -102,6 +117,7 @@ class SensorDeployableControl(sensor : SensorDeployable) extends Actor
} }
object SensorDeployableControl { object SensorDeployableControl {
/** /**
* na * na
* @param target na * @param target na
@ -110,7 +126,8 @@ object SensorDeployableControl {
def DestructionAwareness(target: Damageable.Target with Deployable, attribution: PlanetSideGUID): Unit = { def DestructionAwareness(target: Damageable.Target with Deployable, attribution: PlanetSideGUID): Unit = {
Deployables.AnnounceDestroyDeployable(target, Some(1 seconds)) Deployables.AnnounceDestroyDeployable(target, Some(1 seconds))
val zone = target.Zone val zone = target.Zone
zone.LocalEvents ! LocalServiceMessage(zone.Id, zone.LocalEvents ! LocalServiceMessage(
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
@ -125,7 +142,8 @@ object SensorDeployableControl {
pos.z + math.cos(yRadians).toFloat * 0.875f pos.z + math.cos(yRadians).toFloat * 0.875f
) )
} }
zone.LocalEvents ! LocalServiceMessage(zone.Id, zone.LocalEvents ! LocalServiceMessage(
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)?

View file

@ -17,7 +17,8 @@ import net.psforever.types.PlanetSideGUID
import services.Service import services.Service
import services.vehicle.{VehicleAction, VehicleServiceMessage} import services.vehicle.{VehicleAction, VehicleServiceMessage}
class ShieldGeneratorDeployable(cdef : ShieldGeneratorDefinition) extends ComplexDeployable(cdef) class ShieldGeneratorDeployable(cdef: ShieldGeneratorDefinition)
extends ComplexDeployable(cdef)
with Hackable with Hackable
with JammableUnit with JammableUnit
@ -26,7 +27,8 @@ class ShieldGeneratorDefinition extends ComplexDeployableDefinition(240) {
DeployCategory = DeployableCategory.ShieldGenerators DeployCategory = DeployableCategory.ShieldGenerators
override def Initialize(obj: PlanetSideServerObject with Deployable, context: ActorContext) = { override def Initialize(obj: PlanetSideServerObject with Deployable, context: ActorContext) = {
obj.Actor = context.actorOf(Props(classOf[ShieldGeneratorControl], obj), PlanetSideServerObject.UniqueActorName(obj)) obj.Actor =
context.actorOf(Props(classOf[ShieldGeneratorControl], obj), PlanetSideServerObject.UniqueActorName(obj))
} }
override def Uninitialize(obj: PlanetSideServerObject with Deployable, context: ActorContext) = { override def Uninitialize(obj: PlanetSideServerObject with Deployable, context: ActorContext) = {
@ -34,7 +36,8 @@ class ShieldGeneratorDefinition extends ComplexDeployableDefinition(240) {
} }
} }
class ShieldGeneratorControl(gen : ShieldGeneratorDeployable) extends Actor class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
extends Actor
with JammableBehavior with JammableBehavior
with DamageableEntity with DamageableEntity
with RepairableEntity { with RepairableEntity {
@ -43,7 +46,8 @@ class ShieldGeneratorControl(gen : ShieldGeneratorDeployable) extends Actor
def RepairableObject = gen def RepairableObject = gen
private var handleDamageToShields: Boolean = false private var handleDamageToShields: Boolean = false
def receive : Receive = jammableBehavior def receive: Receive =
jammableBehavior
.orElse(takesDamage) .orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser) .orElse(canBeRepairedByNanoDispenser)
.orElse { .orElse {
@ -62,14 +66,16 @@ class ShieldGeneratorControl(gen : ShieldGeneratorDeployable) extends Actor
override def CanBeRepairedByNanoDispenser(player: Player, item: Tool): Unit = { override def CanBeRepairedByNanoDispenser(player: Player, item: Tool): Unit = {
if (gen.CanRepair) { if (gen.CanRepair) {
super.CanBeRepairedByNanoDispenser(player, item) super.CanBeRepairedByNanoDispenser(player, item)
} } else if (!gen.Destroyed) {
else if(!gen.Destroyed) {
//TODO reinforced shield upgrade not implemented yet //TODO reinforced shield upgrade not implemented yet
//TODO ammunition supply upgrade not implemented yet //TODO ammunition supply upgrade not implemented yet
} }
} }
override protected def PerformDamage(target : Damageable.Target, applyDamageTo : ResolutionCalculations.Output) : Unit = { override protected def PerformDamage(
target: Damageable.Target,
applyDamageTo: ResolutionCalculations.Output
): Unit = {
val originalHealth = gen.Health val originalHealth = gen.Health
val originalShields = gen.Shields val originalShields = gen.Shields
val cause = applyDamageTo(target) val cause = applyDamageTo(target)
@ -80,11 +86,13 @@ class ShieldGeneratorControl(gen : ShieldGeneratorDeployable) extends Actor
val damage = damageToHealth + damageToShields val damage = damageToHealth + damageToShields
if (WillAffectTarget(target, damage, cause)) { if (WillAffectTarget(target, damage, cause)) {
target.History(cause) target.History(cause)
DamageLog(target,s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields") DamageLog(
target,
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
)
handleDamageToShields = damageToShields > 0 handleDamageToShields = damageToShields > 0
HandleDamage(target, cause, damageToHealth) HandleDamage(target, cause, damageToHealth)
} } else {
else {
gen.Health = originalHealth gen.Health = originalHealth
gen.Shields = originalShields gen.Shields = originalShields
} }
@ -107,9 +115,13 @@ class ShieldGeneratorControl(gen : ShieldGeneratorDeployable) extends Actor
*/ */
override def StartJammeredSound(target: Any, dur: Int): Unit = {} override def StartJammeredSound(target: Any, dur: Int): Unit = {}
override def StartJammeredStatus(target : Any, dur : Int) : Unit = target match { override def StartJammeredStatus(target: Any, dur: Int): Unit =
target match {
case obj: PlanetSideServerObject with JammableUnit if !obj.Jammed => case obj: PlanetSideServerObject with JammableUnit if !obj.Jammed =>
obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 1)) obj.Zone.VehicleEvents ! VehicleServiceMessage(
obj.Zone.Id,
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 1)
)
super.StartJammeredStatus(obj, dur) super.StartJammeredStatus(obj, dur)
case _ => ; case _ => ;
} }
@ -119,7 +131,10 @@ class ShieldGeneratorControl(gen : ShieldGeneratorDeployable) extends Actor
override def CancelJammeredStatus(target: Any): Unit = { override def CancelJammeredStatus(target: Any): Unit = {
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.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 0)) obj.Zone.VehicleEvents ! VehicleServiceMessage(
obj.Zone.Id,
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 0)
)
case _ => ; case _ => ;
} }
super.CancelJammeredStatus(target) super.CancelJammeredStatus(target)
@ -127,6 +142,7 @@ class ShieldGeneratorControl(gen : ShieldGeneratorDeployable) extends Actor
} }
object ShieldGeneratorControl { object ShieldGeneratorControl {
/** /**
* na * na
* @param target na * @param target na
@ -137,7 +153,10 @@ object ShieldGeneratorControl {
//shields //shields
if (damageToShields) { if (damageToShields) {
val zone = target.Zone val zone = target.Zone
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, target.Shields)) zone.VehicleEvents ! VehicleServiceMessage(
zone.Id,
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, target.Shields)
)
} }
} }

View file

@ -9,12 +9,14 @@ import scala.collection.mutable
trait SpawnPoint { trait SpawnPoint {
psso: PlanetSideServerObject => psso: PlanetSideServerObject =>
/** /**
* An element of the contract of `PlanetSideServerObject`; * An element of the contract of `PlanetSideServerObject`;
* but, this makes it visible to a `SpawnPoint` object without casting. * but, this makes it visible to a `SpawnPoint` object without casting.
* @see `Identifiable.GUID` * @see `Identifiable.GUID`
*/ */
def GUID: PlanetSideGUID def GUID: PlanetSideGUID
/** /**
* An element of the contract of `PlanetSideServerObject`; * An element of the contract of `PlanetSideServerObject`;
* but, this makes it visible to a `SpawnPoint` object without casting. * but, this makes it visible to a `SpawnPoint` object without casting.
@ -22,6 +24,7 @@ trait SpawnPoint {
* @see `SpecificPoint` * @see `SpecificPoint`
*/ */
def Position: Vector3 def Position: Vector3
/** /**
* An element of the contract of `PlanetSideServerObject`; * An element of the contract of `PlanetSideServerObject`;
* but, this makes it visible to a `SpawnPoint` object without casting. * but, this makes it visible to a `SpawnPoint` object without casting.
@ -29,6 +32,7 @@ trait SpawnPoint {
* @see `SpecificPoint` * @see `SpecificPoint`
*/ */
def Orientation: Vector3 def Orientation: Vector3
/** /**
* An element of an unspoken contract with `Amenity`. * An element of an unspoken contract with `Amenity`.
* While not all `SpawnPoint` objects will be `Amenity` objects, a subclass of the `PlanetSideServerObject` class, * While not all `SpawnPoint` objects will be `Amenity` objects, a subclass of the `PlanetSideServerObject` class,
@ -37,6 +41,7 @@ trait SpawnPoint {
* @see `Amenity.Owner` * @see `Amenity.Owner`
*/ */
def Owner: PlanetSideServerObject def Owner: PlanetSideServerObject
/** /**
* An element of the contract of `PlanetSideServerObject`; * An element of the contract of `PlanetSideServerObject`;
* but, this makes it visible to a `SpawnPoint` object without casting. * but, this makes it visible to a `SpawnPoint` object without casting.
@ -66,7 +71,8 @@ trait SpawnPoint {
object SpawnPoint { object SpawnPoint {
def Default(obj: SpawnPoint, target: PlanetSideGameObject): (Vector3, Vector3) = (obj.Position, obj.Orientation) def Default(obj: SpawnPoint, target: PlanetSideGameObject): (Vector3, Vector3) = (obj.Position, obj.Orientation)
def Tube(obj : SpawnPoint, target : PlanetSideGameObject) : (Vector3, Vector3) = ( def Tube(obj: SpawnPoint, target: PlanetSideGameObject): (Vector3, Vector3) =
(
obj.Position + Vector3.z(1.5f), obj.Position + Vector3.z(1.5f),
obj.Orientation.xy + Vector3.z(obj.Orientation.z + 90 % 360) obj.Orientation.xy + Vector3.z(obj.Orientation.z + 90 % 360)
) )
@ -80,18 +86,20 @@ object SpawnPoint {
val z = ori.z val z = ori.z
val zrot = (z + 90) % 360 val zrot = (z + 90) % 360
val zrad = math.toRadians(zrot) val zrad = math.toRadians(zrot)
val shift = Vector3(math.sin(zrad).toFloat, math.cos(zrad).toFloat, 0) * (3 * side).toFloat //x=sin, y=cos because compass-0 is East, not North val shift = Vector3(
math.sin(zrad).toFloat,
math.cos(zrad).toFloat,
0
) * (3 * side).toFloat //x=sin, y=cos because compass-0 is East, not North
( (
obj.Position + shift + (if (x >= 330) { //ams leaning to the left obj.Position + shift + (if (x >= 330) { //ams leaning to the left
Vector3.z(xsin) Vector3.z(xsin)
} } else { //ams leaning to the right
else { //ams leaning to the right
Vector3.z(-xsin) Vector3.z(-xsin)
}), }),
if (side == 1) { if (side == 1) {
Vector3.z(zrot) Vector3.z(zrot)
} } else {
else {
Vector3.z((z - 90) % 360) Vector3.z((z - 90) % 360)
} }
) )
@ -102,7 +110,8 @@ object SpawnPoint {
case d: SpawnPointDefinition => case d: SpawnPointDefinition =>
val ori = target.Orientation val ori = target.Orientation
val zrad = math.toRadians(ori.z) val zrad = math.toRadians(ori.z)
val radius = scala.math.random.toFloat * d.UseRadius/2 + 20f //20 is definitely outside of the gating energy field val radius =
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) =>
@ -142,8 +151,7 @@ trait SpawnPointDefinition {
def VehicleAllowance_=(allow: Boolean): Boolean = { def VehicleAllowance_=(allow: Boolean): Boolean = {
if (allow && noWarp.isEmpty) { if (allow && noWarp.isEmpty) {
noWarp = Some(mutable.Set.empty[VehicleDefinition]) noWarp = Some(mutable.Set.empty[VehicleDefinition])
} } else if (!allow && noWarp.isDefined) {
else if(!allow && noWarp.isDefined) {
noWarp = None noWarp = None
} }
VehicleAllowance VehicleAllowance

View file

@ -4,8 +4,7 @@ package net.psforever.objects
import net.psforever.objects.ce.TelepadLike import net.psforever.objects.ce.TelepadLike
import net.psforever.objects.definition.ConstructionItemDefinition import net.psforever.objects.definition.ConstructionItemDefinition
class Telepad(private val cdef : ConstructionItemDefinition) extends ConstructionItem(cdef) class Telepad(private val cdef: ConstructionItemDefinition) extends ConstructionItem(cdef) with TelepadLike
with TelepadLike
object Telepad { object Telepad {
def apply(cdef: ConstructionItemDefinition): Telepad = { def apply(cdef: ConstructionItemDefinition): Telepad = {

View file

@ -4,5 +4,4 @@ package net.psforever.objects
import net.psforever.objects.ce.{SimpleDeployable, TelepadLike} import net.psforever.objects.ce.{SimpleDeployable, TelepadLike}
import net.psforever.objects.definition.SimpleDeployableDefinition import net.psforever.objects.definition.SimpleDeployableDefinition
class TelepadDeployable(ddef : SimpleDeployableDefinition) extends SimpleDeployable(ddef) class TelepadDeployable(ddef: SimpleDeployableDefinition) extends SimpleDeployable(ddef) with TelepadLike
with TelepadLike

View file

@ -17,11 +17,14 @@ import scala.annotation.tailrec
* Some weapons Chainblade have ammunition but do not consume it. * Some weapons Chainblade have ammunition but do not consume it.
* @param toolDef the `ObjectDefinition` that constructs this item and maintains some of its immutable fields * @param toolDef the `ObjectDefinition` that constructs this item and maintains some of its immutable fields
*/ */
class Tool(private val toolDef : ToolDefinition) extends Equipment class Tool(private val toolDef: ToolDefinition)
extends Equipment
with FireModeSwitch[FireModeDefinition] with FireModeSwitch[FireModeDefinition]
with JammableUnit { with JammableUnit {
/** index of the current fire mode on the `ToolDefinition`'s list of fire modes */ /** index of the current fire mode on the `ToolDefinition`'s list of fire modes */
private var fireModeIndex: Int = toolDef.DefaultFireModeIndex private var fireModeIndex: Int = toolDef.DefaultFireModeIndex
/** current ammunition slot being used by this fire mode */ /** current ammunition slot being used by this fire mode */
private var ammoSlots: List[Tool.FireModeSlot] = List.empty private var ammoSlots: List[Tool.FireModeSlot] = List.empty
var lastDischarge: Long = 0 var lastDischarge: Long = 0
@ -70,8 +73,7 @@ class Tool(private val toolDef : ToolDefinition) extends Equipment
val projIndices = FireMode.ProjectileTypeIndices val projIndices = FireMode.ProjectileTypeIndices
if (projIndices.isEmpty) { if (projIndices.isEmpty) {
AmmoTypeIndex //e.g., bullet_9mm -> bullet_9mm_projectile, bullet_9mm_AP -> bullet_9mm_AP_projectile AmmoTypeIndex //e.g., bullet_9mm -> bullet_9mm_projectile, bullet_9mm_AP -> bullet_9mm_AP_projectile
} } else {
else {
projIndices(AmmoSlot.AmmoTypeIndex) //e.g., pulsar: f.mode1 -> pulsar_projectile, f.mode2 = pulsar_ap_projectile projIndices(AmmoSlot.AmmoTypeIndex) //e.g., pulsar: f.mode1 -> pulsar_projectile, f.mode2 = pulsar_ap_projectile
} }
}) })
@ -134,17 +136,23 @@ object Tool {
tool.ammoSlots = buildFireModes(tdef, (0 to maxSlot).iterator, tdef.FireModes.toList) tool.ammoSlots = buildFireModes(tdef, (0 to maxSlot).iterator, tdef.FireModes.toList)
} }
@tailrec private def buildFireModes(tdef : ToolDefinition, iter : Iterator[Int], fmodes : List[FireModeDefinition], list : List[FireModeSlot] = Nil) : List[FireModeSlot] = { @tailrec private def buildFireModes(
tdef: ToolDefinition,
iter: Iterator[Int],
fmodes: List[FireModeDefinition],
list: List[FireModeSlot] = Nil
): List[FireModeSlot] = {
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))
case Nil => case Nil =>
throw new IllegalArgumentException(s"tool ${tdef.Name} ammo slot #$index is missing a fire mode specification; do not skip") throw new IllegalArgumentException(
s"tool ${tdef.Name} ammo slot #$index is missing a fire mode specification; do not skip"
)
} }
} }
} }
@ -167,11 +175,13 @@ object Tool {
* is an example of a weapon that benefits from this implementation. * is an example of a weapon that benefits from this implementation.
*/ */
class FireModeSlot(private val tdef: ToolDefinition, private val fdef: FireModeDefinition) { class FireModeSlot(private val tdef: ToolDefinition, private val fdef: FireModeDefinition) {
/** /**
* if this fire mode has multiple types of ammunition * if this fire mode has multiple types of ammunition
* this is the index of the fire mode's ammo List, not a reference to the tool's ammo List * this is the index of the fire mode's ammo List, not a reference to the tool's ammo List
*/ */
private var ammoTypeIndex: Int = 0 private var ammoTypeIndex: Int = 0
/** a reference to the actual `AmmoBox` of this slot */ /** a reference to the actual `AmmoBox` of this slot */
private var box: AmmoBox = AmmoBox(AmmoDefinition, fdef.Magazine) private var box: AmmoBox = AmmoBox(AmmoDefinition, fdef.Magazine)
private var chamber = fdef.Chamber private var chamber = fdef.Chamber
@ -219,8 +229,7 @@ object Tool {
if (toBox.AmmoType == AmmoType) { if (toBox.AmmoType == AmmoType) {
box = toBox box = toBox
Some(Box) Some(Box)
} } else {
else {
None None
} }
} }

View file

@ -32,13 +32,12 @@ object TrapDeployableDefinition {
} }
} }
class TrapDeployableControl(trap : TrapDeployable) extends Actor class TrapDeployableControl(trap: TrapDeployable) extends Actor with DamageableEntity with RepairableEntity {
with DamageableEntity
with RepairableEntity {
def DamageableObject = trap def DamageableObject = trap
def RepairableObject = trap def RepairableObject = trap
def receive : Receive = takesDamage def receive: Receive =
takesDamage
.orElse(canBeRepairedByNanoDispenser) .orElse(canBeRepairedByNanoDispenser)
.orElse { .orElse {
case _ => case _ =>

View file

@ -17,7 +17,8 @@ import net.psforever.objects.serverobject.repair.RepairableWeaponTurret
import net.psforever.objects.serverobject.turret.{TurretDefinition, WeaponTurret} import net.psforever.objects.serverobject.turret.{TurretDefinition, WeaponTurret}
import net.psforever.objects.vital.{StandardResolutions, StandardVehicleDamage, StandardVehicleResistance} import net.psforever.objects.vital.{StandardResolutions, StandardVehicleDamage, StandardVehicleResistance}
class TurretDeployable(tdef : TurretDeployableDefinition) extends ComplexDeployable(tdef) class TurretDeployable(tdef: TurretDeployableDefinition)
extends ComplexDeployable(tdef)
with WeaponTurret with WeaponTurret
with JammableUnit with JammableUnit
with Hackable { with Hackable {
@ -28,7 +29,8 @@ class TurretDeployable(tdef : TurretDeployableDefinition) extends ComplexDeploya
override def Definition = tdef override def Definition = tdef
} }
class TurretDeployableDefinition(private val objectId : Int) extends ComplexDeployableDefinition(objectId) class TurretDeployableDefinition(private val objectId: Int)
extends ComplexDeployableDefinition(objectId)
with TurretDefinition { with TurretDefinition {
Name = "turret_deployable" Name = "turret_deployable"
Packet = new SmallTurretConverter Packet = new SmallTurretConverter
@ -58,7 +60,8 @@ object TurretDeployableDefinition {
/** control actors */ /** control actors */
class TurretControl(turret : TurretDeployable) extends Actor class TurretControl(turret: TurretDeployable)
extends Actor
with FactionAffinityBehavior.Check with FactionAffinityBehavior.Check
with JammableMountedWeapons //note: jammable status is reported as vehicle events, not local events with JammableMountedWeapons //note: jammable status is reported as vehicle events, not local events
with MountableBehavior.TurretMount with MountableBehavior.TurretMount
@ -71,7 +74,8 @@ class TurretControl(turret : TurretDeployable) extends Actor
def DamageableObject = turret def DamageableObject = turret
def RepairableObject = turret def RepairableObject = turret
def receive : Receive = checkBehavior def receive: Receive =
checkBehavior
.orElse(jammableBehavior) .orElse(jammableBehavior)
.orElse(mountBehavior) .orElse(mountBehavior)
.orElse(dismountBehavior) .orElse(dismountBehavior)

View file

@ -66,7 +66,8 @@ import scala.util.{Success, Try}
* stores and unloads pertinent information about the `Vehicle`'s configuration; * stores and unloads pertinent information about the `Vehicle`'s configuration;
* used in the initialization process (`loadVehicleDefinition`) * used in the initialization process (`loadVehicleDefinition`)
*/ */
class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner class Vehicle(private val vehicleDef: VehicleDefinition)
extends AmenityOwner
with Hackable with Hackable
with FactionAffinity with FactionAffinity
with Mountable with Mountable
@ -86,11 +87,13 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
private var flying: Boolean = false private var flying: Boolean = false
private var ntuCapacitor: Int = 0 private var ntuCapacitor: Int = 0
private var capacitor: Int = 0 private var capacitor: Int = 0
/** /**
* Permissions control who gets to access different parts of the vehicle; * Permissions control who gets to access different parts of the vehicle;
* the groups are Driver (seat), Gunner (seats), Passenger (seats), and the Trunk * the groups are Driver (seat), Gunner (seats), Passenger (seats), and the Trunk
*/ */
private val groupPermissions : Array[VehicleLockState.Value] = Array(VehicleLockState.Locked, VehicleLockState.Empire, VehicleLockState.Empire, VehicleLockState.Locked) private val groupPermissions: Array[VehicleLockState.Value] =
Array(VehicleLockState.Locked, VehicleLockState.Empire, VehicleLockState.Empire, VehicleLockState.Locked)
private var seats: Map[Int, Seat] = Map.empty private var seats: Map[Int, Seat] = Map.empty
private var cargoHolds: Map[Int, Cargo] = Map.empty private var cargoHolds: Map[Int, Cargo] = Map.empty
private var weapons: Map[Int, EquipmentSlot] = Map.empty private var weapons: Map[Int, EquipmentSlot] = Map.empty
@ -266,12 +269,13 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
reindexPermissionsGroup(group) match { reindexPermissionsGroup(group) match {
case Some(index) => case Some(index) =>
val current = groupPermissions(index) val current = groupPermissions(index)
val next = try { VehicleLockState(level.toInt) } catch { case _ : Exception => groupPermissions(index) } val next =
try { VehicleLockState(level.toInt) }
catch { case _: Exception => groupPermissions(index) }
if (current != next) { if (current != next) {
groupPermissions(index) = next groupPermissions(index) = next
PermissionGroup(index) PermissionGroup(index)
} } else {
else {
None None
} }
case None => case None =>
@ -285,13 +289,12 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
* @param group the group index * @param group the group index
* @return the modified group index * @return the modified group index
*/ */
private def reindexPermissionsGroup(group : Int) : Option[Int] = if(group > 9 && group < 14) { private def reindexPermissionsGroup(group: Int): Option[Int] =
if (group > 9 && group < 14) {
Some(group - 10) Some(group - 10)
} } else if (group > -1 && group < 4) {
else if(group > -1 && group < 4) {
Some(group) Some(group)
} } else {
else {
None None
} }
@ -304,8 +307,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
def Seat(seatNumber: Int): Option[Seat] = { def Seat(seatNumber: Int): Option[Seat] = {
if (seatNumber >= 0 && seatNumber < this.seats.size) { if (seatNumber >= 0 && seatNumber < this.seats.size) {
this.seats.get(seatNumber) this.seats.get(seatNumber)
} } else {
else {
None None
} }
} }
@ -317,8 +319,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
def CargoHold(cargoNumber: Int): Option[Cargo] = { def CargoHold(cargoNumber: Int): Option[Cargo] = {
if (cargoNumber >= 0) { if (cargoNumber >= 0) {
this.cargoHolds.get(cargoNumber) this.cargoHolds.get(cargoNumber)
} } else {
else {
None None
} }
} }
@ -330,8 +331,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
def SeatPermissionGroup(seatNumber: Int): Option[AccessPermissionGroup.Value] = { def SeatPermissionGroup(seatNumber: Int): Option[AccessPermissionGroup.Value] = {
if (seatNumber == 0) { if (seatNumber == 0) {
Some(AccessPermissionGroup.Driver) Some(AccessPermissionGroup.Driver)
} } else {
else {
Seat(seatNumber) match { Seat(seatNumber) match {
case Some(seat) => case Some(seat) =>
seat.ControlledWeapon match { seat.ControlledWeapon match {
@ -377,13 +377,11 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
@tailrec private def recursivePassengerInSeat(iter: Iterator[(Int, Seat)], player: Player): Option[Int] = { @tailrec private def recursivePassengerInSeat(iter: Iterator[(Int, Seat)], player: Player): Option[Int] = {
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 {
recursivePassengerInSeat(iter, player) recursivePassengerInSeat(iter, player)
} }
} }
@ -404,8 +402,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
case None => case None =>
None None
} }
} } else {
else {
None None
} }
} }
@ -428,7 +425,8 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
def VisibleSlots: Set[Int] = weapons.keySet def VisibleSlots: Set[Int] = weapons.keySet
override def Slot(slotNum: Int): EquipmentSlot = { override def Slot(slotNum: Int): EquipmentSlot = {
weapons.get(slotNum) weapons
.get(slotNum)
// .orElse(utilities.get(slotNum) match { // .orElse(utilities.get(slotNum) match {
// case Some(_) => // case Some(_) =>
// //TODO what do now? // //TODO what do now?
@ -436,17 +434,18 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
// case None => ; // case None => ;
// None // None
// }) // })
.orElse(Some(Inventory.Slot(slotNum))).get .orElse(Some(Inventory.Slot(slotNum)))
.get
} }
override def Find(guid: PlanetSideGUID): Option[Int] = { override def Find(guid: PlanetSideGUID): Option[Int] = {
weapons.find({ case (_, obj) => weapons.find({
case (_, obj) =>
obj.Equipment match { obj.Equipment match {
case Some(item) => case Some(item) =>
if (item.HasGUID && item.GUID == guid) { if (item.HasGUID && item.GUID == guid) {
true true
} } else {
else {
false false
} }
case None => case None =>
@ -521,8 +520,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
case VehicleLockState.Empire => //anyone of the owner's faction case VehicleLockState.Empire => //anyone of the owner's faction
faction == player.Faction faction == player.Faction
} }
} } else {
else {
false false
} }
} }
@ -569,7 +567,8 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
def canEqual(other: Any): Boolean = other.isInstanceOf[Vehicle] def canEqual(other: Any): Boolean = other.isInstanceOf[Vehicle]
override def equals(other : Any) : Boolean = other match { override def equals(other: Any): Boolean =
other match {
case that: Vehicle => case that: Vehicle =>
(that canEqual this) && (that canEqual this) &&
hashCode() == that.hashCode() hashCode() == that.hashCode()
@ -589,6 +588,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
} }
object Vehicle { object Vehicle {
/** /**
* A basic `Trait` connecting all of the actionable `Vehicle` response messages. * A basic `Trait` connecting all of the actionable `Vehicle` response messages.
*/ */
@ -666,25 +666,30 @@ object Vehicle {
//general stuff //general stuff
vehicle.Health = vdef.DefaultHealth vehicle.Health = vdef.DefaultHealth
//create weapons //create weapons
vehicle.weapons = vdef.Weapons.map({case (num, definition) => vehicle.weapons = vdef.Weapons
.map({
case (num, definition) =>
val slot = EquipmentSlot(EquipmentSize.VehicleWeapon) val slot = EquipmentSlot(EquipmentSize.VehicleWeapon)
slot.Equipment = Tool(definition) slot.Equipment = Tool(definition)
num -> slot num -> slot
}).toMap })
.toMap
//create seats //create seats
vehicle.seats = vdef.Seats.map({ case (num, definition) => num -> Seat(definition) }).toMap vehicle.seats = vdef.Seats.map({ case (num, definition) => num -> Seat(definition) }).toMap
// create cargo holds // create cargo holds
vehicle.cargoHolds = vdef.Cargo.map({ case (num, definition) => num -> Cargo(definition) }).toMap vehicle.cargoHolds = vdef.Cargo.map({ case (num, definition) => num -> Cargo(definition) }).toMap
//create utilities //create utilities
vehicle.utilities = vdef.Utilities.map({ vehicle.utilities = vdef.Utilities
.map({
case (num, util) => case (num, util) =>
val obj = Utility(util, vehicle) val obj = Utility(util, vehicle)
val utilObj = obj() val utilObj = obj()
vehicle.Amenities = utilObj vehicle.Amenities = utilObj
utilObj.LocationOffset = vdef.UtilityOffset.get(num) utilObj.LocationOffset = vdef.UtilityOffset.get(num)
num -> obj num -> obj
}).toMap })
.toMap
//trunk //trunk
vdef.TrunkSize match { vdef.TrunkSize match {
case InventoryTile.None => ; case InventoryTile.None => ;

View file

@ -33,7 +33,10 @@ object Vehicles {
case Some(tplayer) => case Some(tplayer) =>
tplayer.VehicleOwned = vehicle.GUID tplayer.VehicleOwned = vehicle.GUID
vehicle.AssignOwnership(playerOpt) vehicle.AssignOwnership(playerOpt)
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(vehicle.Zone.Id, VehicleAction.Ownership(tplayer.GUID, vehicle.GUID)) vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
vehicle.Zone.Id,
VehicleAction.Ownership(tplayer.GUID, vehicle.GUID)
)
Vehicles.ReloadAccessPermissions(vehicle, tplayer.Name) Vehicles.ReloadAccessPermissions(vehicle, tplayer.Name)
Some(vehicle) Some(vehicle)
case None => case None =>
@ -48,18 +51,23 @@ object Vehicles {
* @return the vehicle, if it had a previous owner; * @return the vehicle, if it had a previous owner;
* `None`, otherwise * `None`, otherwise
*/ */
def Disown(guid : PlanetSideGUID, vehicle : Vehicle) : Option[Vehicle] = vehicle.Zone.GUID(vehicle.Owner) match { def Disown(guid: PlanetSideGUID, vehicle: Vehicle): Option[Vehicle] =
vehicle.Zone.GUID(vehicle.Owner) match {
case Some(player: Player) => case Some(player: Player) =>
if (player.VehicleOwned.contains(guid)) { if (player.VehicleOwned.contains(guid)) {
player.VehicleOwned = None player.VehicleOwned = None
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(player.Name, VehicleAction.Ownership(player.GUID, PlanetSideGUID(0))) vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
player.Name,
VehicleAction.Ownership(player.GUID, PlanetSideGUID(0))
)
} }
vehicle.AssignOwnership(None) vehicle.AssignOwnership(None)
val empire = VehicleLockState.Empire.id val empire = VehicleLockState.Empire.id
val factionChannel = s"${vehicle.Faction}" val factionChannel = s"${vehicle.Faction}"
(0 to 2).foreach(group => { (0 to 2).foreach(group => {
vehicle.PermissionGroup(group, empire) vehicle.PermissionGroup(group, empire)
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(factionChannel, vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
factionChannel,
VehicleAction.SeatPermissions(Service.defaultPlayerGUID, guid, group, empire) VehicleAction.SeatPermissions(Service.defaultPlayerGUID, guid, group, empire)
) )
}) })
@ -77,6 +85,7 @@ object Vehicles {
* @param player the player * @param player the player
*/ */
def Disown(player: Player, zone: Zone): Option[Vehicle] = Disown(player, Some(zone)) def Disown(player: Player, zone: Zone): Option[Vehicle] = Disown(player, Some(zone))
/** /**
* Disassociate a player from a vehicle that he owns. * Disassociate a player from a vehicle that he owns.
* The vehicle must exist in the game world on the specified continent. * The vehicle must exist in the game world on the specified continent.
@ -115,12 +124,14 @@ object Vehicles {
val empire = VehicleLockState.Empire.id val empire = VehicleLockState.Empire.id
(0 to 2).foreach(group => { (0 to 2).foreach(group => {
vehicle.PermissionGroup(group, empire) vehicle.PermissionGroup(group, empire)
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(s"${vehicle.Faction}", VehicleAction.SeatPermissions(pguid, vguid, group, empire)) vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
s"${vehicle.Faction}",
VehicleAction.SeatPermissions(pguid, vguid, group, empire)
)
}) })
ReloadAccessPermissions(vehicle, player.Name) ReloadAccessPermissions(vehicle, player.Name)
Some(vehicle) Some(vehicle)
} } else {
else {
None None
} }
} }
@ -195,8 +206,7 @@ object Vehicles {
def CargoOrientation(vehicle: Vehicle): Int = { def CargoOrientation(vehicle: Vehicle): Int = {
if (vehicle.Definition == GlobalDefinitions.router) { if (vehicle.Definition == GlobalDefinitions.router) {
1 1
} } else {
else {
0 0
} }
} }
@ -210,7 +220,6 @@ object Vehicles {
*/ */
def FinishHackingVehicle(target: Vehicle, hacker: Player, unk: Long)(): Unit = { def FinishHackingVehicle(target: Vehicle, hacker: Player, unk: Long)(): Unit = {
log.info(s"Vehicle guid: ${target.GUID} has been jacked") log.info(s"Vehicle guid: ${target.GUID} has been jacked")
import scala.concurrent.duration._
val zone = target.Zone val zone = target.Zone
// Forcefully dismount any cargo // Forcefully dismount any cargo
target.CargoHolds.values.foreach(cargoHold => { target.CargoHolds.values.foreach(cargoHold => {
@ -218,7 +227,13 @@ object Vehicles {
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(target.Zone, cargo.GUID, bailed = target.Flying, requestedByPassenger = false, kicked = true ) CargoBehavior.HandleVehicleCargoDismount(
target.Zone,
cargo.GUID,
bailed = target.Flying,
requestedByPassenger = false,
kicked = true
)
case None => case None =>
log.error("FinishHackingVehicle: vehicle in cargo hold missing driver") log.error("FinishHackingVehicle: vehicle in cargo hold missing driver")
CargoBehavior.HandleVehicleCargoDismount(cargo.GUID, cargo, target.GUID, target, false, false, true) CargoBehavior.HandleVehicleCargoDismount(cargo.GUID, cargo, target.GUID, target, false, false, true)
@ -234,7 +249,10 @@ object Vehicles {
seat.Occupant = None seat.Occupant = None
tplayer.VehicleSeated = None tplayer.VehicleSeated = None
if (tplayer.HasGUID) { if (tplayer.HasGUID) {
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.KickPassenger(tplayer.GUID, 4, unk2 = false, target.GUID)) zone.VehicleEvents ! VehicleServiceMessage(
zone.Id,
VehicleAction.KickPassenger(tplayer.GUID, 4, unk2 = false, target.GUID)
)
} }
case None => ; case None => ;
} }
@ -270,9 +288,15 @@ object Vehicles {
Vehicles.Own(target, hacker) Vehicles.Own(target, hacker)
//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.Id, AvatarAction.SetEmpire(Service.defaultPlayerGUID, target.GUID, hacker.Faction)) zone.AvatarEvents ! AvatarServiceMessage(
zone.Id,
AvatarAction.SetEmpire(Service.defaultPlayerGUID, target.GUID, hacker.Faction)
)
} }
zone.LocalEvents ! LocalServiceMessage(zone.Id, LocalAction.TriggerSound(hacker.GUID, TriggeredSound.HackVehicle, target.Position, 30, 0.49803925f)) zone.LocalEvents ! LocalServiceMessage(
zone.Id,
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
// If AMS is deployed, swap it to the new faction // If AMS is deployed, swap it to the new faction
target.Definition match { target.Definition match {

View file

@ -7,6 +7,7 @@ import scala.collection.mutable
object Certification { object Certification {
object Dependencies { object Dependencies {
/** /**
* Find the certifications that are immediately dependent on the target certification. * Find the certifications that are immediately dependent on the target certification.
* (For `A`, find all `B` that are `B ⇒ A`.) * (For `A`, find all `B` that are `B ⇒ A`.)
@ -27,8 +28,7 @@ object Certification {
do { do {
available = available.flatMap(cert => dependencies(cert)) available = available.flatMap(cert => dependencies(cert))
allocated ++= available allocated ++= available
} } while (available.nonEmpty)
while(available.nonEmpty)
allocated.toSet allocated.toSet
} }
@ -59,8 +59,7 @@ object Certification {
For For
} }
allocated ++= available allocated ++= available
} } while (available.nonEmpty)
while(available.nonEmpty)
allocated.toSet allocated.toSet
} }
@ -72,7 +71,8 @@ object Certification {
* @param certification the target certification * @param certification the target certification
* @return all connected certifications * @return all connected certifications
*/ */
def Like(certification : CertificationType.Value) : Set[CertificationType.Value] = certification match { def Like(certification: CertificationType.Value): Set[CertificationType.Value] =
certification match {
case AssaultBuggy => case AssaultBuggy =>
Set(Harasser) Set(Harasser)
case LightScout => case LightScout =>
@ -96,7 +96,6 @@ object Certification {
AVMAX -> List(), AVMAX -> List(),
AAMAX -> List(), AAMAX -> List(),
UniMAX -> List(), UniMAX -> List(),
StandardAssault -> List(), StandardAssault -> List(),
MediumAssault -> List(AntiVehicular, HeavyAssault, Sniping, SpecialAssault), MediumAssault -> List(AntiVehicular, HeavyAssault, Sniping, SpecialAssault),
AntiVehicular -> List(), AntiVehicular -> List(),
@ -104,7 +103,6 @@ object Certification {
Sniping -> List(), Sniping -> List(),
SpecialAssault -> List(EliteAssault), SpecialAssault -> List(EliteAssault),
EliteAssault -> List(), EliteAssault -> List(),
ATV -> List(Switchblade), ATV -> List(Switchblade),
Switchblade -> List(), Switchblade -> List(),
Harasser -> List(), Harasser -> List(),
@ -115,18 +113,15 @@ object Certification {
ArmoredAssault1 -> List(ArmoredAssault2), ArmoredAssault1 -> List(ArmoredAssault2),
ArmoredAssault2 -> List(BattleFrameRobotics, Flail), ArmoredAssault2 -> List(BattleFrameRobotics, Flail),
Flail -> List(), Flail -> List(),
AirCavalryScout -> List(AirCavalryAssault), AirCavalryScout -> List(AirCavalryAssault),
AirCavalryAssault -> List(AirCavalryInterceptor), AirCavalryAssault -> List(AirCavalryInterceptor),
AirCavalryInterceptor -> List(), AirCavalryInterceptor -> List(),
AirSupport -> List(GalaxyGunship), AirSupport -> List(GalaxyGunship),
GalaxyGunship -> List(), GalaxyGunship -> List(),
Phantasm -> List(), Phantasm -> List(),
BattleFrameRobotics -> List(BFRAntiInfantry, BFRAntiAircraft), BattleFrameRobotics -> List(BFRAntiInfantry, BFRAntiAircraft),
BFRAntiInfantry -> List(), BFRAntiInfantry -> List(),
BFRAntiAircraft -> List(), BFRAntiAircraft -> List(),
Medical -> List(AdvancedMedical), Medical -> List(AdvancedMedical),
AdvancedMedical -> List(), AdvancedMedical -> List(),
Engineering -> List(CombatEngineering), Engineering -> List(CombatEngineering),
@ -143,6 +138,7 @@ object Certification {
} }
object Cost { object Cost {
/** /**
* For a certification, get its point cost. * For a certification, get its point cost.
* @param certification the certification * @param certification the certification
@ -185,7 +181,6 @@ object Certification {
AIMAX -> 3, AIMAX -> 3,
AVMAX -> 3, AVMAX -> 3,
UniMAX -> 6, UniMAX -> 6,
StandardAssault -> 0, StandardAssault -> 0,
MediumAssault -> 2, MediumAssault -> 2,
AntiVehicular -> 3, AntiVehicular -> 3,
@ -193,7 +188,6 @@ object Certification {
Sniping -> 3, Sniping -> 3,
SpecialAssault -> 3, SpecialAssault -> 3,
EliteAssault -> 1, EliteAssault -> 1,
ATV -> 1, ATV -> 1,
Switchblade -> 1, Switchblade -> 1,
Harasser -> 1, Harasser -> 1,
@ -204,18 +198,15 @@ object Certification {
ArmoredAssault1 -> 2, ArmoredAssault1 -> 2,
ArmoredAssault2 -> 3, ArmoredAssault2 -> 3,
Flail -> 1, Flail -> 1,
AirCavalryScout -> 3, AirCavalryScout -> 3,
AirCavalryAssault -> 2, AirCavalryAssault -> 2,
AirCavalryInterceptor -> 2, AirCavalryInterceptor -> 2,
AirSupport -> 3, AirSupport -> 3,
GalaxyGunship -> 2, GalaxyGunship -> 2,
Phantasm -> 3, Phantasm -> 3,
BattleFrameRobotics -> 4, BattleFrameRobotics -> 4,
BFRAntiInfantry -> 1, BFRAntiInfantry -> 1,
BFRAntiAircraft -> 1, BFRAntiAircraft -> 1,
Medical -> 3, Medical -> 3,
AdvancedMedical -> 2, AdvancedMedical -> 2,
Engineering -> 3, Engineering -> 3,

View file

@ -11,8 +11,7 @@ import net.psforever.types.{PlanetSideEmpire, Vector3}
import services.Service import services.Service
import services.avatar.{AvatarAction, AvatarServiceMessage} import services.avatar.{AvatarAction, AvatarServiceMessage}
class CorpseControl(player : Player) extends Actor class CorpseControl(player: Player) extends Actor with ContainableBehavior {
with ContainableBehavior {
def ContainerObject = player def ContainerObject = player
//private [this] val log = org.log4s.getLogger(player.Name) //private [this] val log = org.log4s.getLogger(player.Name)
@ -66,7 +65,8 @@ class CorpseControl(player : Player) extends Actor
def SwapItemCallback(item: Equipment): Unit = { def SwapItemCallback(item: Equipment): Unit = {
val obj = ContainerObject val obj = ContainerObject
val zone = obj.Zone val zone = obj.Zone
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, zone.AvatarEvents ! AvatarServiceMessage(
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)

View file

@ -23,24 +23,31 @@ import scala.collection.mutable
* these structures are updated to reflect proper count. * these structures are updated to reflect proper count.
*/ */
class DeployableToolbox { class DeployableToolbox {
/** /**
* a map of bins for keeping track of the quantities of deployables in a category * a map of bins for keeping track of the quantities of deployables in a category
* keys: categories, values: quantity storage object * keys: categories, values: quantity storage object
*/ */
private val categoryCounts = DeployableCategory.values.toSeq.map(value => { value -> new DeployableToolbox.Bin }).toMap private val categoryCounts =
DeployableCategory.values.toSeq.map(value => { value -> new DeployableToolbox.Bin }).toMap
categoryCounts(DeployableCategory.Telepads).Max = 1024 categoryCounts(DeployableCategory.Telepads).Max = 1024
/** /**
* a map of bins for keeping track of the quantities of individual deployables * a map of bins for keeping track of the quantities of individual deployables
* keys: deployable types, values: quantity storage object * keys: deployable types, values: quantity storage object
*/ */
private val deployableCounts = DeployedItem.values.toSeq.map(value => { value -> new DeployableToolbox.Bin }).toMap private val deployableCounts = DeployedItem.values.toSeq.map(value => { value -> new DeployableToolbox.Bin }).toMap
deployableCounts(DeployedItem.router_telepad_deployable).Max = 1024 deployableCounts(DeployedItem.router_telepad_deployable).Max = 1024
/** /**
* a map of tracked/owned individual deployables * a map of tracked/owned individual deployables
* keys: categories, values: deployable objects * keys: categories, values: deployable objects
*/ */
private val deployableLists = private val deployableLists =
DeployableCategory.values.toSeq.map(value => { value -> mutable.ListBuffer[DeployableToolbox.AcceptableDeployable]() }).toMap DeployableCategory.values.toSeq
.map(value => { value -> mutable.ListBuffer[DeployableToolbox.AcceptableDeployable]() })
.toMap
/** /**
* can only be initialized once * can only be initialized once
* set during the `Initialization` method primarily, and in `Add` and in `Remove` if not * set during the `Initialization` method primarily, and in `Add` and in `Remove` if not
@ -58,8 +65,7 @@ class DeployableToolbox {
DeployableToolbox.Initialize(deployableCounts, categoryCounts, certifications) DeployableToolbox.Initialize(deployableCounts, categoryCounts, certifications)
initialized = true initialized = true
true true
} } else {
else {
false false
} }
} }
@ -72,7 +78,10 @@ class DeployableToolbox {
* occasionally, important former certification values are required for additional configuration; * occasionally, important former certification values are required for additional configuration;
* the new certification should already have been added to this group * the new certification should already have been added to this group
*/ */
def AddToDeployableQuantities(certification : CertificationType.Value, certificationSet : Set[CertificationType.Value]) : Unit = { def AddToDeployableQuantities(
certification: CertificationType.Value,
certificationSet: Set[CertificationType.Value]
): Unit = {
initialized = true initialized = true
DeployableToolbox.AddToDeployableQuantities(deployableCounts, categoryCounts, certification, certificationSet) DeployableToolbox.AddToDeployableQuantities(deployableCounts, categoryCounts, certification, certificationSet)
} }
@ -86,7 +95,10 @@ class DeployableToolbox {
* occasionally, important former certification values are required for additional configuration; * occasionally, important former certification values are required for additional configuration;
* the new certification should already have been excluded from this group * the new certification should already have been excluded from this group
*/ */
def RemoveFromDeployableQuantities(certification : CertificationType.Value, certificationSet : Set[CertificationType.Value]) : Unit = { def RemoveFromDeployableQuantities(
certification: CertificationType.Value,
certificationSet: Set[CertificationType.Value]
): Unit = {
initialized = true initialized = true
DeployableToolbox.RemoveFromDeployablesQuantities(deployableCounts, categoryCounts, certification, certificationSet) DeployableToolbox.RemoveFromDeployablesQuantities(deployableCounts, categoryCounts, certification, certificationSet)
} }
@ -162,8 +174,7 @@ class DeployableToolbox {
dType.Current += 1 dType.Current += 1
dList += obj dList += obj
true true
} } else {
else {
false false
} }
} }
@ -187,8 +198,7 @@ class DeployableToolbox {
deployableCounts(DeployableToolbox.UnifiedType(obj.Definition.Item)).Current -= 1 deployableCounts(DeployableToolbox.UnifiedType(obj.Definition.Item)).Current -= 1
deployables -= obj deployables -= obj
true true
} } else {
else {
false false
} }
} }
@ -213,7 +223,10 @@ class DeployableToolbox {
* @param rule the testing rule for determining a valid deployable * @param rule the testing rule for determining a valid deployable
* @return any deployable that is found * @return any deployable that is found
*/ */
def DisplaceFirst(obj : DeployableToolbox.AcceptableDeployable, rule : (Deployable)=> Boolean) : Option[DeployableToolbox.AcceptableDeployable] = { def DisplaceFirst(
obj: DeployableToolbox.AcceptableDeployable,
rule: (Deployable) => Boolean
): Option[DeployableToolbox.AcceptableDeployable] = {
val definition = obj.Definition val definition = obj.Definition
val category = definition.DeployCategory val category = definition.DeployCategory
val categoryList = deployableLists(category) val categoryList = deployableLists(category)
@ -227,8 +240,7 @@ class DeployableToolbox {
categoryCounts(category).Current -= 1 categoryCounts(category).Current -= 1
deployableCounts(DeployableToolbox.UnifiedType(found.Definition.Item)).Current -= 1 deployableCounts(DeployableToolbox.UnifiedType(found.Definition.Item)).Current -= 1
Some(found) Some(found)
} } else {
else {
None None
} }
} }
@ -246,8 +258,7 @@ class DeployableToolbox {
categoryCounts(category).Current -= 1 categoryCounts(category).Current -= 1
deployableCounts(DeployableToolbox.UnifiedType(found.Definition.Item)).Current -= 1 deployableCounts(DeployableToolbox.UnifiedType(found.Definition.Item)).Current -= 1
Some(found) Some(found)
} } else {
else {
None None
} }
} }
@ -269,7 +280,8 @@ class DeployableToolbox {
def Deployables(filter: DeployedItem.Value): List[PlanetSideGUID] = { def Deployables(filter: DeployedItem.Value): List[PlanetSideGUID] = {
deployableLists(Deployable.Category.Of(filter)) deployableLists(Deployable.Category.Of(filter))
.filter(entry => { entry.Definition.Item == filter }) .filter(entry => { entry.Definition.Item == filter })
.map(_.GUID).toList .map(_.GUID)
.toList
} }
/** /**
@ -327,19 +339,26 @@ class DeployableToolbox {
case CombatEngineering => case CombatEngineering =>
List( List(
DeployedItem.boomer, DeployedItem.he_mine, DeployedItem.spitfire_turret, DeployedItem.motionalarmsensor DeployedItem.boomer,
DeployedItem.he_mine,
DeployedItem.spitfire_turret,
DeployedItem.motionalarmsensor
) flatMap UpdateUIElement ) flatMap UpdateUIElement
case AssaultEngineering => case AssaultEngineering =>
List( List(
DeployedItem.jammer_mine, DeployedItem.portable_manned_turret, DeployedItem.deployable_shield_generator DeployedItem.jammer_mine,
DeployedItem.portable_manned_turret,
DeployedItem.deployable_shield_generator
) flatMap UpdateUIElement ) flatMap UpdateUIElement
case FortificationEngineering => case FortificationEngineering =>
List( List(
DeployedItem.boomer, DeployedItem.boomer,
DeployedItem.he_mine, DeployedItem.he_mine,
DeployedItem.spitfire_turret, DeployedItem.spitfire_cloaked, DeployedItem.spitfire_aa, DeployedItem.spitfire_turret,
DeployedItem.spitfire_cloaked,
DeployedItem.spitfire_aa,
DeployedItem.motionalarmsensor, DeployedItem.motionalarmsensor,
DeployedItem.tank_traps DeployedItem.tank_traps
) flatMap UpdateUIElement ) flatMap UpdateUIElement
@ -403,6 +422,7 @@ class DeployableToolbox {
} }
object DeployableToolbox { object DeployableToolbox {
/** /**
* A `type` intended to properly define the minimum acceptable conditions for a `Deployable` object. * A `type` intended to properly define the minimum acceptable conditions for a `Deployable` object.
*/ */
@ -413,8 +433,10 @@ object DeployableToolbox {
* There are deployable numbers organized by deploybale type and by deployable category. * There are deployable numbers organized by deploybale type and by deployable category.
*/ */
private class Bin { private class Bin {
/** the maximum number of deployables for this criteria that can be managed */ /** the maximum number of deployables for this criteria that can be managed */
private var max: Int = 0 private var max: Int = 0
/** the current number of deployables for this criteria that are being managed */ /** the current number of deployables for this criteria that are being managed */
private var current: Int = 0 private var current: Int = 0
@ -441,8 +463,10 @@ object DeployableToolbox {
* @param item the type of deployable * @param item the type of deployable
* @return the corrected deployable type * @return the corrected deployable type
*/ */
def UnifiedType(item : DeployedItem.Value) : DeployedItem.Value = item match { def UnifiedType(item: DeployedItem.Value): DeployedItem.Value =
case DeployedItem.portable_manned_turret_nc | DeployedItem.portable_manned_turret_tr | DeployedItem.portable_manned_turret_vs => item match {
case DeployedItem.portable_manned_turret_nc | DeployedItem.portable_manned_turret_tr |
DeployedItem.portable_manned_turret_vs =>
DeployedItem.portable_manned_turret DeployedItem.portable_manned_turret
case _ => case _ =>
item item
@ -454,7 +478,11 @@ object DeployableToolbox {
* @param categories a reference to the category `Bin` object * @param categories a reference to the category `Bin` object
* @param certifications a group of certifications for the initial values * @param certifications a group of certifications for the initial values
*/ */
private def Initialize(counts : Map[DeployedItem.Value, DeployableToolbox.Bin], categories : Map[DeployableCategory.Value, DeployableToolbox.Bin], certifications : Set[CertificationType.Value]) : Unit = { private def Initialize(
counts: Map[DeployedItem.Value, DeployableToolbox.Bin],
categories: Map[DeployableCategory.Value, DeployableToolbox.Bin],
certifications: Set[CertificationType.Value]
): Unit = {
import CertificationType._ import CertificationType._
if (certifications.contains(AdvancedEngineering)) { if (certifications.contains(AdvancedEngineering)) {
counts(DeployedItem.boomer).Max = 25 counts(DeployedItem.boomer).Max = 25
@ -481,8 +509,7 @@ object DeployableToolbox {
if (certifications.contains(AdvancedHacking)) { if (certifications.contains(AdvancedHacking)) {
counts(DeployedItem.sensor_shield).Max = 25 counts(DeployedItem.sensor_shield).Max = 25
} }
} } else if (certifications.contains(CombatEngineering)) {
else if(certifications.contains(CombatEngineering)) {
if (certifications.contains(AssaultEngineering)) { if (certifications.contains(AssaultEngineering)) {
counts(DeployedItem.jammer_mine).Max = 20 counts(DeployedItem.jammer_mine).Max = 20
counts(DeployedItem.portable_manned_turret).Max = 1 //the below turret types are unified counts(DeployedItem.portable_manned_turret).Max = 1 //the below turret types are unified
@ -506,8 +533,7 @@ object DeployableToolbox {
categories(DeployableCategory.SmallTurrets).Max = 15 categories(DeployableCategory.SmallTurrets).Max = 15
categories(DeployableCategory.Sensors).Max = 25 categories(DeployableCategory.Sensors).Max = 25
categories(DeployableCategory.TankTraps).Max = 5 categories(DeployableCategory.TankTraps).Max = 5
} } else {
else {
counts(DeployedItem.boomer).Max = 20 counts(DeployedItem.boomer).Max = 20
counts(DeployedItem.he_mine).Max = 20 counts(DeployedItem.he_mine).Max = 20
counts(DeployedItem.spitfire_turret).Max = 10 counts(DeployedItem.spitfire_turret).Max = 10
@ -535,7 +561,12 @@ object DeployableToolbox {
* @param certification the new certification * @param certification the new certification
* @param certificationSet the group of previous certifications being tracked * @param certificationSet the group of previous certifications being tracked
*/ */
def AddToDeployableQuantities(counts : Map[DeployedItem.Value, DeployableToolbox.Bin], categories : Map[DeployableCategory.Value, DeployableToolbox.Bin], certification : CertificationType.Value, certificationSet : Set[CertificationType.Value]) : Unit = { def AddToDeployableQuantities(
counts: Map[DeployedItem.Value, DeployableToolbox.Bin],
categories: Map[DeployableCategory.Value, DeployableToolbox.Bin],
certification: CertificationType.Value,
certificationSet: Set[CertificationType.Value]
): Unit = {
import CertificationType._ import CertificationType._
if (certificationSet contains certification) { if (certificationSet contains certification) {
certification match { certification match {
@ -583,10 +614,20 @@ object DeployableToolbox {
case AdvancedEngineering => case AdvancedEngineering =>
if (!certificationSet.contains(AssaultEngineering)) { if (!certificationSet.contains(AssaultEngineering)) {
AddToDeployableQuantities(counts, categories, AssaultEngineering, certificationSet ++ Set(AssaultEngineering)) AddToDeployableQuantities(
counts,
categories,
AssaultEngineering,
certificationSet ++ Set(AssaultEngineering)
)
} }
if (!certificationSet.contains(FortificationEngineering)) { if (!certificationSet.contains(FortificationEngineering)) {
AddToDeployableQuantities(counts, categories, FortificationEngineering, certificationSet ++ Set(FortificationEngineering)) AddToDeployableQuantities(
counts,
categories,
FortificationEngineering,
certificationSet ++ Set(FortificationEngineering)
)
} }
// case GroundSupport => // case GroundSupport =>
@ -605,7 +646,12 @@ object DeployableToolbox {
* @param certification the new certification * @param certification the new certification
* @param certificationSet the group of previous certifications being tracked * @param certificationSet the group of previous certifications being tracked
*/ */
def RemoveFromDeployablesQuantities(counts : Map[DeployedItem.Value, DeployableToolbox.Bin], categories : Map[DeployableCategory.Value, DeployableToolbox.Bin], certification : CertificationType.Value, certificationSet : Set[CertificationType.Value]) : Unit = { def RemoveFromDeployablesQuantities(
counts: Map[DeployedItem.Value, DeployableToolbox.Bin],
categories: Map[DeployableCategory.Value, DeployableToolbox.Bin],
certification: CertificationType.Value,
certificationSet: Set[CertificationType.Value]
): Unit = {
import CertificationType._ import CertificationType._
if (!certificationSet.contains(certification)) { if (!certificationSet.contains(certification)) {
certification match { certification match {

View file

@ -4,7 +4,11 @@ package net.psforever.objects.avatar
object FirstTimeEvents { object FirstTimeEvents {
object TR { object TR {
val InfantryWeapons: Set[String] = Set( val InfantryWeapons: Set[String] = Set(
"used_chainblade","used_repeater","used_cycler","used_mini_chaingun","used_striker", "used_chainblade",
"used_repeater",
"used_cycler",
"used_mini_chaingun",
"used_striker",
"used_anniversary_guna" "used_anniversary_guna"
) )
@ -18,13 +22,20 @@ object FirstTimeEvents {
"used_colossus_cluster_bomb_pod", "used_colossus_cluster_bomb_pod",
"used_colossus_dual_100mm_cannons", "used_colossus_dual_100mm_cannons",
"used_colossus_tank_cannon", "used_colossus_tank_cannon",
"visited_threemanheavybuggy","visited_battlewagon","visited_apc_tr","visited_prowler", "visited_threemanheavybuggy",
"visited_colossus_flight","visited_colossus_gunner" "visited_battlewagon",
"visited_apc_tr",
"visited_prowler",
"visited_colossus_flight",
"visited_colossus_gunner"
) )
val Other: Set[String] = Set( val Other: Set[String] = Set(
"used_trhev_dualcycler","used_trhev_pounder","used_trhev_burster", "used_trhev_dualcycler",
"used_colossus_dual_100mm_cannons","used_colossus_tank_cannon", "used_trhev_pounder",
"used_trhev_burster",
"used_colossus_dual_100mm_cannons",
"used_colossus_tank_cannon",
"used_energy_gun_tr", "used_energy_gun_tr",
"visited_portable_manned_turret_tr" "visited_portable_manned_turret_tr"
) )
@ -34,7 +45,11 @@ object FirstTimeEvents {
object NC { object NC {
val InfantryWeapons: Set[String] = Set( val InfantryWeapons: Set[String] = Set(
"used_magcutter","used_isp","used_gauss","used_r_shotgun","used_hunterseeker", "used_magcutter",
"used_isp",
"used_gauss",
"used_r_shotgun",
"used_hunterseeker",
"used_anniversary_gun" "used_anniversary_gun"
) )
@ -48,12 +63,18 @@ object FirstTimeEvents {
"used_peregrine_mechhammer", "used_peregrine_mechhammer",
"used_peregrine_particle_cannon", "used_peregrine_particle_cannon",
"used_peregrine_sparrow", "used_peregrine_sparrow",
"visited_twomanheavybuggy","visited_thunderer","visited_apc_nc","visited_vanguard", "visited_twomanheavybuggy",
"visited_peregrine_flight","visited_peregrine_gunner" "visited_thunderer",
"visited_apc_nc",
"visited_vanguard",
"visited_peregrine_flight",
"visited_peregrine_gunner"
) )
val Other: Set[String] = Set( val Other: Set[String] = Set(
"used_nchev_scattercannon","used_nchev_falcon","used_nchev_sparrow", "used_nchev_scattercannon",
"used_nchev_falcon",
"used_nchev_sparrow",
"used_energy_gun_nc", "used_energy_gun_nc",
"visited_portable_manned_turret_nc" "visited_portable_manned_turret_nc"
) )
@ -63,7 +84,11 @@ object FirstTimeEvents {
object VS { object VS {
val InfantryWeapons: Set[String] = Set( val InfantryWeapons: Set[String] = Set(
"used_forceblade","used_beamer","used_pulsar","used_lasher","used_lancer", "used_forceblade",
"used_beamer",
"used_pulsar",
"used_lasher",
"used_lancer",
"used_anniversary_gunb" "used_anniversary_gunb"
) )
@ -78,12 +103,18 @@ object FirstTimeEvents {
"used_aphelion_immolation_cannon", "used_aphelion_immolation_cannon",
"used_aphelion_plasma_rocket_pod", "used_aphelion_plasma_rocket_pod",
"used_aphelion_ppa", "used_aphelion_ppa",
"visited_twomanhoverbuggy","visited_aurora","visited_apc_vs","visited_magrider", "visited_twomanhoverbuggy",
"visited_aphelion_flight","visited_aphelion_gunner" "visited_aurora",
"visited_apc_vs",
"visited_magrider",
"visited_aphelion_flight",
"visited_aphelion_gunner"
) )
val Other: Set[String] = Set( val Other: Set[String] = Set(
"used_vshev_quasar","used_vshev_comet","used_vshev_starfire", "used_vshev_quasar",
"used_vshev_comet",
"used_vshev_starfire",
"used_energy_gun_vs", "used_energy_gun_vs",
"visited_portable_manned_turret_vs" "visited_portable_manned_turret_vs"
) )
@ -93,9 +124,21 @@ object FirstTimeEvents {
object Standard { object Standard {
val InfantryWeapons: Set[String] = Set( val InfantryWeapons: Set[String] = Set(
"used_grenade_plasma","used_grenade_jammer","used_grenade_frag", "used_grenade_plasma",
"used_grenade_jammer",
"used_grenade_frag",
"used_katana", "used_katana",
"used_ilc9","used_suppressor","used_punisher","used_flechette","used_phoenix","used_thumper","used_rocklet","used_bolt_driver","used_heavy_sniper","used_oicw","used_flamethrower" "used_ilc9",
"used_suppressor",
"used_punisher",
"used_flechette",
"used_phoenix",
"used_thumper",
"used_rocklet",
"used_bolt_driver",
"used_heavy_sniper",
"used_oicw",
"used_flamethrower"
) )
val Vehicles: Set[String] = Set( val Vehicles: Set[String] = Set(
@ -116,33 +159,74 @@ object FirstTimeEvents {
"used_vulture_nose_cannon", "used_vulture_nose_cannon",
"used_vulture_tail_cannon", "used_vulture_tail_cannon",
"used_liberator_bombardier", "used_liberator_bombardier",
"visited_ams","visited_ant", "visited_ams",
"visited_quadassault","visited_fury","visited_quadstealth", "visited_ant",
"visited_two_man_assault_buggy","visited_skyguard", "visited_quadassault",
"visited_mediumtransport","visited_apc","visited_lightning", "visited_fury",
"visited_mosquito","visited_lightgunship","visited_wasp", "visited_quadstealth",
"visited_liberator","visited_vulture","visited_dropship","visited_galaxy_gunship", "visited_two_man_assault_buggy",
"visited_phantasm","visited_lodestar" "visited_skyguard",
"visited_mediumtransport",
"visited_apc",
"visited_lightning",
"visited_mosquito",
"visited_lightgunship",
"visited_wasp",
"visited_liberator",
"visited_vulture",
"visited_dropship",
"visited_galaxy_gunship",
"visited_phantasm",
"visited_lodestar"
) )
val Facilities: Set[String] = Set( val Facilities: Set[String] = Set(
"visited_broadcast_warpgate","visited_warpgate_small", "visited_broadcast_warpgate",
"visited_respawn_terminal","visited_deconstruction_terminal", "visited_warpgate_small",
"visited_capture_terminal","visited_secondary_capture","visited_LLU_socket","visited_resource_silo", "visited_respawn_terminal",
"visited_med_terminal","visited_adv_med_terminal","visited_repair_silo", "visited_deconstruction_terminal",
"visited_order_terminal","visited_certification_terminal","visited_implant_terminal","visited_locker", "visited_capture_terminal",
"visited_ground_vehicle_terminal","visited_bfr_terminal","visited_air_vehicle_terminal","visited_galaxy_terminal", "visited_secondary_capture",
"visited_generator","visited_generator_terminal", "visited_LLU_socket",
"visited_wall_turret","used_phalanx","used_phalanx_avcombo","used_phalanx_flakcombo", "visited_resource_silo",
"visited_med_terminal",
"visited_adv_med_terminal",
"visited_repair_silo",
"visited_order_terminal",
"visited_certification_terminal",
"visited_implant_terminal",
"visited_locker",
"visited_ground_vehicle_terminal",
"visited_bfr_terminal",
"visited_air_vehicle_terminal",
"visited_galaxy_terminal",
"visited_generator",
"visited_generator_terminal",
"visited_wall_turret",
"used_phalanx",
"used_phalanx_avcombo",
"used_phalanx_flakcombo",
"visited_external_door_lock" "visited_external_door_lock"
) )
val Other: Set[String] = Set( val Other: Set[String] = Set(
"used_command_uplink", "used_command_uplink",
"used_med_app","used_nano_dispenser","used_bank","used_ace","used_advanced_ace","used_rek","used_trek","used_laze_pointer","used_telepad", "used_med_app",
"visited_motion_sensor","visited_sensor_shield", "used_nano_dispenser",
"visited_spitfire_turret","visited_spitfire_cloaked","visited_spitfire_aa", "used_bank",
"visited_shield_generator","visited_tank_traps" "used_ace",
"used_advanced_ace",
"used_rek",
"used_trek",
"used_laze_pointer",
"used_telepad",
"visited_motion_sensor",
"visited_sensor_shield",
"visited_spitfire_turret",
"visited_spitfire_cloaked",
"visited_spitfire_aa",
"visited_shield_generator",
"visited_tank_traps"
) )
val All: Set[String] = InfantryWeapons ++ Vehicles ++ Facilities ++ Other val All: Set[String] = InfantryWeapons ++ Vehicles ++ Facilities ++ Other
@ -150,13 +234,17 @@ object FirstTimeEvents {
object Cavern { object Cavern {
val InfantryWeapons: Set[String] = Set( val InfantryWeapons: Set[String] = Set(
"used_spiker","used_radiator","used_maelstrom" "used_spiker",
"used_radiator",
"used_maelstrom"
) )
val Vehicles: Set[String] = Set( val Vehicles: Set[String] = Set(
"used_scythe", "used_scythe",
"used_flail_weapon", "used_flail_weapon",
"visited_switchblade","visited_flail","visited_router" "visited_switchblade",
"visited_flail",
"visited_router"
) )
val Facilities: Set[String] = Set( val Facilities: Set[String] = Set(
@ -180,10 +268,32 @@ object FirstTimeEvents {
} }
val Maps: Set[String] = Set( val Maps: Set[String] = Set(
"map01","map02","map03","map04","map05","map06","map07","map08","map09","map10", "map01",
"map11","map12","map13","map14","map15","map16", "map02",
"ugd01","ugd02","ugd03","ugd04","ugd05","ugd06", "map03",
"map96","map97","map98","map99" "map04",
"map05",
"map06",
"map07",
"map08",
"map09",
"map10",
"map11",
"map12",
"map13",
"map14",
"map15",
"map16",
"ugd01",
"ugd02",
"ugd03",
"ugd04",
"ugd05",
"ugd06",
"map96",
"map97",
"map98",
"map99"
) )
val Monoliths: Set[String] = Set( val Monoliths: Set[String] = Set(
@ -247,18 +357,54 @@ object FirstTimeEvents {
) )
val BattleRanks: Set[String] = Set( val BattleRanks: Set[String] = Set(
"xpe_battle_rank_1","xpe_battle_rank_2","xpe_battle_rank_3","xpe_battle_rank_4","xpe_battle_rank_5", "xpe_battle_rank_1",
"xpe_battle_rank_6","xpe_battle_rank_7","xpe_battle_rank_8","xpe_battle_rank_9","xpe_battle_rank_10", "xpe_battle_rank_2",
"xpe_battle_rank_11","xpe_battle_rank_12","xpe_battle_rank_13","xpe_battle_rank_14","xpe_battle_rank_15", "xpe_battle_rank_3",
"xpe_battle_rank_16","xpe_battle_rank_17","xpe_battle_rank_18","xpe_battle_rank_19","xpe_battle_rank_20", "xpe_battle_rank_4",
"xpe_battle_rank_21","xpe_battle_rank_22","xpe_battle_rank_23","xpe_battle_rank_24","xpe_battle_rank_25", "xpe_battle_rank_5",
"xpe_battle_rank_26","xpe_battle_rank_27","xpe_battle_rank_28","xpe_battle_rank_29","xpe_battle_rank_30", "xpe_battle_rank_6",
"xpe_battle_rank_31","xpe_battle_rank_32","xpe_battle_rank_33","xpe_battle_rank_34","xpe_battle_rank_35", "xpe_battle_rank_7",
"xpe_battle_rank_36","xpe_battle_rank_37","xpe_battle_rank_38","xpe_battle_rank_39","xpe_battle_rank_40" "xpe_battle_rank_8",
"xpe_battle_rank_9",
"xpe_battle_rank_10",
"xpe_battle_rank_11",
"xpe_battle_rank_12",
"xpe_battle_rank_13",
"xpe_battle_rank_14",
"xpe_battle_rank_15",
"xpe_battle_rank_16",
"xpe_battle_rank_17",
"xpe_battle_rank_18",
"xpe_battle_rank_19",
"xpe_battle_rank_20",
"xpe_battle_rank_21",
"xpe_battle_rank_22",
"xpe_battle_rank_23",
"xpe_battle_rank_24",
"xpe_battle_rank_25",
"xpe_battle_rank_26",
"xpe_battle_rank_27",
"xpe_battle_rank_28",
"xpe_battle_rank_29",
"xpe_battle_rank_30",
"xpe_battle_rank_31",
"xpe_battle_rank_32",
"xpe_battle_rank_33",
"xpe_battle_rank_34",
"xpe_battle_rank_35",
"xpe_battle_rank_36",
"xpe_battle_rank_37",
"xpe_battle_rank_38",
"xpe_battle_rank_39",
"xpe_battle_rank_40"
) )
val CommandRanks: Set[String] = Set( val CommandRanks: Set[String] = Set(
"xpe_command_rank_1","xpe_command_rank_2","xpe_command_rank_3","xpe_command_rank_4","xpe_command_rank_5" "xpe_command_rank_1",
"xpe_command_rank_2",
"xpe_command_rank_3",
"xpe_command_rank_4",
"xpe_command_rank_5"
) )
val Training: Set[String] = Set( val Training: Set[String] = Set(

View file

@ -2,7 +2,6 @@
package net.psforever.objects.avatar package net.psforever.objects.avatar
import net.psforever.objects.loadouts.Loadout import net.psforever.objects.loadouts.Loadout
import net.psforever.types.LoadoutType
import scala.util.Success import scala.util.Success
@ -25,5 +24,6 @@ class LoadoutManager(size : Int) {
} }
} }
def Loadouts : Seq[(Int, Loadout)] = entries.zipWithIndex.collect { case(Some(loadout), index) => (index, loadout) } toSeq def Loadouts: Seq[(Int, Loadout)] =
entries.zipWithIndex.collect { case (Some(loadout), index) => (index, loadout) } toSeq
} }

View file

@ -27,10 +27,7 @@ import scala.concurrent.duration._
import scala.collection.mutable import scala.collection.mutable
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
class PlayerControl(player : Player) extends Actor class PlayerControl(player: Player) extends Actor with JammableBehavior with Damageable with ContainableBehavior {
with JammableBehavior
with Damageable
with ContainableBehavior {
def JammableObject = player def JammableObject = player
def DamageableObject = player def DamageableObject = player
def ContainerObject = player def ContainerObject = player
@ -40,16 +37,22 @@ class PlayerControl(player : Player) extends Actor
/** Stamina will be used. Stamina will be restored. */ /** Stamina will be used. Stamina will be restored. */
var staminaRegen: Cancellable = Default.Cancellable var staminaRegen: Cancellable = Default.Cancellable
/** /**
* A collection of timers indexed for the implant in each slot. * A collection of timers indexed for the implant in each slot.
* Before an implant is ready, it serves as the initialization timer. * 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. */ * 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) 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.Locker
locker.Zone = player.Zone locker.Zone = player.Zone
locker.Actor = context.actorOf(Props(classOf[LockerContainerControl], locker, player.Name), PlanetSideServerObject.UniqueActorName(locker)) locker.Actor = context.actorOf(
Props(classOf[LockerContainerControl], locker, player.Name),
PlanetSideServerObject.UniqueActorName(locker)
)
} }
override def postStop(): Unit = { override def postStop(): Unit = {
@ -59,7 +62,8 @@ class PlayerControl(player : Player) extends Actor
implantSlotTimers.values.foreach { _.cancel } implantSlotTimers.values.foreach { _.cancel }
} }
def receive : Receive = jammableBehavior def receive: Receive =
jammableBehavior
.orElse(takesDamage) .orElse(takesDamage)
.orElse(containerBehavior) .orElse(containerBehavior)
.orElse { .orElse {
@ -78,7 +82,8 @@ class PlayerControl(player : Player) extends Actor
case Player.StaminaRegen() => case Player.StaminaRegen() =>
if (staminaRegen == Default.Cancellable) { if (staminaRegen == Default.Cancellable) {
staminaRegen.cancel staminaRegen.cancel
staminaRegen = context.system.scheduler.scheduleOnce(delay = 500 milliseconds, self, PlayerControl.StaminaRegen()) staminaRegen =
context.system.scheduler.scheduleOnce(delay = 500 milliseconds, self, PlayerControl.StaminaRegen())
} }
case PlayerControl.StaminaRegen() => case PlayerControl.StaminaRegen() =>
@ -87,13 +92,15 @@ class PlayerControl(player : Player) extends Actor
if (player.skipStaminaRegenForTurns > 0) { if (player.skipStaminaRegenForTurns > 0) {
// Do not renew stamina for a while // Do not renew stamina for a while
player.skipStaminaRegenForTurns -= 1 player.skipStaminaRegenForTurns -= 1
} } else if (
else if ((player.VehicleSeated.nonEmpty || !player.isMoving && !player.Jumping) && player.Stamina < player.MaxStamina) { (player.VehicleSeated.nonEmpty || !player.isMoving && !player.Jumping) && player.Stamina < player.MaxStamina
) {
// Regen stamina roughly every 500ms // Regen stamina roughly every 500ms
StaminaChanged(changeInStamina = 1) StaminaChanged(changeInStamina = 1)
} }
} }
staminaRegen = context.system.scheduler.scheduleOnce(delay = 500 milliseconds, self, PlayerControl.StaminaRegen()) staminaRegen =
context.system.scheduler.scheduleOnce(delay = 500 milliseconds, self, PlayerControl.StaminaRegen())
case Player.StaminaChanged(Some(changeInStamina)) => case Player.StaminaChanged(Some(changeInStamina)) =>
StaminaChanged(changeInStamina) StaminaChanged(changeInStamina)
@ -106,14 +113,17 @@ class PlayerControl(player : Player) extends Actor
DestructionAwareness(player, None) DestructionAwareness(player, None)
} }
case CommonMessages.Use(user, Some(item : Tool)) if item.Definition == GlobalDefinitions.medicalapplicator && player.isAlive => case CommonMessages.Use(user, Some(item: Tool))
if item.Definition == GlobalDefinitions.medicalapplicator && player.isAlive =>
//heal //heal
val originalHealth = player.Health val originalHealth = player.Health
val definition = player.Definition val definition = player.Definition
if(player.MaxHealth > 0 && originalHealth < player.MaxHealth && if (
player.MaxHealth > 0 && originalHealth < player.MaxHealth &&
user.Faction == player.Faction && user.Faction == player.Faction &&
item.Magazine > 0 && item.Magazine > 0 &&
Vector3.Distance(user.Position, player.Position) < definition.RepairDistance) { Vector3.Distance(user.Position, player.Position) < definition.RepairDistance
) {
val zone = player.Zone val zone = player.Zone
val events = zone.AvatarEvents val events = zone.AvatarEvents
val uname = user.Name val uname = user.Name
@ -121,25 +131,46 @@ class PlayerControl(player : Player) extends Actor
if (!(player.isMoving || user.isMoving)) { //only allow stationary heals if (!(player.isMoving || user.isMoving)) { //only allow stationary heals
val newHealth = player.Health = originalHealth + 10 val newHealth = player.Health = originalHealth + 10
val magazine = item.Discharge() val magazine = item.Discharge()
events ! AvatarServiceMessage(uname, AvatarAction.SendResponse(Service.defaultPlayerGUID, InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong))) events ! AvatarServiceMessage(
uname,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
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(HealFromEquipment(PlayerSource(player), PlayerSource(user), newHealth - originalHealth, GlobalDefinitions.medicalapplicator)) player.History(
HealFromEquipment(
PlayerSource(player),
PlayerSource(user),
newHealth - originalHealth,
GlobalDefinitions.medicalapplicator
)
)
} }
if (player != user) { if (player != user) {
//"Someone is trying to heal you" //"Someone is trying to heal you"
events ! AvatarServiceMessage(player.Name, AvatarAction.PlanetsideAttributeToAll(guid, 55, 1)) events ! AvatarServiceMessage(player.Name, AvatarAction.PlanetsideAttributeToAll(guid, 55, 1))
//progress bar remains visible for all heal attempts //progress bar remains visible for all heal attempts
events ! AvatarServiceMessage(uname, AvatarAction.SendResponse(Service.defaultPlayerGUID, RepairMessage(guid, player.Health * 100 / definition.MaxHealth))) events ! AvatarServiceMessage(
uname,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
RepairMessage(guid, player.Health * 100 / definition.MaxHealth)
)
)
} }
} }
case CommonMessages.Use(user, Some(item: Tool)) if item.Definition == GlobalDefinitions.medicalapplicator => case CommonMessages.Use(user, Some(item: Tool)) if item.Definition == GlobalDefinitions.medicalapplicator =>
//revive //revive
if(user != player && if (
user != player &&
user.Faction == player.Faction && user.Faction == player.Faction &&
user.isAlive && !user.isMoving && user.isAlive && !user.isMoving &&
!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),
@ -150,20 +181,36 @@ class PlayerControl(player : Player) extends Actor
case CommonMessages.Use(user, Some(item: Tool)) if item.Definition == GlobalDefinitions.bank => case CommonMessages.Use(user, Some(item: Tool)) if item.Definition == GlobalDefinitions.bank =>
val originalArmor = player.Armor val originalArmor = player.Armor
val definition = player.Definition val definition = player.Definition
if(player.MaxArmor > 0 && originalArmor < player.MaxArmor && if (
player.MaxArmor > 0 && originalArmor < player.MaxArmor &&
user.Faction == player.Faction && user.Faction == player.Faction &&
item.AmmoType == Ammo.armor_canister && item.Magazine > 0 && item.AmmoType == Ammo.armor_canister && item.Magazine > 0 &&
Vector3.Distance(user.Position, player.Position) < definition.RepairDistance) { Vector3.Distance(user.Position, player.Position) < definition.RepairDistance
) {
val zone = player.Zone val zone = player.Zone
val events = zone.AvatarEvents val events = zone.AvatarEvents
val uname = user.Name val uname = user.Name
val guid = player.GUID val guid = player.GUID
if (!(player.isMoving || user.isMoving)) { //only allow stationary repairs if (!(player.isMoving || user.isMoving)) { //only allow stationary repairs
val newArmor = player.Armor = originalArmor + Repairable.Quality + RepairValue(item) + definition.RepairMod val newArmor = player.Armor =
originalArmor + Repairable.Quality + RepairValue(item) + definition.RepairMod
val magazine = item.Discharge() val magazine = item.Discharge()
events ! AvatarServiceMessage(uname, AvatarAction.SendResponse(Service.defaultPlayerGUID, InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong))) events ! AvatarServiceMessage(
uname,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
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(RepairFromEquipment(PlayerSource(player), PlayerSource(user), newArmor - originalArmor, GlobalDefinitions.bank)) player.History(
RepairFromEquipment(
PlayerSource(player),
PlayerSource(user),
newArmor - originalArmor,
GlobalDefinitions.bank
)
)
} }
if (player != user) { if (player != user) {
if (player.isAlive) { if (player.isAlive) {
@ -174,7 +221,11 @@ class PlayerControl(player : Player) extends Actor
context.system.scheduler.scheduleOnce(250 milliseconds, events, msg) context.system.scheduler.scheduleOnce(250 milliseconds, events, msg)
} }
//progress bar remains visible for all repair attempts //progress bar remains visible for all repair attempts
events ! AvatarServiceMessage(uname, AvatarAction.SendResponse(Service.defaultPlayerGUID, RepairMessage(guid, player.Armor * 100 / player.MaxArmor))) events ! AvatarServiceMessage(
uname,
AvatarAction
.SendResponse(Service.defaultPlayerGUID, RepairMessage(guid, player.Armor * 100 / player.MaxArmor))
)
} }
} }
@ -190,13 +241,11 @@ class PlayerControl(player : Player) extends Actor
(if (exosuit == ExoSuitType.MAX) { (if (exosuit == ExoSuitType.MAX) {
if (time - player.GetLastUsedTime(exosuit, subtype) < 300000L) { if (time - player.GetLastUsedTime(exosuit, subtype) < 300000L) {
false false
} } else {
else {
player.SetLastUsedTime(exosuit, subtype, time) player.SetLastUsedTime(exosuit, subtype, time)
true true
} }
} } else {
else {
player.SetLastUsedTime(exosuit, subtype, time) player.SetLastUsedTime(exosuit, subtype, time)
true true
}) })
@ -212,8 +261,7 @@ class PlayerControl(player : Player) extends Actor
if (originalSuit != exosuit || originalSubtype != subtype || originalArmor > toMaxArmor) { if (originalSuit != exosuit || originalSubtype != subtype || originalArmor > toMaxArmor) {
player.History(HealFromExoSuitChange(PlayerSource(player), exosuit)) player.History(HealFromExoSuitChange(PlayerSource(player), exosuit))
player.Armor = toMaxArmor player.Armor = toMaxArmor
} } else {
else {
player.Armor = originalArmor player.Armor = originalArmor
} }
//ensure arm is down, even if it needs to go back up //ensure arm is down, even if it needs to go back up
@ -224,23 +272,25 @@ class PlayerControl(player : Player) extends Actor
val (maxWeapons, normalWeapons) = beforeHolsters.partition(elem => elem.obj.Size == EquipmentSize.Max) val (maxWeapons, normalWeapons) = beforeHolsters.partition(elem => elem.obj.Size == EquipmentSize.Max)
toDelete ++= maxWeapons toDelete ++= maxWeapons
normalWeapons normalWeapons
} } else {
else {
beforeHolsters beforeHolsters
} }
//populate holsters //populate holsters
val (afterHolsters, finalInventory) = if (exosuit == ExoSuitType.MAX) { val (afterHolsters, finalInventory) = if (exosuit == ExoSuitType.MAX) {
(normalHolsters, Players.fillEmptyHolsters(List(player.Slot(4)).iterator, normalHolsters) ++ beforeInventory) (
} normalHolsters,
else if (originalSuit == exosuit) { //note - this will rarely be the situation Players.fillEmptyHolsters(List(player.Slot(4)).iterator, normalHolsters) ++ beforeInventory
)
} else if (originalSuit == exosuit) { //note - this will rarely be the situation
(normalHolsters, Players.fillEmptyHolsters(player.Holsters().iterator, normalHolsters)) (normalHolsters, Players.fillEmptyHolsters(player.Holsters().iterator, normalHolsters))
} } else {
else { val (afterHolsters, toInventory) =
val (afterHolsters, toInventory) = normalHolsters.partition(elem => elem.obj.Size == player.Slot(elem.start).Size) normalHolsters.partition(elem => elem.obj.Size == player.Slot(elem.start).Size)
afterHolsters.foreach({ elem => player.Slot(elem.start).Equipment = elem.obj }) afterHolsters.foreach({ elem => player.Slot(elem.start).Equipment = elem.obj })
val remainder = Players.fillEmptyHolsters(player.Holsters().iterator, toInventory ++ beforeInventory) val remainder = Players.fillEmptyHolsters(player.Holsters().iterator, toInventory ++ beforeInventory)
( (
player.Holsters() player
.Holsters()
.zipWithIndex .zipWithIndex
.map { case (slot, i) => (slot.Equipment, i) } .map { case (slot, i) => (slot.Equipment, i) }
.collect { case (Some(obj), index) => InventoryItem(obj, index) } .collect { case (Some(obj), index) => InventoryItem(obj, index) }
@ -251,12 +301,14 @@ class PlayerControl(player : Player) extends Actor
//put items back into inventory //put items back into inventory
val (stow, drop) = if (originalSuit == exosuit) { val (stow, drop) = if (originalSuit == exosuit) {
(finalInventory, Nil) (finalInventory, Nil)
} } else {
else {
val (a, b) = GridInventory.recoverInventory(finalInventory, player.Inventory) val (a, b) = GridInventory.recoverInventory(finalInventory, player.Inventory)
(a, b.map { (
a,
b.map {
InventoryItem(_, -1) InventoryItem(_, -1)
}) }
)
} }
stow.foreach { elem => stow.foreach { elem =>
player.Inventory.InsertQuickly(elem.start, elem.obj) player.Inventory.InsertQuickly(elem.start, elem.obj)
@ -264,23 +316,38 @@ class PlayerControl(player : Player) extends Actor
//deactivate non-passive implants //deactivate non-passive implants
implantSlotTimers.keys.foreach { index => implantSlotTimers.keys.foreach { index =>
val implantSlot = player.ImplantSlot(index) val implantSlot = player.ImplantSlot(index)
if(implantSlot.Installed.nonEmpty && implantSlot.Active && (implantSlot.Charge(originalSuit) > 0 || implantSlot.Charge(exosuit) > 0)) { if (
implantSlot.Installed.nonEmpty && implantSlot.Active && (implantSlot.Charge(
originalSuit
) > 0 || implantSlot.Charge(exosuit) > 0)
) {
ImplantActivation(index, status = 0) ImplantActivation(index, status = 0)
} }
} }
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, player.Zone.AvatarEvents ! AvatarServiceMessage(
AvatarAction.ChangeExosuit(player.GUID, exosuit, subtype, player.LastDrawnSlot, exosuit == ExoSuitType.MAX && requestToChangeArmor, player.Zone.Id,
beforeHolsters.map { case InventoryItem(obj, _) => (obj, obj.GUID) }, afterHolsters, AvatarAction.ChangeExosuit(
beforeInventory.map { case InventoryItem(obj, _) => (obj, obj.GUID) }, stow, drop, player.GUID,
exosuit,
subtype,
player.LastDrawnSlot,
exosuit == ExoSuitType.MAX && requestToChangeArmor,
beforeHolsters.map { case InventoryItem(obj, _) => (obj, obj.GUID) },
afterHolsters,
beforeInventory.map { case InventoryItem(obj, _) => (obj, obj.GUID) },
stow,
drop,
toDelete.map { case InventoryItem(obj, _) => (obj, obj.GUID) } toDelete.map { case InventoryItem(obj, _) => (obj, obj.GUID) }
) )
) )
true true
} } else {
else {
false false
} }
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, result)) player.Zone.AvatarEvents ! AvatarServiceMessage(
player.Name,
AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, result)
)
case Terminal.InfantryLoadout(exosuit, subtype, holsters, inventory) => case Terminal.InfantryLoadout(exosuit, subtype, holsters, inventory) =>
log.info(s"wants to change equipment loadout to their option #${msg.unk1 + 1}") log.info(s"wants to change equipment loadout to their option #${msg.unk1 + 1}")
@ -300,8 +367,7 @@ class PlayerControl(player : Player) extends Actor
player.FreeHand.Equipment = None player.FreeHand.Equipment = None
if (dropPred(out)) { if (dropPred(out)) {
List(out) List(out)
} } else {
else {
Nil Nil
} }
case _ => case _ =>
@ -310,24 +376,26 @@ class PlayerControl(player : Player) extends Actor
//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 time = System.currentTimeMillis()
val (nextSuit, nextSubtype) = if (Players.CertificationToUseExoSuit(player, exosuit, subtype) && val (nextSuit, nextSubtype) =
if (
Players.CertificationToUseExoSuit(player, exosuit, subtype) &&
(if (exosuit == ExoSuitType.MAX) { (if (exosuit == ExoSuitType.MAX) {
if (time - player.GetLastUsedTime(exosuit, subtype) < 300000L) { if (time - player.GetLastUsedTime(exosuit, subtype) < 300000L) {
false false
} } else {
else {
player.SetLastUsedTime(exosuit, subtype, time) player.SetLastUsedTime(exosuit, subtype, time)
true true
} }
} } else {
else {
player.SetLastUsedTime(exosuit, subtype, time) player.SetLastUsedTime(exosuit, subtype, time)
true true
})) { })
) {
(exosuit, subtype) (exosuit, subtype)
} } else {
else { 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) player.SetLastUsedTime(fallbackSuit, fallbackSubtype, time)
(fallbackSuit, fallbackSubtype) (fallbackSuit, fallbackSubtype)
} }
@ -352,8 +420,7 @@ class PlayerControl(player : Player) extends Actor
if (originalSuit != nextSuit || originalSubtype != nextSubtype || originalArmor > toMaxArmor) { if (originalSuit != nextSuit || originalSubtype != nextSubtype || originalArmor > toMaxArmor) {
player.History(HealFromExoSuitChange(PlayerSource(player), nextSuit)) player.History(HealFromExoSuitChange(PlayerSource(player), nextSuit))
player.Armor = toMaxArmor player.Armor = toMaxArmor
} } else {
else {
player.Armor = originalArmor player.Armor = originalArmor
} }
//ensure arm is down, even if it needs to go back up //ensure arm is down, even if it needs to go back up
@ -367,21 +434,20 @@ class PlayerControl(player : Player) extends Actor
//melee slot preservation for MAX //melee slot preservation for MAX
if (nextSuit == ExoSuitType.MAX) { if (nextSuit == ExoSuitType.MAX) {
holsters.filter(_.start == 4) holsters.filter(_.start == 4)
} } else {
else {
curatedHolsters.filterNot(dropPred) curatedHolsters.filterNot(dropPred)
}, },
curatedInventory.filterNot(dropPred) curatedInventory.filterNot(dropPred)
) )
} } else {
else {
//our exo-suit type was hijacked by changing permissions; we shouldn't even be able to use that loadout(!) //our exo-suit type was hijacked by changing permissions; we shouldn't even be able to use that loadout(!)
//holsters //holsters
val leftoversForInventory = Players.fillEmptyHolsters( val leftoversForInventory = Players.fillEmptyHolsters(
player.Holsters().iterator, player.Holsters().iterator,
(curatedHolsters ++ curatedInventory).filterNot(dropPred) (curatedHolsters ++ curatedInventory).filterNot(dropPred)
) )
val finalHolsters = player.Holsters() val finalHolsters = player
.Holsters()
.zipWithIndex .zipWithIndex
.collect { case (slot, index) if slot.Equipment.nonEmpty => InventoryItem(slot.Equipment.get, index) } .collect { case (slot, index) if slot.Equipment.nonEmpty => InventoryItem(slot.Equipment.get, index) }
.toList .toList
@ -394,16 +460,33 @@ class PlayerControl(player : Player) extends Actor
//deactivate non-passive implants //deactivate non-passive implants
implantSlotTimers.keys.foreach { index => implantSlotTimers.keys.foreach { index =>
val implantSlot = player.ImplantSlot(index) val implantSlot = player.ImplantSlot(index)
if(implantSlot.Installed.nonEmpty && implantSlot.Active && (implantSlot.Charge(originalSuit) > 0 || implantSlot.Charge(nextSuit) > 0)) { if (
implantSlot.Installed.nonEmpty && implantSlot.Active && (implantSlot.Charge(
originalSuit
) > 0 || implantSlot.Charge(nextSuit) > 0)
) {
ImplantActivation(index, status = 0) ImplantActivation(index, status = 0)
} }
} }
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, player.Zone.AvatarEvents ! AvatarServiceMessage(
AvatarAction.ChangeLoadout(player.GUID, nextSuit, nextSubtype, player.LastDrawnSlot, exosuit == ExoSuitType.MAX, player.Zone.Id,
oldHolsters.map { case InventoryItem(obj, _) => (obj, obj.GUID) }, afterHolsters, AvatarAction.ChangeLoadout(
oldInventory.map { case InventoryItem(obj, _) => (obj, obj.GUID) }, afterInventory, toDeleteOrDrop) player.GUID,
nextSuit,
nextSubtype,
player.LastDrawnSlot,
exosuit == ExoSuitType.MAX,
oldHolsters.map { case InventoryItem(obj, _) => (obj, obj.GUID) },
afterHolsters,
oldInventory.map { case InventoryItem(obj, _) => (obj, obj.GUID) },
afterInventory,
toDeleteOrDrop
)
)
player.Zone.AvatarEvents ! AvatarServiceMessage(
player.Name,
AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, true)
) )
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, true))
case Terminal.LearnImplant(implant) => case Terminal.LearnImplant(implant) =>
val zone = player.Zone val zone = player.Zone
@ -419,8 +502,7 @@ class PlayerControl(player : Player) extends Actor
if (!player.Implants.exists({ case (implantType, _, _) => implantType == implant_type })) { if (!player.Implants.exists({ case (implantType, _, _) => implantType == implant_type })) {
//no duplicates //no duplicates
player.InstallImplant(implant) player.InstallImplant(implant)
} } else {
else {
None None
} }
) )
@ -430,26 +512,31 @@ class PlayerControl(player : Player) extends Actor
val result = if (interface.contains(terminal_guid.guid) && slotNumber.isDefined) { val result = if (interface.contains(terminal_guid.guid) && slotNumber.isDefined) {
val slot = slotNumber.get val slot = slotNumber.get
log.info(s"$message - put in slot $slot") log.info(s"$message - put in slot $slot")
events ! AvatarServiceMessage(playerChannel, AvatarAction.SendResponse(Service.defaultPlayerGUID, AvatarImplantMessage(player.GUID, ImplantAction.Add, slot, implant_type.id))) events ! AvatarServiceMessage(
playerChannel,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
AvatarImplantMessage(player.GUID, ImplantAction.Add, slot, implant_type.id)
)
)
ImplantInitializationStart(slot) ImplantInitializationStart(slot)
true true
} } else {
else {
if (interface.isEmpty) { if (interface.isEmpty) {
log.warn(s"$message - not interacting with a terminal") log.warn(s"$message - not interacting with a terminal")
} } else if (!interface.contains(terminal_guid.guid)) {
else if(!interface.contains(terminal_guid.guid)) {
log.warn(s"$message - interacting with the wrong terminal, ${interface.get}") log.warn(s"$message - interacting with the wrong terminal, ${interface.get}")
} } else if (slotNumber.isEmpty) {
else if(slotNumber.isEmpty) {
log.warn(s"$message - already knows that implant") log.warn(s"$message - already knows that implant")
} } else {
else {
log.warn(s"$message - forgot to sit at a terminal") log.warn(s"$message - forgot to sit at a terminal")
} }
false false
} }
events ! AvatarServiceMessage(playerChannel, AvatarAction.TerminalOrderResult(terminal_guid, msg.transaction_type, result)) events ! AvatarServiceMessage(
playerChannel,
AvatarAction.TerminalOrderResult(terminal_guid, msg.transaction_type, result)
)
case Terminal.SellImplant(implant) => case Terminal.SellImplant(implant) =>
val zone = player.Zone val zone = player.Zone
@ -470,31 +557,37 @@ class PlayerControl(player : Player) extends Actor
val slot = slotNumber.get val slot = slotNumber.get
log.info(s"is uninstalling $implant_type - take from slot $slot") log.info(s"is uninstalling $implant_type - take from slot $slot")
UninitializeImplant(slot) UninitializeImplant(slot)
events ! AvatarServiceMessage(playerChannel, AvatarAction.SendResponse(Service.defaultPlayerGUID, AvatarImplantMessage(player.GUID, ImplantAction.Remove, slot, 0))) events ! AvatarServiceMessage(
playerChannel,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
AvatarImplantMessage(player.GUID, ImplantAction.Remove, slot, 0)
)
)
true true
} } else {
else {
val message = s"${player.Name} can not sell $implant_type" val message = s"${player.Name} can not sell $implant_type"
if (interface.isEmpty) { if (interface.isEmpty) {
log.warn(s"$message - not interacting with a terminal") log.warn(s"$message - not interacting with a terminal")
} } else if (!interface.contains(terminal_guid.guid)) {
else if(!interface.contains(terminal_guid.guid)) {
log.warn(s"$message - interacting with the wrong terminal, ${interface.get}") log.warn(s"$message - interacting with the wrong terminal, ${interface.get}")
} } else if (slotNumber.isEmpty) {
else if(slotNumber.isEmpty) {
log.warn(s"$message - does not know that implant") log.warn(s"$message - does not know that implant")
} } else {
else {
log.warn(s"$message - forgot to sit at a terminal") log.warn(s"$message - forgot to sit at a terminal")
} }
false false
} }
events ! AvatarServiceMessage(playerChannel, AvatarAction.TerminalOrderResult(terminal_guid, msg.transaction_type, result)) events ! AvatarServiceMessage(
playerChannel,
AvatarAction.TerminalOrderResult(terminal_guid, msg.transaction_type, result)
)
case _ => ; //terminal messages not handled here case _ => ; //terminal messages not handled here
} }
case Zone.Ground.ItemOnGround(item, _, _) => ; case Zone.Ground.ItemOnGround(item, _, _) =>
;
val name = player.Name val name = player.Name
val zone = player.Zone val zone = player.Zone
val avatarEvents = zone.AvatarEvents val avatarEvents = zone.AvatarEvents
@ -509,24 +602,36 @@ class PlayerControl(player : Player) extends Actor
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 { case (currElem, curr, maxElem, max) => avatar.Deployables.UpdateUIElement(boomer.Definition.Item).foreach {
avatarEvents ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, maxElem, max)) case (currElem, curr, maxElem, max) =>
avatarEvents ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, currElem, curr)) avatarEvents ! AvatarServiceMessage(
name,
AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, maxElem, max)
)
avatarEvents ! AvatarServiceMessage(
name,
AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, currElem, curr)
)
} }
localEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(boomer, zone)) localEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(boomer, zone))
localEvents ! LocalServiceMessage(factionChannel, localEvents ! LocalServiceMessage(
LocalAction.DeployableMapIcon(Service.defaultPlayerGUID, DeploymentAction.Dismiss, factionChannel,
LocalAction.DeployableMapIcon(
Service.defaultPlayerGUID,
DeploymentAction.Dismiss,
DeployableInfo(guid, DeployableIcon.Boomer, boomer.Position, PlanetSideGUID(0)) DeployableInfo(guid, DeployableIcon.Boomer, boomer.Position, PlanetSideGUID(0))
) )
) )
avatarEvents ! AvatarServiceMessage(factionChannel, AvatarAction.SetEmpire(Service.defaultPlayerGUID, guid, PlanetSideEmpire.NEUTRAL)) avatarEvents ! AvatarServiceMessage(
factionChannel,
AvatarAction.SetEmpire(Service.defaultPlayerGUID, guid, PlanetSideEmpire.NEUTRAL)
)
} }
case _ => ; //pointless trigger? or a trigger being deleted? case _ => ; //pointless trigger? or a trigger being deleted?
} }
case _ => ; case _ => ;
} }
case Zone.Ground.CanNotDropItem(_, item, reason) => case Zone.Ground.CanNotDropItem(_, item, reason) =>
log.warn(s"${player.Name} tried to drop a ${item.Definition.Name} on the ground, but it $reason") log.warn(s"${player.Name} tried to drop a ${item.Definition.Name} on the ground, but it $reason")
@ -556,7 +661,9 @@ class PlayerControl(player : Player) extends Actor
val damageToCapacitor = originalCapacitor - capacitor val damageToCapacitor = originalCapacitor - capacitor
HandleDamage(player, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor) HandleDamage(player, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
if (damageToHealth > 0 || damageToArmor > 0 || damageToStamina > 0 || damageToCapacitor > 0) { if (damageToHealth > 0 || damageToArmor > 0 || damageToStamina > 0 || damageToCapacitor > 0) {
damageLog.info(s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor/$originalStamina/$originalCapacitor, AFTER=$health/$armor/$stamina/$capacitor, CHANGE=$damageToHealth/$damageToArmor/$damageToStamina/$damageToCapacitor") damageLog.info(
s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor/$originalStamina/$originalCapacitor, AFTER=$health/$armor/$stamina/$capacitor, CHANGE=$damageToHealth/$damageToArmor/$damageToStamina/$damageToCapacitor"
)
} }
} }
} }
@ -565,7 +672,14 @@ class PlayerControl(player : Player) extends Actor
* na * na
* @param target na * @param target na
*/ */
def HandleDamage(target : Player, cause : ResolvedProjectile, damageToHealth : Int, damageToArmor : Int, damageToStamina : Int, damageToCapacitor : Int) : Unit = { def HandleDamage(
target: Player,
cause: ResolvedProjectile,
damageToHealth: Int,
damageToArmor: Int,
damageToStamina: Int,
damageToCapacitor: Int
): 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
@ -576,7 +690,10 @@ class PlayerControl(player : Player) extends Actor
} }
if (health > 0) { if (health > 0) {
if (damageToCapacitor > 0) { if (damageToCapacitor > 0) {
events ! AvatarServiceMessage(target.Name, AvatarAction.PlanetsideAttributeSelf(targetGUID, 7, target.Capacitor.toLong)) events ! AvatarServiceMessage(
target.Name,
AvatarAction.PlanetsideAttributeSelf(targetGUID, 7, target.Capacitor.toLong)
)
} }
if (damageToHealth > 0 || damageToStamina > 0) { if (damageToHealth > 0 || damageToStamina > 0) {
target.History(cause) target.History(cause)
@ -594,8 +711,7 @@ class PlayerControl(player : Player) extends Actor
if (Damageable.CanJammer(target, cause)) { if (Damageable.CanJammer(target, cause)) {
target.Actor ! JammableUnit.Jammered(cause) target.Actor ! JammableUnit.Jammered(cause)
} }
} } else {
else {
DestructionAwareness(target, Some(cause)) DestructionAwareness(target, Some(cause))
} }
} }
@ -614,9 +730,11 @@ class PlayerControl(player : Player) extends Actor
val name = pSource.Name val name = pSource.Name
zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match { zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
case Some(tplayer) => AvatarAction.HitHint(tplayer.GUID, target.GUID) case Some(tplayer) => AvatarAction.HitHint(tplayer.GUID, target.GUID)
case None => AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, pSource.Position)) case None =>
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, pSource.Position))
} }
case source => AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, source.Position)) case source =>
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, source.Position))
} }
) )
} }
@ -652,11 +770,15 @@ class PlayerControl(player : Player) extends Actor
target.Stamina = 0 target.Stamina = 0
UpdateStamina() //turn off implants / OutOfStamina UpdateStamina() //turn off implants / OutOfStamina
//uninitialize implants //uninitialize implants
target.Implants.indices.foreach { case slot if target.Implant(slot) != ImplantType.None => target.Implants.indices.foreach {
case slot if target.Implant(slot) != ImplantType.None =>
UninitializeImplant(slot) UninitializeImplant(slot)
} }
target.ResetAllImplants() //anything else specific to the backend target.ResetAllImplants() //anything else specific to the backend
events ! AvatarServiceMessage(nameChannel, AvatarAction.Killed(player_guid, target.VehicleSeated)) //align client interface fields with state events ! AvatarServiceMessage(
nameChannel,
AvatarAction.Killed(player_guid, target.VehicleSeated)
) //align client interface fields with state
zone.GUID(target.VehicleSeated) match { zone.GUID(target.VehicleSeated) match {
case Some(obj: Mountable) => case Some(obj: Mountable) =>
//boot cadaver from seat internally (vehicle perspective) //boot cadaver from seat internally (vehicle perspective)
@ -666,8 +788,12 @@ class PlayerControl(player : Player) extends Actor
case _ => ; case _ => ;
} }
//boot cadaver from seat on client //boot cadaver from seat on client
events ! AvatarServiceMessage(nameChannel, AvatarAction.SendResponse(Service.defaultPlayerGUID, events ! AvatarServiceMessage(
ObjectDetachMessage(obj.GUID, player_guid, target.Position, Vector3.Zero)) nameChannel,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
ObjectDetachMessage(obj.GUID, player_guid, target.Position, Vector3.Zero)
)
) )
//make player invisible on client //make player invisible on client
events ! AvatarServiceMessage(nameChannel, AvatarAction.PlanetsideAttributeToAll(player_guid, 29, 1)) events ! AvatarServiceMessage(nameChannel, AvatarAction.PlanetsideAttributeToAll(player_guid, 29, 1))
@ -695,11 +821,17 @@ class PlayerControl(player : Player) extends Actor
} }
events ! AvatarServiceMessage( events ! AvatarServiceMessage(
nameChannel, nameChannel,
AvatarAction.SendResponse(Service.defaultPlayerGUID, DestroyMessage(player_guid, attribute, Service.defaultPlayerGUID, pos)) //how many players get this message? AvatarAction.SendResponse(
Service.defaultPlayerGUID,
DestroyMessage(player_guid, attribute, Service.defaultPlayerGUID, pos)
) //how many players get this message?
) )
events ! AvatarServiceMessage( events ! AvatarServiceMessage(
nameChannel, nameChannel,
AvatarAction.SendResponse(Service.defaultPlayerGUID, AvatarDeadStateMessage(DeadState.Dead, respawnTimer, respawnTimer, pos, target.Faction, true)) AvatarAction.SendResponse(
Service.defaultPlayerGUID,
AvatarDeadStateMessage(DeadState.Dead, respawnTimer, respawnTimer, pos, target.Faction, true)
)
) )
//TODO other methods of death? //TODO other methods of death?
val pentry = PlayerSource(target) val pentry = PlayerSource(target)
@ -711,8 +843,7 @@ class PlayerControl(player : Player) extends Actor
case out @ Some(shot) => case out @ Some(shot) =>
if (System.nanoTime - shot.hit_time < (10 seconds).toNanos) { if (System.nanoTime - shot.hit_time < (10 seconds).toNanos) {
out out
} } else {
else {
None //suicide None //suicide
} }
case None => case None =>
@ -720,7 +851,10 @@ class PlayerControl(player : Player) extends Actor
} }
}) match { }) match {
case Some(shot) => case Some(shot) =>
events ! AvatarServiceMessage(zoneChannel, AvatarAction.DestroyDisplay(shot.projectile.owner, pentry, shot.projectile.attribute_to)) events ! AvatarServiceMessage(
zoneChannel,
AvatarAction.DestroyDisplay(shot.projectile.owner, pentry, shot.projectile.attribute_to)
)
case None => case None =>
events ! AvatarServiceMessage(zoneChannel, AvatarAction.DestroyDisplay(pentry, pentry, 0)) events ! AvatarServiceMessage(zoneChannel, AvatarAction.DestroyDisplay(pentry, pentry, 0))
} }
@ -735,9 +869,13 @@ class PlayerControl(player : Player) extends Actor
* @param dur the duration of the timer, in milliseconds; * @param dur the duration of the timer, in milliseconds;
* by default, 30000 * by default, 30000
*/ */
override def StartJammeredSound(target : Any, dur : Int) : Unit = target match { override def StartJammeredSound(target: Any, dur: Int): Unit =
target match {
case obj: Player if !jammedSound => case obj: Player if !jammedSound =>
obj.Zone.AvatarEvents ! AvatarServiceMessage(obj.Zone.Id, AvatarAction.PlanetsideAttributeToAll(obj.GUID, 27, 1)) obj.Zone.AvatarEvents ! AvatarServiceMessage(
obj.Zone.Id,
AvatarAction.PlanetsideAttributeToAll(obj.GUID, 27, 1)
)
super.StartJammeredSound(obj, 3000) super.StartJammeredSound(obj, 3000)
case _ => ; case _ => ;
} }
@ -755,7 +893,10 @@ class PlayerControl(player : Player) extends Actor
//TODO these features //TODO these features
val zone = player.Zone val zone = player.Zone
player.Implants.indices.foreach { slot => // Deactivate & uninitialize all implants 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 zone.AvatarEvents ! AvatarServiceMessage(
zone.Id,
AvatarAction.PlanetsideAttribute(player.GUID, 28, player.Implant(slot).id * 2)
) // Deactivation sound / effect
ImplantActivation(slot, status = 0) ImplantActivation(slot, status = 0)
UninitializeImplant(slot) UninitializeImplant(slot)
} }
@ -776,17 +917,21 @@ class PlayerControl(player : Player) extends Actor
* @see `JammableBehavior.CancelJammeredSound` * @see `JammableBehavior.CancelJammeredSound`
* @param target an object that can be affected by the jammered status * @param target an object that can be affected by the jammered status
*/ */
override def CancelJammeredSound(target : Any) : Unit = target match { override def CancelJammeredSound(target: Any): Unit =
target match {
case obj: Player if jammedSound => case obj: Player if jammedSound =>
obj.Zone.AvatarEvents ! AvatarServiceMessage(obj.Zone.Id, AvatarAction.PlanetsideAttributeToAll(obj.GUID, 27, 0)) obj.Zone.AvatarEvents ! AvatarServiceMessage(
obj.Zone.Id,
AvatarAction.PlanetsideAttributeToAll(obj.GUID, 27, 0)
)
super.CancelJammeredSound(obj) super.CancelJammeredSound(obj)
case _ => ; case _ => ;
} }
def RepairValue(item : Tool) : Int = if(player.ExoSuit != ExoSuitType.MAX) { def RepairValue(item: Tool): Int =
if (player.ExoSuit != ExoSuitType.MAX) {
item.FireMode.Modifiers.Damage0 item.FireMode.Modifiers.Damage0
} } else {
else {
item.FireMode.Modifiers.Damage3 item.FireMode.Modifiers.Damage3
} }
@ -856,15 +1001,33 @@ class PlayerControl(player : Player) extends Actor
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 { case (currElem, curr, maxElem, max) => avatar.Deployables.UpdateUIElement(boomer.Definition.Item).foreach {
events ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, maxElem, max)) case (currElem, curr, maxElem, max) =>
events ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, currElem, curr)) events ! AvatarServiceMessage(
name,
AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, maxElem, max)
)
events ! AvatarServiceMessage(
name,
AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, currElem, curr)
)
} }
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(boomer), zone)) zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(boomer), zone))
events ! AvatarServiceMessage(factionChannel, AvatarAction.SetEmpire(Service.defaultPlayerGUID, bguid, faction)) events ! AvatarServiceMessage(
zone.LocalEvents ! LocalServiceMessage(factionChannel, factionChannel,
LocalAction.DeployableMapIcon(Service.defaultPlayerGUID, DeploymentAction.Build, AvatarAction.SetEmpire(Service.defaultPlayerGUID, bguid, faction)
DeployableInfo(bguid, DeployableIcon.Boomer, boomer.Position, boomer.Owner.getOrElse(PlanetSideGUID(0))) )
zone.LocalEvents ! LocalServiceMessage(
factionChannel,
LocalAction.DeployableMapIcon(
Service.defaultPlayerGUID,
DeploymentAction.Build,
DeployableInfo(
bguid,
DeployableIcon.Boomer,
boomer.Position,
boomer.Owner.getOrElse(PlanetSideGUID(0))
)
) )
) )
} }
@ -877,7 +1040,10 @@ class PlayerControl(player : Player) extends Actor
def SwapItemCallback(item: Equipment): Unit = { def SwapItemCallback(item: Equipment): Unit = {
val obj = ContainerObject val obj = ContainerObject
val zone = obj.Zone val zone = obj.Zone
zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectDetachMessage(obj.GUID, item.GUID, Vector3.Zero, 0f))) zone.AvatarEvents ! AvatarServiceMessage(
player.Name,
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectDetachMessage(obj.GUID, item.GUID, Vector3.Zero, 0f))
)
} }
/** /**
@ -906,22 +1072,36 @@ class PlayerControl(player : Player) extends Actor
player.skipStaminaRegenForTurns = math.max(player.skipStaminaRegenForTurns, 6) player.skipStaminaRegenForTurns = math.max(player.skipStaminaRegenForTurns, 6)
player.Implants.indices.foreach { slot => // Disable all implants player.Implants.indices.foreach { slot => // Disable all implants
ImplantActivation(slot, status = 0) ImplantActivation(slot, status = 0)
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.SendResponse(Service.defaultPlayerGUID, AvatarImplantMessage(player.GUID, ImplantAction.OutOfStamina, slot, 1))) player.Zone.AvatarEvents ! AvatarServiceMessage(
player.Name,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
AvatarImplantMessage(player.GUID, ImplantAction.OutOfStamina, slot, 1)
)
)
} }
} } else if (currentStamina >= 20) {
else if(currentStamina >= 20) {
val wasFatigued = player.Fatigued val wasFatigued = player.Fatigued
player.Fatigued = false player.Fatigued = false
if (wasFatigued) { //reactivate only if we were fatigued if (wasFatigued) { //reactivate only if we were fatigued
player.Implants.indices.foreach { slot => // Re-enable all implants 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))) player.Zone.AvatarEvents ! AvatarServiceMessage(
player.Name,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
AvatarImplantMessage(player.GUID, ImplantAction.OutOfStamina, slot, 0)
)
)
if (!player.ImplantSlot(slot).Initialized) { if (!player.ImplantSlot(slot).Initialized) {
ImplantInitializationStart(slot) ImplantInitializationStart(slot)
} }
} }
} }
} }
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.PlanetsideAttributeToAll(player.GUID, 2, currentStamina)) player.Zone.AvatarEvents ! AvatarServiceMessage(
player.Name,
AvatarAction.PlanetsideAttributeToAll(player.GUID, 2, currentStamina)
)
} }
/** /**
@ -938,28 +1118,32 @@ class PlayerControl(player : Player) extends Actor
val initializationTime = if (implantSlot.InitializeTime == 0L) { val initializationTime = if (implantSlot.InitializeTime == 0L) {
implantSlot.InitializeTime = time implantSlot.InitializeTime = time
time time
} } else {
else {
implantSlot.InitializeTime implantSlot.InitializeTime
} }
val maxInitializationTime = implantSlot.MaxTimer * 1000 val maxInitializationTime = implantSlot.MaxTimer * 1000
if (time - initializationTime > maxInitializationTime) { if (time - initializationTime > maxInitializationTime) {
//this implant should have already been initialized //this implant should have already been initialized
ImplantInitializationComplete(slot) ImplantInitializationComplete(slot)
} } else {
else {
// Start client side initialization timer // Start client side initialization timer
// Check this along the bottom of the character information window // Check this along the bottom of the character information window
//progress accumulates according to the client's knowledge of the implant initialization time //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 //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 val percent = (100 * (time - initializationTime) / maxInitializationTime.toFloat).toInt
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, percent))) player.Zone.AvatarEvents ! AvatarServiceMessage(
player.Name,
AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, percent))
)
// Callback after initialization timer to complete initialization // Callback after initialization timer to complete initialization
implantSlotTimers(slot).cancel implantSlotTimers(slot).cancel
implantSlotTimers(slot) = context.system.scheduler.scheduleOnce((maxInitializationTime - (time - initializationTime)) milliseconds, self, Player.ImplantInitializationComplete(slot)) implantSlotTimers(slot) = context.system.scheduler.scheduleOnce(
(maxInitializationTime - (time - initializationTime)) milliseconds,
self,
Player.ImplantInitializationComplete(slot)
)
} }
} } else {
else {
ImplantInitializationComplete(slot) ImplantInitializationComplete(slot)
} }
} }
@ -974,7 +1158,13 @@ class PlayerControl(player : Player) extends Actor
val implantSlot = player.ImplantSlot(slot) val implantSlot = player.ImplantSlot(slot)
if (implantSlot.Installed.isDefined) { if (implantSlot.Installed.isDefined) {
implantSlot.Initialized = true implantSlot.Initialized = true
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.SendResponse(Service.defaultPlayerGUID, AvatarImplantMessage(player.GUID, ImplantAction.Initialization, slot, 1))) player.Zone.AvatarEvents ! AvatarServiceMessage(
player.Name,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
AvatarImplantMessage(player.GUID, ImplantAction.Initialization, slot, 1)
)
)
implantSlotTimers(slot).cancel implantSlotTimers(slot).cancel
implantSlotTimers(slot) = Default.Cancellable implantSlotTimers(slot) = Default.Cancellable
} }
@ -996,19 +1186,26 @@ class PlayerControl(player : Player) extends Actor
implantSlotTimers(slot) = Default.Cancellable implantSlotTimers(slot) = Default.Cancellable
implantSlot.Active = false implantSlot.Active = false
//normal deactivation //normal deactivation
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.DeactivateImplantSlot(player.GUID, slot)) player.Zone.AvatarEvents ! AvatarServiceMessage(
player.Name,
AvatarAction.DeactivateImplantSlot(player.GUID, slot)
)
//initialization process (from scratch) //initialization process (from scratch)
implantSlot.InitializeTime = 0 implantSlot.InitializeTime = 0
ImplantInitializationStart(slot) ImplantInitializationStart(slot)
} } else if (status == 0 && implantSlot.Active) {
else if(status == 0 && implantSlot.Active) {
implantSlotTimers(slot).cancel implantSlotTimers(slot).cancel
implantSlotTimers(slot) = Default.Cancellable implantSlotTimers(slot) = Default.Cancellable
implantSlot.Active = false 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.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.DeactivateImplantSlot(player.GUID, slot)) player.Zone.Id,
} AvatarAction.PlanetsideAttribute(player.GUID, 28, player.Implant(slot).id * 2)
else if(status == 1 && implantSlot.Initialized && !player.Fatigued) { ) // 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 { implantSlot.Installed match {
case Some(implant) case Some(implant)
if (implant.Type == ImplantType.PersonalShield && player.ExoSuit == ExoSuitType.Infiltration) || if (implant.Type == ImplantType.PersonalShield && player.ExoSuit == ExoSuitType.Infiltration) ||
@ -1033,9 +1230,17 @@ class PlayerControl(player : Player) extends Actor
val drainInterval = implant.GetCostIntervalByExoSuit(player.ExoSuit) val drainInterval = implant.GetCostIntervalByExoSuit(player.ExoSuit)
if (drainInterval > 0) { // Ongoing stamina drain, if applicable if (drainInterval > 0) { // Ongoing stamina drain, if applicable
implantSlotTimers(slot).cancel implantSlotTimers(slot).cancel
implantSlotTimers(slot) = context.system.scheduler.scheduleWithFixedDelay(initialDelay = 0 seconds, drainInterval milliseconds, self, Player.StaminaChanged(-implant.StaminaCost)) 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(
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)) zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.ActivateImplantSlot(player.GUID, slot))
} }
case _ => case _ =>
@ -1067,12 +1272,23 @@ class PlayerControl(player : Player) extends Actor
implantSlot.Active = false implantSlot.Active = false
implantSlot.Initialized = false implantSlot.Initialized = false
implantSlot.InitializeTime = 0L implantSlot.InitializeTime = 0L
zone.AvatarEvents ! AvatarServiceMessage(playerChannel, AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, 100))) zone.AvatarEvents ! AvatarServiceMessage(
zone.AvatarEvents ! AvatarServiceMessage(zoneChannel, AvatarAction.SendResponse(Service.defaultPlayerGUID, AvatarImplantMessage(guid, ImplantAction.Initialization, slot, 0))) 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 { object PlayerControl {
/** */
/**
*/
private case class StaminaRegen() private case class StaminaRegen()
} }

View file

@ -7,13 +7,15 @@ import net.psforever.objects.definition.{BaseDeployableDefinition, ObjectDefinit
import net.psforever.objects.vital.resistance.ResistanceProfile import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{PlanetSideEmpire, Vector3} import net.psforever.types.{PlanetSideEmpire, Vector3}
final case class ComplexDeployableSource(obj_def : ObjectDefinition with BaseDeployableDefinition, final case class ComplexDeployableSource(
obj_def: ObjectDefinition with BaseDeployableDefinition,
faction: PlanetSideEmpire.Value, faction: PlanetSideEmpire.Value,
health: Int, health: Int,
shields: Int, shields: Int,
ownerName: String, ownerName: String,
position: Vector3, position: Vector3,
orientation : Vector3) extends SourceEntry { orientation: Vector3
) extends SourceEntry {
override def Name = SourceEntry.NameFormat(obj_def.Name) override def Name = SourceEntry.NameFormat(obj_def.Name)
override def Faction = faction override def Faction = faction
def Definition: ObjectDefinition with BaseDeployableDefinition = obj_def def Definition: ObjectDefinition with BaseDeployableDefinition = obj_def

View file

@ -7,12 +7,14 @@ import net.psforever.objects.definition.{BaseDeployableDefinition, ObjectDefinit
import net.psforever.objects.vital.resistance.ResistanceProfile import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{PlanetSideEmpire, Vector3} import net.psforever.types.{PlanetSideEmpire, Vector3}
final case class DeployableSource(obj_def : ObjectDefinition with BaseDeployableDefinition, final case class DeployableSource(
obj_def: ObjectDefinition with BaseDeployableDefinition,
faction: PlanetSideEmpire.Value, faction: PlanetSideEmpire.Value,
health: Int, health: Int,
ownerName: String, ownerName: String,
position: Vector3, position: Vector3,
orientation : Vector3) extends SourceEntry { orientation: Vector3
) extends SourceEntry {
override def Name = SourceEntry.NameFormat(obj_def.Name) override def Name = SourceEntry.NameFormat(obj_def.Name)
override def Faction = faction override def Faction = faction
def Definition: ObjectDefinition with BaseDeployableDefinition = obj_def def Definition: ObjectDefinition with BaseDeployableDefinition = obj_def

View file

@ -6,11 +6,13 @@ import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.vital.resistance.ResistanceProfileMutators import net.psforever.objects.vital.resistance.ResistanceProfileMutators
import net.psforever.types.{PlanetSideEmpire, Vector3} import net.psforever.types.{PlanetSideEmpire, Vector3}
final case class ObjectSource(obj : PlanetSideGameObject with FactionAffinity, final case class ObjectSource(
obj: PlanetSideGameObject with FactionAffinity,
faction: PlanetSideEmpire.Value, faction: PlanetSideEmpire.Value,
position: Vector3, position: Vector3,
orientation: Vector3, orientation: Vector3,
velocity : Option[Vector3]) extends SourceEntry { velocity: Option[Vector3]
) extends SourceEntry {
override def Name = SourceEntry.NameFormat(obj.Definition.Name) override def Name = SourceEntry.NameFormat(obj.Definition.Name)
override def Faction = faction override def Faction = faction
def Definition = obj.Definition def Definition = obj.Definition

View file

@ -6,7 +6,8 @@ import net.psforever.objects.definition.{ExoSuitDefinition, ObjectDefinition}
import net.psforever.objects.vital.resistance.ResistanceProfile import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3} import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3}
final case class PlayerSource(name : String, final case class PlayerSource(
name: String,
char_id: Long, char_id: Long,
obj_def: ObjectDefinition, obj_def: ObjectDefinition,
faction: PlanetSideEmpire.Value, faction: PlanetSideEmpire.Value,
@ -17,7 +18,8 @@ final case class PlayerSource(name : String,
position: Vector3, position: Vector3,
orientation: Vector3, orientation: Vector3,
velocity: Option[Vector3], velocity: Option[Vector3],
modifiers : ResistanceProfile) extends SourceEntry { modifiers: ResistanceProfile
) extends SourceEntry {
override def Name = name override def Name = name
override def Faction = faction override def Faction = faction
override def CharId = char_id override def CharId = char_id
@ -34,8 +36,19 @@ final case class PlayerSource(name : String,
object PlayerSource { object PlayerSource {
def apply(tplayer: Player): PlayerSource = { def apply(tplayer: Player): PlayerSource = {
PlayerSource(tplayer.Name, tplayer.CharId, tplayer.Definition, tplayer.Faction, tplayer.ExoSuit, tplayer.VehicleSeated.nonEmpty, PlayerSource(
tplayer.Health, tplayer.Armor, tplayer.Position, tplayer.Orientation, tplayer.Velocity, tplayer.Name,
ExoSuitDefinition.Select(tplayer.ExoSuit, tplayer.Faction)) tplayer.CharId,
tplayer.Definition,
tplayer.Faction,
tplayer.ExoSuit,
tplayer.VehicleSeated.nonEmpty,
tplayer.Health,
tplayer.Armor,
tplayer.Position,
tplayer.Orientation,
tplayer.Velocity,
ExoSuitDefinition.Select(tplayer.ExoSuit, tplayer.Faction)
)
} }
} }

View file

@ -28,14 +28,16 @@ import net.psforever.types.Vector3
* @param fire_time when the weapon discharged was recorded; * @param fire_time when the weapon discharged was recorded;
* defaults to `System.nanoTime` * defaults to `System.nanoTime`
*/ */
final case class Projectile(profile : ProjectileDefinition, final case class Projectile(
profile: ProjectileDefinition,
tool_def: ToolDefinition, tool_def: ToolDefinition,
fire_mode: FireModeDefinition, fire_mode: FireModeDefinition,
owner: SourceEntry, owner: SourceEntry,
attribute_to: Int, attribute_to: Int,
shot_origin: Vector3, shot_origin: Vector3,
shot_angle: Vector3, shot_angle: Vector3,
fire_time: Long = System.nanoTime) extends PlanetSideGameObject { fire_time: Long = System.nanoTime
) extends PlanetSideGameObject {
Position = shot_origin Position = shot_origin
Orientation = shot_angle Orientation = shot_angle
Velocity = { Velocity = {
@ -45,6 +47,7 @@ final case class Projectile(profile : ProjectileDefinition,
val ground: Float = initVel * math.cos(radAngle).toFloat //base val ground: Float = initVel * math.cos(radAngle).toFloat //base
Vector3.Rz(Vector3(0, -ground, 0), shot_angle.z) + Vector3.z(rise) Vector3.Rz(Vector3(0, -ground, 0), shot_angle.z) + Vector3.z(rise)
} }
/** Information about the current world coordinates and orientation of the projectile */ /** Information about the current world coordinates and orientation of the projectile */
val current: SimpleWorldEntity = new SimpleWorldEntity() val current: SimpleWorldEntity = new SimpleWorldEntity()
private var resolved: ProjectileResolution.Value = ProjectileResolution.Unresolved private var resolved: ProjectileResolution.Value = ProjectileResolution.Unresolved
@ -68,10 +71,13 @@ final case class Projectile(profile : ProjectileDefinition,
} }
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
/** /**
@ -84,7 +90,14 @@ object Projectile {
* @param shot_angle in which direction the projectile was aimed when it was discharged * @param shot_angle in which direction the projectile was aimed when it was discharged
* @return the `Projectile` object * @return the `Projectile` object
*/ */
def apply(profile : ProjectileDefinition, tool_def : ToolDefinition, fire_mode : FireModeDefinition, owner : PlanetSideGameObject with FactionAffinity, shot_origin : Vector3, shot_angle : Vector3) : Projectile = { def apply(
profile: ProjectileDefinition,
tool_def: ToolDefinition,
fire_mode: FireModeDefinition,
owner: PlanetSideGameObject with FactionAffinity,
shot_origin: Vector3,
shot_angle: Vector3
): Projectile = {
Projectile(profile, tool_def, fire_mode, SourceEntry(owner), tool_def.ObjectId, shot_origin, shot_angle) Projectile(profile, tool_def, fire_mode, SourceEntry(owner), tool_def.ObjectId, shot_origin, shot_angle)
} }
@ -99,7 +112,15 @@ object Projectile {
* @param shot_angle in which direction the projectile was aimed when it was discharged * @param shot_angle in which direction the projectile was aimed when it was discharged
* @return the `Projectile` object * @return the `Projectile` object
*/ */
def apply(profile : ProjectileDefinition, tool_def : ToolDefinition, fire_mode : FireModeDefinition, owner : PlanetSideGameObject with FactionAffinity, attribute_to : Int, shot_origin : Vector3, shot_angle : Vector3) : Projectile = { def apply(
profile: ProjectileDefinition,
tool_def: ToolDefinition,
fire_mode: FireModeDefinition,
owner: PlanetSideGameObject with FactionAffinity,
attribute_to: Int,
shot_origin: Vector3,
shot_angle: Vector3
): Projectile = {
Projectile(profile, tool_def, fire_mode, SourceEntry(owner), attribute_to, shot_origin, shot_angle) Projectile(profile, tool_def, fire_mode, SourceEntry(owner), attribute_to, shot_origin, shot_angle)
} }
} }

View file

@ -7,8 +7,7 @@ package net.psforever.objects.ballistics
object ProjectileResolution extends Enumeration { object ProjectileResolution extends Enumeration {
type Type = Value type Type = Value
val val Unresolved, //original basic non-resolution
Unresolved, //original basic non-resolution
MissedShot, //projectile did not encounter any collision object and was despawned MissedShot, //projectile did not encounter any collision object and was despawned
Resolved, //a general "projectile encountered something" status with a more specific resolution Resolved, //a general "projectile encountered something" status with a more specific resolution
Hit, //direct hit, one target Hit, //direct hit, one target

View file

@ -15,10 +15,12 @@ import net.psforever.types.Vector3
* @param damage_model the kind of damage model to which the `target` is/was subject * @param damage_model the kind of damage model to which the `target` is/was subject
* @param hit_pos where the projectile hit * @param hit_pos where the projectile hit
*/ */
final case class ResolvedProjectile(resolution : ProjectileResolution.Value, final case class ResolvedProjectile(
resolution: ProjectileResolution.Value,
projectile: Projectile, projectile: Projectile,
target: SourceEntry, target: SourceEntry,
damage_model: DamageResistanceModel, damage_model: DamageResistanceModel,
hit_pos : Vector3) { hit_pos: Vector3
) {
val hit_time: Long = System.nanoTime val hit_time: Long = System.nanoTime
} }

View file

@ -40,7 +40,8 @@ object SourceEntry {
} }
def NameFormat(name: String): String = { def NameFormat(name: String): String = {
name.replace("_", " ") name
.replace("_", " ")
.split(" ") .split(" ")
.map(_.capitalize) .map(_.capitalize)
.mkString(" ") .mkString(" ")

View file

@ -6,14 +6,16 @@ import net.psforever.objects.definition.VehicleDefinition
import net.psforever.objects.vital.resistance.ResistanceProfile import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{PlanetSideEmpire, Vector3} import net.psforever.types.{PlanetSideEmpire, Vector3}
final case class VehicleSource(obj_def : VehicleDefinition, final case class VehicleSource(
obj_def: VehicleDefinition,
faction: PlanetSideEmpire.Value, faction: PlanetSideEmpire.Value,
health: Int, health: Int,
shields: Int, shields: Int,
position: Vector3, position: Vector3,
orientation: Vector3, orientation: Vector3,
velocity: Option[Vector3], velocity: Option[Vector3],
modifiers : ResistanceProfile) extends SourceEntry { modifiers: ResistanceProfile
) extends SourceEntry {
override def Name = SourceEntry.NameFormat(obj_def.Name) override def Name = SourceEntry.NameFormat(obj_def.Name)
override def Faction = faction override def Faction = faction
def Definition: VehicleDefinition = obj_def def Definition: VehicleDefinition = obj_def

View file

@ -4,8 +4,7 @@ package net.psforever.objects.ce
import net.psforever.objects.definition.ComplexDeployableDefinition import net.psforever.objects.definition.ComplexDeployableDefinition
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
abstract class ComplexDeployable(cdef : ComplexDeployableDefinition) extends PlanetSideServerObject abstract class ComplexDeployable(cdef: ComplexDeployableDefinition) extends PlanetSideServerObject with Deployable {
with Deployable {
private var shields: Int = 0 private var shields: Int = 0
def Shields: Int = shields def Shields: Int = shields

View file

@ -9,10 +9,7 @@ import net.psforever.objects.zones.ZoneAware
import net.psforever.packet.game.DeployableIcon import net.psforever.packet.game.DeployableIcon
import net.psforever.types.PlanetSideEmpire import net.psforever.types.PlanetSideEmpire
trait Deployable extends FactionAffinity trait Deployable extends FactionAffinity with Vitality with OwnableByPlayer with ZoneAware {
with Vitality
with OwnableByPlayer
with ZoneAware {
this: PlanetSideGameObject => this: PlanetSideGameObject =>
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL

View file

@ -4,14 +4,5 @@ package net.psforever.objects.ce
object DeployableCategory extends Enumeration { object DeployableCategory extends Enumeration {
type Type = Value type Type = Value
val val Boomers, Mines, SmallTurrets, Sensors, TankTraps, FieldTurrets, ShieldGenerators, Telepads = Value
Boomers,
Mines,
SmallTurrets,
Sensors,
TankTraps,
FieldTurrets,
ShieldGenerators,
Telepads
= Value
} }

View file

@ -4,8 +4,7 @@ package net.psforever.objects.ce
import net.psforever.objects.PlanetSideGameObject import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.definition.SimpleDeployableDefinition import net.psforever.objects.definition.SimpleDeployableDefinition
abstract class SimpleDeployable(cdef : SimpleDeployableDefinition) extends PlanetSideGameObject abstract class SimpleDeployable(cdef: SimpleDeployableDefinition) extends PlanetSideGameObject with Deployable {
with Deployable {
Health = Definition.MaxHealth Health = Definition.MaxHealth
def Definition = cdef def Definition = cdef

View file

@ -76,8 +76,7 @@ object TelepadLike {
//determine whether to activate both the Router's internal telepad and the deployed remote telepad //determine whether to activate both the Router's internal telepad and the deployed remote telepad
if (router.DeploymentState == DriveState.Deployed && util.Active && telepad.Active) { if (router.DeploymentState == DriveState.Deployed && util.Active && telepad.Active) {
Some((util, telepad)) Some((util, telepad))
} } else {
else {
None None
} }
case _ => case _ =>

View file

@ -9,8 +9,7 @@ import net.psforever.objects.vital.VitalityDefinition
* The definition for game objects that look like other people, and also for players. * The definition for game objects that look like other people, and also for players.
* @param objectId the object's identifier number * @param objectId the object's identifier number
*/ */
class AvatarDefinition(objectId : Int) extends ObjectDefinition(objectId) class AvatarDefinition(objectId: Int) extends ObjectDefinition(objectId) with VitalityDefinition {
with VitalityDefinition {
Avatars(objectId) //let throw NoSuchElementException Avatars(objectId) //let throw NoSuchElementException
Packet = AvatarDefinition.converter Packet = AvatarDefinition.converter
} }

View file

@ -7,8 +7,10 @@ import net.psforever.objects.vehicles.CargoVehicleRestriction
* The definition for a cargo hold. * The definition for a cargo hold.
*/ */
class CargoDefinition extends BasicDefinition { class CargoDefinition extends BasicDefinition {
/** a restriction on the type of exo-suit a person can wear */ /** a restriction on the type of exo-suit a person can wear */
private var vehicleRestriction: CargoVehicleRestriction.Value = CargoVehicleRestriction.Small private var vehicleRestriction: CargoVehicleRestriction.Value = CargoVehicleRestriction.Small
/** the user can escape while the vehicle is moving */ /** the user can escape while the vehicle is moving */
private var bailable: Boolean = true private var bailable: Boolean = true
Name = "cargo" Name = "cargo"

View file

@ -9,10 +9,13 @@ import net.psforever.objects.inventory.InventoryTile
* @param objectId the object's identifier number * @param objectId the object's identifier number
*/ */
abstract class EquipmentDefinition(objectId: Int) extends ObjectDefinition(objectId) { abstract class EquipmentDefinition(objectId: Int) extends ObjectDefinition(objectId) {
/** the size of the item when placed in an EquipmentSlot / holster / mounting */ /** the size of the item when placed in an EquipmentSlot / holster / mounting */
private var size: EquipmentSize.Value = EquipmentSize.Blocked private var size: EquipmentSize.Value = EquipmentSize.Blocked
/** the size of the item when placed in the grid inventory space */ /** the size of the item when placed in the grid inventory space */
private var tile: InventoryTile = InventoryTile.Tile11 private var tile: InventoryTile = InventoryTile.Tile11
/** a correction for the z-coordinate for some dropped items to avoid sinking into the ground */ /** a correction for the z-coordinate for some dropped items to avoid sinking into the ground */
private var dropOffset: Float = 0f private var dropOffset: Float = 0f

View file

@ -13,7 +13,8 @@ import net.psforever.types.{CertificationType, ExoSuitType, PlanetSideEmpire}
* Players are influenced by the exo-suit they wear in a variety of ways, with speed and available equipment slots being major differences. * Players are influenced by the exo-suit they wear in a variety of ways, with speed and available equipment slots being major differences.
* @param suitType the `Enumeration` corresponding to this exo-suit * @param suitType the `Enumeration` corresponding to this exo-suit
*/ */
class ExoSuitDefinition(private val suitType : ExoSuitType.Value) extends BasicDefinition class ExoSuitDefinition(private val suitType: ExoSuitType.Value)
extends BasicDefinition
with ResistanceProfileMutators with ResistanceProfileMutators
with DamageResistanceModel { with DamageResistanceModel {
protected var permissions: List[CertificationType.Value] = List.empty protected var permissions: List[CertificationType.Value] = List.empty
@ -86,8 +87,7 @@ class ExoSuitDefinition(private val suitType : ExoSuitType.Value) extends BasicD
def Holster(slot: Int): EquipmentSize.Value = { def Holster(slot: Int): EquipmentSize.Value = {
if (slot >= 0 && slot < 5) { if (slot >= 0 && slot < 5) {
holsters(slot) holsters(slot)
} } else {
else {
EquipmentSize.Blocked EquipmentSize.Blocked
} }
} }
@ -96,8 +96,7 @@ class ExoSuitDefinition(private val suitType : ExoSuitType.Value) extends BasicD
if (slot >= 0 && slot < 5) { if (slot >= 0 && slot < 5) {
holsters(slot) = value holsters(slot) = value
holsters(slot) holsters(slot)
} } else {
else {
EquipmentSize.Blocked EquipmentSize.Blocked
} }
} }
@ -113,7 +112,8 @@ class ExoSuitDefinition(private val suitType : ExoSuitType.Value) extends BasicD
def canEqual(other: Any): Boolean = other.isInstanceOf[ExoSuitDefinition] def canEqual(other: Any): Boolean = other.isInstanceOf[ExoSuitDefinition]
override def equals(other: Any): Boolean = other match { override def equals(other: Any): Boolean =
other match {
case that: ExoSuitDefinition => case that: ExoSuitDefinition =>
(that canEqual this) && (that canEqual this) &&
suitType == that.suitType suitType == that.suitType
@ -172,12 +172,7 @@ object SpecialExoSuitDefinition {
object Mode extends Enumeration { object Mode extends Enumeration {
type Type = Value type Type = Value
val val Normal, Anchored, Overdrive, Shielded = Value
Normal,
Anchored,
Overdrive,
Shielded
= Value
} }
} }
@ -197,7 +192,8 @@ object ExoSuitDefinition {
case ExoSuitType.Infiltration => GlobalDefinitions.Infiltration.Use case ExoSuitType.Infiltration => GlobalDefinitions.Infiltration.Use
case ExoSuitType.Agile => GlobalDefinitions.Agile.Use case ExoSuitType.Agile => GlobalDefinitions.Agile.Use
case ExoSuitType.Reinforced => GlobalDefinitions.Reinforced.Use case ExoSuitType.Reinforced => GlobalDefinitions.Reinforced.Use
case ExoSuitType.MAX => faction match { case ExoSuitType.MAX =>
faction match {
case PlanetSideEmpire.TR => GlobalDefinitions.TRMAX.Use case PlanetSideEmpire.TR => GlobalDefinitions.TRMAX.Use
case PlanetSideEmpire.NC => GlobalDefinitions.NCMAX.Use case PlanetSideEmpire.NC => GlobalDefinitions.NCMAX.Use
case PlanetSideEmpire.VS => GlobalDefinitions.VSMAX.Use case PlanetSideEmpire.VS => GlobalDefinitions.VSMAX.Use

View file

@ -19,12 +19,16 @@ import scala.collection.mutable
*/ */
class ImplantDefinition(private val implantType: Int) extends BasicDefinition { class ImplantDefinition(private val implantType: Int) extends BasicDefinition {
ImplantType(implantType) 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
/** a passive certification is activated as soon as it is ready (or other condition) */ /** a passive certification is activated as soon as it is ready (or other condition) */
private var passive: Boolean = false private var passive: Boolean = false
/** how much turning on the implant costs */ /** how much turning on the implant costs */
private var activationStaminaCost: Int = 0 private var activationStaminaCost: Int = 0
/** how much energy does this implant cost to remain activate per interval tick */ /** how much energy does this implant cost to remain activate per interval tick */
private var staminaCost: Int = 0 private var staminaCost: Int = 0
@ -69,7 +73,6 @@ class ImplantDefinition(private val implantType : Int) extends BasicDefinition {
StaminaCost StaminaCost
} }
def CostIntervalDefault: Int = { def CostIntervalDefault: Int = {
costIntervalDefault costIntervalDefault
} }
@ -78,7 +81,8 @@ class ImplantDefinition(private val implantType : Int) extends BasicDefinition {
CostIntervalDefault CostIntervalDefault
} }
def GetCostIntervalByExoSuit(exosuit : ExoSuitType.Value) : Int = costIntervalByExoSuit.getOrElse(exosuit, CostIntervalDefault) def GetCostIntervalByExoSuit(exosuit: ExoSuitType.Value): Int =
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) def Type: ImplantType.Value = ImplantType(implantType)

View file

@ -18,6 +18,7 @@ import net.psforever.objects.definition.converter.{ObjectCreateConverter, Packet
* @param objectId the object's identifier number * @param objectId the object's identifier number
*/ */
abstract class ObjectDefinition(private val objectId: Int) extends BasicDefinition { abstract class ObjectDefinition(private val objectId: Int) extends BasicDefinition {
/** a data converter for this type of object */ /** a data converter for this type of object */
protected var packet: PacketConverter = new ObjectCreateConverter[PlanetSideGameObject]() {} protected var packet: PacketConverter = new ObjectCreateConverter[PlanetSideGameObject]() {}
Name = "object definition" Name = "object definition"
@ -26,7 +27,8 @@ abstract class ObjectDefinition(private val objectId : Int) extends BasicDefinit
* Get the conversion object. * Get the conversion object.
* @return * @return
*/ */
final def Packet : ObjectCreateConverter[PlanetSideGameObject] = packet.asInstanceOf[ObjectCreateConverter[PlanetSideGameObject]] final def Packet: ObjectCreateConverter[PlanetSideGameObject] =
packet.asInstanceOf[ObjectCreateConverter[PlanetSideGameObject]]
/** /**
* Assign this definition a conversion object. * Assign this definition a conversion object.

View file

@ -10,7 +10,8 @@ import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
* `Tool` objects emit `ProjectileDefinition` objects and that is later wrapped into a `Projectile` object. * `Tool` objects emit `ProjectileDefinition` objects and that is later wrapped into a `Projectile` object.
* @param objectId the object's identifier number * @param objectId the object's identifier number
*/ */
class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId) class ProjectileDefinition(objectId: Int)
extends ObjectDefinition(objectId)
with JammingUnit with JammingUnit
with StandardDamageProfile { with StandardDamageProfile {
private val projectileType: Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException private val projectileType: Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException
@ -26,7 +27,8 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
private var damageRadius: Float = 1f private var damageRadius: Float = 1f
private var useDamage1Subtract: Boolean = false private var useDamage1Subtract: Boolean = false
private var existsOnRemoteClients: Boolean = false //`true` spawns a server-managed object private var existsOnRemoteClients: Boolean = false //`true` spawns a server-managed object
private var remoteClientData : (Int, Int) = (0, 0) //artificial values; for ObjectCreateMessage packet (oicw_little_buddy is undefined) private var remoteClientData: (Int, Int) =
(0, 0) //artificial values; for ObjectCreateMessage packet (oicw_little_buddy is undefined)
private var autoLock: Boolean = false private var autoLock: Boolean = false
private var additionalEffect: Boolean = false private var additionalEffect: Boolean = false
private var jammerProjectile: Boolean = false private var jammerProjectile: Boolean = false
@ -168,9 +170,9 @@ object ProjectileDefinition {
def CalculateDerivedFields(pdef: ProjectileDefinition): Unit = { def CalculateDerivedFields(pdef: ProjectileDefinition): Unit = {
val (distanceMax, distanceFromAcceleration, finalVelocity): (Float, Float, Float) = if (pdef.Acceleration == 0) { val (distanceMax, distanceFromAcceleration, finalVelocity): (Float, Float, Float) = if (pdef.Acceleration == 0) {
(pdef.InitialVelocity * pdef.Lifespan, 0, pdef.InitialVelocity.toFloat) (pdef.InitialVelocity * pdef.Lifespan, 0, pdef.InitialVelocity.toFloat)
} } else {
else { val distanceFromAcceleration =
val distanceFromAcceleration = (pdef.AccelerationUntil * pdef.InitialVelocity) + (0.5f * pdef.Acceleration * pdef.AccelerationUntil * pdef.AccelerationUntil) (pdef.AccelerationUntil * pdef.InitialVelocity) + (0.5f * pdef.Acceleration * pdef.AccelerationUntil * pdef.AccelerationUntil)
val finalVelocity = pdef.InitialVelocity + pdef.Acceleration * pdef.AccelerationUntil val finalVelocity = pdef.InitialVelocity + pdef.Acceleration * pdef.AccelerationUntil
val distanceAfterAcceleration = finalVelocity * (pdef.Lifespan - pdef.AccelerationUntil) val distanceAfterAcceleration = finalVelocity * (pdef.Lifespan - pdef.AccelerationUntil)
(distanceFromAcceleration + distanceAfterAcceleration, distanceFromAcceleration, finalVelocity) (distanceFromAcceleration + distanceAfterAcceleration, distanceFromAcceleration, finalVelocity)
@ -181,11 +183,9 @@ object ProjectileDefinition {
pdef.distanceNoDegrade = if (pdef.DegradeDelay == 0f) { pdef.distanceNoDegrade = if (pdef.DegradeDelay == 0f) {
pdef.distanceMax pdef.distanceMax
} } else if (pdef.DegradeDelay < pdef.AccelerationUntil) {
else if(pdef.DegradeDelay < pdef.AccelerationUntil) {
(pdef.DegradeDelay * pdef.InitialVelocity) + (0.5f * pdef.Acceleration * pdef.DegradeDelay * pdef.DegradeDelay) (pdef.DegradeDelay * pdef.InitialVelocity) + (0.5f * pdef.Acceleration * pdef.DegradeDelay * pdef.DegradeDelay)
} } else {
else {
pdef.distanceFromAcceleration + pdef.finalVelocity * (pdef.DegradeDelay - pdef.AccelerationUntil) pdef.distanceFromAcceleration + pdef.finalVelocity * (pdef.DegradeDelay - pdef.AccelerationUntil)
} }
} }

View file

@ -7,10 +7,13 @@ import net.psforever.objects.vehicles.SeatArmorRestriction
* The definition for a seat. * The definition for a seat.
*/ */
class SeatDefinition extends BasicDefinition { class SeatDefinition extends BasicDefinition {
/** a restriction on the type of exo-suit a person can wear */ /** a restriction on the type of exo-suit a person can wear */
private var armorRestriction: SeatArmorRestriction.Value = SeatArmorRestriction.NoMax private var armorRestriction: SeatArmorRestriction.Value = SeatArmorRestriction.NoMax
/** the user can escape while the vehicle is moving */ /** the user can escape while the vehicle is moving */
private var bailable: Boolean = false private var bailable: Boolean = false
/** any controlled weapon */ /** any controlled weapon */
private var weaponMount: Option[Int] = None private var weaponMount: Option[Int] = None
Name = "seat" Name = "seat"

View file

@ -7,7 +7,12 @@ import net.psforever.objects.ce.{Deployable, DeployableCategory, DeployedItem}
import net.psforever.objects.definition.converter.SmallDeployableConverter import net.psforever.objects.definition.converter.SmallDeployableConverter
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.vital.resistance.ResistanceProfileMutators import net.psforever.objects.vital.resistance.ResistanceProfileMutators
import net.psforever.objects.vital.{DamageResistanceModel, NoResistanceSelection, StandardDeployableDamage, VitalityDefinition} import net.psforever.objects.vital.{
DamageResistanceModel,
NoResistanceSelection,
StandardDeployableDamage,
VitalityDefinition
}
import scala.concurrent.duration._ import scala.concurrent.duration._
@ -42,7 +47,8 @@ trait BaseDeployableDefinition {
def Uninitialize(obj: PlanetSideServerObject with Deployable, context: ActorContext): Unit = {} def Uninitialize(obj: PlanetSideServerObject with Deployable, context: ActorContext): Unit = {}
} }
abstract class DeployableDefinition(objectId : Int) extends ObjectDefinition(objectId) abstract class DeployableDefinition(objectId: Int)
extends ObjectDefinition(objectId)
with DamageResistanceModel with DamageResistanceModel
with ResistanceProfileMutators with ResistanceProfileMutators
with VitalityDefinition with VitalityDefinition

View file

@ -15,10 +15,12 @@ import scala.concurrent.duration._
* An object definition system used to construct and retain the parameters of various vehicles. * An object definition system used to construct and retain the parameters of various vehicles.
* @param objectId the object id that is associated with this sort of `Vehicle` * @param objectId the object id that is associated with this sort of `Vehicle`
*/ */
class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) class VehicleDefinition(objectId: Int)
extends ObjectDefinition(objectId)
with VitalityDefinition with VitalityDefinition
with ResistanceProfileMutators with ResistanceProfileMutators
with DamageResistanceModel { with DamageResistanceModel {
/** vehicle shields offered through amp station facility benefits (generally: 20% of health + 1) */ /** vehicle shields offered through amp station facility benefits (generally: 20% of health + 1) */
private var maxShields: Int = 0 private var maxShields: Int = 0
/* key - seat index, value - seat object */ /* key - seat index, value - seat object */

View file

@ -21,8 +21,7 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
MakeInventoryData(obj), MakeInventoryData(obj),
GetDrawnSlot(obj) GetDrawnSlot(obj)
) )
} } else {
else {
PlayerData( PlayerData(
MakeAppearanceData(obj), MakeAppearanceData(obj),
MakeCharacterData(obj), MakeCharacterData(obj),
@ -44,8 +43,7 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
MakeDetailedInventoryData(obj), MakeDetailedInventoryData(obj),
GetDrawnSlot(obj) GetDrawnSlot(obj)
) )
} } else {
else {
DetailedPlayerData.apply( DetailedPlayerData.apply(
MakeAppearanceData(obj), MakeAppearanceData(obj),
MakeDetailedCharacterData(obj), MakeDetailedCharacterData(obj),
@ -58,6 +56,7 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
} }
object AvatarConverter { object AvatarConverter {
/** /**
* Compose some data from a `Player` into a representation common to both `CharacterData` and `DetailedCharacterData`. * Compose some data from a `Player` into a representation common to both `CharacterData` and `DetailedCharacterData`.
* @param obj the `Player` game object * @param obj the `Player` game object
@ -115,8 +114,7 @@ object AvatarConverter {
StatConverter.Health(obj.Health, obj.MaxHealth), StatConverter.Health(obj.Health, obj.MaxHealth),
if (MaxArmor == 0) { if (MaxArmor == 0) {
0 0
} } else {
else {
StatConverter.Health(obj.Armor, MaxArmor) StatConverter.Health(obj.Armor, MaxArmor)
}, },
DressBattleRank(obj), DressBattleRank(obj),
@ -129,30 +127,44 @@ object AvatarConverter {
def MakeDetailedCharacterData(obj: Player): Option[Int] => DetailedCharacterData = { def MakeDetailedCharacterData(obj: Player): Option[Int] => DetailedCharacterData = {
val bep: Long = obj.BEP val bep: Long = obj.BEP
val maxOpt : Option[Long] = if(obj.ExoSuit == ExoSuitType.MAX) { Some(0L) } else { None } val maxOpt: Option[Long] = if (obj.ExoSuit == ExoSuitType.MAX) { Some(0L) }
else { None }
val ba: DetailedCharacterA = DetailedCharacterA( val ba: DetailedCharacterA = DetailedCharacterA(
bep, bep,
obj.CEP, obj.CEP,
0L, 0L, 0L, 0L,
obj.MaxHealth, obj.Health, 0L,
0L,
obj.MaxHealth,
obj.Health,
false, false,
obj.Armor, obj.Armor,
0L, 0L,
obj.MaxStamina, obj.Stamina, obj.MaxStamina,
obj.Stamina,
maxOpt, maxOpt,
0, 0, 0L, 0,
0,
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.Certifications.toList.sortBy(_.id) //TODO is sorting necessary?
) )
val bb: (Long, Option[Int]) => DetailedCharacterB = DetailedCharacterB( val bb: (Long, Option[Int]) => DetailedCharacterB = DetailedCharacterB(
None, None,
MakeImplantEntries(obj), MakeImplantEntries(obj),
Nil, Nil, Nil,
Nil,
obj.FirstTimeEvents, obj.FirstTimeEvents,
tutorials = List.empty[String], //TODO tutorial list tutorials = List.empty[String], //TODO tutorial list
0L, 0L, 0L, 0L, 0L, 0L,
0L,
0L,
0L,
0L,
Some(DCDExtra2(0, 0)), Some(DCDExtra2(0, 0)),
Nil, Nil, false, Nil,
Nil,
false,
MakeCosmetics(obj) MakeCosmetics(obj)
) )
pad_length: Option[Int] => DetailedCharacterData(ba, bb(bep, pad_length))(pad_length) pad_length: Option[Int] => DetailedCharacterData(ba, bb(bep, pad_length))(pad_length)
@ -163,7 +175,9 @@ object AvatarConverter {
} }
def MakeDetailedInventoryData(obj: Player): InventoryData = { def MakeDetailedInventoryData(obj: Player): InventoryData = {
InventoryData((MakeHolsters(obj, BuildDetailedEquipment) ++ MakeFifthSlot(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)) InventoryData(
(MakeHolsters(obj, BuildDetailedEquipment) ++ MakeFifthSlot(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)
)
} }
/** /**
@ -176,14 +190,11 @@ object AvatarConverter {
val bep: Long = obj.BEP val bep: Long = obj.BEP
if (bep > 2583440) { //BR25+ if (bep > 2583440) { //BR25+
UniformStyle.ThirdUpgrade UniformStyle.ThirdUpgrade
} } else if (bep > 308989) { //BR14+
else if(bep > 308989) { //BR14+
UniformStyle.SecondUpgrade UniformStyle.SecondUpgrade
} } else if (bep > 44999) { //BR7+
else if(bep > 44999) { //BR7+
UniformStyle.FirstUpgrade UniformStyle.FirstUpgrade
} } else { //BR1+
else { //BR1+
UniformStyle.Normal UniformStyle.Normal
} }
} }
@ -198,20 +209,15 @@ object AvatarConverter {
val cep = obj.CEP val cep = obj.CEP
if (cep > 599999) { if (cep > 599999) {
5 5
} } else if (cep > 299999) {
else if(cep > 299999) {
4 4
} } else if (cep > 149999) {
else if(cep > 149999) {
3 3
} } else if (cep > 49999) {
else if(cep > 49999) {
2 2
} } else if (cep > 9999) {
else if(cep > 9999) {
1 1
} } else {
else {
0 0
} }
} }
@ -225,14 +231,16 @@ object AvatarConverter {
private def MakeImplantEntries(obj: Player): List[ImplantEntry] = { private def MakeImplantEntries(obj: Player): List[ImplantEntry] = {
//val numImplants : Int = DetailedCharacterData.numberOfImplantSlots(obj.BEP) //val numImplants : Int = DetailedCharacterData.numberOfImplantSlots(obj.BEP)
//val implants = obj.Implants //val implants = obj.Implants
obj.Implants.map({ case(implant, initialization, _) => obj.Implants
.map({
case (implant, initialization, _) =>
if (initialization == 0) { if (initialization == 0) {
ImplantEntry(implant, None) ImplantEntry(implant, None)
} } else {
else {
ImplantEntry(implant, Some(math.max(0, initialization).toInt)) ImplantEntry(implant, Some(math.max(0, initialization).toInt))
} }
}).toList })
.toList
} }
/** /**
@ -260,8 +268,7 @@ object AvatarConverter {
def MakeCosmetics(obj: Player): Option[Cosmetics] = def MakeCosmetics(obj: Player): Option[Cosmetics] =
if (DetailedCharacterData.isBR24(obj.BEP)) { if (DetailedCharacterData.isBR24(obj.BEP)) {
obj.PersonalStyleFeatures.orElse(Some(Cosmetics())) obj.PersonalStyleFeatures.orElse(Some(Cosmetics()))
} } else {
else {
None None
} }
@ -276,7 +283,12 @@ object AvatarConverter {
obj.Inventory.Items obj.Inventory.Items
.map(item => { .map(item => {
val equip: Equipment = item.obj val equip: Equipment = item.obj
InternalSlot(equip.Definition.ObjectId, equip.GUID, item.start, equip.Definition.Packet.DetailedConstructorData(equip).get) InternalSlot(
equip.Definition.ObjectId,
equip.GUID,
item.start,
equip.Definition.Packet.DetailedConstructorData(equip).get
)
}) })
} }
@ -326,7 +338,12 @@ object AvatarConverter {
* @return the game object in decoded packet form * @return the game object in decoded packet form
*/ */
def BuildDetailedEquipment(index: Int, equip: Equipment): InternalSlot = { def BuildDetailedEquipment(index: Int, equip: Equipment): InternalSlot = {
InternalSlot(equip.Definition.ObjectId, equip.GUID, index, equip.Definition.Packet.DetailedConstructorData(equip).get) InternalSlot(
equip.Definition.ObjectId,
equip.GUID,
index,
equip.Definition.Packet.DetailedConstructorData(equip).get
)
} }
/** /**
@ -337,11 +354,15 @@ object AvatarConverter {
* @param index which holster is currently being explored * @param index which holster is currently being explored
* @return the `List` of inventory data created from the holsters * @return the `List` of inventory data created from the holsters
*/ */
@tailrec private def recursiveMakeHolsters(iter : Iterator[EquipmentSlot], builder : (Int, Equipment) => InternalSlot, list : List[InternalSlot] = Nil, index : Int = 0) : List[InternalSlot] = { @tailrec private def recursiveMakeHolsters(
iter: Iterator[EquipmentSlot],
builder: (Int, Equipment) => InternalSlot,
list: List[InternalSlot] = Nil,
index: Int = 0
): List[InternalSlot] = {
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
@ -351,8 +372,7 @@ object AvatarConverter {
list :+ builder(index, equip), list :+ builder(index, equip),
index + 1 index + 1
) )
} } else {
else {
recursiveMakeHolsters(iter, builder, list, index + 1) recursiveMakeHolsters(iter, builder, list, index + 1)
} }
} }

View file

@ -13,8 +13,10 @@ class BoomerTriggerConverter extends ObjectCreateConverter[SimpleItem]() {
} }
override def DetailedConstructorData(obj: SimpleItem): Try[DetailedConstructionToolData] = { override def DetailedConstructorData(obj: SimpleItem): Try[DetailedConstructionToolData] = {
Success(DetailedConstructionToolData( Success(
DetailedConstructionToolData(
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)) CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0))
)) )
)
} }
} }

View file

@ -2,7 +2,7 @@
package net.psforever.objects.definition.converter package net.psforever.objects.definition.converter
import net.psforever.objects.{Player, Tool} import net.psforever.objects.{Player, Tool}
import net.psforever.objects.equipment.{Equipment, EquipmentSlot} import net.psforever.objects.equipment.EquipmentSlot
import net.psforever.packet.game.objectcreate._ import net.psforever.packet.game.objectcreate._
import net.psforever.types.{PlanetSideGUID, _} import net.psforever.types.{PlanetSideGUID, _}
@ -15,7 +15,8 @@ import scala.util.{Failure, Success, Try}
* Details that would not be apparent on that screen such as implants or certifications are ignored. * Details that would not be apparent on that screen such as implants or certifications are ignored.
*/ */
class CharacterSelectConverter extends AvatarConverter { class CharacterSelectConverter extends AvatarConverter {
override def ConstructorData(obj : Player) : Try[PlayerData] = Failure(new Exception("CharacterSelectConverter should not be used to generate CharacterData")) override def ConstructorData(obj: Player): Try[PlayerData] =
Failure(new Exception("CharacterSelectConverter should not be used to generate CharacterData"))
override def DetailedConstructorData(obj: Player): Try[DetailedPlayerData] = { override def DetailedConstructorData(obj: Player): Try[DetailedPlayerData] = {
Success( Success(
@ -82,30 +83,44 @@ class CharacterSelectConverter extends AvatarConverter {
private def MakeDetailedCharacterData(obj: Player): Option[Int] => DetailedCharacterData = { private def MakeDetailedCharacterData(obj: Player): Option[Int] => DetailedCharacterData = {
val bep: Long = obj.BEP val bep: Long = obj.BEP
val maxOpt : Option[Long] = if(obj.ExoSuit == ExoSuitType.MAX) { Some(0L) } else { None } val maxOpt: Option[Long] = if (obj.ExoSuit == ExoSuitType.MAX) { Some(0L) }
else { None }
val ba: DetailedCharacterA = DetailedCharacterA( val ba: DetailedCharacterA = DetailedCharacterA(
bep, bep,
obj.CEP, obj.CEP,
0L, 0L, 0L, 0L,
1, 1, 0L,
0L,
1,
1,
false, false,
0, 0,
0L, 0L,
1, 1, 1,
1,
maxOpt, maxOpt,
0, 0, 0L, 0,
0,
0L,
List(0, 0, 0, 0, 0, 0), List(0, 0, 0, 0, 0, 0),
certs = List.empty[CertificationType.Value] certs = List.empty[CertificationType.Value]
) )
val bb: (Long, Option[Int]) => DetailedCharacterB = DetailedCharacterB( val bb: (Long, Option[Int]) => DetailedCharacterB = DetailedCharacterB(
None, None,
MakeImplantEntries(obj), //necessary for correct stream length MakeImplantEntries(obj), //necessary for correct stream length
Nil, Nil, Nil,
Nil,
firstTimeEvents = List.empty[String], firstTimeEvents = List.empty[String],
tutorials = List.empty[String], tutorials = List.empty[String],
0L, 0L, 0L, 0L, 0L, 0L,
0L,
0L,
0L,
0L,
Some(DCDExtra2(0, 0)), Some(DCDExtra2(0, 0)),
Nil, Nil, false, Nil,
Nil,
false,
AvatarConverter.MakeCosmetics(obj) AvatarConverter.MakeCosmetics(obj)
) )
pad_length: Option[Int] => DetailedCharacterData(ba, bb(bep, pad_length))(pad_length) pad_length: Option[Int] => DetailedCharacterData(ba, bb(bep, pad_length))(pad_length)
@ -129,11 +144,14 @@ class CharacterSelectConverter extends AvatarConverter {
* @see `AvatarConverter.recursiveMakeHolsters` * @see `AvatarConverter.recursiveMakeHolsters`
* @return the `List` of inventory data created from the holsters * @return the `List` of inventory data created from the holsters
*/ */
@tailrec private def recursiveMakeHolsters(iter : Iterator[EquipmentSlot], list : List[InternalSlot] = Nil, index : Int = 0) : List[InternalSlot] = { @tailrec private def recursiveMakeHolsters(
iter: Iterator[EquipmentSlot],
list: List[InternalSlot] = Nil,
index: Int = 0
): List[InternalSlot] = {
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) =>

View file

@ -3,7 +3,7 @@ package net.psforever.objects.definition.converter
import net.psforever.objects.SimpleItem import net.psforever.objects.SimpleItem
import net.psforever.packet.game.objectcreate.{CommonFieldData, DetailedCommandDetonaterData, HandheldData} import net.psforever.packet.game.objectcreate.{CommonFieldData, DetailedCommandDetonaterData, HandheldData}
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} import net.psforever.types.PlanetSideGUID
import scala.util.{Success, Try} import scala.util.{Success, Try}

View file

@ -76,30 +76,44 @@ class CorpseConverter extends AvatarConverter {
} }
private def MakeDetailedCharacterData(obj: Player): Option[Int] => DetailedCharacterData = { private def MakeDetailedCharacterData(obj: Player): Option[Int] => DetailedCharacterData = {
val maxOpt : Option[Long] = if(obj.ExoSuit == ExoSuitType.MAX) { Some(0L) } else { None } val maxOpt: Option[Long] = if (obj.ExoSuit == ExoSuitType.MAX) { Some(0L) }
else { None }
val ba: DetailedCharacterA = DetailedCharacterA( val ba: DetailedCharacterA = DetailedCharacterA(
bep = 0L, bep = 0L,
cep = 0L, cep = 0L,
0L, 0L, 0L, 0L,
0, 0, 0L,
0L,
0,
0,
false, false,
0, 0,
0L, 0L,
0, 0, 0,
0,
maxOpt, maxOpt,
0, 0, 0L, 0,
0,
0L,
List(0, 0, 0, 0, 0, 0), List(0, 0, 0, 0, 0, 0),
certs = List.empty[CertificationType.Value] certs = List.empty[CertificationType.Value]
) )
val bb: (Long, Option[Int]) => DetailedCharacterB = DetailedCharacterB( val bb: (Long, Option[Int]) => DetailedCharacterB = DetailedCharacterB(
None, None,
implants = List.empty[ImplantEntry], implants = List.empty[ImplantEntry],
Nil, Nil, Nil,
Nil,
firstTimeEvents = List.empty[String], firstTimeEvents = List.empty[String],
tutorials = List.empty[String], tutorials = List.empty[String],
0L, 0L, 0L, 0L, 0L, 0L,
0L,
0L,
0L,
0L,
Some(DCDExtra2(0, 0)), Some(DCDExtra2(0, 0)),
Nil, Nil, false, Nil,
Nil,
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)
@ -139,11 +153,14 @@ class CorpseConverter extends AvatarConverter {
* @param index which holster is currently being explored * @param index which holster is currently being explored
* @return the `List` of inventory data created from the holsters * @return the `List` of inventory data created from the holsters
*/ */
@tailrec private def recursiveMakeHolsters(iter : Iterator[EquipmentSlot], list : List[InternalSlot] = Nil, index : Int = 0) : List[InternalSlot] = { @tailrec private def recursiveMakeHolsters(
iter: Iterator[EquipmentSlot],
list: List[InternalSlot] = Nil,
index: Int = 0
): List[InternalSlot] = {
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
@ -152,8 +169,7 @@ class CorpseConverter extends AvatarConverter {
list :+ BuildEquipment(index, equip), list :+ BuildEquipment(index, equip),
index + 1 index + 1
) )
} } else {
else {
recursiveMakeHolsters(iter, list, index + 1) recursiveMakeHolsters(iter, list, index + 1)
} }
} }
@ -166,7 +182,12 @@ class CorpseConverter extends AvatarConverter {
* @return the game object in decoded packet form * @return the game object in decoded packet form
*/ */
private def BuildEquipment(index: Int, equip: Equipment): InternalSlot = { private def BuildEquipment(index: Int, equip: Equipment): InternalSlot = {
InternalSlot(equip.Definition.ObjectId, equip.GUID, index, equip.Definition.Packet.DetailedConstructorData(equip).get) InternalSlot(
equip.Definition.ObjectId,
equip.GUID,
index,
equip.Definition.Packet.DetailedConstructorData(equip).get
)
} }
} }

View file

@ -8,13 +8,16 @@ import scala.util.{Failure, Success, Try}
class DestroyedVehicleConverter extends ObjectCreateConverter[Vehicle]() { class DestroyedVehicleConverter extends ObjectCreateConverter[Vehicle]() {
override def DetailedConstructorData(obj: Vehicle): Try[DestroyedVehicleData] = override def DetailedConstructorData(obj: Vehicle): Try[DestroyedVehicleData] =
Failure(new Exception("DestroyedVehicleConverter should not be used to generate detailed DestroyedVehicleData (nothing should)")) Failure(
new Exception(
"DestroyedVehicleConverter should not be used to generate detailed DestroyedVehicleData (nothing should)"
)
)
override def ConstructorData(obj: Vehicle): Try[DestroyedVehicleData] = { override def ConstructorData(obj: Vehicle): Try[DestroyedVehicleData] = {
if (obj.Health > 0) { if (obj.Health > 0) {
Failure(new Exception("Vehicle used on DestroyedVehicleConverter has not yet been destroyed (Health == 0)")) Failure(new Exception("Vehicle used on DestroyedVehicleConverter has not yet been destroyed (Health == 0)"))
} } else {
else {
Success(DestroyedVehicleData(PlacementData(obj.Position, obj.Orientation))) Success(DestroyedVehicleData(PlacementData(obj.Position, obj.Orientation)))
} }
} }

View file

@ -38,8 +38,7 @@ class DroppodConverter extends ObjectCreateConverter[Vehicle]() {
unk = false unk = false
) )
) )
} } else { //destroyed
else { //destroyed
Success( Success(
DroppodData( DroppodData(
CommonFieldDataWithPlacement( CommonFieldDataWithPlacement(

View file

@ -36,8 +36,7 @@ class FieldTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
Some(InventoryData(FieldTurretConverter.MakeMountings(obj))) Some(InventoryData(FieldTurretConverter.MakeMountings(obj)))
) )
) )
} } else {
else {
Success( Success(
OneMannedFieldTurretData( OneMannedFieldTurretData(
CommonFieldDataWithPlacement( CommonFieldDataWithPlacement(
@ -66,11 +65,13 @@ class FieldTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
object FieldTurretConverter { object FieldTurretConverter {
private def MakeMountings(obj: WeaponTurret): List[InventoryItemData.InventoryItem] = { private def MakeMountings(obj: WeaponTurret): List[InventoryItemData.InventoryItem] = {
obj.Weapons.map({ obj.Weapons
.map({
case (index, slot) => case (index, slot) =>
val equip: Equipment = slot.Equipment.get val equip: Equipment = slot.Equipment.get
val equipDef = equip.Definition val equipDef = equip.Definition
InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get) InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get)
}).toList })
.toList
} }
} }

View file

@ -34,7 +34,9 @@ class InternalTelepadDeployableConverter extends ObjectCreateConverter[PlanetSid
) )
case None => case None =>
Failure(new IllegalStateException("InternalTelepadDeployableConverter: telepad needs to know id of its parent Router")) Failure(
new IllegalStateException("InternalTelepadDeployableConverter: telepad needs to know id of its parent Router")
)
} }
} }
} }

View file

@ -21,16 +21,19 @@ class LockerContainerConverter extends ObjectCreateConverter[LockerEquipment]()
override def DetailedConstructorData(obj: LockerEquipment): Try[DetailedLockerContainerData] = { override def DetailedConstructorData(obj: LockerEquipment): Try[DetailedLockerContainerData] = {
if (obj.Inventory.Size > 0) { if (obj.Inventory.Size > 0) {
Success(DetailedLockerContainerData( Success(
DetailedLockerContainerData(
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)), CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
Some(InventoryData(MakeDetailedInventory(obj.Inventory))) Some(InventoryData(MakeDetailedInventory(obj.Inventory)))
)) )
} )
else { } else {
Success(DetailedLockerContainerData( Success(
DetailedLockerContainerData(
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)), CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
None None
)) )
)
} }
} }
@ -44,7 +47,12 @@ class LockerContainerConverter extends ObjectCreateConverter[LockerEquipment]()
inv.Items inv.Items
.map(item => { .map(item => {
val equip: Equipment = item.obj val equip: Equipment = item.obj
InternalSlot(equip.Definition.ObjectId, equip.GUID, item.start, equip.Definition.Packet.ConstructorData(equip).get) InternalSlot(
equip.Definition.ObjectId,
equip.GUID,
item.start,
equip.Definition.Packet.ConstructorData(equip).get
)
}) })
} }
@ -58,7 +66,12 @@ class LockerContainerConverter extends ObjectCreateConverter[LockerEquipment]()
inv.Items inv.Items
.map(item => { .map(item => {
val equip: Equipment = item.obj val equip: Equipment = item.obj
InternalSlot(equip.Definition.ObjectId, equip.GUID, item.start, equip.Definition.Packet.DetailedConstructorData(equip).get) InternalSlot(
equip.Definition.ObjectId,
equip.GUID,
item.start,
equip.Definition.Packet.DetailedConstructorData(equip).get
)
}) })
} }
} }

View file

@ -17,17 +17,22 @@ sealed trait PacketConverter
* @tparam A the type of game object * @tparam A the type of game object
*/ */
abstract class ObjectCreateConverter[A <: PlanetSideGameObject] extends PacketConverter { abstract class ObjectCreateConverter[A <: PlanetSideGameObject] extends PacketConverter {
/** /**
* Take a game object and transform it into its equivalent data for an `0x17` packet. * Take a game object and transform it into its equivalent data for an `0x17` packet.
* @param obj the game object * @param obj the game object
* @return the specific `ConstructorData` that is equivalent to this object * @return the specific `ConstructorData` that is equivalent to this object
*/ */
def ConstructorData(obj : A) : Try[ConstructorData] = { Failure(new NoSuchMethodException(s"method not defined for object $obj")) } def ConstructorData(obj: A): Try[ConstructorData] = {
Failure(new NoSuchMethodException(s"method not defined for object $obj"))
}
/** /**
* Take a game object and transform it into its equivalent data for an `0x18` packet. * Take a game object and transform it into its equivalent data for an `0x18` packet.
* @param obj the game object * @param obj the game object
* @return the specific `ConstructorData` that is equivalent to this object * @return the specific `ConstructorData` that is equivalent to this object
*/ */
def DetailedConstructorData(obj : A) : Try[ConstructorData] = { Failure(new NoSuchMethodException(s"method not defined for object $obj")) } def DetailedConstructorData(obj: A): Try[ConstructorData] = {
Failure(new NoSuchMethodException(s"method not defined for object $obj"))
}
} }

View file

@ -2,7 +2,13 @@
package net.psforever.objects.definition.converter package net.psforever.objects.definition.converter
import net.psforever.objects.ballistics.Projectile import net.psforever.objects.ballistics.Projectile
import net.psforever.packet.game.objectcreate.{CommonFieldData, CommonFieldDataWithPlacement, FlightPhysics, PlacementData, RemoteProjectileData} import net.psforever.packet.game.objectcreate.{
CommonFieldData,
CommonFieldDataWithPlacement,
FlightPhysics,
PlacementData,
RemoteProjectileData
}
import net.psforever.types.PlanetSideGUID import net.psforever.types.PlanetSideGUID
import scala.util.{Failure, Success, Try} import scala.util.{Failure, Success, Try}
@ -39,5 +45,7 @@ class ProjectileConverter extends ObjectCreateConverter[Projectile]() {
} }
override def DetailedConstructorData(obj: Projectile): Try[RemoteProjectileData] = override def DetailedConstructorData(obj: Projectile): Try[RemoteProjectileData] =
Failure(new Exception("ProjectileConverter should not be used to generate detailed projectile data (nothing should)")) Failure(
new Exception("ProjectileConverter should not be used to generate detailed projectile data (nothing should)")
)
} }

View file

@ -20,7 +20,8 @@ class REKConverter extends ObjectCreateConverter[SimpleItem]() {
false, false,
Some(false), Some(false),
None, None,
PlanetSideGUID(0)) PlanetSideGUID(0)
)
) )
) )
} }

View file

@ -21,11 +21,13 @@ object SeatConverter {
var offset = initialOffset var offset = initialOffset
seats seats
.filter({ case (_, seat) => seat.isOccupied }) .filter({ case (_, seat) => seat.isOccupied })
.map({case (index, seat) => .map({
case (index, seat) =>
val player = seat.Occupant.get val player = seat.Occupant.get
val entry = InventoryItemData(ObjectClass.avatar, player.GUID, index, SeatConverter.MakeSeat(player, offset)) val entry = InventoryItemData(ObjectClass.avatar, player.GUID, index, SeatConverter.MakeSeat(player, offset))
offset += entry.bitsize offset += entry.bitsize
entry entry
}).toList })
.toList
} }
} }

View file

@ -33,8 +33,7 @@ class ShieldGeneratorConverter extends ObjectCreateConverter[ShieldGeneratorDepl
health health
) )
) )
} } else {
else {
Success( Success(
AegisShieldGeneratorData( AegisShieldGeneratorData(
CommonFieldDataWithPlacement( CommonFieldDataWithPlacement(

View file

@ -36,8 +36,7 @@ class SmallTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
Some(InventoryData(SmallTurretConverter.MakeMountings(obj))) Some(InventoryData(SmallTurretConverter.MakeMountings(obj)))
) )
) )
} } else {
else {
Success( Success(
SmallTurretData( SmallTurretData(
CommonFieldDataWithPlacement( CommonFieldDataWithPlacement(
@ -66,11 +65,13 @@ class SmallTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
object SmallTurretConverter { object SmallTurretConverter {
private def MakeMountings(obj: WeaponTurret): List[InventoryItemData.InventoryItem] = { private def MakeMountings(obj: WeaponTurret): List[InventoryItemData.InventoryItem] = {
obj.Weapons.map({ obj.Weapons
.map({
case ((index, slot)) => case ((index, slot)) =>
val equip: Equipment = slot.Equipment.get val equip: Equipment = slot.Equipment.get
val equipDef = equip.Definition val equipDef = equip.Definition
InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get) InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get)
}).toList })
.toList
} }
} }

View file

@ -1,6 +1,7 @@
package net.psforever.objects.definition.converter package net.psforever.objects.definition.converter
object StatConverter { object StatConverter {
/** /**
* Takes a measure of a value against the maximum possible value and * Takes a measure of a value against the maximum possible value and
* transforms it to a scaled number that can be written within a specific domain.<br> * transforms it to a scaled number that can be written within a specific domain.<br>

View file

@ -33,8 +33,7 @@ class TRAPConverter extends ObjectCreateConverter[TrapDeployable]() {
health health
) )
) )
} } else {
else {
Success( Success(
TRAPData( TRAPData(
CommonFieldDataWithPlacement( CommonFieldDataWithPlacement(

View file

@ -35,8 +35,7 @@ class TelepadDeployableConverter extends ObjectCreateConverter[TelepadDeployable
) )
) )
) )
} } else {
else {
Success( Success(
DroppedItemData( DroppedItemData(
PlacementData(obj.Position, obj.Orientation), PlacementData(obj.Position, obj.Orientation),
@ -60,7 +59,9 @@ class TelepadDeployableConverter extends ObjectCreateConverter[TelepadDeployable
} }
case None => case None =>
Failure(new IllegalStateException("TelepadDeployableConverter: telepad needs to know id of its associated Router")) Failure(
new IllegalStateException("TelepadDeployableConverter: telepad needs to know id of its associated Router")
)
} }
} }
} }

View file

@ -9,10 +9,12 @@ import scala.util.{Success, Try}
class ToolConverter extends ObjectCreateConverter[Tool]() { class ToolConverter extends ObjectCreateConverter[Tool]() {
override def ConstructorData(obj: Tool): Try[WeaponData] = { override def ConstructorData(obj: Tool): Try[WeaponData] = {
val slots : List[InternalSlot] = (0 until obj.MaxAmmoSlot).map(index => { val slots: List[InternalSlot] = (0 until obj.MaxAmmoSlot)
.map(index => {
val box = obj.AmmoSlots(index).Box val box = obj.AmmoSlots(index).Box
InternalSlot(box.Definition.ObjectId, box.GUID, index, box.Definition.Packet.ConstructorData(box).get) InternalSlot(box.Definition.ObjectId, box.GUID, index, box.Definition.Packet.ConstructorData(box).get)
}).toList })
.toList
Success( Success(
WeaponData( WeaponData(
CommonFieldData( CommonFieldData(
@ -33,10 +35,12 @@ class ToolConverter extends ObjectCreateConverter[Tool]() {
} }
override def DetailedConstructorData(obj: Tool): Try[DetailedWeaponData] = { override def DetailedConstructorData(obj: Tool): Try[DetailedWeaponData] = {
val slots : List[InternalSlot] = (0 until obj.MaxAmmoSlot).map(index => { val slots: List[InternalSlot] = (0 until obj.MaxAmmoSlot)
.map(index => {
val box = obj.AmmoSlots(index).Box val box = obj.AmmoSlots(index).Box
InternalSlot(box.Definition.ObjectId, box.GUID, index, box.Definition.Packet.DetailedConstructorData(box).get) InternalSlot(box.Definition.ObjectId, box.GUID, index, box.Definition.Packet.DetailedConstructorData(box).get)
}).toList })
.toList
Success( Success(
DetailedWeaponData( DetailedWeaponData(
CommonFieldData( CommonFieldData(

View file

@ -12,8 +12,10 @@ class VariantVehicleConverter extends VehicleConverter {
landed is 0 landed is 0
flying is 7 flying is 7
*/ */
Some(VariantVehicleData( Some(
VariantVehicleData(
if (obj.Definition.CanFly && obj.Flying) 7 else 0 if (obj.Definition.CanFly && obj.Flying) 7 else 0
)) )
)
} }
} }

View file

@ -44,8 +44,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
Some(InventoryData(MakeDriverSeat(obj) ++ MakeUtilities(obj) ++ MakeMountings(obj))) Some(InventoryData(MakeDriverSeat(obj) ++ MakeUtilities(obj) ++ MakeMountings(obj)))
)(SpecificFormatModifier) )(SpecificFormatModifier)
) )
} } else { //destroyed
else { //destroyed
Success( Success(
VehicleData( VehicleData(
PlacementData(obj.Position, obj.Orientation), PlacementData(obj.Position, obj.Orientation),
@ -86,7 +85,8 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
} }
private def MakeMountings(obj: Vehicle): List[InventoryItemData.InventoryItem] = { private def MakeMountings(obj: Vehicle): List[InventoryItemData.InventoryItem] = {
obj.Weapons.collect { case (index, slot) if slot.Equipment.nonEmpty => obj.Weapons.collect {
case (index, slot) if slot.Equipment.nonEmpty =>
val equip: Equipment = slot.Equipment.get val equip: Equipment = slot.Equipment.get
val equipDef = equip.Definition val equipDef = equip.Definition
InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get) InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get)
@ -94,12 +94,15 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
} }
protected def MakeUtilities(obj: Vehicle): List[InventoryItemData.InventoryItem] = { protected def MakeUtilities(obj: Vehicle): List[InventoryItemData.InventoryItem] = {
Vehicle.EquipmentUtilities(obj.Utilities).map({ Vehicle
.EquipmentUtilities(obj.Utilities)
.map({
case (index, utilContainer) => case (index, utilContainer) =>
val util = utilContainer() val util = utilContainer()
val utilDef = util.Definition val utilDef = util.Definition
InventoryItemData(utilDef.ObjectId, util.GUID, index, utilDef.Packet.ConstructorData(util).get) InventoryItemData(utilDef.ObjectId, util.GUID, index, utilDef.Packet.ConstructorData(util).get)
}).toList })
.toList
} }
protected def SpecificFormatModifier: VehicleFormat.Value = VehicleFormat.Normal protected def SpecificFormatModifier: VehicleFormat.Value = VehicleFormat.Normal

View file

@ -28,13 +28,12 @@ abstract class GUIDException(message : String, cause : Throwable, obj : Identifi
* @param obj the entity being manipulated when the complaint arose * @param obj the entity being manipulated when the complaint arose
* @param cause the cause of this error * @param cause the cause of this error
*/ */
class NoGUIDException(message : String, class NoGUIDException(message: String, obj: IdentifiableEntity = None.orNull, cause: Throwable = None.orNull)
obj : IdentifiableEntity = None.orNull, extends GUIDException(message, cause, obj, null)
cause : Throwable = None.orNull
) extends GUIDException(message, cause, obj, null)
object NoGUIDException { object NoGUIDException {
def unapply(e : NoGUIDException): Option[(String, IdentifiableEntity, Throwable)] = Some((e.getMessage, e.getEntity, e.getCause)) def unapply(e: NoGUIDException): Option[(String, IdentifiableEntity, Throwable)] =
Some((e.getMessage, e.getEntity, e.getCause))
} }
/** /**
@ -44,12 +43,14 @@ object NoGUIDException {
* @param guid the identifier number being manipulated when the ciomplaint arose * @param guid the identifier number being manipulated when the ciomplaint arose
* @param cause the cause of this error * @param cause the cause of this error
*/ */
class AssigningGUIDException(message : String, class AssigningGUIDException(
message: String,
obj: IdentifiableEntity, obj: IdentifiableEntity,
guid: PlanetSideGUID, guid: PlanetSideGUID,
cause: Throwable = None.orNull cause: Throwable = None.orNull
) extends GUIDException(message, cause, obj, guid) ) extends GUIDException(message, cause, obj, guid)
object AssigningGUIDException { object AssigningGUIDException {
def unapply(e : AssigningGUIDException): Option[(String, Throwable, IdentifiableEntity, PlanetSideGUID)] = Some((e.getMessage, e.getCause, e.getEntity, e.getGUID)) def unapply(e: AssigningGUIDException): Option[(String, Throwable, IdentifiableEntity, PlanetSideGUID)] =
Some((e.getMessage, e.getCause, e.getEntity, e.getGUID))
} }

View file

@ -28,12 +28,16 @@ import net.psforever.types.{PlanetSideGUID, StalePlanetSideGUID}
* @throws `NoGUIDException` if a GUID has not yet been assigned * @throws `NoGUIDException` if a GUID has not yet been assigned
*/ */
abstract class IdentifiableEntity extends Identifiable { abstract class IdentifiableEntity extends Identifiable {
/** storage for the GUID information; starts as a `StalePlanetSideGUID` */ /** storage for the GUID information; starts as a `StalePlanetSideGUID` */
private var current: PlanetSideGUID = StalePlanetSideGUID(0) private var current: PlanetSideGUID = StalePlanetSideGUID(0)
/** indicate the validity of the current GUID */ /** indicate the validity of the current GUID */
private var guidValid: Boolean = false private var guidValid: Boolean = false
/** the current accessor; can be re-assigned */ /** the current accessor; can be re-assigned */
private var guidAccessor: IdentifiableEntity => PlanetSideGUID = IdentifiableEntity.getWhenNoGUID private var guidAccessor: IdentifiableEntity => PlanetSideGUID = IdentifiableEntity.getWhenNoGUID
/** the current mutator; can be re-assigned */ /** the current mutator; can be re-assigned */
private var guidMutator: (IdentifiableEntity, PlanetSideGUID) => PlanetSideGUID = IdentifiableEntity.setWhenNoGUID private var guidMutator: (IdentifiableEntity, PlanetSideGUID) => PlanetSideGUID = IdentifiableEntity.setWhenNoGUID
@ -71,6 +75,7 @@ abstract class IdentifiableEntity extends Identifiable {
} }
object IdentifiableEntity { object IdentifiableEntity {
/** /**
* Raise an `Exception` because the entity is never considered having a GUID to give. * Raise an `Exception` because the entity is never considered having a GUID to give.
* @param o the any entity with a GUID * @param o the any entity with a GUID

View file

@ -40,6 +40,7 @@ trait WorldEntity {
} }
object WorldEntity { object WorldEntity {
/** /**
* A velocity of non-zero is the same as moving. * A velocity of non-zero is the same as moving.
* @return `true`, if we are moving; `false`, otherwise * @return `true`, if we are moving; `false`, otherwise
@ -58,7 +59,8 @@ object WorldEntity {
* @param test the (squared) velocity to test against * @param test the (squared) velocity to test against
* @return `true`, if we are moving; `false`, otherwise * @return `true`, if we are moving; `false`, otherwise
*/ */
def isMoving(velocity : Option[Vector3], test : Vector3) : Boolean = WorldEntity.isMoving(velocity, Vector3.MagnitudeSquared(test)) def isMoving(velocity: Option[Vector3], test: Vector3): Boolean =
WorldEntity.isMoving(velocity, Vector3.MagnitudeSquared(test))
/** /**
* This object is not considered moving unless it is moving at least as fast as a certain velocity. * This object is not considered moving unless it is moving at least as fast as a certain velocity.

View file

@ -8,19 +8,13 @@ import net.psforever.objects.serverobject.turret.FacilityTurret
final case class TargetValidation(category: EffectTarget.Category.Value, test: EffectTarget.Validation.Value) final case class TargetValidation(category: EffectTarget.Category.Value, test: EffectTarget.Validation.Value)
object EffectTarget { object EffectTarget {
/** /**
* A classification of the target of interactions. * A classification of the target of interactions.
* Arbitrary, but useful. * Arbitrary, but useful.
*/ */
object Category extends Enumeration { object Category extends Enumeration {
val val Aircraft, Deployable, Equipment, Player, Turret, Vehicle = Value
Aircraft,
Deployable,
Equipment,
Player,
Turret,
Vehicle
= Value
} }
object Validation { object Validation {
@ -28,56 +22,64 @@ object EffectTarget {
def Invalid(target: PlanetSideGameObject): Boolean = false def Invalid(target: PlanetSideGameObject): Boolean = false
def Medical(target : PlanetSideGameObject) : Boolean = target match { def Medical(target: PlanetSideGameObject): Boolean =
target match {
case p: Player => case p: Player =>
p.Health > 0 && (p.Health < p.MaxHealth || p.Armor < p.MaxArmor) p.Health > 0 && (p.Health < p.MaxHealth || p.Armor < p.MaxArmor)
case _ => case _ =>
false false
} }
def HealthCrystal(target : PlanetSideGameObject) : Boolean = target match { def HealthCrystal(target: PlanetSideGameObject): Boolean =
target match {
case p: Player => case p: Player =>
p.Health > 0 && p.Health < p.MaxHealth p.Health > 0 && p.Health < p.MaxHealth
case _ => case _ =>
false false
} }
def RepairSilo(target : PlanetSideGameObject) : Boolean = target match { def RepairSilo(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle => case v: Vehicle =>
!GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && v.Health < v.MaxHealth !GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && v.Health < v.MaxHealth
case _ => case _ =>
false false
} }
def PadLanding(target : PlanetSideGameObject) : Boolean = target match { def PadLanding(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle => case v: Vehicle =>
GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && v.Health < v.MaxHealth GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && v.Health < v.MaxHealth
case _ => case _ =>
false false
} }
def Player(target : PlanetSideGameObject) : Boolean = target match { def Player(target: PlanetSideGameObject): Boolean =
target match {
case p: Player => case p: Player =>
p.isAlive p.isAlive
case _ => case _ =>
false false
} }
def MotionSensor(target : PlanetSideGameObject) : Boolean = target match { def MotionSensor(target: PlanetSideGameObject): Boolean =
target match {
case s: SensorDeployable => case s: SensorDeployable =>
s.Health > 0 s.Health > 0
case _ => case _ =>
false false
} }
def Spitfire(target : PlanetSideGameObject) : Boolean = target match { def Spitfire(target: PlanetSideGameObject): Boolean =
target match {
case t: TurretDeployable => case t: TurretDeployable =>
t.Definition.DeployCategory == DeployableCategory.SmallTurrets && t.Health > 0 t.Definition.DeployCategory == DeployableCategory.SmallTurrets && t.Health > 0
case _ => case _ =>
false false
} }
def Turret(target : PlanetSideGameObject) : Boolean = target match { def Turret(target: PlanetSideGameObject): Boolean =
target match {
case t: TurretDeployable => case t: TurretDeployable =>
t.Definition.DeployCategory == DeployableCategory.FieldTurrets && t.Health > 0 t.Definition.DeployCategory == DeployableCategory.FieldTurrets && t.Health > 0
case t: FacilityTurret => case t: FacilityTurret =>
@ -86,14 +88,16 @@ object EffectTarget {
false false
} }
def AMS(target : PlanetSideGameObject) : Boolean = target match { def AMS(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle => case v: Vehicle =>
v.Health > 0 && v.Definition == GlobalDefinitions.ams v.Health > 0 && v.Definition == GlobalDefinitions.ams
case _ => case _ =>
false false
} }
def VehicleNotAMS(target : PlanetSideGameObject) : Boolean = target match { def VehicleNotAMS(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle => case v: Vehicle =>
v.Health > 0 && v.Definition != GlobalDefinitions.ams v.Health > 0 && v.Definition != GlobalDefinitions.ams
case _ => case _ =>

View file

@ -14,8 +14,7 @@ import net.psforever.types.PlanetSideEmpire
* and, special carried (like a lattice logic unit); * and, special carried (like a lattice logic unit);
* and, dropped on the ground in the game world and render where it was deposited. * and, dropped on the ground in the game world and render where it was deposited.
*/ */
abstract class Equipment extends PlanetSideGameObject abstract class Equipment extends PlanetSideGameObject with FactionAffinity {
with FactionAffinity {
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
def Faction: PlanetSideEmpire.Value = faction def Faction: PlanetSideEmpire.Value = faction

View file

@ -6,9 +6,7 @@ package net.psforever.objects.equipment
* Check the comments for originating use. * Check the comments for originating use.
*/ */
object EquipmentSize extends Enumeration { object EquipmentSize extends Enumeration {
val val Blocked, Melee, //special
Blocked,
Melee, //special
Pistol, //2x2 and 3x3 Pistol, //2x2 and 3x3
Rifle, //6x3 and 9x3 Rifle, //6x3 and 9x3
Max, //max weapon only Max, //max weapon only
@ -33,11 +31,9 @@ object EquipmentSize extends Enumeration {
def isEqual(type1: EquipmentSize.Value, type2: EquipmentSize.Value): Boolean = { def isEqual(type1: EquipmentSize.Value, type2: EquipmentSize.Value): Boolean = {
if (type1 == Blocked || type2 == Blocked) { if (type1 == Blocked || type2 == Blocked) {
false false
} } else if (type1 == Inventory || type2 == Inventory) {
else if(type1 == Inventory || type2 == Inventory) {
true true
} } else {
else {
type1 == type2 type1 == type2
} }
} }

View file

@ -43,8 +43,7 @@ class EquipmentSlot {
if (tool.isEmpty && EquipmentSize.isEqual(size, assignEquipment.get.Size)) { if (tool.isEmpty && EquipmentSize.isEqual(size, assignEquipment.get.Size)) {
tool = assignEquipment tool = assignEquipment
} }
} } else {
else {
tool = None tool = None
} }
Equipment Equipment

View file

@ -7,26 +7,39 @@ import net.psforever.objects.vital.damage.DamageProfile
import scala.collection.mutable import scala.collection.mutable
class FireModeDefinition { class FireModeDefinition {
/** indices pointing to all ammo types used, in (an) order /** indices pointing to all ammo types used, in (an) order
* the ammo types list will be available from the `ToolDefinition` */ * the ammo types list will be available from the `ToolDefinition`
*/
private val ammoTypeIndices: mutable.ListBuffer[Int] = mutable.ListBuffer[Int]() private val ammoTypeIndices: mutable.ListBuffer[Int] = mutable.ListBuffer[Int]()
/** custom indices pointing to the projectile type used for this mode's ammo types /** custom indices pointing to the projectile type used for this mode's ammo types
* for most weapon fire modes, this list will be empty and the projectile will align with the `ammoTypeIndex` * for most weapon fire modes, this list will be empty and the projectile will align with the `ammoTypeIndex`
* if at least one ammo type has a redirected projectile type, all projectiles must be defined for this mode */ * if at least one ammo type has a redirected projectile type, all projectiles must be defined for this mode
*/
private val projectileTypeIndices: mutable.ListBuffer[Int] = mutable.ListBuffer[Int]() private val projectileTypeIndices: mutable.ListBuffer[Int] = mutable.ListBuffer[Int]()
/** ammunition slot number this fire mode utilizes */ /** ammunition slot number this fire mode utilizes */
private var ammoSlotIndex: Int = 0 private var ammoSlotIndex: Int = 0
/** how many rounds are replenished each reload cycle */ /** how many rounds are replenished each reload cycle */
private var magazine: Int = 1 private var magazine: Int = 1
/** how many rounds are replenished each reload cycle, per type of ammunition loaded /** how many rounds are replenished each reload cycle, per type of ammunition loaded
* key - ammo type index, value - magazine capacity*/ * key - ammo type index, value - magazine capacity
*/
private val customAmmoMagazine: mutable.HashMap[Ammo.Value, Int] = mutable.HashMap[Ammo.Value, Int]() private val customAmmoMagazine: mutable.HashMap[Ammo.Value, Int] = mutable.HashMap[Ammo.Value, Int]()
/** how much is subtracted from the magazine each fire cycle; /** how much is subtracted from the magazine each fire cycle;
* most weapons will only fire 1 round per fire cycle; the flamethrower in fire mode 1 fires 50 */ * most weapons will only fire 1 round per fire cycle; the flamethrower in fire mode 1 fires 50
*/
private var rounds: Int = 1 private var rounds: Int = 1
/** how many sub-rounds are queued per round fired; /** how many sub-rounds are queued per round fired;
* the flechette fires 8 pellets per shell and generates 8 fire reports before the ammo count goes down */ * the flechette fires 8 pellets per shell and generates 8 fire reports before the ammo count goes down
*/
private var chamber: Int = 1 private var chamber: Int = 1
/** modifiers for each damage type */ /** modifiers for each damage type */
private val modifiers: DamageModifiers = new DamageModifiers private val modifiers: DamageModifiers = new DamageModifiers
@ -96,6 +109,7 @@ class FireModeDefinition {
} }
class PelletFireModeDefinition extends FireModeDefinition { class PelletFireModeDefinition extends FireModeDefinition {
/** /**
* Shoot a weapon, remove an anticipated amount of ammunition.<br> * Shoot a weapon, remove an anticipated amount of ammunition.<br>
* <br> * <br>
@ -112,14 +126,14 @@ class PelletFireModeDefinition extends FireModeDefinition {
if (chamber <= 0) { if (chamber <= 0) {
ammoSlot.Chamber = Chamber ammoSlot.Chamber = Chamber
magazine - Rounds magazine - Rounds
} } else {
else {
magazine magazine
} }
} }
} }
class InfiniteFireModeDefinition extends FireModeDefinition { class InfiniteFireModeDefinition extends FireModeDefinition {
/** /**
* Shoot a weapon, remove an anticipated amount of ammunition.<br> * Shoot a weapon, remove an anticipated amount of ammunition.<br>
* <br> * <br>

Some files were not shown because too many files have changed in this diff Show more