diff --git a/src/main/java/com/projectswg/holocore/intents/CivilWarPointIntent.java b/src/main/java/com/projectswg/holocore/intents/CivilWarPointIntent.java
new file mode 100644
index 000000000..e64314f1c
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/intents/CivilWarPointIntent.java
@@ -0,0 +1,59 @@
+/***********************************************************************************
+ * Copyright (c) 2018 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator 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. *
+ * *
+ * 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.intents;
+
+import com.projectswg.common.control.Intent;
+import com.projectswg.holocore.resources.objects.player.PlayerObject;
+
+import javax.annotation.Nonnull;
+
+public class CivilWarPointIntent extends Intent {
+
+ private final PlayerObject receiver;
+ private final int points;
+
+ @Nonnull
+ private CivilWarPointIntent(PlayerObject receiver, int points) {
+ this.receiver = receiver;
+ this.points = points;
+ }
+
+ @Nonnull
+ public PlayerObject getReceiver() {
+ return receiver;
+ }
+
+ public int getPoints() {
+ return points;
+ }
+
+ @Nonnull
+ public static void broadcast(PlayerObject receiver, int points) {
+ new CivilWarPointIntent(receiver, points).broadcast();
+ }
+
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/commands/callbacks/QaToolCmdCallback.java b/src/main/java/com/projectswg/holocore/resources/commands/callbacks/QaToolCmdCallback.java
index 76a38ba2d..509b8d6ae 100644
--- a/src/main/java/com/projectswg/holocore/resources/commands/callbacks/QaToolCmdCallback.java
+++ b/src/main/java/com/projectswg/holocore/resources/commands/callbacks/QaToolCmdCallback.java
@@ -32,6 +32,8 @@ import com.projectswg.common.data.location.Terrain;
import com.projectswg.common.data.sui.SuiEvent;
import com.projectswg.common.debug.Log;
+import com.projectswg.holocore.intents.CivilWarPointIntent;
+import com.projectswg.holocore.resources.objects.player.PlayerObject;
import com.projectswg.holocore.scripts.commands.admin.qatool.QaToolDetails;
import com.projectswg.holocore.intents.chat.SystemMessageIntent;
import com.projectswg.holocore.intents.experience.ExperienceIntent;
@@ -102,6 +104,9 @@ public class QaToolCmdCallback implements ICmdCallback {
else
SystemMessageIntent.broadcastPersonal(player, "QATool XP: Expected format: /qatool xp ");
break;
+ case "gcw":
+ grantGcw(player, command[1]);
+ break;
default:
displayMainWindow(player);
break;
@@ -226,6 +231,19 @@ public class QaToolCmdCallback implements ICmdCallback {
}
}
+ private void grantGcw(Player player, String pointsArg) {
+ PlayerObject receiver = player.getCreatureObject().getPlayerObject();
+
+ try {
+ int points = Integer.parseInt(pointsArg);
+ CivilWarPointIntent.broadcast(receiver, points);
+ Log.i("GCW command: %s gave themselves %d GCW points", player.getUsername(), points);
+ } catch (NumberFormatException e) {
+ SystemMessageIntent.broadcastPersonal(player, String.format("GCW command: %s is not a number", pointsArg));
+ }
+
+ }
+
/* Utility Methods */
private void createMessageBox(Player player, String title, String prompt) {
diff --git a/src/main/java/com/projectswg/holocore/services/faction/CivilWarService.java b/src/main/java/com/projectswg/holocore/services/faction/CivilWarService.java
new file mode 100644
index 000000000..b6492a863
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/services/faction/CivilWarService.java
@@ -0,0 +1,406 @@
+/************************************************************************************
+ * Copyright (c) 2015 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator 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. *
+ * *
+ * 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.faction;
+
+import com.projectswg.common.concurrency.PswgScheduledThreadPool;
+import com.projectswg.common.control.Service;
+import com.projectswg.common.data.encodables.oob.ProsePackage;
+import com.projectswg.common.data.encodables.oob.StringId;
+import com.projectswg.common.data.encodables.tangible.PvpFaction;
+import com.projectswg.common.data.encodables.tangible.PvpStatus;
+import com.projectswg.common.debug.Log;
+import com.projectswg.common.network.packets.swg.zone.PlayClientEffectObjectMessage;
+import com.projectswg.common.network.packets.swg.zone.PlayMusicMessage;
+import com.projectswg.holocore.intents.CivilWarPointIntent;
+import com.projectswg.holocore.intents.chat.SystemMessageIntent;
+import com.projectswg.holocore.intents.combat.CreatureKilledIntent;
+import com.projectswg.holocore.intents.object.DestroyObjectIntent;
+import com.projectswg.holocore.intents.object.ObjectCreatedIntent;
+import com.projectswg.holocore.resources.objects.SWGObject;
+import com.projectswg.holocore.resources.objects.creature.CreatureDifficulty;
+import com.projectswg.holocore.resources.objects.creature.CreatureObject;
+import com.projectswg.holocore.resources.objects.player.PlayerObject;
+import com.projectswg.holocore.resources.server_info.SdbLoader;
+import com.projectswg.holocore.resources.server_info.StandardLog;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.DayOfWeek;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneOffset;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+final class CivilWarService extends Service {
+
+ private static final int IMPERIAL_INDEX = 0;
+ private static final int REBEL_INDEX = 1;
+
+ private final Map rankAbilities;
+ private final Set playerObjects;
+ private final PswgScheduledThreadPool threadPool;
+ private final DayOfWeek updateWeekDay;
+ private final LocalTime updateTime;
+ private final ZoneOffset updateOffset;
+ private int rankEpoch;
+
+ CivilWarService() {
+ rankAbilities = new HashMap<>();
+ playerObjects = ConcurrentHashMap.newKeySet();
+ threadPool = new PswgScheduledThreadPool(1, "civil-war-service");
+ // Rank update time is the night between Thursday and Friday at 00:00 UTC
+ updateWeekDay = DayOfWeek.FRIDAY;
+ updateTime = LocalTime.MIDNIGHT;
+ updateOffset = ZoneOffset.UTC;
+
+ loadRankAbilities();
+
+ registerForIntent(CivilWarPointIntent.class, this::handleCivilWarPointIntent);
+ registerForIntent(CreatureKilledIntent.class, this::handleCreatureKilledIntent);
+ registerForIntent(DestroyObjectIntent.class, this::handleDestroyObjectIntent);
+ registerForIntent(ObjectCreatedIntent.class, this::handleObjectCreatedIntent);
+ }
+
+ @Override
+ public boolean initialize() {
+ threadPool.start();
+ scheduleRankUpdate();
+
+ return super.initialize();
+ }
+
+ @Override
+ public boolean terminate() {
+ threadPool.stop(); // To stop more threads from running
+ threadPool.awaitTermination(1000); // Waits for them to complete executing
+
+ return super.terminate();
+ }
+
+ int leftoverPoints(float progress, int points) {
+ return (int) ((progress - 100) / 100 * (float) points);
+ }
+
+ boolean isRankDown(float oldProgress, float newProgress) {
+ return oldProgress + newProgress < 0;
+ }
+
+ int nextUpdateTime(LocalDate now) {
+ LocalDate nextUpdateDate = now.with(updateWeekDay);
+
+ return (int) nextUpdateDate.toEpochSecond(updateTime, updateOffset);
+ }
+
+ boolean isDecayRank(int rank) {
+ return rank >= 7; // Lieutenant and above has decay
+ }
+
+ float rankProgress(float currentProgress, float decay, int currentRank, int points) {
+ float progress = currentProgress - decay;
+
+ progress += (float) points / (currentRank * 100.0f);
+
+ return progress;
+ }
+
+ boolean isFactionEligible(PvpFaction killerFaction, PvpFaction corpseFaction, boolean specialForces) {
+ return killerFaction != PvpFaction.NEUTRAL && corpseFaction != PvpFaction.NEUTRAL && killerFaction != corpseFaction && specialForces;
+ }
+
+ byte makeMultiplier(boolean specialForces, boolean player) {
+ byte multiplier = 1;
+
+ if (specialForces) {
+ multiplier += 1;
+ }
+
+ if (player) {
+ multiplier += 18;
+ }
+
+ return multiplier;
+ }
+
+ int baseForDifficulty(CreatureDifficulty difficulty) {
+ switch (difficulty) {
+ case NORMAL:
+ return 5;
+ case ELITE:
+ return 10;
+ case BOSS:
+ return 15;
+ default:
+ throw new IllegalArgumentException("Unhandled CreatureDifficulty: " + difficulty);
+ }
+ }
+
+ int pointsGranted(int base, byte multiplier) {
+ return base * multiplier;
+ }
+
+ private void loadRankAbilities() {
+ String what = "rank abilities";
+ long startTime = StandardLog.onStartLoad(what);
+
+ try (SdbLoader.SdbResultSet set = SdbLoader.load(new File("serverdata/gcw/abilities.sdb"))) {
+ while (set.next()) {
+ int rank = (int) set.getInt("rank");
+ String imperial = set.getText("imperial");
+ String rebel = set.getText("rebel");
+ String[] abilities = new String[2];
+
+ abilities[IMPERIAL_INDEX] = imperial;
+ abilities[REBEL_INDEX] = rebel;
+
+ rankAbilities.put(rank, abilities);
+ }
+ } catch (IOException e) {
+ Log.e(e);
+ }
+
+ StandardLog.onEndLoad(rankAbilities.size(), what, startTime);
+ }
+
+ private void scheduleRankUpdate() {
+ LocalDate now = LocalDate.now();
+ int nowEpoch = (int) now.toEpochSecond(updateTime, updateOffset);
+ rankEpoch = nextUpdateTime(now); // Is in the future
+ int delay = (rankEpoch - nowEpoch) * 1000; // Must be milliseconds
+
+ threadPool.execute(delay, this::updateRanks);
+ playerObjects.forEach(this::updateTimer);
+ }
+
+ private void updateTimer(PlayerObject playerObject) {
+ playerObject.setGcwNextUpdate(rankEpoch);
+ }
+
+ private void changeRank(PlayerObject playerObject, int newRank) {
+ assert (newRank < 1 || newRank > 12);
+
+ int oldRank = playerObject.getCurrentRank();
+
+ assert (oldRank > 1);
+
+ int highestRank = Math.max(oldRank, newRank);
+ int lowestRank = Math.min(oldRank, newRank);
+ CreatureObject creature = (CreatureObject) playerObject.getParent();
+ PvpFaction faction = creature.getPvpFaction();
+ int abilityIndex = faction == PvpFaction.IMPERIAL ? IMPERIAL_INDEX : REBEL_INDEX;
+
+ playerObject.setCurrentRank(newRank);
+
+ if (oldRank > newRank) {
+ // They've been demoted
+ for (int rank = highestRank; rank >= lowestRank; rank--) {
+ if (!isDecayRank(rank)) {
+ break;
+ }
+
+ String ability = rankAbilities.get(rank)[abilityIndex];
+
+ creature.removeAbility(ability);
+ }
+ } else if (oldRank < newRank) {
+ // They've been promoted
+ for (int rank = lowestRank; rank <= highestRank; rank++) {
+ if (!isDecayRank(rank)) {
+ continue;
+ }
+
+ String ability = rankAbilities.get(rank)[abilityIndex];
+
+ creature.addAbility(ability);
+ }
+ }
+ }
+
+ private void updateRank(PlayerObject playerObject) {
+ int currentRank = playerObject.getCurrentRank();
+ float oldProgress = playerObject.getRankProgress();
+ int points = playerObject.getGcwPoints();
+ float decay = 0;
+
+ if (isDecayRank(currentRank)) {
+ decay = currentRank;
+ }
+
+ float newProgress = rankProgress(oldProgress, decay, currentRank, points);
+
+ if (newProgress >= 100) {
+ int promotion = playerObject.getCurrentRank() + 1;
+
+ changeRank(playerObject, promotion);
+
+ // Leftover points carry over
+ int leftoverPoints = leftoverPoints(newProgress, points);
+
+ // Calculate progress within the new rank using leftover points
+ float nextRankProgress = rankProgress(newProgress, 0, promotion, leftoverPoints);
+
+ playerObject.setRankProgress(nextRankProgress);
+ } else if (newProgress > 0) {
+ // Set their new progress
+ playerObject.setRankProgress(newProgress);
+ } else if (newProgress < 0 && isRankDown(oldProgress, newProgress)) {
+ int demotion = playerObject.getCurrentRank() - 1;
+
+ changeRank(playerObject, demotion);
+
+ // Push their progress backwards in the new rank
+ int leftoverPoints = leftoverPoints(newProgress, points);
+
+ // Calculate progress within the new rank using leftover points
+ float nextRankProgress = 100 - rankProgress(newProgress, 0, demotion, leftoverPoints);
+
+ playerObject.setRankProgress(nextRankProgress);
+ }
+
+ int kills = playerObject.getPvpKills();
+
+ // Reset current kills and points
+ playerObject.setGcwPoints(0);
+ playerObject.setPvpKills(0);
+
+ // Add current stats to lifetime stats
+ playerObject.setLifetimeGcwPoints(playerObject.getLifetimeGcwPoints() + points);
+ playerObject.setLifetimePvpKills(playerObject.getLifetimePvpKills() + kills);
+
+ // TODO award GCW abilities
+ }
+
+ private void updateRanks() {
+ playerObjects.forEach(playerObject -> {
+ if (playerObject.getCurrentRank() > 0)
+ updateRank(playerObject);
+ });
+
+ scheduleRankUpdate(); // Schedule next rank update
+ }
+
+ private void grantPoints(ProsePackage prose, PlayerObject receiver, int points) {
+ receiver.setGcwPoints(receiver.getGcwPoints() + points);
+ SystemMessageIntent.broadcastPersonal(receiver.getOwner(), prose);
+ }
+
+ private void handleCivilWarPointIntent(CivilWarPointIntent cwpi) {
+ int points = cwpi.getPoints();
+ PlayerObject receiver = cwpi.getReceiver();
+ ProsePackage prose = new ProsePackage(new StringId("gcw", "gcw_rank_generic_point_grant"), "DI", points);
+
+ grantPoints(prose, receiver, points);
+ }
+
+ private void handleCreatureKilledIntent(CreatureKilledIntent cki) {
+ CreatureObject corpseCreature = cki.getCorpse();
+ CreatureObject killerCreature = cki.getKiller();
+ PvpFaction killerFaction = killerCreature.getPvpFaction();
+
+ if (!killerCreature.isPlayer()) {
+ return;
+ }
+
+ boolean specialForces = corpseCreature.getPvpStatus() == PvpStatus.SPECIALFORCES;
+
+ if (!isFactionEligible(killerFaction, corpseCreature.getPvpFaction(), specialForces)) {
+ return;
+ }
+
+ PlayerObject killerPlayer = killerCreature.getPlayerObject();
+
+ byte multiplier = makeMultiplier(specialForces, corpseCreature.isPlayer());
+ int base = baseForDifficulty(corpseCreature.getDifficulty());
+ int granted = pointsGranted(base, multiplier);
+ ProsePackage prose;
+
+ if (specialForces) {
+ // Increment kill counter
+ killerPlayer.setPvpKills(killerPlayer.getPvpKills() + 1);
+
+ // Determine which effect and sound to play
+ String effectFile;
+ String soundFile;
+
+ if (killerFaction == PvpFaction.REBEL) {
+ effectFile = "clienteffect/holoemote_rebel.cef";
+ soundFile = "sound/music_themequest_victory_rebel.snd";
+ } else {
+ effectFile = "clienteffect/holoemote_imperial.cef";
+ soundFile = "sound/music_themequest_victory_imperial.snd";
+ }
+
+ // PvP GCW point system message
+ prose = new ProsePackage("StringId", new StringId("gcw", "gcw_rank_pvp_kill_point_grant"), "DI", granted, "TT", corpseCreature
+ .getObjectName());
+
+ // Send visual effect to killer and everyone around
+ killerCreature.sendObserversAndSelf(new PlayClientEffectObjectMessage(effectFile, "head", killerCreature.getObjectId(), ""));
+
+ // Send sound to just to the killer
+ killerCreature.sendSelf(new PlayMusicMessage(0, soundFile, 0, false));
+
+ // TODO planetary control points?
+ } else {
+ // NPC GCW point system message
+ prose = new ProsePackage(new StringId("gcw", "gcw_rank_generic_point_grant"), "DI", granted);
+ }
+
+ // Increment GCW point counter
+ grantPoints(prose, killerPlayer, granted);
+ }
+
+ private void handleDestroyObjectIntent(DestroyObjectIntent doi) {
+ SWGObject object = doi.getObject();
+
+ if (!(object instanceof PlayerObject)) {
+ return;
+ }
+
+ playerObjects.remove(object);
+ }
+
+ private void handleObjectCreatedIntent(ObjectCreatedIntent oci) {
+ SWGObject object = oci.getObject();
+
+ if (!(object instanceof PlayerObject)) {
+ return;
+ }
+
+ PlayerObject playerObject = (PlayerObject) object;
+
+ // Let's make sure the timer's displayed for them
+ updateTimer(playerObject);
+
+ playerObjects.add(playerObject);
+ }
+
+}
diff --git a/src/main/java/com/projectswg/holocore/services/faction/FactionService.java b/src/main/java/com/projectswg/holocore/services/faction/FactionManager.java
similarity index 96%
rename from src/main/java/com/projectswg/holocore/services/faction/FactionService.java
rename to src/main/java/com/projectswg/holocore/services/faction/FactionManager.java
index 8e6337722..e7529c180 100644
--- a/src/main/java/com/projectswg/holocore/services/faction/FactionService.java
+++ b/src/main/java/com/projectswg/holocore/services/faction/FactionManager.java
@@ -32,7 +32,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import com.projectswg.common.concurrency.PswgScheduledThreadPool;
-import com.projectswg.common.control.Service;
+import com.projectswg.common.control.Manager;
import com.projectswg.common.data.encodables.tangible.PvpFaction;
import com.projectswg.common.data.encodables.tangible.PvpFlag;
import com.projectswg.common.data.encodables.tangible.PvpStatus;
@@ -45,15 +45,17 @@ import com.projectswg.holocore.resources.objects.creature.CreatureObject;
import com.projectswg.holocore.resources.objects.tangible.TangibleObject;
import com.projectswg.holocore.resources.player.Player;
-public final class FactionService extends Service {
+public final class FactionManager extends Manager {
private final Map> statusChangers;
private final PswgScheduledThreadPool executor;
- public FactionService() {
+ public FactionManager() {
statusChangers = new ConcurrentHashMap<>();
executor = new PswgScheduledThreadPool(1, "faction-service");
+ addChildService(new CivilWarService());
+
registerForIntent(FactionIntent.class, this::handleFactionIntent);
}
@@ -97,7 +99,13 @@ public final class FactionService extends Service {
if(target.getBaselineType() == BaselineType.CREO && target.getPvpFaction() != PvpFaction.NEUTRAL) {
// We're given rank 1 upon joining a non-neutral faction
- ((CreatureObject) target).setFactionRank((byte) 1);
+ CreatureObject creatureTarget = (CreatureObject) target;
+
+ if (!creatureTarget.isPlayer()) {
+ return;
+ }
+
+ creatureTarget.getPlayerObject().setCurrentRank(1);
}
}
diff --git a/src/main/java/com/projectswg/holocore/services/galaxy/GameManager.java b/src/main/java/com/projectswg/holocore/services/galaxy/GameManager.java
index 72222d059..24df2f667 100644
--- a/src/main/java/com/projectswg/holocore/services/galaxy/GameManager.java
+++ b/src/main/java/com/projectswg/holocore/services/galaxy/GameManager.java
@@ -36,7 +36,7 @@ import com.projectswg.holocore.services.commands.CommandService;
import com.projectswg.holocore.services.commands.EntertainmentService;
import com.projectswg.holocore.services.crafting.CraftingManager;
import com.projectswg.holocore.services.experience.ExperienceManager;
-import com.projectswg.holocore.services.faction.FactionService;
+import com.projectswg.holocore.services.faction.FactionManager;
import com.projectswg.holocore.services.group.GroupService;
import com.projectswg.holocore.services.sui.SuiService;
@@ -49,7 +49,7 @@ public class GameManager extends Manager {
addChildService(new CollectionService());
addChildService(new CollectionBadgeManager());
addChildService(new EnvironmentService());
- addChildService(new FactionService());
+ addChildService(new FactionManager());
addChildService(new GroupService());
addChildService(new SkillModService());
addChildService(new EntertainmentService());
diff --git a/src/test/java/com/projectswg/holocore/services/TestServices.java b/src/test/java/com/projectswg/holocore/services/TestServices.java
index 99c6412bb..346fd3be4 100644
--- a/src/test/java/com/projectswg/holocore/services/TestServices.java
+++ b/src/test/java/com/projectswg/holocore/services/TestServices.java
@@ -27,6 +27,7 @@
***********************************************************************************/
package com.projectswg.holocore.services;
+import com.projectswg.holocore.services.faction.TestCivilWarService;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@@ -38,6 +39,7 @@ import com.projectswg.holocore.services.player.TestPlayer;
@RunWith(Suite.class)
@SuiteClasses({
+ TestCivilWarService.class,
TestRareLootService.class,
TestCrafting.class,
TestPlayer.class,
diff --git a/src/test/java/com/projectswg/holocore/services/faction/TestCivilWarService.java b/src/test/java/com/projectswg/holocore/services/faction/TestCivilWarService.java
new file mode 100644
index 000000000..d91f5c3fe
--- /dev/null
+++ b/src/test/java/com/projectswg/holocore/services/faction/TestCivilWarService.java
@@ -0,0 +1,106 @@
+/************************************************************************************
+ * Copyright (c) 2015 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator 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. *
+ * *
+ * 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.faction;
+
+import com.projectswg.common.data.encodables.tangible.PvpFaction;
+import com.projectswg.holocore.resources.objects.creature.CreatureDifficulty;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.time.LocalDate;
+
+public class TestCivilWarService {
+
+ private final CivilWarService service;
+
+ public TestCivilWarService() {
+ service = new CivilWarService();
+ }
+
+ @Test
+ public void testIsFactionEligible() {
+ Assert.assertTrue(service.isFactionEligible(PvpFaction.REBEL, PvpFaction.IMPERIAL, true));
+ Assert.assertFalse(service.isFactionEligible(PvpFaction.NEUTRAL, PvpFaction.NEUTRAL, false));
+ Assert.assertFalse(service.isFactionEligible(PvpFaction.NEUTRAL, PvpFaction.REBEL, false));
+ Assert.assertFalse(service.isFactionEligible(PvpFaction.NEUTRAL, PvpFaction.IMPERIAL, false));
+ Assert.assertFalse(service.isFactionEligible(PvpFaction.REBEL, PvpFaction.REBEL, true));
+ }
+
+ @Test
+ public void testMakeMultiplier() {
+ Assert.assertEquals(1, service.makeMultiplier(false, false));
+ Assert.assertEquals(2, service.makeMultiplier(true, false));
+ Assert.assertEquals(19, service.makeMultiplier(false, true));
+ Assert.assertEquals(20, service.makeMultiplier(true, true));
+ }
+
+ @Test
+ public void testBaseForDifficulty() {
+ Assert.assertEquals(5, service.baseForDifficulty(CreatureDifficulty.NORMAL));
+ Assert.assertEquals(10, service.baseForDifficulty(CreatureDifficulty.ELITE));
+ Assert.assertEquals(15, service.baseForDifficulty(CreatureDifficulty.BOSS));
+ }
+
+ @Test
+ public void testPointsGranted() {
+ Assert.assertEquals(200, service.pointsGranted(10, (byte) 20));
+ }
+
+ @Test
+ public void testNextUpdateTime() {
+ LocalDate now = LocalDate.of(2018, 2, 19); // It's a Monday
+ LocalDate rankDay = LocalDate.of(2018, 2, 23); // It's a Friday
+ int expectedRankTime = 1519344000; // The Friday that week, at 00:00 UTC (night between thursday and friday)
+
+ Assert.assertEquals(expectedRankTime, service.nextUpdateTime(now));
+ Assert.assertEquals(expectedRankTime, service.nextUpdateTime(rankDay));
+ }
+
+ @Test
+ public void testIsDecayRank() {
+ Assert.assertFalse(service.isDecayRank(6));
+ Assert.assertTrue(service.isDecayRank(7));
+ }
+
+ @Test
+ public void testRankProgress() {
+ Assert.assertEquals(2.86f, service.rankProgress(10.0f, 20.0f, 7, 9000), 1);
+ }
+
+ @Test
+ public void testIsRankDown() {
+ Assert.assertFalse(service.isRankDown(40, 50));
+ Assert.assertTrue(service.isRankDown(3, -10));
+ }
+
+ @Test
+ public void testLeftoverPoints() {
+ Assert.assertEquals(6000, service.leftoverPoints(130, 20000));
+ }
+}