exp for ntu and ntu silo operation restored; message about progress system given more limited scope; adjusted cep for llu carrier kill; kd accumulates by kills and maintains between lives; ifflock does not discriminate rehack faction; no rewards for killing allies, or self

This commit is contained in:
Fate-JH 2023-11-07 16:07:08 -05:00
parent 52deb2b212
commit 7f61206ddd
14 changed files with 257 additions and 125 deletions

View file

@ -362,7 +362,7 @@ game {
# If a player died while carrying an lattice logic unit,
# award the player who is accredited with the kill command experience as long as the time it had been carried longer than this duration.
# Can set to Duration.Inf to never pass.
llu-slayer-credit-duration = 1 minute
llu-slayer-credit-duration = 30 seconds
# If a player died while carrying an lattice logic unit,
# and satisfies the carrying duration,
# award the player who is accredited with the kill command experience.
@ -389,6 +389,11 @@ game {
# Anyone who is currently enrolled in the promotion system remains enrolled during normal game play.
# Relenting on the promotion debt back to battle rank 2 is still possible.
active = true
# This battle rank and any battle ranks of ordinal decrement that allow opt-in to the progression system.
broadcast-battle-rank = 1
# This is the minimum battle rank that can be set as part of the promotion system.
# Used to escape debt and return to normal play.
reset-battle-rank = 2
# This is the maximum battle rank that can be set as part of the promotion system.
max-battle-rank = 13
# How much direct combat contributes to paying back promotion debt.

View file

@ -1063,6 +1063,9 @@ class AvatarActor(
case DeleteAvatar(id) =>
import ctx._
val performDeletion = for {
_ <- ctx.run(query[persistence.Weaponstatsession].filter(_.avatarId == lift(id)).delete)
_ <- ctx.run(query[persistence.Kdasession].filter(_.avatarId == lift(id)).delete)
_ <- ctx.run(query[persistence.Buildingcapture].filter(_.avatarId == lift(id)).delete)
_ <- ctx.run(query[persistence.Shortcut].filter(_.avatarId == lift(id)).delete)
_ <- ctx.run(query[persistence.Implant].filter(_.avatarId == lift(id)).delete)
_ <- ctx.run(query[persistence.Loadout].filter(_.avatarId == lift(id)).delete)
@ -1646,11 +1649,16 @@ class AvatarActor(
if ({
val oldBr = BattleRank.withExperience(avatar.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) {
val resetBr = Config.app.game.promotion.resetBattleRank
if (
Config.app.game.promotion.active &&
oldBr <= Config.app.game.promotion.broadcastBattleRank &&
newBr > resetBr && newBr < Config.app.game.promotion.maxBattleRank + 1
) {
experienceDebt = bep
AvatarActor.saveExperienceDebt(avatar.id, bep, bep)
true
} else if (experienceDebt > 0 && newBr == 2) {
} else if (experienceDebt > 0 && newBr == resetBr) {
experienceDebt = 0
AvatarActor.saveExperienceDebt(avatar.id, exp = 0, bep)
true
@ -1663,7 +1671,6 @@ class AvatarActor(
setCep(0L)
}
restoreBasicCerts()
removeAllImplants()
sessionActor ! SessionActor.CharSaved
sessionActor ! SessionActor.SendResponse(ChatMsg(ChatMessageType.UNK_229, "@AckSuccessSetBattleRank"))
} else if (experienceDebt > 0) {
@ -1674,9 +1681,9 @@ class AvatarActor(
Behaviors.same
case AwardCep(cep) =>
if (experienceDebt > 0L) {
if (experienceDebt == 0L) {
setCep(avatar.cep + cep)
} else {
} else if (cep > 0) {
sessionActor ! SessionActor.SendResponse(ExperienceAddedMessage(0))
}
Behaviors.same
@ -1851,16 +1858,77 @@ class AvatarActor(
}
def performAvatarLogin(avatarId: Long, accountId: Long, replyTo: ActorRef[AvatarLoginResponse]): Unit = {
performAvatarLogin0(avatarId, accountId, replyTo)
// import ctx._
// val result = for {
// //log this login
// _ <- ctx.run(
// query[persistence.Avatar]
// .filter(_.id == lift(avatarId))
// .update(_.lastLogin -> lift(LocalDateTime.now()))
// )
// //log this choice of faction (no empire switching)
// _ <- ctx.run(
// query[persistence.Account]
// .filter(_.id == lift(accountId))
// .update(
// _.lastFactionId -> lift(avatar.faction.id),
// _.avatarLoggedIn -> lift(avatarId)
// )
// )
// //retrieve avatar data
// loadouts <- initializeAllLoadouts()
// implants <- ctx.run(query[persistence.Implant].filter(_.avatarId == lift(avatarId)))
// certs <- ctx.run(query[persistence.Certification].filter(_.avatarId == lift(avatarId)))
// locker <- loadLocker(avatarId)
// friends <- loadFriendList(avatarId)
// ignored <- loadIgnoredList(avatarId)
// shortcuts <- loadShortcuts(avatarId)
// saved <- AvatarActor.loadSavedAvatarData(avatarId)
// debt <- AvatarActor.loadExperienceDebt(avatarId)
// card <- AvatarActor.loadCampaignKdaData(avatarId)
// } yield (loadouts, implants, certs, locker, friends, ignored, shortcuts, saved, debt, card)
// result.onComplete {
// case Success((_loadouts, implants, certs, lockerInv, friendsList, ignoredList, shortcutList, saved, debt, card)) =>
// avatarCopy(
// avatar.copy(
// loadouts = avatar.loadouts.copy(suit = _loadouts),
// certifications =
// certs.map(cert => Certification.withValue(cert.id)).toSet ++ Config.app.game.baseCertifications,
// implants = implants.map(implant => Some(Implant(implant.toImplantDefinition))).padTo(3, None),
// shortcuts = shortcutList,
// locker = lockerInv,
// people = MemberLists(
// friend = friendsList,
// ignored = ignoredList
// ),
// cooldowns = Cooldowns(
// purchase = AvatarActor.buildCooldownsFromClob(saved.purchaseCooldowns, Avatar.purchaseCooldowns, log),
// use = AvatarActor.buildCooldownsFromClob(saved.useCooldowns, Avatar.useCooldowns, log)
// ),
// scorecard = card
// )
// )
// // if we need to start stamina regeneration
// tryRestoreStaminaForSession(stamina = 1).collect { _ => defaultStaminaRegen(initialDelay = 0.5f seconds) }
// experienceDebt = debt
// replyTo ! AvatarLoginResponse(avatar)
// case Failure(e) =>
// log.error(e)("db failure")
// }
}
def performAvatarLogin0(avatarId: Long, accountId: Long, replyTo: ActorRef[AvatarLoginResponse]): Unit = {
import ctx._
val result = for {
//log this login
_ <- ctx.run(
loginTime <- ctx.run(
query[persistence.Avatar]
.filter(_.id == lift(avatarId))
.update(_.lastLogin -> lift(LocalDateTime.now()))
)
//log this choice of faction (no empire switching)
_ <- ctx.run(
loginFaction <- ctx.run(
query[persistence.Account]
.filter(_.id == lift(accountId))
.update(
@ -1868,50 +1936,64 @@ class AvatarActor(
_.avatarLoggedIn -> lift(avatarId)
)
)
//retrieve avatar data
loadouts <- initializeAllLoadouts()
} yield (loginTime, loginFaction)
result.onComplete {
case Success(_) =>
sessionActor ! SessionActor.AvatarLoadingSync(step = 0)
performAvatarLogin1(avatarId, replyTo)
case Failure(e) =>
log.error(e)("db failure")
}
}
def performAvatarLogin1(avatarId: Long, replyTo: ActorRef[AvatarLoginResponse]): Unit = {
import ctx._
val result = for {
//retrieve avatar data for OCDM packet
implants <- ctx.run(query[persistence.Implant].filter(_.avatarId == lift(avatarId)))
certs <- ctx.run(query[persistence.Certification].filter(_.avatarId == lift(avatarId)))
debt <- AvatarActor.loadExperienceDebt(avatarId)
locker <- loadLocker(avatarId)
} yield (certs, implants, locker, debt)
result.onComplete {
case Success((certs, implants, lockerInv, debt)) =>
avatarCopy(
avatar.copy(
certifications =
certs.map(cert => Certification.withValue(cert.id)).toSet ++ Config.app.game.baseCertifications,
implants = implants.map(implant => Some(Implant(implant.toImplantDefinition))).padTo(3, None),
locker = lockerInv
)
)
experienceDebt = debt
// if we need to start stamina regeneration
tryRestoreStaminaForSession(stamina = 1).collect { _ => defaultStaminaRegen(initialDelay = 0.5f seconds) }
sessionActor ! SessionActor.AvatarLoadingSync(step = 1)
replyTo ! AvatarLoginResponse(avatar)
performAvatarLogin2(avatarId, replyTo)
case Failure(e) =>
log.error(e)("db failure")
}
}
//noinspection ScalaUnusedSymbol
def performAvatarLogin2(avatarId: Long, replyTo: ActorRef[AvatarLoginResponse]): Unit = {
val result = for {
//retrieve avatar data for other packets
loadouts <- initializeAllLoadouts()
friends <- loadFriendList(avatarId)
ignored <- loadIgnoredList(avatarId)
shortcuts <- loadShortcuts(avatarId)
saved <- AvatarActor.loadSavedAvatarData(avatarId)
debt <- AvatarActor.loadExperienceDebt(avatarId)
card <- AvatarActor.loadCampaignKdaData(avatarId)
} yield (loadouts, implants, certs, locker, friends, ignored, shortcuts, saved, debt, card)
} yield (loadouts, friends, ignored, shortcuts, saved, card)
result.onComplete {
case Success((_loadouts, implants, certs, lockerInv, friendsList, ignoredList, shortcutList, saved, debt, card)) =>
//shortcuts must have a hotbar option for each implant
// val implantShortcuts = shortcutList.filter {
// case Some(e) => e.purpose == 0
// case None => false
// }
// implants.filterNot { implant =>
// implantShortcuts.exists {
// case Some(a) => a.tile.equals(implant.name)
// case None => false
// }
// }.foreach { c =>
// shortcutList.indexWhere { _.isEmpty } match {
// case -1 => ()
// case index =>
// shortcutList.update(index, Some(AvatarShortcut(2, c.name)))
// }
// }
//
case Success((loadoutList, friendsList, ignoredList, shortcutList, saved, card)) =>
avatarCopy(
avatar.copy(
loadouts = avatar.loadouts.copy(suit = _loadouts),
certifications =
certs.map(cert => Certification.withValue(cert.id)).toSet ++ Config.app.game.baseCertifications,
implants = implants.map(implant => Some(Implant(implant.toImplantDefinition))).padTo(3, None),
loadouts = avatar.loadouts.copy(suit = loadoutList),
shortcuts = shortcutList,
locker = lockerInv,
people = MemberLists(
friend = friendsList,
ignored = ignoredList
),
people = MemberLists(friend = friendsList, ignored = ignoredList),
cooldowns = Cooldowns(
purchase = AvatarActor.buildCooldownsFromClob(saved.purchaseCooldowns, Avatar.purchaseCooldowns, log),
use = AvatarActor.buildCooldownsFromClob(saved.useCooldowns, Avatar.useCooldowns, log)
@ -1919,10 +2001,7 @@ class AvatarActor(
scorecard = card
)
)
// if we need to start stamina regeneration
tryRestoreStaminaForSession(stamina = 1).collect { _ => defaultStaminaRegen(initialDelay = 0.5f seconds) }
experienceDebt = debt
replyTo ! AvatarLoginResponse(avatar)
sessionActor ! SessionActor.AvatarLoadingSync(step = 2)
case Failure(e) =>
log.error(e)("db failure")
}
@ -3046,7 +3125,6 @@ class AvatarActor(
}
player.HistoryAndContributions()
}
zone.actor ! ZoneActor.RewardOurSupporters(playerSource, historyTranscript, killStat, exp)
val target = killStat.info.targetAfter.asInstanceOf[PlayerSource]
val targetMounted = target.seatedIn.map { case (v: VehicleSource, seat) =>
val definition = v.Definition
@ -3077,6 +3155,7 @@ class AvatarActor(
}
if (exp > 0L) {
setBep(avatar.bep + exp, msg)
zone.actor ! ZoneActor.RewardOurSupporters(playerSource, historyTranscript, killStat, exp)
}
}

View file

@ -1282,7 +1282,10 @@ class ChatActor(
false
}
} else if (contents.startsWith("!progress")) {
if (!session.account.gm && BattleRank.withExperience(session.avatar.bep).value < Config.app.game.promotion.maxBattleRank + 1) {
val ourRank = BattleRank.withExperience(session.avatar.bep).value
if (!session.account.gm &&
(ourRank <= Config.app.game.promotion.broadcastBattleRank ||
ourRank > Config.app.game.promotion.resetBattleRank && ourRank < Config.app.game.promotion.maxBattleRank + 1)) {
setBattleRank(dropFirstWord(contents), session, AvatarActor.Progress)
true
} else {
@ -1299,7 +1302,7 @@ class ChatActor(
private def dropFirstWord(str: String): String = {
val noExtraSpaces = str.replaceAll("\\s+", " ").toLowerCase.trim
noExtraSpaces.indexOf({ char: String => char.equals(" ") }) match {
noExtraSpaces.indexOf(" ") match {
case -1 => ""
case beforeFirstBlank => noExtraSpaces.drop(beforeFirstBlank + 1)
}

View file

@ -81,6 +81,8 @@ object SessionActor {
final case class UpdateIgnoredPlayers(msg: FriendsResponse) extends Command
final case class AvatarLoadingSync(step: Int) extends Command
final case object CharSaved extends Command
private[session] case object CharSavedMsg extends Command
@ -255,6 +257,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case SessionActor.SetConnectionState(state) =>
sessionFuncs.connectionState = state
case SessionActor.AvatarLoadingSync(state) =>
sessionFuncs.zoning.spawn.handleAvatarLoadingSync(state)
/* uncommon messages (utility, or once in a while) */
case SessionActor.AvatarAwardMessageBundle(pkts, delay) =>
sessionFuncs.zoning.spawn.performAvatarAwardMessageDelivery(pkts, delay)

View file

@ -3,8 +3,6 @@ package net.psforever.actors.session.support
import akka.actor.typed.scaladsl.adapter._
import akka.actor.{ActorContext, typed}
import net.psforever.objects.avatar.SpecialCarry
import net.psforever.objects.serverobject.llu.CaptureFlag
import net.psforever.packet.game.objectcreate.ConstructorData
import net.psforever.services.Service
import net.psforever.objects.zones.exp
@ -496,25 +494,6 @@ class SessionAvatarHandlers(
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@SVCP_Killed_OnPadOnCreate"))
case _ => ()
}
adversarial.collect {
case attacker
if player.Carrying.contains(SpecialCarry.CaptureFlag) &&
attacker.Faction != player.Faction &&
sessionData
.specialItemSlotGuid
.flatMap { continent.GUID }
.collect {
case llu: CaptureFlag =>
System.currentTimeMillis() - llu.LastCollectionTime > Config.app.game.experience.cep.lluSlayerCreditDuration.toMillis
case _ =>
false
}
.contains(true) =>
continent.AvatarEvents ! AvatarServiceMessage(
attacker.Name,
AvatarAction.AwardCep(attacker.CharId, Config.app.game.experience.cep.lluSlayerCredit)
)
}
adversarial.map {_.Name }.orElse { Some(s"a ${reason.getClass.getSimpleName}") }
}.getOrElse { s"an unfortunate circumstance (probably ${player.Sex.pronounObject} own fault)" }
log.info(s"${player.Name} has died, killed by $cause")

View file

@ -1579,6 +1579,24 @@ class SessionData(
case Some(llu: CaptureFlag) => Some((llu, llu.Carrier))
case _ => None
}) match {
case Some((llu, Some(carrier: Player)))
if carrier.GUID == player.GUID && !player.isAlive =>
player.LastDamage.foreach { damage =>
damage
.interaction
.adversarial
.map { _.attacker }
.collect {
case attacker
if attacker.Faction != player.Faction &&
System.currentTimeMillis() - llu.LastCollectionTime >= Config.app.game.experience.cep.lluSlayerCreditDuration.toMillis =>
continent.AvatarEvents ! AvatarServiceMessage(
attacker.Name,
AvatarAction.AwardCep(attacker.CharId, Config.app.game.experience.cep.lluSlayerCredit)
)
}
}
continent.LocalEvents ! CaptureFlagManager.DropFlag(llu)
case Some((llu, Some(carrier: Player))) if carrier.GUID == player.GUID =>
continent.LocalEvents ! CaptureFlagManager.DropFlag(llu)
case Some((_, Some(carrier: Player))) =>

View file

@ -13,7 +13,7 @@ import net.psforever.objects.inventory.InventoryItem
import net.psforever.objects.serverobject.mount.Seat
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
import net.psforever.objects.vital.{InGameHistory, ReconstructionActivity, SpawningActivity}
import net.psforever.objects.vital.{InGameHistory, IncarnationActivity, ReconstructionActivity, SpawningActivity}
import net.psforever.packet.game.{CampaignStatistic, MailMessage, SessionStatistic}
import scala.collection.mutable
@ -79,7 +79,7 @@ object ZoningOperations {
"High Command",
"Progress versus Promotion",
"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." ++
s"You only have this opportunity while you are less than or equal to battle rank ${Config.app.game.promotion.broadcastBattleRank}." ++
"\n\n" ++
"The normal method of rank advancement comes from the battlefield - fighting enemies, helping allies, and capturing facilities. " ++
"\n\n" ++
@ -91,7 +91,7 @@ object ZoningOperations {
"In addition, you will be ineligible of having your command experience be recognized during this time." ++
"\n\n" ++
"If you wish to continue, set your desired battle rank now - use '!progress' followed by a battle rank index. " ++
"If you accept, but it becomes too much of burden, you may ask to revert to battle rank 2 at any time. " ++
s"If you accept, but it becomes too much of burden, you may ask to revert to battle rank ${Config.app.game.promotion.resetBattleRank} at any time. " ++
"Your normal sense of progress will be restored."
)
)
@ -629,22 +629,7 @@ class ZoningOperations(
ServiceManager.serviceManager ! Lookup("propertyOverrideManager")
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 0)) // disable festive backpacks
sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list
(
FriendsResponse.packetSequence(
MemberAction.InitializeFriendList,
avatar.people.friend
.map { f =>
game.Friend(f.name, AvatarActor.onlineIfNotIgnoredEitherWay(avatar, f.name))
}
) ++
//ignored list (no one ever online)
FriendsResponse.packetSequence(
MemberAction.InitializeIgnoreList,
avatar.people.ignored.map { f => game.Friend(f.name) }
)
).foreach {
sendResponse
}
spawn.initializeFriendsAndIgnoredLists()
//the following subscriptions last until character switch/logout
galaxyService ! Service.Join("galaxy") //for galaxy-wide messages
galaxyService ! Service.Join(s"${avatar.faction}") //for hotspots, etc.
@ -2797,16 +2782,7 @@ class ZoningOperations(
}
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0))
avatar.shortcuts
.zipWithIndex
.collect { case (Some(shortcut), index) =>
sendResponse(CreateShortcutMessage(
guid,
index + 1,
Some(AvatarShortcut.convert(shortcut))
))
}
sendResponse(ChangeShortcutBankMessage(guid, 0))
initializeShortcutsAndBank(guid)
//Favorites lists
avatarActor ! AvatarActor.InitialRefreshLoadouts()
@ -2940,20 +2916,28 @@ class ZoningOperations(
}
.collect { case Some(thing: PlanetSideGameObject with FactionAffinity) => Some(SourceEntry(thing)) }
.flatten
player.LogActivity({
if (player.History.headOption.exists { _.isInstanceOf[SpawningActivity] }) {
ReconstructionActivity(PlayerSource(player), continent.Number, effortBy)
} else {
SpawningActivity(PlayerSource(player), continent.Number, effortBy)
}
})
val lastEntryOpt = player.History.lastOption
if (lastEntryOpt.exists { !_.isInstanceOf[IncarnationActivity] }) {
player.LogActivity({
lastEntryOpt match {
case Some(_) =>
ReconstructionActivity(PlayerSource(player), continent.Number, effortBy)
case None =>
SpawningActivity(PlayerSource(player), continent.Number, effortBy)
}
})
}
}
upstreamMessageCount = 0
setAvatar = true
if (
BattleRank.withExperience(tplayer.avatar.bep).value == 1 &&
Config.app.game.promotion.active &&
!account.gm) {
!account.gm && /* gm's are excluded */
Config.app.game.promotion.active && /* play versus progress system must be active */
BattleRank.withExperience(tplayer.avatar.bep).value <= Config.app.game.promotion.broadcastBattleRank && /* must be below a certain battle rank */
avatar.scorecard.Lives.isEmpty && /* first life after login */
avatar.scorecard.CurrentLife.prior.isEmpty && /* no revives */
player.History.size == 1 /* did nothing but come into existence */
) {
ZoningOperations.reportProgressionSystem(context.self)
}
}
@ -2968,6 +2952,59 @@ class ZoningOperations(
HandleSetCurrentAvatar(tplayer)
}
/**
* Respond to feedback of how the avatar's data is being handled
* in a way that properly reflects the state of the server at the moment.
* @param state indicator for the progress of the avatar
*/
def handleAvatarLoadingSync(state: Int): Unit = {
if (state == 2 && zoneLoaded.contains(true)) {
initializeFriendsAndIgnoredLists()
initializeShortcutsAndBank(player.GUID)
avatarActor ! AvatarActor.RefreshPurchaseTimes()
loginAvatarStatisticsFields()
avatarActor ! AvatarActor.InitialRefreshLoadouts()
}
}
/**
* Set up and dispatch a list of `FriendsResponse` packets related to both formal friends and ignored players.
*/
def initializeFriendsAndIgnoredLists(): Unit = {
(
FriendsResponse.packetSequence(
MemberAction.InitializeFriendList,
avatar.people.friend
.map { f =>
game.Friend(f.name, AvatarActor.onlineIfNotIgnoredEitherWay(avatar, f.name))
}
) ++
//ignored list (no one ever online)
FriendsResponse.packetSequence(
MemberAction.InitializeIgnoreList,
avatar.people.ignored.map { f => game.Friend(f.name) }
)
).foreach {
sendResponse
}
}
/**
* Set up and dispatch a list of `CreateShortcutMessage` packets and a single `ChangeShortcutBankMessage` packet.
*/
def initializeShortcutsAndBank(guid: PlanetSideGUID): Unit = {
avatar.shortcuts
.zipWithIndex
.collect { case (Some(shortcut), index) =>
sendResponse(CreateShortcutMessage(
guid,
index + 1,
Some(AvatarShortcut.convert(shortcut))
))
}
sendResponse(ChangeShortcutBankMessage(guid, 0))
}
/**
* Draw the icon for this deployable object.<br>
* <br>
@ -3247,7 +3284,7 @@ class ZoningOperations(
/**
* Accessible method to switch population of the Character Info window's statistics page
* from whatever it currently js to after each respawn.
* from whatever it currently is to after each respawn.
* At the time of "login", only campaign (total, historical) deaths are reported for convenience.
* At the time of "respawn", all fields - campaign and session - should be reported if applicable.
*/

View file

@ -163,6 +163,7 @@ object ScoreCard {
case PlanetSideEmpire.VS => fields.copy(vs_s = fields.vs_s + 1)
case PlanetSideEmpire.NEUTRAL => fields.copy(ps_s = fields.ps_s + 1)
}
statisticMap.put(objectId, outEntry)
outEntry
case _ =>
val out = Statistic(0, 0, 0, 0, 0, 0, 0, 0)

View file

@ -27,7 +27,7 @@ class IFFLockControl(lock: IFFLock)
.orElse {
case CommonMessages.Use(player, Some(item: SimpleItem))
if item.Definition == GlobalDefinitions.remote_electronics_kit =>
if (lock.Faction != player.Faction && lock.HackedBy.isEmpty) {
if (lock.Faction != player.Faction) {
sender() ! CommonMessages.Progress(
GenericHackables.GetHackSpeed(player, lock),
GenericHackables.FinishHacking(lock, player, 1114636288L),
@ -42,8 +42,7 @@ class IFFLockControl(lock: IFFLock)
} else {
val log = org.log4s.getLogger
log.warn(s"IFF lock is being hacked by ${player.Faction}, but don't know how to handle this state:")
log.warn(s"Lock - Faction=${lock.Faction}, HackedBy=${lock.HackedBy}")
log.warn(s"Player - Faction=${player.Faction}")
log.warn(s"Lock - Faction=${lock.Faction}, HackedBy=${lock.HackedBy.map{_.player}}")
}
case IFFLock.DoorOpenRequest(target, door, replyTo) =>

View file

@ -192,12 +192,11 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
//experience is reported as normal
val deposit: Long =
(Config.app.game.experience.sep.ntuSiloDepositReward.toFloat *
math.floor(amount).toFloat /
math.floor(resourceSilo.MaxNtuCapacitor / resourceSilo.Definition.ChargeTime.toMillis.toFloat)
amount / (resourceSilo.MaxNtuCapacitor * resourceSilo.Definition.ChargeTime.toSeconds.toFloat)
).toLong
vehicle.Zone.AvatarEvents ! AvatarServiceMessage(
owner.name,
AvatarAction.AwardBep(0, deposit, ExperienceType.Normal)
AvatarAction.AwardBep(owner.charId, deposit, ExperienceType.Normal)
)
zones.exp.ToDatabase.reportNtuActivity(owner.charId, resourceSilo.Zone.Number, resourceSilo.Owner.GUID.guid, deposit)
}

View file

@ -133,6 +133,7 @@ trait AntTransferBehavior extends TransferBehavior with NtuStorageBehavior {
ActivatePanelsForChargingEvent(ChargeTransferObject)
}
//noinspection ScalaUnusedSymbol
def WithdrawAndTransmit(vehicle: Vehicle, maxRequested: Float): Any = {
val chargeable = ChargeTransferObject
var chargeToDeposit = Math.min(Math.min(chargeable.NtuCapacitor, 100), maxRequested)
@ -188,7 +189,7 @@ trait AntTransferBehavior extends TransferBehavior with NtuStorageBehavior {
transferTarget match {
case Some(silo: ResourceSilo) =>
scala.math.min(
scala.math.min(silo.MaxNtuCapacitor / silo.Definition.ChargeTime.toMillis.toFloat, chargeable.NtuCapacitor),
scala.math.min(silo.MaxNtuCapacitor / silo.Definition.ChargeTime.toSeconds.toFloat, chargeable.NtuCapacitor),
max
)
case _ =>

View file

@ -50,14 +50,16 @@ trait SupportActivityCausedByAnother {
def amount: Int
}
trait IncarnationActivity extends GeneralActivity
final case class SpawningActivity(src: SourceEntry, zoneNumber: Int, unit: Option[SourceEntry])
extends GeneralActivity
extends IncarnationActivity
final case class ReconstructionActivity(src: SourceEntry, zoneNumber: Int, unit: Option[SourceEntry])
extends GeneralActivity
extends IncarnationActivity
final case class RevivingActivity(target: SourceEntry, user: PlayerSource, amount: Int, equipment: EquipmentDefinition)
extends GeneralActivity with SupportActivityCausedByAnother
extends IncarnationActivity with SupportActivityCausedByAnother
final case class ShieldCharge(amount: Int, cause: Option[SourceEntry])
extends GeneralActivity

View file

@ -315,8 +315,10 @@ object KillAssists {
history: Iterable[InGameActivity]
): Long = {
//base value (the kill experience before modifiers)
val base = Support.baseExperience(victim, history)
if (base > 1) {
lazy val base = Support.baseExperience(victim, history)
if (killer.Faction == victim.Faction || killer.unique == victim.unique) {
0L
} else if (base > 1) {
//include battle rank disparity modifier
val battleRankDisparity = {
import net.psforever.objects.avatar.BattleRank

View file

@ -289,9 +289,11 @@ case class CommandExperiencePoints(
)
case class PromotionSystem(
active: Boolean,
maxBattleRank: Int,
battleExperiencePointsModifier: Float,
supportExperiencePointsModifier: Float,
captureExperiencePointsModifier: Float
active: Boolean,
broadcastBattleRank: Int,
resetBattleRank: Int,
maxBattleRank: Int,
battleExperiencePointsModifier: Float,
supportExperiencePointsModifier: Float,
captureExperiencePointsModifier: Float
)