Merge pull request #1491 from madsboddum/feature/quests/go-to-location

Added support for quest.task.ground.go_to_location
This commit is contained in:
Josh
2024-01-13 19:56:40 -06:00
committed by GitHub
3 changed files with 189 additions and 9 deletions
@@ -1,5 +1,5 @@
/***********************************************************************************
* Copyright (c) 2023 /// Project SWG /// www.projectswg.com *
* Copyright (c) 2024 /// 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. *
@@ -168,6 +168,37 @@ public class QuestLoader extends DataLoader {
if (columns.contains("music_on_failure")) {
musicOnFailure = set.getText("music_on_failure");
}
boolean createWaypoint = false;
if (columns.contains("create_waypoint")) {
createWaypoint = set.getBoolean("create_waypoint");
}
String planetName = null;
if (columns.contains("planet_name")) {
planetName = set.getText("planet_name");
}
double locationX = 0;
if (columns.contains("location_x")) {
locationX = set.getReal("location_x");
}
double locationY = 0;
if (columns.contains("location_y")) {
locationY = set.getReal("location_y");
}
double locationZ = 0;
if (columns.contains("location_z")) {
locationZ = set.getReal("location_z");
}
String waypointName = null;
if (columns.contains("waypoint_name")) {
waypointName = set.getText("waypoint_name");
}
double radius = 0;
if (columns.contains("radius")) {
String radiusText = set.getText("radius"); // Is seen as empty string if not set
if (!radiusText.isBlank()) {
radius = Double.parseDouble(radiusText);
}
}
QuestTaskInfo questTaskInfo = new QuestTaskInfo();
@@ -208,6 +239,13 @@ public class QuestLoader extends DataLoader {
questTaskInfo.setMusicOnActivate(musicOnActivate);
questTaskInfo.setMusicOnComplete(musicOnComplete);
questTaskInfo.setMusicOnFailure(musicOnFailure);
questTaskInfo.setCreateWaypoint(createWaypoint);
questTaskInfo.setPlanetName(planetName);
questTaskInfo.setLocationX(locationX);
questTaskInfo.setLocationY(locationY);
questTaskInfo.setLocationZ(locationZ);
questTaskInfo.setWaypointName(waypointName);
questTaskInfo.setRadius(radius);
questTaskInfos.add(questTaskInfo);
}
@@ -325,6 +363,13 @@ public class QuestLoader extends DataLoader {
private String musicOnActivate;
private String musicOnComplete;
private String musicOnFailure;
private boolean createWaypoint;
private String planetName;
private double locationX;
private double locationY;
private double locationZ;
private String waypointName;
private double radius;
private QuestTaskInfo() {
nextTasksOnComplete = new ArrayList<>();
@@ -571,6 +616,62 @@ public class QuestLoader extends DataLoader {
this.musicOnFailure = musicOnFailure;
}
public boolean isCreateWaypoint() {
return createWaypoint;
}
public void setCreateWaypoint(boolean createWaypoint) {
this.createWaypoint = createWaypoint;
}
public String getPlanetName() {
return planetName;
}
public void setPlanetName(String planetName) {
this.planetName = planetName;
}
public double getLocationX() {
return locationX;
}
public void setLocationX(double locationX) {
this.locationX = locationX;
}
public double getLocationY() {
return locationY;
}
public void setLocationY(double locationY) {
this.locationY = locationY;
}
public double getLocationZ() {
return locationZ;
}
public void setLocationZ(double locationZ) {
this.locationZ = locationZ;
}
public String getWaypointName() {
return waypointName;
}
public void setWaypointName(String waypointName) {
this.waypointName = waypointName;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
@Override
public String toString() {
return "QuestTaskInfo{" + "index=" + index + ", type='" + type + '\'' + ", name='" + name + '\'' + '}';
@@ -1,5 +1,5 @@
/***********************************************************************************
* Copyright (c) 2023 /// Project SWG /// www.projectswg.com *
* Copyright (c) 2024 /// 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. *
@@ -30,6 +30,9 @@ import com.projectswg.common.data.CRC
import com.projectswg.common.data.encodables.oob.OutOfBandPackage
import com.projectswg.common.data.encodables.oob.ProsePackage
import com.projectswg.common.data.encodables.oob.StringId
import com.projectswg.common.data.encodables.oob.waypoint.WaypointColor
import com.projectswg.common.data.location.Location
import com.projectswg.common.data.location.Terrain
import com.projectswg.common.data.swgfile.ClientFactory
import com.projectswg.common.network.packets.swg.zone.CommPlayerMessage
import com.projectswg.common.network.packets.swg.zone.PlayMusicMessage
@@ -44,7 +47,10 @@ import com.projectswg.holocore.intents.gameplay.player.quest.CompleteQuestIntent
import com.projectswg.holocore.intents.gameplay.player.quest.GrantQuestIntent
import com.projectswg.holocore.intents.gameplay.player.quest.GrantQuestIntent.Companion.broadcast
import com.projectswg.holocore.intents.support.global.chat.SystemMessageIntent
import com.projectswg.holocore.intents.support.global.zone.PlayerTransformedIntent
import com.projectswg.holocore.intents.support.objects.swg.DestroyObjectIntent
import com.projectswg.holocore.intents.support.objects.swg.ObjectCreatedIntent
import com.projectswg.holocore.intents.support.objects.swg.ObjectTeleportIntent
import com.projectswg.holocore.resources.support.data.server_info.StandardLog
import com.projectswg.holocore.resources.support.data.server_info.loader.QuestLoader.QuestTaskInfo
import com.projectswg.holocore.resources.support.data.server_info.loader.ServerData
@@ -56,6 +62,7 @@ import com.projectswg.holocore.resources.support.objects.StaticItemCreator.creat
import com.projectswg.holocore.resources.support.objects.swg.SWGObject
import com.projectswg.holocore.resources.support.objects.swg.custom.AIObject
import com.projectswg.holocore.resources.support.objects.swg.player.PlayerObject
import com.projectswg.holocore.resources.support.objects.swg.waypoint.WaypointObject
import com.projectswg.holocore.resources.support.random.Die
import com.projectswg.holocore.resources.support.random.RandomDie
import me.joshlarson.jlcommon.concurrency.ScheduledThreadPool
@@ -126,12 +133,9 @@ class QuestService(private val destroyMultiAndLootDie: Die = RandomDie()) : Serv
val killer = intent.killer
val owner = killer.owner ?: return
val playerObject = killer.playerObject
val entries = playerObject.quests.entries
for ((key, quest) in entries) {
if (quest.isComplete) {
continue
}
val questName = key.string
val incompleteQuests = playerObject.quests.entries.filter { !it.value.isComplete }.map { it.key }
for (incompleteQuest in incompleteQuests) {
val questName = incompleteQuest.string
val activeTaskListInfos = getActiveTaskInfos(questName, playerObject)
for (activeTaskListInfo in activeTaskListInfos) {
val type = activeTaskListInfo.type
@@ -142,6 +146,44 @@ class QuestService(private val destroyMultiAndLootDie: Die = RandomDie()) : Serv
}
}
}
@IntentHandler
private fun handlePlayerTransformedIntent(intent: PlayerTransformedIntent) {
val player = intent.player.ownerShallow ?: return
handlePlayerChangeLocation(player, intent.newLocation)
}
@IntentHandler
private fun handleObjectTeleportIntent(intent: ObjectTeleportIntent) {
val player = intent.`object`.ownerShallow ?: return
handlePlayerChangeLocation(player, intent.newLocation)
}
private fun handlePlayerChangeLocation(player: Player, newLocation: Location) {
val playerObject = player.playerObject
val incompleteQuests = playerObject.quests.entries.filter { !it.value.isComplete }.map { it.key }
for (incompleteQuest in incompleteQuests) {
val questName = incompleteQuest.string
val activeTaskListInfos = getActiveTaskInfos(questName, playerObject)
for (activeTaskListInfo in activeTaskListInfos) {
val type = activeTaskListInfo.type
if (type == "quest.task.ground.go_to_location") {
val terrain = Terrain.getTerrainFromName(activeTaskListInfo.planetName)
val radius = activeTaskListInfo.radius
if (newLocation.isWithinDistance(terrain, activeTaskListInfo.locationX, activeTaskListInfo.locationY, activeTaskListInfo.locationZ, radius)) {
StandardLog.onPlayerTrace(this, player, "arrived at location for task %d of quest %s", activeTaskListInfo.index, questName)
completeTask(questName, player, activeTaskListInfo)
player.sendPacket(PlayMusicMessage(0, "sound/ui_objective_reached.snd", 1, false))
playerObject.waypoints.values.find { it.name == activeTaskListInfo.waypointName }?.let { // if you bothered with renaming the waypoint, you get to keep it
playerObject.removeWaypoint(it.objectId)
DestroyObjectIntent(it).broadcast()
}
}
}
}
}
}
private fun handleKillDestroyMultiAndLoot(activeTaskListInfo: QuestTaskInfo, questName: String, owner: Player, corpse: AIObject) {
if (!isKillPartOfTask(activeTaskListInfo, corpse)) {
@@ -297,9 +339,26 @@ class QuestService(private val destroyMultiAndLootDie: Die = RandomDie()) : Serv
"quest.task.ground.destroy_multi_and_loot" -> handleDestroyMultiAndLoot(player, questName, currentTask)
"quest.task.ground.reward" -> handleReward(player, questName, playerObject, currentTask)
"quest.task.ground.nothing" -> handleNothing(player, questName, currentTask)
"quest.task.ground.go_to_location" -> handleGoToLocation(player, currentTask)
}
}
private fun handleGoToLocation(player: Player, currentTask: QuestTaskInfo) {
if (currentTask.isCreateWaypoint) {
createQuestWaypoint(currentTask, player)
}
}
private fun createQuestWaypoint(currentTask: QuestTaskInfo, player: Player) {
val waypoint = ObjectCreator.createObjectFromTemplate("object/waypoint/shared_waypoint.iff") as WaypointObject
waypoint.setPosition(Terrain.getTerrainFromName(currentTask.planetName), currentTask.locationX, currentTask.locationY, currentTask.locationZ)
waypoint.color = WaypointColor.YELLOW
waypoint.name = currentTask.waypointName
waypoint.isActive = true
player.playerObject.addWaypoint(waypoint)
ObjectCreatedIntent(waypoint).broadcast()
}
private fun completeTask(questName: String, player: Player, currentTask: QuestTaskInfo) {
val playerObject = player.getPlayerObject()
playerObject.removeActiveQuestTask(questName, currentTask.index)
@@ -1,5 +1,5 @@
/***********************************************************************************
* Copyright (c) 2023 /// Project SWG /// www.projectswg.com *
* Copyright (c) 2024 /// 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. *
@@ -27,6 +27,8 @@
package com.projectswg.holocore.services.gameplay.player.quest
import com.projectswg.common.data.location.Location
import com.projectswg.common.data.location.Location.LocationBuilder
import com.projectswg.common.data.location.Terrain
import com.projectswg.common.network.packets.swg.login.creation.ClientCreateCharacter
import com.projectswg.common.network.packets.swg.zone.CommPlayerMessage
import com.projectswg.common.network.packets.swg.zone.object_controller.quest.QuestCompletedMessage
@@ -206,6 +208,24 @@ class QuestTaskTypeTest : TestRunnerSynchronousIntents() {
assertNotNull(questTaskTimerData, "Quest task time should have been sent")
}
@Test
@DisplayName("quest.task.ground.go_to_location")
fun goToLocation() {
val player = createPlayer()
GrantQuestIntent.broadcast(player, "quest/test_go_to_location")
val desiredLocation = LocationBuilder()
.setTerrain(Terrain.TATOOINE)
.setX(-1200.0)
.setY(0.0)
.setZ(-3600.0)
.build()
player.creatureObject.moveToLocation(desiredLocation)
val questCompletedMessage = player.waitForNextPacket(QuestCompletedMessage::class.java)
assertNotNull(questCompletedMessage, "Quest not completed in time")
}
private class CharacterSnapshot(private val player: GenericPlayer) { // Helper class to snapshot a character's state
val xp = player.playerObject.getExperiencePoints("dance")
val rebelFactionPoints = player.playerObject.getFactionPoints()["rebel"] ?: 0