mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-07-16 08:55:18 +00:00
adjustment to calculations for the long life bonus experience and to the lifespan experience limits
This commit is contained in:
parent
5b0ffdb5aa
commit
d86580b21f
2 changed files with 170 additions and 104 deletions
|
|
@ -259,27 +259,27 @@ game {
|
||||||
threat-assessment-of = [
|
threat-assessment-of = [
|
||||||
{
|
{
|
||||||
id = 0
|
id = 0
|
||||||
value = 1.0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id = 1
|
|
||||||
value = 1.25
|
value = 1.25
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id = 1
|
||||||
|
value = 1.5
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id = 2
|
id = 2
|
||||||
value = 2.0
|
value = 2.15
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id = 3
|
id = 3
|
||||||
value = 1.0
|
value = 1.25
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id = 4
|
id = 4
|
||||||
value = 0.75
|
value = 1.0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id = 258
|
id = 258
|
||||||
value = 0
|
value = 10.0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id = 410
|
id = 410
|
||||||
|
|
@ -295,11 +295,11 @@ game {
|
||||||
max-threat-level = [
|
max-threat-level = [
|
||||||
{
|
{
|
||||||
id = 0
|
id = 0
|
||||||
level = 900
|
level = 2000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id = 1
|
id = 1
|
||||||
level = 1200
|
level = 2000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id = 2
|
id = 2
|
||||||
|
|
@ -307,11 +307,11 @@ game {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id = 3
|
id = 3
|
||||||
level = 1000
|
level = 2000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id = 4
|
id = 4
|
||||||
level = 20000
|
level = 900
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id = 258
|
id = 258
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ object Support {
|
||||||
)
|
)
|
||||||
if (shortLifeBonus > TheShortestLifeIsWorth) {
|
if (shortLifeBonus > TheShortestLifeIsWorth) {
|
||||||
val longLifeBonus: Long = {
|
val longLifeBonus: Long = {
|
||||||
val threat = baseExperienceLongLifeFactors(victim, recordOfWornTimes)
|
val threat = baseExperienceLongLifeFactors(victim, recordOfWornTimes, defaultValue = 100f * shortLifeBonus.toFloat)
|
||||||
if (withKills) {
|
if (withKills) {
|
||||||
threat
|
threat
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -74,41 +74,44 @@ object Support {
|
||||||
history: List[InGameActivity],
|
history: List[InGameActivity],
|
||||||
initialExosuit: ExoSuitType.Value = ExoSuitType.Standard
|
initialExosuit: ExoSuitType.Value = ExoSuitType.Standard
|
||||||
): Map[Int, Long] = {
|
): Map[Int, Long] = {
|
||||||
val wornTime: mutable.HashMap[Int, Long] = mutable.HashMap[Int, Long]()
|
val wornTime: mutable.HashMap[Int, Long] = mutable.HashMap[Int, Long]()
|
||||||
var currentSuit: Int = initialExosuit.id
|
var currentSuit: Int = initialExosuit.id
|
||||||
var lastSuitAct: Long = history.head.time
|
var lastActTime: Long = history.head.time
|
||||||
var lastDismountAct: Option[VehicleMountChange] = None
|
|
||||||
var lastMountAct: Option[VehicleMountChange] = None
|
var lastMountAct: Option[VehicleMountChange] = None
|
||||||
//collect history events that encompass changes to exo-suits and to mounting conditions
|
//collect history events that encompass changes to exo-suits and to mounting conditions
|
||||||
history.collect {
|
history.collect {
|
||||||
case suitChange: ExoSuitChange =>
|
case suitChange: ExoSuitChange =>
|
||||||
//use previous vehicle dismount to distinguish between infantry exo-suit use and pilot exo-suit use
|
updateEquippedEntry(
|
||||||
val timePassed = lastDismountAct.map(_.time).getOrElse(lastSuitAct) - suitChange.time
|
currentSuit,
|
||||||
wornTime.get(currentSuit) match {
|
suitChange.time - lastActTime,
|
||||||
case None => wornTime.update(currentSuit, timePassed)
|
wornTime
|
||||||
case Some(oldTime) => wornTime.update(currentSuit, oldTime + timePassed)
|
)
|
||||||
}
|
|
||||||
currentSuit = suitChange.exosuit.id
|
currentSuit = suitChange.exosuit.id
|
||||||
lastSuitAct = suitChange.time
|
lastActTime = suitChange.time
|
||||||
lastDismountAct = None
|
|
||||||
case mount: VehicleMountActivity =>
|
case mount: VehicleMountActivity =>
|
||||||
wornTime.getOrElseUpdate(mount.vehicle.Definition.ObjectId, 0L)
|
updateEquippedEntry(
|
||||||
lastDismountAct = None
|
currentSuit,
|
||||||
|
mount.time - lastActTime,
|
||||||
|
wornTime
|
||||||
|
)
|
||||||
|
lastActTime = mount.time
|
||||||
lastMountAct = Some(mount)
|
lastMountAct = Some(mount)
|
||||||
case dismount: VehicleDismountActivity
|
case dismount: VehicleDismountActivity
|
||||||
if dismount.pairedEvent.isEmpty =>
|
if dismount.pairedEvent.isEmpty =>
|
||||||
//though we have reference to a previous mount activity, only care about the dismount activity's knowledge
|
updateEquippedEntry(
|
||||||
wornTime.getOrElseUpdate(dismount.vehicle.Definition.ObjectId, 0L)
|
dismount.vehicle.Definition.ObjectId,
|
||||||
lastDismountAct = Some(dismount)
|
dismount.time - lastActTime,
|
||||||
|
wornTime
|
||||||
|
)
|
||||||
|
lastActTime = dismount.time
|
||||||
lastMountAct = None
|
lastMountAct = None
|
||||||
case dismount: VehicleDismountActivity =>
|
case dismount: VehicleDismountActivity =>
|
||||||
val timePassed = dismount.time - dismount.pairedEvent.get.time
|
updateEquippedEntry(
|
||||||
val input = dismount.vehicle.Definition.ObjectId
|
dismount.vehicle.Definition.ObjectId,
|
||||||
wornTime.get(input) match {
|
dismount.time - dismount.pairedEvent.get.time,
|
||||||
case None => wornTime.update(input, timePassed)
|
wornTime
|
||||||
case Some(oldTime) => wornTime.update(input, oldTime + timePassed)
|
)
|
||||||
}
|
lastActTime = dismount.time
|
||||||
lastDismountAct = Some(dismount)
|
|
||||||
lastMountAct = None
|
lastMountAct = None
|
||||||
}
|
}
|
||||||
//no more changes; add remaining time from unresolved activity
|
//no more changes; add remaining time from unresolved activity
|
||||||
|
|
@ -116,26 +119,52 @@ object Support {
|
||||||
lastMountAct
|
lastMountAct
|
||||||
.collect { mount =>
|
.collect { mount =>
|
||||||
//dying in a vehicle is a reason to care about the last mount activity
|
//dying in a vehicle is a reason to care about the last mount activity
|
||||||
val input = mount.vehicle.Definition.ObjectId
|
updateEquippedEntry(
|
||||||
val lastMountTime = lastTime - mount.time
|
mount.vehicle.Definition.ObjectId,
|
||||||
wornTime.get(input) match {
|
lastTime - mount.time,
|
||||||
case None => wornTime.update(input, lastMountTime)
|
wornTime
|
||||||
case Some(oldTime) => wornTime.update(input, oldTime + lastMountTime)
|
)
|
||||||
}
|
|
||||||
Some(mount)
|
Some(mount)
|
||||||
}
|
}
|
||||||
.orElse {
|
.orElse {
|
||||||
//dying while on foot
|
//dying while on foot
|
||||||
val lastSuitTime = lastTime - lastDismountAct.map(_.time).getOrElse(lastSuitAct)
|
updateEquippedEntry(
|
||||||
wornTime.get(currentSuit) match {
|
currentSuit,
|
||||||
case None => wornTime.update(currentSuit, lastSuitTime)
|
lastTime - lastActTime,
|
||||||
case Some(oldTime) => wornTime.update(currentSuit, oldTime + lastSuitTime)
|
wornTime
|
||||||
}
|
)
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
wornTime.toMap
|
wornTime.toMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ...
|
||||||
|
* @param equipmentId the equipment
|
||||||
|
* @param timePassed how long it was in use
|
||||||
|
* @param wornTime mapping between equipment (object class ids) and the time that equipment has been used (ms)
|
||||||
|
* @return the length of time the equipment was used
|
||||||
|
*/
|
||||||
|
private def updateEquippedEntry(
|
||||||
|
equipmentId: Int,
|
||||||
|
timePassed: Long,
|
||||||
|
wornTime: mutable.HashMap[Int, Long]
|
||||||
|
): Long = {
|
||||||
|
wornTime
|
||||||
|
.get(equipmentId)
|
||||||
|
.collect {
|
||||||
|
oldTime =>
|
||||||
|
val time = oldTime + timePassed
|
||||||
|
wornTime.update(equipmentId, time)
|
||||||
|
time
|
||||||
|
}
|
||||||
|
.orElse {
|
||||||
|
wornTime.update(equipmentId, timePassed)
|
||||||
|
Some(timePassed)
|
||||||
|
}
|
||||||
|
.get
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the experience value to reflect the value of a player's short term lifespan.
|
* Calculate the experience value to reflect the value of a player's short term lifespan.
|
||||||
* In effect, determine a token experience value for short unproductive lives.
|
* In effect, determine a token experience value for short unproductive lives.
|
||||||
|
|
@ -200,76 +229,113 @@ object Support {
|
||||||
*/
|
*/
|
||||||
private def baseExperienceLongLifeFactors(
|
private def baseExperienceLongLifeFactors(
|
||||||
player: PlayerSource,
|
player: PlayerSource,
|
||||||
recordOfWornTimes: Map[Int, Long]
|
recordOfWornTimes: Map[Int, Long],
|
||||||
|
defaultValue: Float
|
||||||
): Long = {
|
): Long = {
|
||||||
val bep = Config.app.game.experience.bep
|
//awarded values for a target's lifespan based on the distribution of their tactical choices
|
||||||
//awarded value for a target's lifespan based on the distribution of their tactical choices
|
val individualThreatEstimates: Map[Int, Float] = calculateThreatEstimatesPerEntry(recordOfWornTimes)
|
||||||
val threatEstimate = (recordOfWornTimes.foldLeft(0L) {
|
val totalThreatEstimate: Float = individualThreatEstimates.values.sum
|
||||||
case (sum, (key, amount)) =>
|
val maxThreatCapacity: Float = {
|
||||||
if (key > 10) sum + amount
|
val (exosuitTimes, otherTimes) = recordOfWornTimes.partition(_._1 < 10)
|
||||||
else sum + (amount * bep.lifeSpan.threatAssessmentOf.find { case ThreatAssessment(a, _) => a == key }.map(_.value).getOrElse(1.0f)).toLong
|
calculateMaxThreatCapacityPerEntry(
|
||||||
} * bep.lifeSpan.lifeSpanThreatRate).toLong
|
(if (exosuitTimes.values.sum > otherTimes.values.sum) {
|
||||||
//maximum award for a target's lifespan based on the greatest potential of their tactical choices
|
individualThreatEstimates.filter(_._1 < 10)
|
||||||
val maxThreatLevel : Long = estimateMaxThreatLevel(
|
} else {
|
||||||
recordOfWornTimes,
|
individualThreatEstimates.filter(_._1 > 10)
|
||||||
recordOfWornTimes.maxBy { case (_, value) => value }._1
|
}).maxBy(_._2)._1,
|
||||||
)
|
defaultValue
|
||||||
|
)
|
||||||
|
}
|
||||||
//menace modifier -> min = kills, max = 8 x kills
|
//menace modifier -> min = kills, max = 8 x kills
|
||||||
val menace = (player.progress.kills.size.toFloat * (1f + Support.calculateMenace(player).toFloat)).toLong
|
val menace = (player.progress.kills.size.toFloat * (1f + Support.calculateMenace(player).toFloat)).toLong
|
||||||
//cap
|
//last kill experience
|
||||||
math.min(threatEstimate + menace, maxThreatLevel)
|
val lastKillExperience = player.progress.kills
|
||||||
|
.lastOption
|
||||||
|
.collect { kill =>
|
||||||
|
val reduce = ((System.currentTimeMillis() - kill.time.toDate.getTime).toFloat * 0.001f).toLong
|
||||||
|
math.max(0L, kill.experienceEarned - reduce)
|
||||||
|
}
|
||||||
|
.getOrElse(0L)
|
||||||
|
//cap lifespan then add extra
|
||||||
|
math.min(totalThreatEstimate, maxThreatCapacity).toLong + menace + lastKillExperience
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the reward available based on a tactical option by id.
|
||||||
|
* @param recordOfWornTimes between equipment (object class ids) and the time that equipment has been used (ms)
|
||||||
|
* @return value of the equipment
|
||||||
|
*/
|
||||||
|
private def calculateThreatEstimatesPerEntry(recordOfWornTimes: Map[Int, Long]): Map[Int, Float] = {
|
||||||
|
recordOfWornTimes.map {
|
||||||
|
case (key, amount) => (key, amount * calculateThreatEstimatesPerEntry(key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the reward available based on a tactical option by id.
|
||||||
|
* If not listed in a previous table of values,
|
||||||
|
* obtain the definition associated with the equipment id and test use the mass of the entity.
|
||||||
|
* The default value is 0.
|
||||||
|
* @param key equipment id used to collect the ceiling value
|
||||||
|
* @return value of the equipment
|
||||||
|
* @see `Config.app.game.experience.bep.threatAssessmentOf`
|
||||||
|
* @see `VitalityDefinition.mass`
|
||||||
|
*/
|
||||||
|
private def calculateThreatEstimatesPerEntry(key: Int): Float = {
|
||||||
|
Config.app.game.experience.bep.lifeSpan.threatAssessmentOf
|
||||||
|
.find { case ThreatAssessment(a, _) => a == key }
|
||||||
|
.map(_.value)
|
||||||
|
.getOrElse {
|
||||||
|
getDefinitionById(key)
|
||||||
|
.map(o => 2f + math.log10(o.mass.toDouble).toFloat)
|
||||||
|
.getOrElse(0f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the maximum possible reward available based on tactical options.
|
* Calculate the maximum possible reward available based on tactical options.
|
||||||
* @param recordOfWornTimes between equipment (object class ids) and the time that equipment has been used (ms)
|
* If not listed in a previous table of values,
|
||||||
* @param testKey equipment id used to estimate one sample for the ceiling value
|
* obtain the definition associated with the equipment id and test use the maximum health of the entity.
|
||||||
* @param defaultThreatLevel what to use for an unresolved ceiling value;
|
* @param key equipment id used to estimate one sample for the ceiling value
|
||||||
* defaults to 0
|
* @param defaultValue what to use for an unresolved ceiling value;
|
||||||
* @return maximum value of the kill in what the game called "battle experience points"
|
* defaults to 0
|
||||||
|
* @return maximum value for this equipment
|
||||||
|
* @see `Config.app.game.experience.bep.maxThreatLevel`
|
||||||
|
* @see `VitalityDefinition.MaxHealth`
|
||||||
*/
|
*/
|
||||||
private def estimateMaxThreatLevel(
|
private def calculateMaxThreatCapacityPerEntry(
|
||||||
recordOfWornTimes: Map[Int, Long],
|
key: Int,
|
||||||
testKey: Int,
|
defaultValue: Float
|
||||||
defaultThreatLevel: Long = 0L
|
): Float = {
|
||||||
): Long = {
|
Config.app.game.experience.bep.lifeSpan.maxThreatLevel
|
||||||
val exoSuitMaxThreatId = recordOfWornTimes
|
.find { case ThreatLevel(a, _) => a == key }
|
||||||
.filter { case (key, _) => key < 10 }
|
.map(_.level.toFloat)
|
||||||
.maxBy { case (_, value) => value }
|
.getOrElse {
|
||||||
._1
|
getDefinitionById(key)
|
||||||
val estimatedExosuitThreatLevel = estimateMaxThreatLevelBasedOnKey(exoSuitMaxThreatId, defaultThreatLevel)
|
.map(_.MaxHealth.toFloat * 1.2f)
|
||||||
math.max(
|
.getOrElse(defaultValue)
|
||||||
estimateMaxThreatLevelBasedOnKey(testKey, estimatedExosuitThreatLevel),
|
}
|
||||||
estimatedExosuitThreatLevel
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the maximum possible reward available based on a tactical option by id.
|
* ...
|
||||||
* @param refId equipment id used to collect the ceiling value
|
* @param key equipment id
|
||||||
* @param defaultThreatLevel what to use for an unresolved ceiling value
|
* @return the definition if the definition can be found;
|
||||||
* @return maximum value of the kill in what the game called "battle experience points"
|
* `None`, otherwise
|
||||||
* @see `Config.app.game.experience.bep.maxThreatLevel`
|
|
||||||
* @see `DefinitionUtil.idToDefinition`
|
* @see `DefinitionUtil.idToDefinition`
|
||||||
* @see `VitalityDefinition.MaxHealth`
|
* @see `GlobalDefinitions`
|
||||||
|
* @see `VitalityDefinition`
|
||||||
*/
|
*/
|
||||||
private def estimateMaxThreatLevelBasedOnKey(
|
private def getDefinitionById(key: Int): Option[VitalityDefinition] = {
|
||||||
refId: Int,
|
try {
|
||||||
defaultThreatLevel: Long
|
DefinitionUtil.idToDefinition(key) match {
|
||||||
): Long = {
|
case o: VitalityDefinition => Some(o)
|
||||||
Config.app.game.experience.bep.lifeSpan.maxThreatLevel
|
case _ => None
|
||||||
.find { case ThreatLevel(key, _) => key == refId }
|
}
|
||||||
.map(_.level)
|
} catch {
|
||||||
.getOrElse(
|
case _: Exception => None
|
||||||
try {
|
}
|
||||||
DefinitionUtil.idToDefinition(refId) match {
|
|
||||||
case o: VitalityDefinition => (o.MaxHealth * 1.5f).toLong
|
|
||||||
case _ => defaultThreatLevel
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
case _: Exception => defaultThreatLevel
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue