diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/TransferItemCallback.kt b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/TransferItemCallback.kt index e1531f8bf..2685ead49 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/TransferItemCallback.kt +++ b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/TransferItemCallback.kt @@ -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. * * * @@ -27,6 +26,7 @@ package com.projectswg.holocore.resources.support.global.commands.callbacks import com.projectswg.common.network.packets.swg.zone.PlayMusicMessage +import com.projectswg.holocore.intents.gameplay.combat.BuffIntent import com.projectswg.holocore.intents.gameplay.combat.LootItemIntent import com.projectswg.holocore.intents.support.global.chat.SystemMessageIntent import com.projectswg.holocore.intents.support.global.chat.SystemMessageIntent.Companion.broadcastPersonal @@ -392,12 +392,16 @@ class TransferItemCallback : ICmdCallback { } private fun changeWeapon(actor: CreatureObject, target: SWGObject, equip: Boolean) { + val weaponHinderanceBuffName = "weaponHinderance" // CU permasnare buff. It doesn't reduce speed, but it prevents speed buffs from making the player run faster. + if (equip) { // The equipped weapon must now be set to the target object actor.equippedWeapon = target as WeaponObject + BuffIntent(weaponHinderanceBuffName, actor, actor, false).broadcast() } else { // The equipped weapon must now be set to the default weapon, which happens inside CreatureObject.setEquippedWeapon() actor.equippedWeapon = null + BuffIntent(weaponHinderanceBuffName, actor, actor, true).broadcast() } } } 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 1a1181655..3cbdc43c7 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 @@ -884,6 +884,12 @@ public class CreatureObject extends TangibleObject { } public void setMaxHealth(int maxHealth) { + int currentHealth = getHealth(); + if (currentHealth > maxHealth) { + // Ensure it's not possible to have more health than the max health + setHealth(maxHealth); + } + creo6.setMaxHealth(maxHealth); } 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 5502bd89d..2a46c05ab 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 @@ -270,7 +270,7 @@ class BuffService : Service() { creature.setMovementPercent(0.0) } MovementLoader.MovementType.SNARE, MovementLoader.MovementType.PERMASNARE -> { - creature.setMovementPercent(strength) + creature.setMovementPercent(1.0 - strength) } MovementLoader.MovementType.BOOST, MovementLoader.MovementType.PERMABOOST -> { creature.setMovementPercent(1.0 + strength) diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/player/experience/skills/SkillManager.kt b/src/main/java/com/projectswg/holocore/services/gameplay/player/experience/skills/SkillManager.kt index 0cbc200f5..6bdf6df61 100644 --- a/src/main/java/com/projectswg/holocore/services/gameplay/player/experience/skills/SkillManager.kt +++ b/src/main/java/com/projectswg/holocore/services/gameplay/player/experience/skills/SkillManager.kt @@ -1,11 +1,10 @@ /*********************************************************************************** - * Copyright (c) 2023 /// 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. * * * @@ -26,9 +25,10 @@ ***********************************************************************************/ package com.projectswg.holocore.services.gameplay.player.experience.skills +import com.projectswg.holocore.services.gameplay.player.experience.skills.skillmod.HamSkillModService import com.projectswg.holocore.services.gameplay.player.experience.skills.skillmod.SkillModService import me.joshlarson.jlcommon.control.Manager import me.joshlarson.jlcommon.control.ManagerStructure -@ManagerStructure(children = [SkillModService::class, SkillService::class]) +@ManagerStructure(children = [HamSkillModService::class, SkillModService::class, SkillService::class]) class SkillManager : Manager() diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/player/experience/skills/skillmod/HamSkillModService.kt b/src/main/java/com/projectswg/holocore/services/gameplay/player/experience/skills/skillmod/HamSkillModService.kt new file mode 100644 index 000000000..72b4ec953 --- /dev/null +++ b/src/main/java/com/projectswg/holocore/services/gameplay/player/experience/skills/skillmod/HamSkillModService.kt @@ -0,0 +1,57 @@ +/*********************************************************************************** + * 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.player.experience.skills.skillmod + +import com.projectswg.holocore.intents.gameplay.player.experience.SkillModIntent +import com.projectswg.holocore.resources.support.data.server_info.StandardLog +import me.joshlarson.jlcommon.control.IntentHandler +import me.joshlarson.jlcommon.control.Service + +/** + * This service is responsible for modifying the health, action, and mind (HAM) of creatures based on skill mods. + * An example of a skill mod is a health mod that increases the health of a creature by a percentage (like healthPercent). + */ +class HamSkillModService : Service() { + @IntentHandler + private fun handleSkillModIntent(intent: SkillModIntent) { + val skillModName = intent.skillModName + val adjustModifier = intent.adjustModifier + + when (skillModName) { + "healthPercent" -> handleHealthPercent(intent, adjustModifier) + } + } + + private fun handleHealthPercent(intent: SkillModIntent, adjustModifier: Int) { + for (creature in intent.affectedCreatures) { + val originalMaxHealth = creature.maxHealth + val extraMaxHealth = ((adjustModifier / 100.0) * creature.maxHealth).toInt() // If healthPercent is 10, then mod is 1.1. A creature with 1000 health will then have 1100 health. + creature.maxHealth += extraMaxHealth + + StandardLog.onPlayerTrace(this, creature, "max health increased by $adjustModifier%% $originalMaxHealth -> ${creature.maxHealth}") // %% is used to escape the % character + } + } +} \ No newline at end of file diff --git a/src/test/java/com/projectswg/holocore/headless/buffs.kt b/src/test/java/com/projectswg/holocore/headless/buffs.kt index 5906e8db4..af4adcfea 100644 --- a/src/test/java/com/projectswg/holocore/headless/buffs.kt +++ b/src/test/java/com/projectswg/holocore/headless/buffs.kt @@ -25,9 +25,15 @@ ***********************************************************************************/ package com.projectswg.holocore.headless +import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject 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 + player.waitForNextObjectDelta(player.creatureObject.objectId, 6, 19, 1, TimeUnit.SECONDS) ?: throw IllegalStateException("Failed to receive buff object delta for player") +} + +fun ZonedInCharacter.sendTargetBuffCommand(buffCommand: String, target: CreatureObject) { + sendCommand(buffCommand, target) + player.waitForNextObjectDelta(target.objectId, 6, 19, 1, TimeUnit.SECONDS) ?: throw IllegalStateException("Failed to receive buff object delta for player") +} diff --git a/src/test/java/com/projectswg/holocore/services/gameplay/combat/NutrientInjectionTest.kt b/src/test/java/com/projectswg/holocore/services/gameplay/combat/NutrientInjectionTest.kt new file mode 100644 index 000000000..e5dcf280f --- /dev/null +++ b/src/test/java/com/projectswg/holocore/services/gameplay/combat/NutrientInjectionTest.kt @@ -0,0 +1,73 @@ +/*********************************************************************************** + * 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 + +import com.projectswg.holocore.headless.* +import com.projectswg.holocore.resources.support.global.player.AccessLevel +import com.projectswg.holocore.test.runners.AcceptanceTest +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class NutrientInjectionTest : AcceptanceTest() { + + @Test + fun buffSelf() { + val zonedInCharacter1 = createMasterMedic() + zonedInCharacter1.waitForHealthChange() // Wait for health update for becoming a master medic + val char1OriginalMaxHealth = zonedInCharacter1.player.creatureObject.maxHealth + + zonedInCharacter1.sendSelfBuffCommand("nutrientInjection") + + val char1NewMaxHealth = zonedInCharacter1.player.creatureObject.maxHealth + assertTrue(char1NewMaxHealth > char1OriginalMaxHealth) + } + + @Test + fun buffFriendlyTarget() { + val zonedInCharacter1 = createMasterMedic() + val zonedInCharacter2 = createZonedInCharacter("Chartwo") + val char2OriginalMaxHealth = zonedInCharacter2.player.creatureObject.maxHealth + + zonedInCharacter1.waitUntilAwareOf(zonedInCharacter2.player.creatureObject) + zonedInCharacter1.sendTargetBuffCommand("nutrientInjection", zonedInCharacter2.player.creatureObject) + + val char2NewMaxHealth = zonedInCharacter2.player.creatureObject.maxHealth + assertTrue(char2NewMaxHealth > char2OriginalMaxHealth) + } + + private fun createMasterMedic(): ZonedInCharacter { + val zonedInCharacter1 = createZonedInCharacter("Charone") + zonedInCharacter1.adminGrantSkill("science_medic_master") + return zonedInCharacter1 + } + + private fun createZonedInCharacter(characterName: String): ZonedInCharacter { + val user = generateUser(accessLevel = AccessLevel.DEV) + return HeadlessSWGClient.createZonedInCharacter(user.username, user.password, characterName) + } + +} + diff --git a/src/test/java/com/projectswg/holocore/test/runners/AcceptanceTest.kt b/src/test/java/com/projectswg/holocore/test/runners/AcceptanceTest.kt index a6aea8dd4..37d789df5 100644 --- a/src/test/java/com/projectswg/holocore/test/runners/AcceptanceTest.kt +++ b/src/test/java/com/projectswg/holocore/test/runners/AcceptanceTest.kt @@ -49,6 +49,7 @@ import com.projectswg.holocore.services.gameplay.player.badge.BadgeManager import com.projectswg.holocore.services.gameplay.player.character.TippingService import com.projectswg.holocore.services.gameplay.player.experience.ExperiencePointService import com.projectswg.holocore.services.gameplay.player.experience.skills.SkillService +import com.projectswg.holocore.services.gameplay.player.experience.skills.skillmod.HamSkillModService import com.projectswg.holocore.services.gameplay.player.group.GroupService import com.projectswg.holocore.services.support.global.chat.ChatManager import com.projectswg.holocore.services.support.global.commands.CommandExecutionService @@ -114,6 +115,7 @@ abstract class AcceptanceTest : TestRunnerSynchronousIntents() { registerService(BuffService()) registerService(DuelService()) registerService(FactionFlagService()) + registerService(HamSkillModService()) } @AfterEach