Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 46 additions & 47 deletions serverdata/player/movement.sdb
Original file line number Diff line number Diff line change
@@ -1,60 +1,59 @@
movement_id type strength affects_onfoot affects_vehicle affects_mount comments
TEXT TEXT FLOAT BOOLEAN BOOLEAN BOOLEAN TEXT
armorHinderanceMove permasnare 0 TRUE FALSE FALSE Movement penalty for wearing uncertified armor
avoidIncapacitation snare 80 TRUE TRUE TRUE
avoidIncapacitation_1 snare 80 TRUE TRUE TRUE
avoidIncapacitation_2 snare 80 TRUE TRUE TRUE
avoidIncapacitation_3 snare 80 TRUE TRUE TRUE
avoidIncapacitation_4 snare 80 TRUE TRUE TRUE
avoidIncapacitation_5 snare 80 TRUE TRUE TRUE
avoidIncapacitation snare 80 TRUE TRUE TRUE
avoidIncapacitation_1 snare 80 TRUE TRUE TRUE
avoidIncapacitation_2 snare 80 TRUE TRUE TRUE
avoidIncapacitation_3 snare 80 TRUE TRUE TRUE
avoidIncapacitation_4 snare 80 TRUE TRUE TRUE
avoidIncapacitation_5 snare 80 TRUE TRUE TRUE
burstRun boost 75 TRUE FALSE FALSE Burst run ability
caltropSnare snare 50 TRUE FALSE TRUE caltrop trap snare
concussionShot root 0 TRUE TRUE TRUE
concussionShot_1 root 0 TRUE TRUE TRUE
crippleAttack snare 25 TRUE FALSE FALSE
crippleAttack_1 snare 45 TRUE FALSE FALSE
cripplingShot snare 25 TRUE FALSE FALSE
cripplingShot_1 snare 45 TRUE FALSE FALSE
concussionShot root 0 TRUE TRUE TRUE
concussionShot_1 root 0 TRUE TRUE TRUE
crippleAttack snare 25 TRUE FALSE FALSE
crippleAttack_1 snare 45 TRUE FALSE FALSE
cripplingShot snare 25 TRUE FALSE FALSE
cripplingShot_1 snare 45 TRUE FALSE FALSE
cryobanSnare snare 80 TRUE FALSE FALSE A snare applied to targets within the radius of a cryoban grenade.
cyberneticLegs permaboost 40 TRUE FALSE FALSE granted by cybernetic legs
cyborgBurstRun boost 90 TRUE FALSE FALSE Cybernetic Burst run ability
drink_accarragm boost 27 TRUE FALSE FALSE
drink_rancoraid boost 5 TRUE FALSE FALSE
electrolyteDrain snare 20 TRUE FALSE FALSE
electrolyteDrain_1 snare 40 TRUE FALSE FALSE
enfeeble root 0 TRUE FALSE FALSE
forceRun_1 boost 100 TRUE FALSE FALSE
forceRun_2 boost 150 TRUE FALSE FALSE
forceSuppression root 0 TRUE TRUE TRUE
forceSuppression_1 root 0 TRUE TRUE TRUE
drink_accarragm boost 27 TRUE FALSE FALSE
drink_rancoraid boost 5 TRUE FALSE FALSE
electrolyteDrain snare 20 TRUE FALSE FALSE
electrolyteDrain_1 snare 40 TRUE FALSE FALSE
enfeeble root 0 TRUE FALSE FALSE
forceRun_1 boost 100 TRUE FALSE FALSE
forceRun_2 boost 150 TRUE FALSE FALSE
forceSuppression root 0 TRUE TRUE TRUE
forceSuppression_1 root 0 TRUE TRUE TRUE
forceThrow snare 50 TRUE FALSE TRUE caltrop trap snare
forceWave snare 25 TRUE FALSE FALSE
forceWave_1 snare 45 TRUE FALSE FALSE
forceWeaken snare 15 TRUE FALSE FALSE
forceWeaken_1 snare 25 TRUE FALSE FALSE
forceWave snare 25 TRUE FALSE FALSE
forceWave_1 snare 45 TRUE FALSE FALSE
forceWeaken snare 15 TRUE FALSE FALSE
forceWeaken_1 snare 25 TRUE FALSE FALSE
invis_forceCloak snare 50 TRUE FALSE FALSE stealth sneak
invis_urbanStealth snare 50 TRUE FALSE FALSE stealth sneak
invis_wildernessStealth snare 50 TRUE FALSE FALSE stealth sneak
kneecapShot snare 15 TRUE FALSE FALSE
kneecapShot_1 snare 25 TRUE FALSE FALSE
paralyze root 0 TRUE FALSE FALSE
paralyze_1 root 0 TRUE FALSE FALSE
petPinAttack root 0 TRUE FALSE FALSE
petPinAttack_1 root 0 TRUE FALSE FALSE
petRunSpeed_1 permaboost 25 TRUE FALSE FALSE
petRunSpeed_2 permaboost 50 TRUE FALSE FALSE
petRunSpeed_3 permaboost 75 TRUE FALSE FALSE
petSnareAttack snare 25 TRUE FALSE FALSE
petSnareAttack_1 snare 45 TRUE FALSE FALSE
restrainingShot snare 15 TRUE FALSE FALSE
restrainingShot_1 snare 25 TRUE FALSE FALSE
startled snare 30 TRUE TRUE TRUE
stasis root 0 TRUE TRUE TRUE
stop root 0 TRUE TRUE TRUE
stoppingShot root 0 TRUE TRUE TRUE
stoppingShot_1 root 0 TRUE TRUE TRUE
stoppingShot_2 root 0 TRUE TRUE TRUE
test_stun stun 0 TRUE FALSE FALSE
kneecapShot snare 15 TRUE FALSE FALSE
kneecapShot_1 snare 25 TRUE FALSE FALSE
paralyze root 0 TRUE FALSE FALSE
paralyze_1 root 0 TRUE FALSE FALSE
petPinAttack root 0 TRUE FALSE FALSE
petPinAttack_1 root 0 TRUE FALSE FALSE
petRunSpeed_1 permaboost 25 TRUE FALSE FALSE
petRunSpeed_2 permaboost 50 TRUE FALSE FALSE
petRunSpeed_3 permaboost 75 TRUE FALSE FALSE
petSnareAttack snare 25 TRUE FALSE FALSE
petSnareAttack_1 snare 45 TRUE FALSE FALSE
restrainingShot snare 15 TRUE FALSE FALSE
restrainingShot_1 snare 25 TRUE FALSE FALSE
startled snare 30 TRUE TRUE TRUE
stasis root 0 TRUE TRUE TRUE
stop root 0 TRUE TRUE TRUE
stoppingShot root 0 TRUE TRUE TRUE
stoppingShot_1 root 0 TRUE TRUE TRUE
stoppingShot_2 root 0 TRUE TRUE TRUE
testColdSnare1 snare 7 TRUE FALSE FALSE TESTING ONLY - Do not use
testColdSnare2 snare 21 TRUE FALSE FALSE TESTING ONLY - Do not use.
testItemBoost1 permaboost 8 TRUE FALSE FALSE TESTING ONLY - Do not use
Expand All @@ -74,7 +73,7 @@ testVehicleBoost boost 300 TRUE TRUE FALSE TESTING ONLY - Do not use
testVehicleSnare snare 75 TRUE TRUE FALSE TESTING ONLY - Do not use
testWebSnare1 snare 14 TRUE FALSE FALSE TESTING ONLY - Do not use
testWebSnare2 snare 28 TRUE FALSE FALSE TESTING ONLY - Do not use
towForemanBurstRun boost 30 TRUE FALSE FALSE
towForemanBurstRun boost 30 TRUE FALSE FALSE
tranqDart snare 66 TRUE FALSE FALSE Scout tranquilizer dart
wavering snare 80 TRUE FALSE FALSE
wavering snare 80 TRUE FALSE FALSE
weaponHinderance permasnare 0 TRUE FALSE FALSE Movement penalty for using a ranged weapon
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/***********************************************************************************
* Copyright (c) 2025 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is an emulation project for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create one or more emulators which will provide servers for *
* players to continue playing a game similar to the one they used to play. *
* *
* This file is part of Holocore. *
* *
* --------------------------------------------------------------------------------*
* *
* Holocore is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* Holocore is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with Holocore. If not, see <http://www.gnu.org/licenses/>. *
***********************************************************************************/
package com.projectswg.holocore.resources.support.data.server_info.loader

