diff --git a/serverdata/player/movement.sdb b/serverdata/player/movement.sdb
index bb8441227..dad0ab42b 100644
--- a/serverdata/player/movement.sdb
+++ b/serverdata/player/movement.sdb
@@ -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
@@ -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
\ No newline at end of file
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/MovementLoader.kt b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/MovementLoader.kt
new file mode 100644
index 000000000..78454cdc0
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/MovementLoader.kt
@@ -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 . *
+ ***********************************************************************************/
+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 = 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,
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/ServerData.kt b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/ServerData.kt
index e89594640..ee7b7f72f 100644
--- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/ServerData.kt
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/ServerData.kt
@@ -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. *
@@ -45,6 +45,7 @@ object ServerData {
*/
val buffs by SoftDataLoaderDelegate(::BuffLoader)
val factions by SoftDataLoaderDelegate(::FactionLoader)
+ val movements by SoftDataLoaderDelegate(::MovementLoader)
/*
* Skill / Collection
diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/BurstRunCmdCallback.kt b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/BurstRunCmdCallback.kt
deleted file mode 100644
index cd40d242b..000000000
--- a/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/BurstRunCmdCallback.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.projectswg.holocore.resources.support.global.commands.callbacks
-
-import com.projectswg.holocore.resources.support.global.commands.ICmdCallback
-import com.projectswg.holocore.resources.support.global.player.Player
-import com.projectswg.holocore.resources.support.objects.swg.SWGObject
-import com.projectswg.holocore.resources.support.objects.swg.creature.MovementModifierIdentifier
-
-class BurstRunCmdCallback : ICmdCallback {
- override fun execute(player: Player, target: SWGObject?, args: String) {
- val creatureObject = player.creatureObject
- creatureObject.setMovementScale(MovementModifierIdentifier.BURST_RUN, 2f, false)
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObject.java b/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObject.java
index 1affe795a..1a1181655 100644
--- a/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObject.java
+++ b/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObject.java
@@ -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. *
@@ -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) {
diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObjectClientServerNP.java b/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObjectClientServerNP.java
index e4118c5cb..cc494fc03 100644
--- a/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObjectClientServerNP.java
+++ b/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObjectClientServerNP.java
@@ -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. *
@@ -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);
}
diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/combat/buffs/BuffService.kt b/src/main/java/com/projectswg/holocore/services/gameplay/combat/buffs/BuffService.kt
index a3a3f0161..5502bd89d 100644
--- a/src/main/java/com/projectswg/holocore/services/gameplay/combat/buffs/BuffService.kt
+++ b/src/main/java/com/projectswg/holocore/services/gameplay/combat/buffs/BuffService.kt
@@ -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
@@ -51,14 +52,7 @@ class BuffService : Service() {
private val coroutineScope = HolocoreCoroutine.childScope()
private val callbackMap: MutableMap = 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()
@@ -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, "")
@@ -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
}
@@ -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) {
@@ -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)
+ }
+ }
+ }
}
diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/combat/buffs/Publish24MovementSystem.kt b/src/main/java/com/projectswg/holocore/services/gameplay/combat/buffs/Publish24MovementSystem.kt
new file mode 100644
index 000000000..ef6a19f2c
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/services/gameplay/combat/buffs/Publish24MovementSystem.kt
@@ -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 . *
+ ***********************************************************************************/
+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? {
+ 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.strongest(): MovementLoader.MovementInfo {
+ return this.maxBy { it.strength }
+}
diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/combat/buffs/RemoveBurstRunBuffCallback.kt b/src/main/java/com/projectswg/holocore/services/gameplay/combat/buffs/RemoveBurstRunBuffCallback.kt
deleted file mode 100644
index 43536c0cb..000000000
--- a/src/main/java/com/projectswg/holocore/services/gameplay/combat/buffs/RemoveBurstRunBuffCallback.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.projectswg.holocore.services.gameplay.combat.buffs
-
-import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject
-import com.projectswg.holocore.resources.support.objects.swg.creature.MovementModifierIdentifier
-
-class RemoveBurstRunBuffCallback : BuffCallback {
- override fun execute(creatureObject: CreatureObject) {
- creatureObject.removeMovementScale(MovementModifierIdentifier.BURST_RUN)
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/projectswg/holocore/services/support/global/commands/CommandExecutionService.java b/src/main/java/com/projectswg/holocore/services/support/global/commands/CommandExecutionService.java
index 93f4e9308..d52d7cc36 100644
--- a/src/main/java/com/projectswg/holocore/services/support/global/commands/CommandExecutionService.java
+++ b/src/main/java/com/projectswg/holocore/services/support/global/commands/CommandExecutionService.java
@@ -1,11 +1,10 @@
/***********************************************************************************
- * Copyright (c) 2024 /// Project SWG /// www.projectswg.com *
+ * Copyright (c) 2025 /// Project SWG /// www.projectswg.com *
* *
- * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * 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 an emulator which will provide a server for players to *
- * continue playing a game similar to the one they used to play. We are basing *
- * it on the final publish of the game prior to end-game events. *
+ * 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. *
* *
@@ -112,7 +111,6 @@ private void registerCallbacks() {
registerScriptCallback("cmdStartLogout", LogoutCmdCallback::new);
registerCppCallback("requestDraftSlots", RequestDraftSlotsCallback::new);
registerScriptCallback("knockdownRecovery", KnockdownRecoveryCmdCallback::new);
- registerScriptCallback("burstRun", BurstRunCmdCallback::new);
registerScriptCallback("cmdMeditate", CmdMeditate::new);
registerScriptCallback("cmdTip", CmdTip::new);
diff --git a/src/test/java/com/projectswg/holocore/headless/buffs.kt b/src/test/java/com/projectswg/holocore/headless/buffs.kt
new file mode 100644
index 000000000..5906e8db4
--- /dev/null
+++ b/src/test/java/com/projectswg/holocore/headless/buffs.kt
@@ -0,0 +1,33 @@
+/***********************************************************************************
+ * 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 . *
+ ***********************************************************************************/
+package com.projectswg.holocore.headless
+
+import java.util.concurrent.TimeUnit
+
+fun ZonedInCharacter.sendSelfBuffCommand(buffCommand: String) {
+ sendCommand(buffCommand)
+ player.waitForNextObjectDelta(player.creatureObject.objectId, 4, 4, 1, TimeUnit.SECONDS) ?: throw IllegalStateException("Failed to receive buff object delta for player")
+}
\ No newline at end of file
diff --git a/src/test/java/com/projectswg/holocore/resources/support/data/server_info/loader/MovementLoaderTest.kt b/src/test/java/com/projectswg/holocore/resources/support/data/server_info/loader/MovementLoaderTest.kt
new file mode 100644
index 000000000..487840870
--- /dev/null
+++ b/src/test/java/com/projectswg/holocore/resources/support/data/server_info/loader/MovementLoaderTest.kt
@@ -0,0 +1,47 @@
+/***********************************************************************************
+ * 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 . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.support.data.server_info.loader
+
+import org.junit.jupiter.api.Test
+
+import org.junit.jupiter.api.Assertions.*
+
+class MovementLoaderTest {
+
+ @Test
+ fun load() {
+ val movement = ServerData.movements.getMovement("testItemBoost2")!!
+
+ assertAll(
+ { assertEquals("testitemboost2", movement.movementId) },
+ { assertEquals(MovementLoader.MovementType.PERMABOOST, movement.type)},
+ { assertEquals(16, movement.strength) },
+ { assertTrue(movement.affectsOnfoot) },
+ { assertFalse(movement.affectsVehicle) },
+ { assertFalse(movement.affectsMount) }
+ )
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/projectswg/holocore/services/gameplay/combat/buffs/BurstRunTest.kt b/src/test/java/com/projectswg/holocore/services/gameplay/combat/buffs/BurstRunTest.kt
index 646777dd0..d8411cd96 100644
--- a/src/test/java/com/projectswg/holocore/services/gameplay/combat/buffs/BurstRunTest.kt
+++ b/src/test/java/com/projectswg/holocore/services/gameplay/combat/buffs/BurstRunTest.kt
@@ -1,39 +1,45 @@
+/***********************************************************************************
+ * 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 . *
+ ***********************************************************************************/
package com.projectswg.holocore.services.gameplay.combat.buffs
-import com.projectswg.holocore.resources.support.global.commands.callbacks.BurstRunCmdCallback
-import com.projectswg.holocore.test.resources.GenericCreatureObject
-import org.junit.jupiter.api.Assertions.*
+import com.projectswg.holocore.headless.HeadlessSWGClient
+import com.projectswg.holocore.headless.sendSelfBuffCommand
+import com.projectswg.holocore.test.runners.AcceptanceTest
+import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
-class BurstRunTest {
+class BurstRunTest : AcceptanceTest() {
@Test
fun `Burst Run increases movement speed when executed`() {
- val creatureObject = createCreatureObjectWithBurstRun()
+ val user = generateUser()
+ val character1 = HeadlessSWGClient.createZonedInCharacter(user.username, user.password, "Charone")
- assertEquals(2f, creatureObject.movementScale)
- }
-
- @Test
- fun `Burst Run decreases movement speed when buff expires`() {
- val creatureObject = createCreatureObjectWithBurstRun()
- val removeBurstRunBuffCallback = RemoveBurstRunBuffCallback()
-
- removeBurstRunBuffCallback.execute(creatureObject)
-
- assertEquals(1f, creatureObject.movementScale)
- }
-
- private fun createCreatureObjectWithBurstRun(): GenericCreatureObject {
- val burstRunCmdCallback = BurstRunCmdCallback()
- val creatureObject = GenericCreatureObject(0)
- val player = creatureObject.owner
-
- if (player != null) {
- burstRunCmdCallback.execute(player, null, "")
+ character1.sendSelfBuffCommand("burstRun")
- } else {
- fail("Broken test setup")
- }
- return creatureObject
+ val creatureObject = character1.player.creatureObject
+ assertEquals(1.75f, creatureObject.movementPercent)
}
}
\ No newline at end of file
diff --git a/src/test/java/com/projectswg/holocore/services/gameplay/combat/buffs/Publish24MovementSystemTest.kt b/src/test/java/com/projectswg/holocore/services/gameplay/combat/buffs/Publish24MovementSystemTest.kt
new file mode 100644
index 000000000..e1b3a5b32
--- /dev/null
+++ b/src/test/java/com/projectswg/holocore/services/gameplay/combat/buffs/Publish24MovementSystemTest.kt
@@ -0,0 +1,121 @@
+/***********************************************************************************
+ * 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 . *
+ ***********************************************************************************/
+package com.projectswg.holocore.services.gameplay.combat.buffs
+
+import com.projectswg.holocore.resources.support.data.server_info.loader.ServerData
+import org.junit.jupiter.api.Test
+
+class Publish24MovementSystemTest {
+ @Test
+ fun rootBeforeSnare() {
+ val testRoot1 = ServerData.movements.getMovement("testRoot1")!!
+ val testWebSnare1 = ServerData.movements.getMovement("testWebSnare1")!!
+ val movementModifiers = listOf(testRoot1, testWebSnare1)
+
+ val selectedModifier = Publish24MovementSystem.selectMovementModifier(movementModifiers)
+
+ assert(selectedModifier == testRoot1)
+ }
+
+ @Test
+ fun rootBeforeBoost() {
+ val testRoot1 = ServerData.movements.getMovement("testRoot1")!!
+ val testMedBoost1 = ServerData.movements.getMovement("testMedBoost1")!!
+ val movementModifiers = listOf(testRoot1, testMedBoost1)
+
+ val selectedModifier = Publish24MovementSystem.selectMovementModifier(movementModifiers)
+
+ assert(selectedModifier == testRoot1)
+ }
+
+ @Test
+ fun rootBeforePermaboost() {
+ val testRoot1 = ServerData.movements.getMovement("testRoot1")!!
+ val testItemBoost1 = ServerData.movements.getMovement("testItemBoost1")!!
+ val movementModifiers = listOf(testRoot1, testItemBoost1)
+
+ val selectedModifier = Publish24MovementSystem.selectMovementModifier(movementModifiers)
+
+ assert(selectedModifier == testRoot1)
+ }
+
+ @Test
+ fun snareBeforeBoost() {
+ val testWebSnare1 = ServerData.movements.getMovement("testWebSnare1")!!
+ val testItemBoost1 = ServerData.movements.getMovement("testItemBoost1")!!
+ val movementModifiers = listOf(testWebSnare1, testItemBoost1)
+
+ val selectedModifier = Publish24MovementSystem.selectMovementModifier(movementModifiers)
+
+ assert(selectedModifier == testWebSnare1)
+ }
+
+ @Test
+ fun strongestSnare() {
+ val testItemSnare1 = ServerData.movements.getMovement("testItemSnare1")!!
+ val testWebSnare1 = ServerData.movements.getMovement("testWebSnare1")!!
+ val testWebSnare2 = ServerData.movements.getMovement("testWebSnare2")!!
+ val movementModifiers = listOf(testItemSnare1, testWebSnare1, testWebSnare2)
+
+ val selectedModifier = Publish24MovementSystem.selectMovementModifier(movementModifiers)
+
+ assert(selectedModifier == testWebSnare2)
+ }
+
+ @Test
+ fun strongestPermasnare() {
+ val testItemSnare1 = ServerData.movements.getMovement("testItemSnare1")!!
+ val testItemSnare2 = ServerData.movements.getMovement("testItemSnare2")!!
+ val movementModifiers = listOf(testItemSnare1, testItemSnare2)
+
+ val selectedModifier = Publish24MovementSystem.selectMovementModifier(movementModifiers)
+
+ assert(selectedModifier == testItemSnare2)
+ }
+
+ @Test
+ fun strongestBoost() {
+ val testMedBoost1 = ServerData.movements.getMovement("testMedBoost1")!!
+ val testMedBoost2 = ServerData.movements.getMovement("testMedBoost2")!!
+ val movementModifiers = listOf(testMedBoost1, testMedBoost2)
+
+ val selectedModifier = Publish24MovementSystem.selectMovementModifier(movementModifiers)
+
+ assert(selectedModifier == testMedBoost2)
+ }
+
+ @Test
+ fun strongestPermaboost() {
+ val testItemBoost1 = ServerData.movements.getMovement("testItemBoost1")!!
+ val testItemBoost2 = ServerData.movements.getMovement("testItemBoost2")!!
+ val movementModifiers = listOf(testItemBoost1, testItemBoost2)
+
+ val selectedModifier = Publish24MovementSystem.selectMovementModifier(movementModifiers)
+
+ assert(selectedModifier == testItemBoost2)
+ }
+
+}
\ No newline at end of file