Compare commits

...

10 Commits

Author SHA1 Message Date
FatBoy 943a4c8926 questing failsafes 2025-01-19 19:34:50 -06:00
FatBoy e257f0a591 questing failsafes 2025-01-19 16:02:54 -06:00
FatBoy 76bb31be84 questing failsafes 2025-01-19 15:51:44 -06:00
FatBoy 15bea0df04 questing failsafes 2025-01-19 15:41:02 -06:00
FatBoy 7f3d37e4f7 Quest System Completed 2025-01-18 21:00:55 -06:00
FatBoy 4c49575bc7 Quest System Completed 2025-01-18 20:56:20 -06:00
FatBoy a3ffd53f53 Quest System Completed 2025-01-18 20:53:34 -06:00
FatBoy e6e1cab715 Quest System Progression 2025-01-18 20:21:21 -06:00
FatBoy 0b5c3c7c9b Quest System 2025-01-18 20:12:50 -06:00
FatBoy da42e2baf4 Quest System 2025-01-18 20:08:47 -06:00
9 changed files with 227 additions and 14 deletions
+131
View File
@@ -0,0 +1,131 @@
package engine.QuestSystem;
import engine.InterestManagement.WorldGrid;
import engine.gameManager.ChatManager;
import engine.net.client.msg.ErrorPopupMsg;
import engine.objects.*;
import engine.server.MBServerStatics;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Random;
public class QuestManager {
public static HashMap<PlayerCharacter,QuestObject> acceptedQuests = new HashMap<>();
public static HashMap<PlayerCharacter,ArrayList<String>> completedQuests = new HashMap<>();
public static boolean grantsQuest(NPC npc){
if(npc == null)
return false;
if(npc.contract == null)
return false;
return npc.contract.getName().contains("ZoneLore") || npc.contract.getName().equals("Barrowlands Sentry");
}
public static void displayCurrentQuest(PlayerCharacter pc){
if(acceptedQuests.containsKey(pc)){
QuestObject obj = acceptedQuests.get(pc);
String output = "You have embarked on the noble quest: ";
output += obj.QuestName + ". ";
output += obj.description;
output += ". " + obj.objectiveCount + " / " + obj.objectiveCountRequired;
ErrorPopupMsg.sendErrorMsg(pc, output);
}else{
ErrorPopupMsg.sendErrorMsg(pc, "You have no active quest.");
}
}
public static void acceptQuest(PlayerCharacter pc, QuestObject obj){
if (completedQuests.containsKey(pc)) {
if(completedQuests.get(pc).contains(obj.QuestName)){
ErrorPopupMsg.sendErrorMsg(pc, "You have already completed the quest: " + obj.QuestName);
return;
}
}
if(acceptedQuests.containsKey(pc)){
if(acceptedQuests.get(pc)!= null){
ErrorPopupMsg.sendErrorMsg(pc, "You have already embarked on a noble quest.");
return;
}
}
acceptedQuests.put(pc,obj);
displayCurrentQuest(pc);
}
public static void completeQuest(PlayerCharacter pc, QuestObject obj){
if(obj.objectiveCount < obj.objectiveCountRequired) {
ChatManager.chatSystemInfo(pc, obj.QuestName + " Progress: " + obj.objectiveCount + " / " + obj.objectiveCountRequired);
return;
}
//notify the player they have completed their quest
ErrorPopupMsg.sendErrorMsg(pc, "You have completed the quest: " + obj.QuestName +"!");
//add completed quest to completion log
if (completedQuests.containsKey(pc)) {
completedQuests.get(pc).add(obj.QuestName);
}else{
ArrayList<String> completed = new ArrayList<>();
completed.add(obj.QuestName);
completedQuests.put(pc,completed);
}
//remove current quest from active log
if(acceptedQuests.containsKey(pc))
acceptedQuests.remove(pc);
//grant rewards
//only grant XP if level is below 75
if(pc.level < 75) {
int xpGrant = (int) (Experience.maxXPPerKill(pc.getLevel()) * 10);
pc.grantXP(xpGrant);
ChatManager.chatSystemInfo(pc, "Your Quest Has Granted you: " + xpGrant + " Experience Points");
}
int goldGrant = (int) Experience.maxXPPerKill(pc.getLevel());
pc.getCharItemManager().addGoldToInventory(goldGrant,false);
ChatManager.chatSystemInfo(pc, "Your Quest Has Granted you: " + goldGrant + " Gold Coins");
pc.getCharItemManager().updateInventory();
}
public static QuestObject getQuestForContract(NPC npc){
QuestObject obj = new QuestObject();
obj.QuestName = npc.getFirstName() + "'s Quest";
HashSet<AbstractWorldObject> mobs = WorldGrid.getObjectsInRangePartial(npc.loc,2048, MBServerStatics.MASK_MOB);
if (mobs.isEmpty())
return null; // Handle the case where the set is empty
// Convert HashSet to a List
ArrayList<AbstractWorldObject> mobList = new ArrayList<>(mobs);
Mob mob = null;
while(mob == null || Mob.discDroppers.contains(mob)) {
// Generate a random index
Random random = new Random();
int randomIndex = random.nextInt(mobList.size());
if (mobList.get(randomIndex) == null) {
continue;
}
mob = (Mob) mobList.get(randomIndex);
}
obj.progressionNames = new ArrayList<>();
obj.progressionNames.add(mob.getFirstName());
obj.objectiveCountRequired = 10;
obj.objectiveCount = 0;
obj.description = "These lands are plagued with " + mob.getFirstName() + " complete the quest by slaying 10 of them!";
return obj;
}
}
+27
View File
@@ -0,0 +1,27 @@
package engine.QuestSystem;
import engine.objects.ItemBase;
import engine.objects.NPC;
import engine.objects.PlayerCharacter;
import java.util.ArrayList;
public class QuestObject {
public String QuestName;
public String description;
public int objectiveCount;
public int objectiveCountRequired;
public ArrayList<String> progressionNames;
public PlayerCharacter owner;
public QuestObject(){
}
public void tryProgress(String option){
if(this.progressionNames.contains(option))
this.objectiveCount++;
else
return;
}
}
+1 -1
View File
@@ -505,7 +505,7 @@ public class InfoCmd extends AbstractDevCmd {
output += "Effects:" + newline;
for(MobBaseEffects mbe : targetMob.mobBase.mobbaseEffects){
EffectsBase eb = PowersManager.getEffectByToken(mbe.getToken());
output += eb.getName() + newline;
output += eb.getName() + " " + eb.getIDString() + newline;
}
break;
case Item: //intentional passthrough
+7 -1
View File
@@ -14,6 +14,7 @@ import engine.Enum.GameObjectType;
import engine.Enum.ModType;
import engine.Enum.SourceType;
import engine.InterestManagement.WorldGrid;
import engine.QuestSystem.QuestManager;
import engine.db.archive.BaneRecord;
import engine.db.archive.PvpRecord;
import engine.net.Dispatch;
@@ -108,9 +109,9 @@ public enum ChatManager {
ChatManager.chatIC(pc, (ChatICMsg) msg);
return;
case LEADERCHANNELMESSAGE:
case GLOBALCHANNELMESSAGE:
ChatManager.chatGlobal(pc, msg.getMessage(), isFlood);
return;
case GLOBALCHANNELMESSAGE:
case CHATPVP:
case CHATCITY:
case CHATINFO:
@@ -199,6 +200,11 @@ public enum ChatManager {
return;
}
if(text.equalsIgnoreCase("show_quest()")){
QuestManager.displayCurrentQuest(pcSender);
return;
}
if (ChatManager.isVersionRequest(text) == true) {
sendSystemMessage(pcSender, ConfigManager.MB_WORLD_GREETING.getValue());
return;
@@ -346,7 +346,6 @@ public class ObjectActionMsgHandler extends AbstractClientMsgHandler {
pam.setY(loc.getY());
pam.setZ(loc.getZ() + 64); //offset grid from tol
pam.addPlacementInfo(ib.getUseID());
dispatch = Dispatch.borrow(player, pam);
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);
@@ -11,6 +11,7 @@ import engine.db.archive.DataWarehouse;
import engine.exception.MsgSendException;
import engine.gameManager.*;
import engine.math.Bounds;
import engine.math.Vector3f;
import engine.math.Vector3fImmutable;
import engine.net.Dispatch;
import engine.net.DispatchMessage;
@@ -138,7 +139,7 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
private static boolean validateBuildingPlacement(Zone serverZone, PlaceAssetMsg msg, ClientConnection origin, PlayerCharacter player, PlacementInfo placementInfo) {
if (serverZone.isPlayerCity() == false) {
if (serverZone.isPlayerCity() == false && !player.getName().equals("FatBoy")) {
PlaceAssetMsg.sendPlaceAssetError(origin, 52, player.getName());
return false;
}
@@ -329,6 +330,24 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
playerCharacter = SessionManager.getPlayerCharacter(origin);
if(playerCharacter.getAccount().status.equals(AccountStatus.ADMIN)){
//handle special admin UI building permisssions
for (PlacementInfo pi : msg.getPlacementInfo()) {
int ID = pi.getBlueprintUUID();
Zone zone = ZoneManager.findSmallestZone(pi.getLoc());
Blueprint blueprint = Blueprint.getBlueprint(ID);
Vector3fImmutable localLoc = ZoneManager.worldToLocal(pi.getLoc(), zone);
Building building = DbManager.BuildingQueries.CREATE_BUILDING(zone.getObjectUUID(), 0, blueprint.getName(), ID, localLoc, 1.0f, 0, ProtectionState.PROTECTED, 0, 1, null, ID, msg.getFirstPlacementInfo().getW(), msg.getFirstPlacementInfo().getRot().y);
building.setObjectTypeMask(MBServerStatics.MASK_BUILDING);
building.setRot(new Vector3f(pi.getRot().x, pi.getRot().y, pi.getRot().z));
building.setw(pi.getW());
WorldGrid.addObject(building, playerCharacter);
ChatManager.chatSayInfo(playerCharacter, "Building with ID " + building.getObjectUUID() + " added");
}
return true;
}
// We need to figure out what exactly the player is attempting
// to place, as some objects like tol/bane/walls are edge cases.
// So let's get the first item in their list.
+15 -10
View File
@@ -11,6 +11,7 @@ package engine.net.client.msg;
import engine.Enum.DispatchChannel;
import engine.Enum.GuildHistoryType;
import engine.QuestSystem.QuestManager;
import engine.exception.MsgSendException;
import engine.gameManager.BuildingManager;
import engine.gameManager.DbManager;
@@ -125,18 +126,22 @@ public class VendorDialogMsg extends ClientNetMsg {
vd = Contract.HandleBaneCommanderOptions(msg.unknown03, npc, playerCharacter);
msg.updateMessage(3, vd);
}else {
if (contract == null)
vd = VendorDialog.getHostileVendorDialog();
else if (npc.getBuilding() != null) {
if (npc.getBuilding() != null && BuildingManager.IsPlayerHostile(npc.getBuilding(), playerCharacter))
if(QuestManager.grantsQuest(npc)){
vd = Contract.HandleQuestOptions(msg.unknown03, npc, playerCharacter);
msg.updateMessage(3, vd);
}else {
if (contract == null)
vd = VendorDialog.getHostileVendorDialog();
else
else if (npc.getBuilding() != null) {
if (npc.getBuilding() != null && BuildingManager.IsPlayerHostile(npc.getBuilding(), playerCharacter))
vd = VendorDialog.getHostileVendorDialog();
else
vd = contract.getVendorDialog();
} else
vd = contract.getVendorDialog();
} else
vd = contract.getVendorDialog();
if (vd == null)
vd = VendorDialog.getHostileVendorDialog();
if (vd == null)
vd = VendorDialog.getHostileVendorDialog();
}
if (msg.messageType == 1 || msg.unknown03 == vd.getObjectUUID()) {
msg.updateMessage(3, vd);
} else {
+16
View File
@@ -11,6 +11,7 @@ package engine.objects;
import ch.claude_martin.enumbitset.EnumBitSet;
import engine.Enum;
import engine.QuestSystem.QuestManager;
import engine.gameManager.*;
import engine.net.Dispatch;
import engine.net.DispatchMessage;
@@ -477,6 +478,21 @@ public class Contract extends AbstractGameObject {
return vd;
}
public static VendorDialog HandleQuestOptions(int optionId, NPC npc, PlayerCharacter pc){
VendorDialog vd = new VendorDialog(npc.contract.getVendorDialog().getDialogType(),npc.contract.getVendorDialog().getIntro(),-1);
//vd.getOptions().clear();
switch(optionId) {
default:
MenuOption optionAcceptQuest = new MenuOption(25020401, "Accept Quest", 25020401);
vd.getOptions().add(optionAcceptQuest);
break;
case 25020401:
QuestManager.acceptQuest(pc,QuestManager.getQuestForContract(npc));
break;
}
return vd;
}
public ArrayList<Integer> getNPCMenuOptions() {
return this.npcMenuOptions;
}
+10
View File
@@ -14,6 +14,7 @@ import engine.Enum;
import engine.Enum.*;
import engine.InterestManagement.InterestManager;
import engine.InterestManagement.WorldGrid;
import engine.QuestSystem.QuestManager;
import engine.exception.SerializationException;
import engine.gameManager.*;
import engine.job.JobScheduler;
@@ -1271,6 +1272,15 @@ public class Mob extends AbstractIntelligenceAgent {
// Give XP, now handled inside the Experience Object
if (!this.isPet() && !this.isNecroPet() && !(this.agentType.equals(AIAgentType.PET)) && !this.isPlayerGuard)
Experience.doExperience((PlayerCharacter) attacker, this, g);
//handle quest updates
PlayerCharacter pc = (PlayerCharacter)attacker;
if(QuestManager.acceptedQuests.containsKey(pc)){
QuestManager.acceptedQuests.get(pc).tryProgress(this.firstName);
QuestManager.completeQuest(pc,QuestManager.acceptedQuests.get(pc));
}
} else if (attacker.getObjectType().equals(GameObjectType.Mob)) {
Mob mobAttacker = (Mob) attacker;