import com.projectswg.holocore.resources.support.data.server_info.SdbLoader
import com.projectswg.holocore.resources.support.data.server_info.SdbLoader.SdbResultSet
import java.io.File

class MovementLoader : DataLoader() {

private val movements: MutableMap<String, MovementInfo> = mutableMapOf()

fun getMovement(movementId: String): MovementInfo? {
return movements[movementId.lowercase()]
}

override fun load() {
SdbLoader.load(File("serverdata/player/movement.sdb")).use { set ->
while (set.next()) {
val movementInfo = MovementInfo(set)
movements[movementInfo.movementId] = movementInfo
}
}
}

class MovementInfo(set: SdbResultSet) {
val movementId = set.getText("movement_id").lowercase()
val type = MovementType.valueOf(set.getText("type").uppercase())
val strength = set.getInt("strength").toInt()
val affectsOnfoot = set.getBoolean("affects_onfoot")
val affectsVehicle = set.getBoolean("affects_vehicle")
val affectsMount = set.getBoolean("affects_mount")
}

enum class MovementType {
PERMASNARE,
SNARE,
BOOST,
PERMABOOST,
ROOT,
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/***********************************************************************************
* Copyright (c) 2024 /// Project SWG /// www.projectswg.com *
* Copyright (c) 2025 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is an emulation project for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
Expand Down Expand Up @@ -45,6 +45,7 @@ object ServerData {
*/
val buffs by SoftDataLoaderDelegate(::BuffLoader)
val factions by SoftDataLoaderDelegate(::FactionLoader)
val movements by SoftDataLoaderDelegate(::MovementLoader)

/*
* Skill / Collection
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/***********************************************************************************
* Copyright (c) 2024 /// Project SWG /// www.projectswg.com *
* Copyright (c) 2025 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is an emulation project for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
Expand Down Expand Up @@ -494,7 +494,7 @@ public float getMovementPercent() {
}

/**
* Use this for snares and roots. Do not use it for something like /setSpeed or Burst Run.
* Overrides the movementScale of the creature. Useful for temporary effects, such as snares, roots and speed boost buffs.
* @param movementPercent 1 for full speed, 0.5 for half etc
*/
public void setMovementPercent(double movementPercent) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/***********************************************************************************
* Copyright (c) 2024 /// Project SWG /// www.projectswg.com *
* Copyright (c) 2025 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is an emulation project for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
Expand Down Expand Up @@ -141,8 +141,6 @@ public float getMovementPercent() {
}

public void setMovementPercent(float movementPercent) {
assert(movementPercent >= 0 && movementPercent <= 1); // movementPercent should only be used for snares and roots

this.movementPercent = movementPercent;
sendDelta(4, movementPercent);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import com.projectswg.holocore.intents.support.global.zone.PlayerEventIntent
import com.projectswg.holocore.resources.support.data.server_info.StandardLog
import com.projectswg.holocore.resources.support.data.server_info.loader.BuffLoader.BuffInfo
import com.projectswg.holocore.resources.support.data.server_info.loader.DataLoader.Companion.commands
import com.projectswg.holocore.resources.support.data.server_info.loader.MovementLoader
import com.projectswg.holocore.resources.support.data.server_info.loader.ServerData
import com.projectswg.holocore.resources.support.global.player.PlayerEvent
import com.projectswg.holocore.resources.support.objects.swg.creature.Buff
Expand All @@ -51,14 +52,7 @@ class BuffService : Service() {
private val coroutineScope = HolocoreCoroutine.childScope()
private val callbackMap: MutableMap<String, BuffCallback> = HashMap()
private val buffs = ServerData.buffs

init {
registerCallbacks()
}

private fun registerCallbacks() {
callbackMap["removeBurstRun"] = RemoveBurstRunBuffCallback()
}
private val movements = ServerData.movements

override fun stop(): Boolean {
coroutineScope.cancelAndWait()
Expand Down Expand Up @@ -187,8 +181,8 @@ class BuffService : Service() {
val bufferUsername = if (bufferPlayer == null) "NULL" else bufferPlayer.username
StandardLog.onPlayerTrace(this, receiver, "received buff '%s' from %s/%s; applyTime: %d, buffDuration: %d", buffData.name, bufferUsername, buffer.objectName, applyTime, buffDuration)

checkBuffEffects(buffData, receiver, true)
receiver.addBuff(buffData.crc, Buff(endTime))
checkBuffEffects(buffData, receiver, true)

sendParticleEffect(buffData.particle, receiver, "")

Expand Down Expand Up @@ -221,10 +215,10 @@ class BuffService : Service() {
}

private fun checkBuffEffects(buffData: BuffInfo, creature: CreatureObject, add: Boolean) {
for (i in 0..4) checkBuffEffect(creature, buffData.getEffectName(i), buffData.getEffectValue(i), add)
for (i in 0..4) checkBuffEffect(creature, buffData.getEffectName(i), buffData.getEffectValue(i), buffData.name, add)
}

private fun checkBuffEffect(creature: CreatureObject, effectName: String?, effectValue: Double, add: Boolean) {
private fun checkBuffEffect(creature: CreatureObject, effectName: String?, effectValue: Double, buffName: String, add: Boolean) {
if (effectName.isNullOrEmpty()) {
return
}
Expand All @@ -236,6 +230,11 @@ class BuffService : Service() {
} else {
checkSkillMod(add, effectName, effectValue, creature)
}

if (effectName == "movement" && movements.getMovement(buffName) != null) {
checkMovementMod(creature)
}

}

private fun checkSkillMod(add: Boolean, effectName: String, effectValue: Double, creature: CreatureObject) {
Expand All @@ -253,4 +252,29 @@ class BuffService : Service() {
creature.removeCommand(effectName)
}
}

private fun checkMovementMod(creature: CreatureObject) {
val movementMods = creature.buffs.keys.map { it.string }.mapNotNull { movements.getMovement(it) }
val selectMovementModifier = Publish24MovementSystem.selectMovementModifier(movementMods)

if (selectMovementModifier == null) {
creature.setMovementPercent(1.0)
return
}

val type = selectMovementModifier.type
val strength = selectMovementModifier.strength.toDouble() / 100.0

when (type) {
MovementLoader.MovementType.ROOT -> {
creature.setMovementPercent(0.0)
}
MovementLoader.MovementType.SNARE, MovementLoader.MovementType.PERMASNARE -> {
creature.setMovementPercent(strength)
}
MovementLoader.MovementType.BOOST, MovementLoader.MovementType.PERMABOOST -> {
creature.setMovementPercent(1.0 + strength)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/***********************************************************************************
* Copyright (c) 2024 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is an emulation project for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create one or more emulators which will provide servers for *
* players to continue playing a game similar to the one they used to play. *
* *
* This file is part of Holocore. *
* *
* --------------------------------------------------------------------------------*
* *
* Holocore is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* Holocore is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with Holocore. If not, see <http://www.gnu.org/licenses/>. *
***********************************************************************************/
package com.projectswg.holocore.services.gameplay.combat.buffs

import com.projectswg.holocore.resources.support.data.server_info.loader.MovementLoader


/**
* In Publish 24 (final publish of the Combat Upgrade), the movement modifiers from buffs were changed for balance reasons.
* Movement modifiers no longer stack, and only the strongest modifier is applied.
* Negative effects (roots and snares) also take priority over positive effects (speed boosts).
*/
object Publish24MovementSystem {
fun selectMovementModifier(modifiers: List<MovementLoader.MovementInfo>): MovementLoader.MovementInfo? {
val roots = modifiers.filter { it.type == MovementLoader.MovementType.ROOT }
if (roots.isNotEmpty())
return roots.first()

val snares = modifiers.filter { it.type == MovementLoader.MovementType.SNARE || it.type == MovementLoader.MovementType.PERMASNARE }
if (snares.isNotEmpty())
return snares.strongest()

val boosts = modifiers.filter { it.type == MovementLoader.MovementType.BOOST || it.type == MovementLoader.MovementType.PERMABOOST }
if (boosts.isNotEmpty())
return boosts.strongest()

return null
}
}

private fun List<MovementLoader.MovementInfo>.strongest(): MovementLoader.MovementInfo {
return this.maxBy { it.strength }
}
Loading
Loading