mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
QoL changes; event chat messages for exp when in debt; different calculations for sep; timestamps for progress system start and clear; hopefully proper cleanup for progress system
This commit is contained in:
parent
e9dbd5f259
commit
d3392ecab2
|
|
@ -19,4 +19,77 @@ BEGIN
|
||||||
END;
|
END;
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
/* New */
|
||||||
|
ALTER TABLE "progressiondebt"
|
||||||
|
ADD COLUMN IF NOT EXISTS "max_experience" INT NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN IF NOT EXISTS "enroll_time" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ADD COLUMN IF NOT EXISTS "clear_time" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Upon indoctrinating a player into the progression system,
|
||||||
|
update the peak experience for the battle rank for future reference
|
||||||
|
and record when the player asked for this enhanced rank promotion.
|
||||||
|
*/
|
||||||
|
CREATE OR REPLACE FUNCTION fn_progressiondebt_updateEnrollment()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
AS
|
||||||
|
$$
|
||||||
|
DECLARE avatarId Int;
|
||||||
|
DECLARE oldExp Int;
|
||||||
|
DECLARE newExp Int;
|
||||||
|
BEGIN
|
||||||
|
avatarId := NEW.avatar_id;
|
||||||
|
newExp := NEW.experience;
|
||||||
|
oldExp := OLD.experience;
|
||||||
|
BEGIN
|
||||||
|
IF (oldExp = 0 AND newExp > 0) THEN
|
||||||
|
UPDATE progressiondebt
|
||||||
|
SET experience = newExp, max_experience = newExp, enroll_time = CURRENT_TIMESTAMP
|
||||||
|
WHERE avatar_id = avatarId;
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER psf_progressiondebt_updateEnrollment
|
||||||
|
BEFORE UPDATE
|
||||||
|
ON progressiondebt
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE fn_progressiondebt_updateEnrollment();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Upon unlisting a player from the progression system,
|
||||||
|
update the time when the player has completed his tensure.
|
||||||
|
*/
|
||||||
|
CREATE OR REPLACE FUNCTION fn_progressiondebt_updateClearTime()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
AS
|
||||||
|
$$
|
||||||
|
DECLARE avatarId Int;
|
||||||
|
DECLARE oldExp Int;
|
||||||
|
DECLARE newExp Int;
|
||||||
|
DECLARE newMaxExp Int;
|
||||||
|
BEGIN
|
||||||
|
avatarId := NEW.avatar_id;
|
||||||
|
newExp := NEW.experience;
|
||||||
|
oldExp := OLD.experience;
|
||||||
|
newMaxExp := NEW.max_experience;
|
||||||
|
BEGIN
|
||||||
|
IF (oldExp > newExp AND newExp = 0) THEN
|
||||||
|
UPDATE progressiondebt
|
||||||
|
SET experience = 0, max_experience = newMaxExp, clear_time = CURRENT_TIMESTAMP
|
||||||
|
WHERE avatar_id = avatarId;
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER psf_progressiondebt_updateClearTime
|
||||||
|
BEFORE UPDATE
|
||||||
|
ON progressiondebt
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE fn_progressiondebt_updateClearTime();
|
||||||
|
|
|
||||||
|
|
@ -273,16 +273,14 @@ game {
|
||||||
#
|
#
|
||||||
# name - label by which this event is organized
|
# name - label by which this event is organized
|
||||||
# base - whole number value
|
# base - whole number value
|
||||||
# shots-multiplier - whether use count matters for this event
|
# shots-min - lower limit of use count
|
||||||
# - when set to 0.0 (default), it does not
|
# - minimum amount of shots required before applying multiplier
|
||||||
# shots-limit - upper limit of use count
|
# shots-max - upper limit of use count
|
||||||
# - cap the count here, if higher
|
# - cap the count here, if higher
|
||||||
# shots-cutoff - if the use count exceeds this number, the event no longer applies
|
# shots-cutoff - if the use count exceeds this number, the event no longer applies
|
||||||
# - a hard limit that should zero the contribution reward
|
# - a hard limit that should zero the contribution reward
|
||||||
# - the *-cutoff should probably apply before *-limit, maybe
|
# shots-multiplier - whether use count matters for this event
|
||||||
# shots-nat-log - when set, may the use count to a natural logarithmic curve
|
# - when set to 0.0 (default), it does not
|
||||||
# - actually the exponent on the use count before the logarithm
|
|
||||||
# - similar to shots-limit, but the curve plateaus quickly
|
|
||||||
# amount-multiplier - whether active amount matters for this event
|
# amount-multiplier - whether active amount matters for this event
|
||||||
# - when set to 0.0 (default), it does not
|
# - when set to 0.0 (default), it does not
|
||||||
events = [
|
events = [
|
||||||
|
|
@ -290,26 +288,26 @@ game {
|
||||||
name = "support-heal"
|
name = "support-heal"
|
||||||
base = 10
|
base = 10
|
||||||
shots-multiplier = 5.0
|
shots-multiplier = 5.0
|
||||||
shots-limit = 100
|
shots-max = 100
|
||||||
amount-multiplier = 2.0
|
amount-multiplier = 2.0
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "support-repair"
|
name = "support-repair"
|
||||||
base = 10
|
base = 10
|
||||||
shots-multiplier = 5.0
|
shots-multiplier = 5.0
|
||||||
shots-limit = 100
|
shots-max = 100
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "support-repair-terminal"
|
name = "support-repair-terminal"
|
||||||
base = 10
|
base = 10
|
||||||
shots-multiplier = 5.0
|
shots-multiplier = 5.0
|
||||||
shots-limit = 100
|
shots-max = 100
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "support-repair-turret"
|
name = "support-repair-turret"
|
||||||
base = 10
|
base = 10
|
||||||
shots-multiplier = 5.0
|
shots-multiplier = 5.0
|
||||||
shots-limit = 100
|
shots-max = 100
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "mounted-kill"
|
name = "mounted-kill"
|
||||||
|
|
@ -332,27 +330,23 @@ game {
|
||||||
name = "ams-resupply"
|
name = "ams-resupply"
|
||||||
base = 15
|
base = 15
|
||||||
shots-multiplier = 1.0
|
shots-multiplier = 1.0
|
||||||
shots-nat-log = 5.0
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "lodestar-repair"
|
name = "lodestar-repair"
|
||||||
base = 10
|
base = 10
|
||||||
shots-multiplier = 1.0
|
shots-multiplier = 1.0
|
||||||
shots-nat-log = 5.0
|
shots-max = 100
|
||||||
shots-limit = 100
|
|
||||||
amount-multiplier = 1.0
|
amount-multiplier = 1.0
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "lodestar-rearm"
|
name = "lodestar-rearm"
|
||||||
base = 10
|
base = 10
|
||||||
shots-multiplier = 1.0
|
shots-multiplier = 1.0
|
||||||
shots-nat-log = 5.0
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "revival"
|
name = "revival"
|
||||||
base = 0
|
base = 0
|
||||||
shots-multiplier = 15.0
|
shots-multiplier = 15.0
|
||||||
shots-nat-log = 5.0
|
|
||||||
shots-cutoff = 10
|
shots-cutoff = 10
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -376,6 +370,7 @@ game {
|
||||||
# The maximum command experience that can be earned in a facility capture based on squad size
|
# The maximum command experience that can be earned in a facility capture based on squad size
|
||||||
maximum-per-squad-size = [990, 1980, 3466, 4950, 6436, 7920, 9406, 10890, 12376, 13860]
|
maximum-per-squad-size = [990, 1980, 3466, 4950, 6436, 7920, 9406, 10890, 12376, 13860]
|
||||||
# When the cep has to be capped for squad size, add a small value to the capped value
|
# When the cep has to be capped for squad size, add a small value to the capped value
|
||||||
|
# This is that value
|
||||||
# -1 reuses the cep before being capped
|
# -1 reuses the cep before being capped
|
||||||
squad-size-limit-overflow = -1
|
squad-size-limit-overflow = -1
|
||||||
# When the cep has to be capped for squad size, calculate a small amount to add to the capped value
|
# When the cep has to be capped for squad size, calculate a small amount to add to the capped value
|
||||||
|
|
@ -399,6 +394,8 @@ game {
|
||||||
# How much direct combat contributes to paying back promotion debt.
|
# How much direct combat contributes to paying back promotion debt.
|
||||||
# Typically, it does not contribute.
|
# Typically, it does not contribute.
|
||||||
battle-experience-points-modifier = 0f
|
battle-experience-points-modifier = 0f
|
||||||
|
support-experience-points-modifier = 2f
|
||||||
|
capture-experience-points-modifier = 1f
|
||||||
# Don't forget to pay back that debt.
|
# Don't forget to pay back that debt.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,16 @@ import net.psforever.util.Database._
|
||||||
import net.psforever.util.{Config, Database, DefinitionUtil}
|
import net.psforever.util.{Config, Database, DefinitionUtil}
|
||||||
|
|
||||||
object AvatarActor {
|
object AvatarActor {
|
||||||
|
private val basicLoginCertifications: Set[Certification] = Set(
|
||||||
|
Certification.StandardExoSuit,
|
||||||
|
Certification.AgileExoSuit,
|
||||||
|
Certification.ReinforcedExoSuit,
|
||||||
|
Certification.StandardAssault,
|
||||||
|
Certification.MediumAssault,
|
||||||
|
Certification.ATV,
|
||||||
|
Certification.Harasser
|
||||||
|
)
|
||||||
|
|
||||||
def apply(sessionActor: ActorRef[SessionActor.Command]): Behavior[Command] =
|
def apply(sessionActor: ActorRef[SessionActor.Command]): Behavior[Command] =
|
||||||
Behaviors
|
Behaviors
|
||||||
.supervise[Command] {
|
.supervise[Command] {
|
||||||
|
|
@ -336,7 +346,7 @@ object AvatarActor {
|
||||||
case "Kit" =>
|
case "Kit" =>
|
||||||
container.Slot(objectIndex).Equipment =
|
container.Slot(objectIndex).Equipment =
|
||||||
Kit(DefinitionUtil.idToDefinition(objectId).asInstanceOf[KitDefinition])
|
Kit(DefinitionUtil.idToDefinition(objectId).asInstanceOf[KitDefinition])
|
||||||
case "Telepad" | "BoomerTrigger" => ;
|
case "Telepad" | "BoomerTrigger" => ()
|
||||||
//special types of equipment that are not actually loaded
|
//special types of equipment that are not actually loaded
|
||||||
case name =>
|
case name =>
|
||||||
log.error(s"failing to add unknown equipment to a container - $name")
|
log.error(s"failing to add unknown equipment to a container - $name")
|
||||||
|
|
@ -368,10 +378,10 @@ object AvatarActor {
|
||||||
cooldownDurations.get(DefinitionUtil.fromString(name)) match {
|
cooldownDurations.get(DefinitionUtil.fromString(name)) match {
|
||||||
case Some(duration) if now.compareTo(cooldown.plusMillis(duration.toMillis.toInt)) == -1 =>
|
case Some(duration) if now.compareTo(cooldown.plusMillis(duration.toMillis.toInt)) == -1 =>
|
||||||
cooldowns.put(name, cooldown)
|
cooldowns.put(name, cooldown)
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case _: Exception => ;
|
case _: Exception => ()
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
log.warn(s"ignoring invalid cooldown string: '$value'")
|
log.warn(s"ignoring invalid cooldown string: '$value'")
|
||||||
|
|
@ -534,9 +544,9 @@ object AvatarActor {
|
||||||
otherAvatar.headOption match {
|
otherAvatar.headOption match {
|
||||||
case Some(a) =>
|
case Some(a) =>
|
||||||
func(a.id, a.name, a.factionId)
|
func(a.id, a.name, a.factionId)
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None //satisfy the orElse
|
None //satisfy the orElse
|
||||||
|
|
@ -857,23 +867,30 @@ object AvatarActor {
|
||||||
case Success(debt) if debt.nonEmpty =>
|
case Success(debt) if debt.nonEmpty =>
|
||||||
out.completeWith(Future(debt.head.experience))
|
out.completeWith(Future(debt.head.experience))
|
||||||
case _ =>
|
case _ =>
|
||||||
|
ctx.run(
|
||||||
|
query[persistence.Progressiondebt]
|
||||||
|
.filter(_.avatarId == lift(avatarId))
|
||||||
|
.update(_.experience -> lift(0L))
|
||||||
|
)
|
||||||
out.completeWith(Future(0L))
|
out.completeWith(Future(0L))
|
||||||
}
|
}
|
||||||
out.future
|
out.future
|
||||||
}
|
}
|
||||||
|
|
||||||
def saveExperienceDebt(avatarId: Long, exp: Long): Future[Int] = {
|
def saveExperienceDebt(avatarId: Long, exp: Long, max: Long): Future[Int] = {
|
||||||
import ctx._
|
import ctx._
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
val out: Promise[Int] = Promise()
|
val out: Promise[Int] = Promise()
|
||||||
val result = ctx.run(query[persistence.Progressiondebt].filter(_.avatarId == lift(avatarId)))
|
val result = ctx.run(
|
||||||
result.onComplete {
|
query[persistence.Progressiondebt]
|
||||||
case Success(debt) if debt.nonEmpty =>
|
.filter(_.avatarId == lift(avatarId))
|
||||||
ctx.run(
|
.update(
|
||||||
query[persistence.Progressiondebt]
|
_.experience -> lift(exp),
|
||||||
.filter(_.avatarId == lift(avatarId))
|
_.maxExperience -> lift(max)
|
||||||
.update(_.experience -> lift(exp))
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
result.onComplete {
|
||||||
|
case Success(debt) if debt.toInt > 0 =>
|
||||||
out.completeWith(Future(1))
|
out.completeWith(Future(1))
|
||||||
case _ =>
|
case _ =>
|
||||||
out.completeWith(Future(0))
|
out.completeWith(Future(0))
|
||||||
|
|
@ -1127,15 +1144,7 @@ class AvatarActor(
|
||||||
val inits = for {
|
val inits = for {
|
||||||
_ <- ctx.run(
|
_ <- ctx.run(
|
||||||
liftQuery(
|
liftQuery(
|
||||||
List(
|
basicLoginCertifications.map { cert => persistence.Certification(cert.value, avatarId) }.toList
|
||||||
persistence.Certification(Certification.StandardExoSuit.value, avatarId),
|
|
||||||
persistence.Certification(Certification.AgileExoSuit.value, avatarId),
|
|
||||||
persistence.Certification(Certification.ReinforcedExoSuit.value, avatarId),
|
|
||||||
persistence.Certification(Certification.StandardAssault.value, avatarId),
|
|
||||||
persistence.Certification(Certification.MediumAssault.value, avatarId),
|
|
||||||
persistence.Certification(Certification.ATV.value, avatarId),
|
|
||||||
persistence.Certification(Certification.Harasser.value, avatarId)
|
|
||||||
)
|
|
||||||
).foreach(c => query[persistence.Certification].insertValue(c))
|
).foreach(c => query[persistence.Certification].insertValue(c))
|
||||||
)
|
)
|
||||||
_ <- ctx.run(
|
_ <- ctx.run(
|
||||||
|
|
@ -1212,135 +1221,11 @@ class AvatarActor(
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case LearnCertification(terminalGuid, certification) =>
|
case LearnCertification(terminalGuid, certification) =>
|
||||||
import ctx._
|
performCertificationAction(terminalGuid, certification, learnCertificationInTheFuture, TransactionType.Buy)
|
||||||
|
|
||||||
if (avatar.certifications.contains(certification)) {
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
ItemTransactionResultMessage(terminalGuid, TransactionType.Learn, success = false)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
val replace = certification.replaces.intersect(avatar.certifications)
|
|
||||||
Future
|
|
||||||
.sequence(replace.map(cert => {
|
|
||||||
ctx
|
|
||||||
.run(
|
|
||||||
query[persistence.Certification]
|
|
||||||
.filter(_.avatarId == lift(avatar.id))
|
|
||||||
.filter(_.id == lift(cert.value))
|
|
||||||
.delete
|
|
||||||
)
|
|
||||||
.map(_ => cert)
|
|
||||||
}))
|
|
||||||
.onComplete {
|
|
||||||
case Failure(exception) =>
|
|
||||||
log.error(exception)("db failure")
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = false)
|
|
||||||
)
|
|
||||||
case Success(_replace) =>
|
|
||||||
_replace.foreach { cert =>
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
PlanetsideAttributeMessage(session.get.player.GUID, 25, cert.value)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ctx
|
|
||||||
.run(
|
|
||||||
query[persistence.Certification]
|
|
||||||
.insert(_.id -> lift(certification.value), _.avatarId -> lift(avatar.id))
|
|
||||||
)
|
|
||||||
.onComplete {
|
|
||||||
case Failure(exception) =>
|
|
||||||
log.error(exception)("db failure")
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = false)
|
|
||||||
)
|
|
||||||
|
|
||||||
case Success(_) =>
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
PlanetsideAttributeMessage(session.get.player.GUID, 24, certification.value)
|
|
||||||
)
|
|
||||||
replaceAvatar(
|
|
||||||
avatar.copy(certifications = avatar.certifications.diff(replace) + certification)
|
|
||||||
)
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = true)
|
|
||||||
)
|
|
||||||
sessionActor ! SessionActor.CharSaved
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case SellCertification(terminalGuid, certification) =>
|
case SellCertification(terminalGuid, certification) =>
|
||||||
import ctx._
|
performCertificationAction(terminalGuid, certification, sellCertificationInTheFuture, TransactionType.Sell)
|
||||||
|
|
||||||
if (!avatar.certifications.contains(certification)) {
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
ItemTransactionResultMessage(terminalGuid, TransactionType.Learn, success = false)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
var requiredByCert: Set[Certification] = Set(certification)
|
|
||||||
var removeThese: Set[Certification] = Set(certification)
|
|
||||||
val allCerts: Set[Certification] = Certification.values.toSet
|
|
||||||
do {
|
|
||||||
removeThese = allCerts.filter { testingCert =>
|
|
||||||
testingCert.requires.intersect(removeThese).nonEmpty
|
|
||||||
}
|
|
||||||
requiredByCert = requiredByCert ++ removeThese
|
|
||||||
} while (removeThese.nonEmpty)
|
|
||||||
|
|
||||||
Future
|
|
||||||
.sequence(
|
|
||||||
avatar.certifications
|
|
||||||
.intersect(requiredByCert)
|
|
||||||
.map(cert => {
|
|
||||||
ctx
|
|
||||||
.run(
|
|
||||||
query[persistence.Certification]
|
|
||||||
.filter(_.avatarId == lift(avatar.id))
|
|
||||||
.filter(_.id == lift(cert.value))
|
|
||||||
.delete
|
|
||||||
)
|
|
||||||
.map(_ => cert)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.onComplete {
|
|
||||||
case Failure(exception) =>
|
|
||||||
log.error(exception)("db failure")
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = false)
|
|
||||||
)
|
|
||||||
case Success(certs) =>
|
|
||||||
val player = session.get.player
|
|
||||||
replaceAvatar(avatar.copy(certifications = avatar.certifications.diff(certs)))
|
|
||||||
certs.foreach { cert =>
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
PlanetsideAttributeMessage(player.GUID, 25, cert.value)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = true)
|
|
||||||
)
|
|
||||||
sessionActor ! SessionActor.CharSaved
|
|
||||||
//wearing invalid armor?
|
|
||||||
if (
|
|
||||||
if (certification == Certification.ReinforcedExoSuit) player.ExoSuit == ExoSuitType.Reinforced
|
|
||||||
else if (certification == Certification.InfiltrationSuit) player.ExoSuit == ExoSuitType.Infiltration
|
|
||||||
else if (player.ExoSuit == ExoSuitType.MAX) {
|
|
||||||
lazy val subtype =
|
|
||||||
InfantryLoadout.DetermineSubtypeA(ExoSuitType.MAX, player.Slot(slot = 0).Equipment)
|
|
||||||
if (certification == Certification.UniMAX) true
|
|
||||||
else if (certification == Certification.AAMAX) subtype == 1
|
|
||||||
else if (certification == Certification.AIMAX) subtype == 2
|
|
||||||
else if (certification == Certification.AVMAX) subtype == 3
|
|
||||||
else false
|
|
||||||
} else false
|
|
||||||
) {
|
|
||||||
player.Actor ! PlayerControl.SetExoSuit(ExoSuitType.Standard, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case SetCertifications(certifications) =>
|
case SetCertifications(certifications) =>
|
||||||
|
|
@ -1394,84 +1279,19 @@ class AvatarActor(
|
||||||
implant.definition.implantType.value
|
implant.definition.implantType.value
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
deinitializeImplants()
|
deinitializeImplants()
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case LearnImplant(terminalGuid, definition) =>
|
case LearnImplant(terminalGuid, definition) =>
|
||||||
// TODO there used to be a terminal check here, do we really need it?
|
// TODO there used to be a terminal check here, do we really need it?
|
||||||
val index = avatar.implants.zipWithIndex.collectFirst {
|
buyImplantAction(terminalGuid, definition)
|
||||||
case (Some(implant), _index) if implant.definition.implantType == definition.implantType => _index
|
|
||||||
case (None, _index) if _index < avatar.br.implantSlots => _index
|
|
||||||
}
|
|
||||||
index match {
|
|
||||||
case Some(_index) =>
|
|
||||||
import ctx._
|
|
||||||
ctx
|
|
||||||
.run(query[persistence.Implant].insert(_.name -> lift(definition.Name), _.avatarId -> lift(avatar.id)))
|
|
||||||
.onComplete {
|
|
||||||
case Success(_) =>
|
|
||||||
replaceAvatar(avatar.copy(implants = avatar.implants.updated(_index, Some(Implant(definition)))))
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
AvatarImplantMessage(
|
|
||||||
session.get.player.GUID,
|
|
||||||
ImplantAction.Add,
|
|
||||||
_index,
|
|
||||||
definition.implantType.value
|
|
||||||
)
|
|
||||||
)
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
ItemTransactionResultMessage(terminalGuid, TransactionType.Learn, success = true)
|
|
||||||
)
|
|
||||||
context.self ! ResetImplants()
|
|
||||||
sessionActor ! SessionActor.CharSaved
|
|
||||||
case Failure(exception) => log.error(exception)("db failure")
|
|
||||||
}
|
|
||||||
|
|
||||||
case None =>
|
|
||||||
log.warn("attempted to learn implant but could not find slot")
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
ItemTransactionResultMessage(terminalGuid, TransactionType.Learn, success = false)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case SellImplant(terminalGuid, definition) =>
|
case SellImplant(terminalGuid, definition) =>
|
||||||
// TODO there used to be a terminal check here, do we really need it?
|
// TODO there used to be a terminal check here, do we really need it?
|
||||||
val index = avatar.implants.zipWithIndex.collectFirst {
|
sellImplantAction(terminalGuid, definition)
|
||||||
case (Some(implant), _index) if implant.definition.implantType == definition.implantType => _index
|
|
||||||
}
|
|
||||||
index match {
|
|
||||||
case Some(_index) =>
|
|
||||||
import ctx._
|
|
||||||
ctx
|
|
||||||
.run(
|
|
||||||
query[persistence.Implant]
|
|
||||||
.filter(_.name == lift(definition.Name))
|
|
||||||
.filter(_.avatarId == lift(avatar.id))
|
|
||||||
.delete
|
|
||||||
)
|
|
||||||
.onComplete {
|
|
||||||
case Success(_) =>
|
|
||||||
replaceAvatar(avatar.copy(implants = avatar.implants.updated(_index, None)))
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Remove, _index, 0)
|
|
||||||
)
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = true)
|
|
||||||
)
|
|
||||||
context.self ! ResetImplants()
|
|
||||||
sessionActor ! SessionActor.CharSaved
|
|
||||||
case Failure(exception) => log.error(exception)("db failure")
|
|
||||||
}
|
|
||||||
|
|
||||||
case None =>
|
|
||||||
log.warn("attempted to sell implant but could not find slot")
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = false)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case SaveLoadout(player, loadoutType, label, number) =>
|
case SaveLoadout(player, loadoutType, label, number) =>
|
||||||
|
|
@ -1597,7 +1417,7 @@ class AvatarActor(
|
||||||
case _ => true
|
case _ => true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (updateTheTimes) {
|
if (updateTheTimes) {
|
||||||
|
|
@ -1790,36 +1610,27 @@ class AvatarActor(
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case AwardBep(bep, ExperienceType.Support) =>
|
case AwardBep(bep, ExperienceType.Support) =>
|
||||||
val gain = bep - experienceDebt
|
awardProgressionOrExperience(
|
||||||
if (gain > 0L) {
|
setSupportAction,
|
||||||
awardSupportExperience(gain, previousDelay = 0L)
|
bep,
|
||||||
} else {
|
Config.app.game.promotion.supportExperiencePointsModifier
|
||||||
experienceDebt = experienceDebt - bep
|
)
|
||||||
}
|
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case AwardBep(bep, modifier) =>
|
case AwardBep(bep, modifier) =>
|
||||||
val mod = Config.app.game.promotion.battleExperiencePointsModifier
|
awardProgressionOrExperience(
|
||||||
if (experienceDebt == 0L) {
|
setBepAction(modifier),
|
||||||
setBep(avatar.bep + bep, modifier)
|
avatar.bep + bep,
|
||||||
} else if (mod > 0f) {
|
Config.app.game.promotion.battleExperiencePointsModifier
|
||||||
val modifiedBep = (bep.toFloat * Config.app.game.promotion.battleExperiencePointsModifier).toLong
|
)
|
||||||
val gain = modifiedBep - experienceDebt
|
|
||||||
if (gain > 0L) {
|
|
||||||
setBep(avatar.bep + gain, modifier)
|
|
||||||
} else {
|
|
||||||
experienceDebt = experienceDebt - modifiedBep
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case AwardFacilityCaptureBep(bep) =>
|
case AwardFacilityCaptureBep(bep) =>
|
||||||
val gain = bep - experienceDebt
|
awardProgressionOrExperience(
|
||||||
if (gain > 0L) {
|
setBepAction(ExperienceType.Normal),
|
||||||
setBep(gain, ExperienceType.Normal)
|
avatar.bep + bep,
|
||||||
} else {
|
Config.app.game.promotion.captureExperiencePointsModifier
|
||||||
experienceDebt = experienceDebt - bep
|
)
|
||||||
}
|
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case SupportExperienceDeposit(bep, delayBy) =>
|
case SupportExperienceDeposit(bep, delayBy) =>
|
||||||
|
|
@ -1837,25 +1648,36 @@ class AvatarActor(
|
||||||
val newBr = BattleRank.withExperience(bep).value
|
val newBr = BattleRank.withExperience(bep).value
|
||||||
if (Config.app.game.promotion.active && oldBr == 1 && newBr > 1 && newBr < Config.app.game.promotion.maxBattleRank + 1) {
|
if (Config.app.game.promotion.active && oldBr == 1 && newBr > 1 && newBr < Config.app.game.promotion.maxBattleRank + 1) {
|
||||||
experienceDebt = bep
|
experienceDebt = bep
|
||||||
if (avatar.cep > 0) {
|
AvatarActor.saveExperienceDebt(avatar.id, bep, bep)
|
||||||
setCep(0L)
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
} else if (experienceDebt > 0 && newBr == 2) {
|
} else if (experienceDebt > 0 && newBr == 2) {
|
||||||
experienceDebt = 0
|
experienceDebt = 0
|
||||||
|
AvatarActor.saveExperienceDebt(avatar.id, exp = 0, bep)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
setBep(bep, ExperienceType.Normal)
|
setBep(bep, ExperienceType.Normal)
|
||||||
|
if (avatar.cep > 0) {
|
||||||
|
setCep(0L)
|
||||||
|
}
|
||||||
|
restoreBasicCerts()
|
||||||
|
removeAllImplants()
|
||||||
|
sessionActor ! SessionActor.CharSaved
|
||||||
sessionActor ! SessionActor.SendResponse(ChatMsg(ChatMessageType.UNK_229, "@AckSuccessSetBattleRank"))
|
sessionActor ! SessionActor.SendResponse(ChatMsg(ChatMessageType.UNK_229, "@AckSuccessSetBattleRank"))
|
||||||
|
} else if (experienceDebt > 0) {
|
||||||
|
sessionActor ! SessionActor.SendResponse(ChatMsg(ChatMessageType.CMT_QUIT, s"You already must earn back $experienceDebt."))
|
||||||
|
} else {
|
||||||
|
sessionActor ! SessionActor.SendResponse(ChatMsg(ChatMessageType.CMT_QUIT, "You may not suffer this debt."))
|
||||||
}
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case AwardCep(cep) =>
|
case AwardCep(cep) =>
|
||||||
if (experienceDebt > 0L) {
|
if (experienceDebt > 0L) {
|
||||||
setCep(avatar.cep + cep)
|
setCep(avatar.cep + cep)
|
||||||
|
} else {
|
||||||
|
sessionActor ! SessionActor.SendResponse(ExperienceAddedMessage(0))
|
||||||
}
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
|
|
@ -1973,7 +1795,7 @@ class AvatarActor(
|
||||||
case RemoveShortcut(slot) =>
|
case RemoveShortcut(slot) =>
|
||||||
import ctx._
|
import ctx._
|
||||||
avatar.shortcuts.lift(slot).flatten match {
|
avatar.shortcuts.lift(slot).flatten match {
|
||||||
case None => ;
|
case None => ()
|
||||||
case Some(_) =>
|
case Some(_) =>
|
||||||
ctx.run(
|
ctx.run(
|
||||||
query[persistence.Shortcut]
|
query[persistence.Shortcut]
|
||||||
|
|
@ -1996,7 +1818,7 @@ class AvatarActor(
|
||||||
AvatarActor.saveAvatarData(avatar)
|
AvatarActor.saveAvatarData(avatar)
|
||||||
saveLockerFunc()
|
saveLockerFunc()
|
||||||
AvatarActor.updateToolDischargeFor(avatar)
|
AvatarActor.updateToolDischargeFor(avatar)
|
||||||
AvatarActor.saveExperienceDebt(avatar.id, experienceDebt)
|
AvatarActor.saveExperienceDebt(avatar.id, experienceDebt, avatar.bep)
|
||||||
AvatarActor.avatarNoLongerLoggedIn(account.get.id)
|
AvatarActor.avatarNoLongerLoggedIn(account.get.id)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
|
|
@ -2072,7 +1894,7 @@ class AvatarActor(
|
||||||
// }
|
// }
|
||||||
// }.foreach { c =>
|
// }.foreach { c =>
|
||||||
// shortcutList.indexWhere { _.isEmpty } match {
|
// shortcutList.indexWhere { _.isEmpty } match {
|
||||||
// case -1 => ;
|
// case -1 => ()
|
||||||
// case index =>
|
// case index =>
|
||||||
// shortcutList.update(index, Some(AvatarShortcut(2, c.name)))
|
// shortcutList.update(index, Some(AvatarShortcut(2, c.name)))
|
||||||
// }
|
// }
|
||||||
|
|
@ -2180,7 +2002,7 @@ class AvatarActor(
|
||||||
avatar.implants.zipWithIndex.foreach {
|
avatar.implants.zipWithIndex.foreach {
|
||||||
case (Some(_), slot) =>
|
case (Some(_), slot) =>
|
||||||
sessionActor ! SessionActor.SendResponse(AvatarImplantMessage(guid, ImplantAction.OutOfStamina, slot, 0))
|
sessionActor ! SessionActor.SendResponse(AvatarImplantMessage(guid, ImplantAction.OutOfStamina, slot, 0))
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(guid, 2, totalStamina))
|
sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(guid, 2, totalStamina))
|
||||||
|
|
@ -2223,7 +2045,7 @@ class AvatarActor(
|
||||||
sessionActor ! SessionActor.SendResponse(
|
sessionActor ! SessionActor.SendResponse(
|
||||||
AvatarImplantMessage(player.GUID, ImplantAction.OutOfStamina, slot, 1)
|
AvatarImplantMessage(player.GUID, ImplantAction.OutOfStamina, slot, 1)
|
||||||
)
|
)
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(player.GUID, 2, totalStamina))
|
sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(player.GUID, 2, totalStamina))
|
||||||
|
|
@ -2268,7 +2090,7 @@ class AvatarActor(
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, 0))
|
AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, 0))
|
||||||
)
|
)
|
||||||
|
|
||||||
case (None, _) => ;
|
case (None, _) => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2324,7 +2146,7 @@ class AvatarActor(
|
||||||
avatar.name,
|
avatar.name,
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(index + 6, 0))
|
AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(index + 6, 0))
|
||||||
)
|
)
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2388,7 +2210,7 @@ class AvatarActor(
|
||||||
tool.GUID = PlanetSideGUID(gen.getAndIncrement)
|
tool.GUID = PlanetSideGUID(gen.getAndIncrement)
|
||||||
case Some(item: Equipment) =>
|
case Some(item: Equipment) =>
|
||||||
item.GUID = PlanetSideGUID(gen.getAndIncrement)
|
item.GUID = PlanetSideGUID(gen.getAndIncrement)
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
player.GUID = PlanetSideGUID(gen.getAndIncrement)
|
player.GUID = PlanetSideGUID(gen.getAndIncrement)
|
||||||
|
|
@ -2426,7 +2248,7 @@ class AvatarActor(
|
||||||
item.Invalidate()
|
item.Invalidate()
|
||||||
case Some(item: Equipment) =>
|
case Some(item: Equipment) =>
|
||||||
item.Invalidate()
|
item.Invalidate()
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
player.Invalidate()
|
player.Invalidate()
|
||||||
|
|
@ -2696,7 +2518,7 @@ class AvatarActor(
|
||||||
subtype
|
subtype
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2884,7 +2706,7 @@ class AvatarActor(
|
||||||
session match {
|
session match {
|
||||||
case Some(sess) if sess.player != null =>
|
case Some(sess) if sess.player != null =>
|
||||||
sess.player.avatar = copyAvatar
|
sess.player.avatar = copyAvatar
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2903,7 +2725,7 @@ class AvatarActor(
|
||||||
case MemberAction.RemoveFriend => getAvatarForFunc(name, formatForOtherFunc(memberActionRemoveFriend))
|
case MemberAction.RemoveFriend => getAvatarForFunc(name, formatForOtherFunc(memberActionRemoveFriend))
|
||||||
case MemberAction.AddIgnoredPlayer => getAvatarForFunc(name, memberActionAddIgnored)
|
case MemberAction.AddIgnoredPlayer => getAvatarForFunc(name, memberActionAddIgnored)
|
||||||
case MemberAction.RemoveIgnoredPlayer => getAvatarForFunc(name, formatForOtherFunc(memberActionRemoveIgnored))
|
case MemberAction.RemoveIgnoredPlayer => getAvatarForFunc(name, formatForOtherFunc(memberActionRemoveIgnored))
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2947,7 +2769,7 @@ class AvatarActor(
|
||||||
def memberActionAddFriend(charId: Long, name: String, faction: Int): Unit = {
|
def memberActionAddFriend(charId: Long, name: String, faction: Int): Unit = {
|
||||||
val people = avatar.people
|
val people = avatar.people
|
||||||
people.friend.find { _.name.equals(name) } match {
|
people.friend.find { _.name.equals(name) } match {
|
||||||
case Some(_) => ;
|
case Some(_) => ()
|
||||||
case None =>
|
case None =>
|
||||||
import ctx._
|
import ctx._
|
||||||
ctx.run(
|
ctx.run(
|
||||||
|
|
@ -2983,7 +2805,7 @@ class AvatarActor(
|
||||||
replaceAvatar(
|
replaceAvatar(
|
||||||
avatar.copy(people = people.copy(friend = people.friend.filterNot { _.charId == charId }))
|
avatar.copy(people = people.copy(friend = people.friend.filterNot { _.charId == charId }))
|
||||||
)
|
)
|
||||||
case None => ;
|
case None => ()
|
||||||
}
|
}
|
||||||
ctx.run(
|
ctx.run(
|
||||||
query[persistence.Friend]
|
query[persistence.Friend]
|
||||||
|
|
@ -3042,7 +2864,7 @@ class AvatarActor(
|
||||||
def memberActionAddIgnored(charId: Long, name: String, faction: Int): Unit = {
|
def memberActionAddIgnored(charId: Long, name: String, faction: Int): Unit = {
|
||||||
val people = avatar.people
|
val people = avatar.people
|
||||||
people.ignored.find { _.name.equals(name) } match {
|
people.ignored.find { _.name.equals(name) } match {
|
||||||
case Some(_) => ;
|
case Some(_) => ()
|
||||||
case None =>
|
case None =>
|
||||||
import ctx._
|
import ctx._
|
||||||
ctx.run(
|
ctx.run(
|
||||||
|
|
@ -3078,7 +2900,7 @@ class AvatarActor(
|
||||||
replaceAvatar(
|
replaceAvatar(
|
||||||
avatar.copy(people = people.copy(ignored = people.ignored.filterNot { _.charId == charId }))
|
avatar.copy(people = people.copy(ignored = people.ignored.filterNot { _.charId == charId }))
|
||||||
)
|
)
|
||||||
case None => ;
|
case None => ()
|
||||||
}
|
}
|
||||||
ctx.run(
|
ctx.run(
|
||||||
query[persistence.Ignored]
|
query[persistence.Ignored]
|
||||||
|
|
@ -3163,12 +2985,7 @@ class AvatarActor(
|
||||||
}
|
}
|
||||||
|
|
||||||
def awardSupportExperience(bep: Long, previousDelay: Long): Unit = {
|
def awardSupportExperience(bep: Long, previousDelay: Long): Unit = {
|
||||||
setBep(avatar.bep + bep, ExperienceType.Support) //todo simplify support testing
|
setBep(avatar.bep + bep, ExperienceType.Support)
|
||||||
// supportExperiencePool = supportExperiencePool + bep
|
|
||||||
// avatar.scorecard.rate(bep)
|
|
||||||
// if (supportExperienceTimer.isCancelled) {
|
|
||||||
// resetSupportExperienceTimer(previousBep = 0, previousDelay = 0)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def actuallyAwardSupportExperience(bep: Long, delayBy: Long): Unit = {
|
def actuallyAwardSupportExperience(bep: Long, delayBy: Long): Unit = {
|
||||||
|
|
@ -3183,6 +3000,34 @@ class AvatarActor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def awardProgressionOrExperience(
|
||||||
|
awardAction: Long => Unit,
|
||||||
|
experience: Long,
|
||||||
|
modifier: Float
|
||||||
|
): Unit = {
|
||||||
|
if (experienceDebt == 0L) {
|
||||||
|
awardAction(experience)
|
||||||
|
} else if (modifier > 0f) {
|
||||||
|
val modifiedBep = (experience.toFloat * modifier).toLong
|
||||||
|
val gain = modifiedBep - experienceDebt
|
||||||
|
if (gain > 0L) {
|
||||||
|
experienceDebt = 0L
|
||||||
|
awardAction(experience)
|
||||||
|
} else {
|
||||||
|
experienceDebt = experienceDebt - modifiedBep
|
||||||
|
sessionActor ! SessionActor.SendResponse(ExperienceAddedMessage())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def setBepAction(modifier: ExperienceType)(value: Long): Unit = {
|
||||||
|
setBep(value, modifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def setSupportAction(value: Long): Unit = {
|
||||||
|
awardSupportExperience(value, previousDelay = 0L)
|
||||||
|
}
|
||||||
|
|
||||||
def updateKills(killStat: Kill): Unit = {
|
def updateKills(killStat: Kill): Unit = {
|
||||||
val exp = killStat.experienceEarned
|
val exp = killStat.experienceEarned
|
||||||
val (modifiedExp, msg) = updateExperienceAndType(killStat.experienceEarned)
|
val (modifiedExp, msg) = updateExperienceAndType(killStat.experienceEarned)
|
||||||
|
|
@ -3431,6 +3276,245 @@ class AvatarActor(
|
||||||
output.future
|
output.future
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def performCertificationAction(
|
||||||
|
terminalGuid: PlanetSideGUID,
|
||||||
|
certification: Certification,
|
||||||
|
action: Certification => Future[Boolean],
|
||||||
|
transaction: TransactionType.Value
|
||||||
|
): Unit = {
|
||||||
|
action(certification).onComplete {
|
||||||
|
case Success(true) =>
|
||||||
|
sessionActor ! SessionActor.SendResponse(
|
||||||
|
ItemTransactionResultMessage(terminalGuid, transaction, success = true)
|
||||||
|
)
|
||||||
|
sessionActor ! SessionActor.CharSaved
|
||||||
|
case _ =>
|
||||||
|
sessionActor ! SessionActor.SendResponse(
|
||||||
|
ItemTransactionResultMessage(terminalGuid, transaction, success = false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def learnCertificationInTheFuture(certification: Certification): Future[Boolean] = {
|
||||||
|
import ctx._
|
||||||
|
val out: Promise[Boolean] = Promise()
|
||||||
|
val replace = certification.replaces.intersect(avatar.certifications)
|
||||||
|
Future
|
||||||
|
.sequence(replace.map(cert => {
|
||||||
|
ctx
|
||||||
|
.run(
|
||||||
|
query[persistence.Certification]
|
||||||
|
.filter(_.avatarId == lift(avatar.id))
|
||||||
|
.filter(_.id == lift(cert.value))
|
||||||
|
.delete
|
||||||
|
)
|
||||||
|
.map(_ => cert)
|
||||||
|
}))
|
||||||
|
.onComplete {
|
||||||
|
case Failure(exception) =>
|
||||||
|
log.error(exception)("db failure")
|
||||||
|
case Success(_replace) =>
|
||||||
|
_replace.foreach { cert =>
|
||||||
|
sessionActor ! SessionActor.SendResponse(
|
||||||
|
PlanetsideAttributeMessage(session.get.player.GUID, 25, cert.value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ctx
|
||||||
|
.run(
|
||||||
|
query[persistence.Certification]
|
||||||
|
.insert(_.id -> lift(certification.value), _.avatarId -> lift(avatar.id))
|
||||||
|
)
|
||||||
|
.onComplete {
|
||||||
|
case Failure(exception) =>
|
||||||
|
log.error(exception)("db failure")
|
||||||
|
out.completeWith(Future(false))
|
||||||
|
case Success(_) =>
|
||||||
|
sessionActor ! SessionActor.SendResponse(
|
||||||
|
PlanetsideAttributeMessage(session.get.player.GUID, 24, certification.value)
|
||||||
|
)
|
||||||
|
replaceAvatar(
|
||||||
|
avatar.copy(certifications = avatar.certifications.diff(replace) + certification)
|
||||||
|
)
|
||||||
|
out.completeWith(Future(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.future
|
||||||
|
}
|
||||||
|
|
||||||
|
def sellCertificationInTheFuture(certification: Certification): Future[Boolean] = {
|
||||||
|
import ctx._
|
||||||
|
val out: Promise[Boolean] = Promise()
|
||||||
|
var requiredByCert: Set[Certification] = Set(certification)
|
||||||
|
var removeThese: Set[Certification] = Set(certification)
|
||||||
|
val allCerts: Set[Certification] = Certification.values.toSet
|
||||||
|
do {
|
||||||
|
removeThese = allCerts.filter { testingCert =>
|
||||||
|
testingCert.requires.intersect(removeThese).nonEmpty
|
||||||
|
}
|
||||||
|
requiredByCert = requiredByCert ++ removeThese
|
||||||
|
} while (removeThese.nonEmpty)
|
||||||
|
|
||||||
|
Future
|
||||||
|
.sequence(
|
||||||
|
avatar.certifications
|
||||||
|
.intersect(requiredByCert)
|
||||||
|
.map(cert => {
|
||||||
|
ctx
|
||||||
|
.run(
|
||||||
|
query[persistence.Certification]
|
||||||
|
.filter(_.avatarId == lift(avatar.id))
|
||||||
|
.filter(_.id == lift(cert.value))
|
||||||
|
.delete
|
||||||
|
)
|
||||||
|
.map(_ => cert)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.onComplete {
|
||||||
|
case Failure(exception) =>
|
||||||
|
log.error(exception)("db failure")
|
||||||
|
out.complete(Success(false))
|
||||||
|
case Success(certs) if certs.isEmpty =>
|
||||||
|
out.complete(Success(false))
|
||||||
|
case Success(certs) =>
|
||||||
|
val player = session.get.player
|
||||||
|
replaceAvatar(avatar.copy(certifications = avatar.certifications.diff(certs)))
|
||||||
|
certs.foreach { cert =>
|
||||||
|
sessionActor ! SessionActor.SendResponse(
|
||||||
|
PlanetsideAttributeMessage(player.GUID, 25, cert.value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
//wearing invalid armor?
|
||||||
|
if (
|
||||||
|
if (certification == Certification.ReinforcedExoSuit) player.ExoSuit == ExoSuitType.Reinforced
|
||||||
|
else if (certification == Certification.InfiltrationSuit) player.ExoSuit == ExoSuitType.Infiltration
|
||||||
|
else if (player.ExoSuit == ExoSuitType.MAX) {
|
||||||
|
lazy val subtype =
|
||||||
|
InfantryLoadout.DetermineSubtypeA(ExoSuitType.MAX, player.Slot(slot = 0).Equipment)
|
||||||
|
if (certification == Certification.UniMAX) true
|
||||||
|
else if (certification == Certification.AAMAX) subtype == 1
|
||||||
|
else if (certification == Certification.AIMAX) subtype == 2
|
||||||
|
else if (certification == Certification.AVMAX) subtype == 3
|
||||||
|
else false
|
||||||
|
} else false
|
||||||
|
) {
|
||||||
|
player.Actor ! PlayerControl.SetExoSuit(ExoSuitType.Standard, 0)
|
||||||
|
}
|
||||||
|
out.complete(Success(true))
|
||||||
|
}
|
||||||
|
out.future
|
||||||
|
}
|
||||||
|
|
||||||
|
def restoreBasicCerts(): Unit = {
|
||||||
|
val certs = avatar.certifications
|
||||||
|
certs.diff(AvatarActor.basicLoginCertifications).foreach { sellCertificationInTheFuture }
|
||||||
|
AvatarActor.basicLoginCertifications.diff(certs).foreach { learnCertificationInTheFuture }
|
||||||
|
}
|
||||||
|
|
||||||
|
def buyImplantAction(
|
||||||
|
terminalGuid: PlanetSideGUID,
|
||||||
|
definition: ImplantDefinition
|
||||||
|
): Unit = {
|
||||||
|
buyImplantInTheFuture(definition).onComplete {
|
||||||
|
case Success(true) =>
|
||||||
|
sessionActor ! SessionActor.SendResponse(
|
||||||
|
ItemTransactionResultMessage(terminalGuid, TransactionType.Buy, success = true)
|
||||||
|
)
|
||||||
|
resetAnImplant(definition.implantType)
|
||||||
|
sessionActor ! SessionActor.CharSaved
|
||||||
|
case _ =>
|
||||||
|
sessionActor ! SessionActor.SendResponse(
|
||||||
|
ItemTransactionResultMessage(terminalGuid, TransactionType.Buy, success = false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def buyImplantInTheFuture(definition: ImplantDefinition): Future[Boolean] = {
|
||||||
|
val out: Promise[Boolean] = Promise()
|
||||||
|
avatar.implants.zipWithIndex.collectFirst {
|
||||||
|
case (Some(implant), _) if implant.definition.implantType == definition.implantType => None
|
||||||
|
case (None, index) if index < avatar.br.implantSlots => Some(index)
|
||||||
|
}.flatten match {
|
||||||
|
case Some(index) =>
|
||||||
|
import ctx._
|
||||||
|
ctx
|
||||||
|
.run(query[persistence.Implant].insert(_.name -> lift(definition.Name), _.avatarId -> lift(avatar.id)))
|
||||||
|
.onComplete {
|
||||||
|
case Success(_) =>
|
||||||
|
replaceAvatar(avatar.copy(implants = avatar.implants.updated(index, Some(Implant(definition)))))
|
||||||
|
sessionActor ! SessionActor.SendResponse(
|
||||||
|
AvatarImplantMessage(
|
||||||
|
session.get.player.GUID,
|
||||||
|
ImplantAction.Add,
|
||||||
|
index,
|
||||||
|
definition.implantType.value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
out.completeWith(Future(true))
|
||||||
|
case Failure(exception) =>
|
||||||
|
log.error(exception)("db failure")
|
||||||
|
out.completeWith(Future(false))
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
log.warn("attempted to learn implant but could not find slot")
|
||||||
|
out.completeWith(Future(false))
|
||||||
|
}
|
||||||
|
out.future
|
||||||
|
}
|
||||||
|
|
||||||
|
def sellImplantAction(
|
||||||
|
terminalGuid: PlanetSideGUID,
|
||||||
|
definition: ImplantDefinition
|
||||||
|
): Unit = {
|
||||||
|
sellImplantInTheFuture(definition).onComplete {
|
||||||
|
case Success(true) =>
|
||||||
|
sessionActor ! SessionActor.SendResponse(
|
||||||
|
ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = true)
|
||||||
|
)
|
||||||
|
sessionActor ! SessionActor.CharSaved
|
||||||
|
case _ =>
|
||||||
|
sessionActor ! SessionActor.SendResponse(
|
||||||
|
ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def sellImplantInTheFuture(definition: ImplantDefinition): Future[Boolean] = {
|
||||||
|
val out: Promise[Boolean] = Promise()
|
||||||
|
avatar.implants.zipWithIndex.collectFirst {
|
||||||
|
case (Some(implant), index) if implant.definition.implantType == definition.implantType => index
|
||||||
|
} match {
|
||||||
|
case Some(index) =>
|
||||||
|
import ctx._
|
||||||
|
ctx
|
||||||
|
.run(
|
||||||
|
query[persistence.Implant]
|
||||||
|
.filter(_.name == lift(definition.Name))
|
||||||
|
.filter(_.avatarId == lift(avatar.id))
|
||||||
|
.delete
|
||||||
|
)
|
||||||
|
.onComplete {
|
||||||
|
case Success(_) =>
|
||||||
|
replaceAvatar(avatar.copy(implants = avatar.implants.updated(index, None)))
|
||||||
|
sessionActor ! SessionActor.SendResponse(
|
||||||
|
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Remove, index, 0)
|
||||||
|
)
|
||||||
|
out.completeWith(Future(true))
|
||||||
|
case Failure(exception) =>
|
||||||
|
log.error(exception)("db failure")
|
||||||
|
out.completeWith(Future(false))
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
log.warn("attempted to sell implant but could not find slot")
|
||||||
|
out.completeWith(Future(false))
|
||||||
|
}
|
||||||
|
out.future
|
||||||
|
}
|
||||||
|
|
||||||
|
def removeAllImplants(): Unit = {
|
||||||
|
avatar.implants.collect { case Some(imp) => imp.definition }.foreach { sellImplantInTheFuture }
|
||||||
|
context.self ! ResetImplants()
|
||||||
|
}
|
||||||
|
|
||||||
def resetSupportExperienceTimer(previousBep: Long, previousDelay: Long): Unit = {
|
def resetSupportExperienceTimer(previousBep: Long, previousDelay: Long): Unit = {
|
||||||
val bep: Long = if (supportExperiencePool < 10L) {
|
val bep: Long = if (supportExperiencePool < 10L) {
|
||||||
supportExperiencePool
|
supportExperiencePool
|
||||||
|
|
|
||||||
|
|
@ -732,10 +732,18 @@ class ChatActor(
|
||||||
}
|
}
|
||||||
|
|
||||||
case (CMT_SETBATTLERANK, _, contents) if gmCommandAllowed =>
|
case (CMT_SETBATTLERANK, _, contents) if gmCommandAllowed =>
|
||||||
setBattleRank(message, contents, session, AvatarActor.SetBep)
|
if (!setBattleRank(contents, session, AvatarActor.SetBep)) {
|
||||||
|
sessionActor ! SessionActor.SendResponse(
|
||||||
|
message.copy(messageType = UNK_229, contents = "@CMT_SETBATTLERANK_usage")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
case (CMT_SETCOMMANDRANK, _, contents) if gmCommandAllowed =>
|
case (CMT_SETCOMMANDRANK, _, contents) if gmCommandAllowed =>
|
||||||
setCommandRank(message, contents, session)
|
if (!setCommandRank(contents, session)) {
|
||||||
|
sessionActor ! SessionActor.SendResponse(
|
||||||
|
message.copy(messageType = UNK_229, contents = "@CMT_SETCOMMANDRANK_usage")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
case (CMT_ADDBATTLEEXPERIENCE, _, contents) if gmCommandAllowed =>
|
case (CMT_ADDBATTLEEXPERIENCE, _, contents) if gmCommandAllowed =>
|
||||||
contents.toIntOption match {
|
contents.toIntOption match {
|
||||||
|
|
@ -1124,7 +1132,7 @@ class ChatActor(
|
||||||
true
|
true
|
||||||
|
|
||||||
} else if (contents.startsWith("!list")) {
|
} else if (contents.startsWith("!list")) {
|
||||||
val zone = contents.split(" ").lift(1) match {
|
val zone = dropFirstWord(contents).split(" ").headOption match {
|
||||||
case None =>
|
case None =>
|
||||||
Some(session.zone)
|
Some(session.zone)
|
||||||
case Some(id) =>
|
case Some(id) =>
|
||||||
|
|
@ -1170,8 +1178,8 @@ class ChatActor(
|
||||||
true
|
true
|
||||||
|
|
||||||
} else if (contents.startsWith("!ntu") && gmCommandAllowed) {
|
} else if (contents.startsWith("!ntu") && gmCommandAllowed) {
|
||||||
val buffer = contents.toLowerCase.split("\\s+")
|
val buffer = dropFirstWord(contents).toLowerCase.split("\\s+")
|
||||||
val (facility, customNtuValue) = (buffer.lift(1), buffer.lift(2)) match {
|
val (facility, customNtuValue) = (buffer.headOption, buffer.lift(1)) match {
|
||||||
case (Some(x), Some(y)) if y.toIntOption.nonEmpty => (Some(x), Some(y.toInt))
|
case (Some(x), Some(y)) if y.toIntOption.nonEmpty => (Some(x), Some(y.toInt))
|
||||||
case (Some(x), None) if x.toIntOption.nonEmpty => (None, Some(x.toInt))
|
case (Some(x), None) if x.toIntOption.nonEmpty => (None, Some(x.toInt))
|
||||||
case _ => (None, None)
|
case _ => (None, None)
|
||||||
|
|
@ -1202,8 +1210,8 @@ class ChatActor(
|
||||||
true
|
true
|
||||||
|
|
||||||
} else if (contents.startsWith("!zonerotate") && gmCommandAllowed) {
|
} else if (contents.startsWith("!zonerotate") && gmCommandAllowed) {
|
||||||
val buffer = contents.toLowerCase.split("\\s+")
|
val buffer = dropFirstWord(contents).toLowerCase.split("\\s+")
|
||||||
cluster ! InterstellarClusterService.CavernRotation(buffer.lift(1) match {
|
cluster ! InterstellarClusterService.CavernRotation(buffer.headOption match {
|
||||||
case Some("-list") | Some("-l") =>
|
case Some("-list") | Some("-l") =>
|
||||||
CavernRotationService.ReportRotationOrder(sessionActor.toClassic)
|
CavernRotationService.ReportRotationOrder(sessionActor.toClassic)
|
||||||
case _ =>
|
case _ =>
|
||||||
|
|
@ -1224,8 +1232,8 @@ class ChatActor(
|
||||||
|
|
||||||
} else if (contents.startsWith("!macro")) {
|
} else if (contents.startsWith("!macro")) {
|
||||||
val avatar = session.avatar
|
val avatar = session.avatar
|
||||||
val args = contents.split(" ").filter(_ != "")
|
val args = dropFirstWord(contents).split(" ").filter(_ != "")
|
||||||
(args.lift(1), args.lift(2)) match {
|
(args.headOption, args.lift(1)) match {
|
||||||
case (Some(cmd), other) =>
|
case (Some(cmd), other) =>
|
||||||
cmd.toLowerCase() match {
|
cmd.toLowerCase() match {
|
||||||
case "medkit" =>
|
case "medkit" =>
|
||||||
|
|
@ -1275,9 +1283,10 @@ class ChatActor(
|
||||||
}
|
}
|
||||||
} else if (contents.startsWith("!progress")) {
|
} else if (contents.startsWith("!progress")) {
|
||||||
if (!session.account.gm && BattleRank.withExperience(session.avatar.bep).value < Config.app.game.promotion.maxBattleRank + 1) {
|
if (!session.account.gm && BattleRank.withExperience(session.avatar.bep).value < Config.app.game.promotion.maxBattleRank + 1) {
|
||||||
setBattleRank(message, contents, session, AvatarActor.Progress)
|
setBattleRank(dropFirstWord(contents), session, AvatarActor.Progress)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
setBattleRank(contents="1", session, AvatarActor.Progress)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1288,12 +1297,19 @@ class ChatActor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def dropFirstWord(str: String): String = {
|
||||||
|
val noExtraSpaces = str.replaceAll("\\s+", " ").toLowerCase.trim
|
||||||
|
noExtraSpaces.indexOf({ char: String => char.equals(" ") }) match {
|
||||||
|
case -1 => ""
|
||||||
|
case beforeFirstBlank => noExtraSpaces.drop(beforeFirstBlank + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def setBattleRank(
|
def setBattleRank(
|
||||||
message: ChatMsg,
|
|
||||||
contents: String,
|
contents: String,
|
||||||
session: Session,
|
session: Session,
|
||||||
msgFunc: Long => AvatarActor.Command
|
msgFunc: Long => AvatarActor.Command
|
||||||
): Unit = {
|
): Boolean = {
|
||||||
val buffer = contents.toLowerCase.split("\\s+")
|
val buffer = contents.toLowerCase.split("\\s+")
|
||||||
val (target, rank) = (buffer.lift(0), buffer.lift(1)) match {
|
val (target, rank) = (buffer.lift(0), buffer.lift(1)) match {
|
||||||
case (Some(target), Some(rank)) if target == session.avatar.name =>
|
case (Some(target), Some(rank)) if target == session.avatar.name =>
|
||||||
|
|
@ -1301,6 +1317,8 @@ class ChatActor(
|
||||||
case Some(rank) => (None, BattleRank.withValueOpt(rank))
|
case Some(rank) => (None, BattleRank.withValueOpt(rank))
|
||||||
case None => (None, None)
|
case None => (None, None)
|
||||||
}
|
}
|
||||||
|
case (Some("-h"), _) | (Some("-help"), _) =>
|
||||||
|
(None, Some(BattleRank.BR1))
|
||||||
case (Some(_), Some(_)) =>
|
case (Some(_), Some(_)) =>
|
||||||
// picking other targets is not supported for now
|
// picking other targets is not supported for now
|
||||||
(None, None)
|
(None, None)
|
||||||
|
|
@ -1314,14 +1332,16 @@ class ChatActor(
|
||||||
(target, rank) match {
|
(target, rank) match {
|
||||||
case (_, Some(rank)) if rank.value <= Config.app.game.maxBattleRank =>
|
case (_, Some(rank)) if rank.value <= Config.app.game.maxBattleRank =>
|
||||||
avatarActor ! msgFunc(rank.experience)
|
avatarActor ! msgFunc(rank.experience)
|
||||||
|
true
|
||||||
case _ =>
|
case _ =>
|
||||||
sessionActor ! SessionActor.SendResponse(
|
false
|
||||||
message.copy(messageType = UNK_229, contents = "@CMT_SETBATTLERANK_usage")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def setCommandRank(message: ChatMsg, contents: String, session: Session): Unit = {
|
def setCommandRank(
|
||||||
|
contents: String,
|
||||||
|
session: Session
|
||||||
|
): Boolean = {
|
||||||
val buffer = contents.toLowerCase.split("\\s+")
|
val buffer = contents.toLowerCase.split("\\s+")
|
||||||
val (target, rank) = (buffer.lift(0), buffer.lift(1)) match {
|
val (target, rank) = (buffer.lift(0), buffer.lift(1)) match {
|
||||||
case (Some(target), Some(rank)) if target == session.avatar.name =>
|
case (Some(target), Some(rank)) if target == session.avatar.name =>
|
||||||
|
|
@ -1342,11 +1362,9 @@ class ChatActor(
|
||||||
(target, rank) match {
|
(target, rank) match {
|
||||||
case (_, Some(rank)) =>
|
case (_, Some(rank)) =>
|
||||||
avatarActor ! AvatarActor.SetCep(rank.experience)
|
avatarActor ! AvatarActor.SetCep(rank.experience)
|
||||||
//sessionActor ! SessionActor.SendResponse(message.copy(contents = "@AckSuccessSetCommandRank"))
|
true
|
||||||
case _ =>
|
case _ =>
|
||||||
sessionActor ! SessionActor.SendResponse(
|
false
|
||||||
message.copy(messageType = UNK_229, contents = "@CMT_SETCOMMANDRANK_usage")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ object ZoningOperations {
|
||||||
"If you consider yourself as a veteran soldier, despite looking so green, please read this.\n" ++
|
"If you consider yourself as a veteran soldier, despite looking so green, please read this.\n" ++
|
||||||
"You only have this opportunity while you are battle rank 1." ++
|
"You only have this opportunity while you are battle rank 1." ++
|
||||||
"\n\n" ++
|
"\n\n" ++
|
||||||
"The normal method of rank advancement comes from progress on the battlefield - fighting enemies, helping allies, and capturing facilities. " ++
|
"The normal method of rank advancement comes from the battlefield - fighting enemies, helping allies, and capturing facilities. " ++
|
||||||
"\n\n" ++
|
"\n\n" ++
|
||||||
s"You may, however, rapidly promote yourself to at most battle rank ${Config.app.game.promotion.maxBattleRank}. " ++
|
s"You may, however, rapidly promote yourself to at most battle rank ${Config.app.game.promotion.maxBattleRank}. " ++
|
||||||
"You have access to all of the normal benefits, certification points, implants, etc., of your chosen rank. " ++
|
"You have access to all of the normal benefits, certification points, implants, etc., of your chosen rank. " ++
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ object FacilityHackParticipation {
|
||||||
if (dataSum != 0) {
|
if (dataSum != 0) {
|
||||||
math.max(0.15f, math.min(2f, dataSum / dataCount.toFloat))
|
math.max(0.15f, math.min(2f, dataSum / dataCount.toFloat))
|
||||||
} else {
|
} else {
|
||||||
1f //can't do anything; multiplier should not affect values
|
1f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,14 +47,15 @@ final case class MajorFacilityHackParticipation(building: Building) extends Faci
|
||||||
import scala.concurrent.Promise
|
import scala.concurrent.Promise
|
||||||
import scala.util.Success
|
import scala.util.Success
|
||||||
val requestLayers: Promise[ZoneHotSpotProjector.ExposedHeat] = Promise[ZoneHotSpotProjector.ExposedHeat]()
|
val requestLayers: Promise[ZoneHotSpotProjector.ExposedHeat] = Promise[ZoneHotSpotProjector.ExposedHeat]()
|
||||||
val request = updateHotSpotInfoOnly()
|
// val request = updateHotSpotInfoOnly()
|
||||||
requestLayers.completeWith(request)
|
// requestLayers.completeWith(request)
|
||||||
request.onComplete {
|
// request.onComplete {
|
||||||
case Success(ZoneHotSpotProjector.ExposedHeat(_, _, activity)) =>
|
// case Success(ZoneHotSpotProjector.ExposedHeat(_, _, activity)) =>
|
||||||
hotSpotLayersOverTime = timeSensitiveFilterAndAppend(hotSpotLayersOverTime, activity, System.currentTimeMillis() - 900000L)
|
// hotSpotLayersOverTime = timeSensitiveFilterAndAppend(hotSpotLayersOverTime, activity, System.currentTimeMillis() - 900000L)
|
||||||
case _ =>
|
// case _ =>
|
||||||
requestLayers.completeWith(Future(ZoneHotSpotProjector.ExposedHeat(Vector3.Zero, 0, Nil)))
|
// requestLayers.completeWith(Future(ZoneHotSpotProjector.ExposedHeat(building.Position.xy, building.Definition.SOIRadius, Nil)))
|
||||||
}
|
// }
|
||||||
|
requestLayers.completeWith(Future(ZoneHotSpotProjector.ExposedHeat(building.Position.xy, building.Definition.SOIRadius, Nil)))
|
||||||
requestLayers.future
|
requestLayers.future
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,7 +141,7 @@ final case class MajorFacilityHackParticipation(building: Building) extends Faci
|
||||||
*/
|
*/
|
||||||
val finalMap = mutable.HashMap[Vector3, Map[PlanetSideEmpire.Value, Seq[Long]]]()
|
val finalMap = mutable.HashMap[Vector3, Map[PlanetSideEmpire.Value, Seq[Long]]]()
|
||||||
.addAll(
|
.addAll(
|
||||||
hotSpotLayersOverTime.take(1).flatMap { entry =>
|
hotSpotLayersOverTime.flatMap { entry =>
|
||||||
entry.map { f => (f.DisplayLocation, Map.empty[PlanetSideEmpire.Value, Seq[Long]]) }
|
entry.map { f => (f.DisplayLocation, Map.empty[PlanetSideEmpire.Value, Seq[Long]]) }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -215,18 +215,14 @@ object Support {
|
||||||
.find(evt => event.equals(evt.name))
|
.find(evt => event.equals(evt.name))
|
||||||
.map { event =>
|
.map { event =>
|
||||||
val shots = weaponStat.shots
|
val shots = weaponStat.shots
|
||||||
|
val shotsMax = event.shotsMax
|
||||||
val shotsMultiplier = event.shotsMultiplier
|
val shotsMultiplier = event.shotsMultiplier
|
||||||
if (shotsMultiplier > 0f && shots < event.shotsCutoff) {
|
if (shotsMultiplier > 0f && shots < event.shotsCutoff) {
|
||||||
val modifiedShotsReward: Float = {
|
val modifiedShotsReward: Float =
|
||||||
val partialShots = math.min(event.shotsLimit, shots).toFloat
|
shotsMultiplier * math.log(math.min(shotsMax, shots).toDouble + 2d).toFloat
|
||||||
shotsMultiplier * (if (event.shotsNatLog > 0f) {
|
val modifiedAmountReward: Float =
|
||||||
math.log(math.pow(partialShots, event.shotsNatLog) + 2d).toFloat
|
event.amountMultiplier * weaponStat.amount.toFloat
|
||||||
} else {
|
event.base.toFloat + modifiedShotsReward + modifiedAmountReward
|
||||||
partialShots
|
|
||||||
})
|
|
||||||
}
|
|
||||||
val modifiedAmountReward: Float = event.amountMultiplier * weaponStat.amount.toFloat
|
|
||||||
event.base + modifiedShotsReward + modifiedAmountReward
|
|
||||||
} else {
|
} else {
|
||||||
0f
|
0f
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
package net.psforever.packet.game
|
package net.psforever.packet.game
|
||||||
|
|
||||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||||
import scodec.Codec
|
import scodec.bits.BitVector
|
||||||
|
import scodec.{Attempt, Codec}
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -12,21 +13,33 @@ import scodec.codecs._
|
||||||
* It merely generates the message:<br>
|
* It merely generates the message:<br>
|
||||||
* `"You have been awarded x experience points."`<br>
|
* `"You have been awarded x experience points."`<br>
|
||||||
* ... where `x` is the number of experience points that have been promised.
|
* ... where `x` is the number of experience points that have been promised.
|
||||||
* If the `Boolean` parameter is `true`, `x` will be equal to the number provided followed by the word "Command."
|
|
||||||
* If the `Boolean` parameter is `false`, `x` will be represented as an obvious blank space character.
|
|
||||||
* (Yes, it prints to the events chat like that.)
|
|
||||||
* @param exp the number of (Command) experience points earned
|
* @param exp the number of (Command) experience points earned
|
||||||
* @param unk defaults to `true` for effect;
|
* @param cmd if `true`, the message will be tailored for "Command" experience;
|
||||||
* if `false`, the number of experience points in the message will be blanked
|
* if `false`, the number of experience points and the "Command" flair will be blanked
|
||||||
*/
|
*/
|
||||||
final case class ExperienceAddedMessage(exp: Int, unk: Boolean = true) extends PlanetSideGamePacket {
|
final case class ExperienceAddedMessage(exp: Int, cmd: Boolean) extends PlanetSideGamePacket {
|
||||||
type Packet = ExperienceAddedMessage
|
type Packet = ExperienceAddedMessage
|
||||||
def opcode = GamePacketOpcode.ExperienceAddedMessage
|
def opcode: GamePacketOpcode.Value = GamePacketOpcode.ExperienceAddedMessage
|
||||||
def encode = ExperienceAddedMessage.encode(this)
|
def encode: Attempt[BitVector] = ExperienceAddedMessage.encode(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
object ExperienceAddedMessage extends Marshallable[ExperienceAddedMessage] {
|
object ExperienceAddedMessage extends Marshallable[ExperienceAddedMessage] {
|
||||||
|
/**
|
||||||
|
* Produce a packet whose message to the event chat is
|
||||||
|
* "You have been awarded experience points."
|
||||||
|
* @return `ExperienceAddedMessage` packet
|
||||||
|
*/
|
||||||
|
def apply(): ExperienceAddedMessage = ExperienceAddedMessage(0, cmd = false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce a packet whose message to the event chat is
|
||||||
|
* "You have been awarded 'exp' Command experience points."
|
||||||
|
* @param exp the number of Command experience points earned
|
||||||
|
* @return `ExperienceAddedMessage` packet
|
||||||
|
*/
|
||||||
|
def apply(exp: Int): ExperienceAddedMessage = ExperienceAddedMessage(exp, cmd = true)
|
||||||
|
|
||||||
implicit val codec: Codec[ExperienceAddedMessage] = (
|
implicit val codec: Codec[ExperienceAddedMessage] = (
|
||||||
("exp" | uintL(15)) :: ("unk" | bool)
|
("exp" | uintL(bits = 15)) :: ("unk" | bool)
|
||||||
).as[ExperienceAddedMessage]
|
).as[ExperienceAddedMessage]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
// Copyright (c) 2023 PSForever
|
// Copyright (c) 2023 PSForever
|
||||||
package net.psforever.persistence
|
package net.psforever.persistence
|
||||||
|
|
||||||
|
import org.joda.time.LocalDateTime
|
||||||
|
|
||||||
case class Progressiondebt(
|
case class Progressiondebt(
|
||||||
avatarId:Long,
|
avatarId:Long,
|
||||||
experience: Long
|
experience: Long,
|
||||||
|
maxExperience: Long = -1,
|
||||||
|
enrollTime: LocalDateTime = LocalDateTime.now(),
|
||||||
|
clearTime: LocalDateTime = LocalDateTime.now()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -272,25 +272,26 @@ case class SupportExperiencePoints(
|
||||||
case class SupportExperienceEvent(
|
case class SupportExperienceEvent(
|
||||||
name: String,
|
name: String,
|
||||||
base: Long,
|
base: Long,
|
||||||
shotsMultiplier: Float = 0f,
|
shotsMax: Int = 50,
|
||||||
shotsNatLog: Double = 0f,
|
|
||||||
shotsLimit: Int = 50,
|
|
||||||
shotsCutoff: Int = 50,
|
shotsCutoff: Int = 50,
|
||||||
|
shotsMultiplier: Float = 0f,
|
||||||
amountMultiplier: Float = 0f
|
amountMultiplier: Float = 0f
|
||||||
)
|
)
|
||||||
|
|
||||||
case class CommandExperiencePoints(
|
case class CommandExperiencePoints(
|
||||||
rate: Float,
|
rate: Float,
|
||||||
lluCarrierModifier: Float,
|
lluCarrierModifier: Float,
|
||||||
lluSlayerCreditDuration: Duration,
|
lluSlayerCreditDuration: Duration,
|
||||||
lluSlayerCredit: Long,
|
lluSlayerCredit: Long,
|
||||||
maximumPerSquadSize: Seq[Int],
|
maximumPerSquadSize: Seq[Int],
|
||||||
squadSizeLimitOverflow: Int,
|
squadSizeLimitOverflow: Int,
|
||||||
squadSizeLimitOverflowMultiplier: Float
|
squadSizeLimitOverflowMultiplier: Float
|
||||||
)
|
)
|
||||||
|
|
||||||
case class PromotionSystem(
|
case class PromotionSystem(
|
||||||
active: Boolean,
|
active: Boolean,
|
||||||
maxBattleRank: Int,
|
maxBattleRank: Int,
|
||||||
battleExperiencePointsModifier: Float
|
battleExperiencePointsModifier: Float,
|
||||||
|
supportExperiencePointsModifier: Float,
|
||||||
|
captureExperiencePointsModifier: Float
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue