Emberveil sourcetests/MonksFriendCheck.java
import com.rs2.Constants;
import com.rs2.game.content.quests.QuestAssistant;
import com.rs2.game.content.quests.impl.MonksFriend;
import com.rs2.game.dialogues.DialogueOptions;
import com.rs2.game.items.ItemDefinitions;
import com.rs2.game.players.*;
import com.rs2.world.GlobalDropsHandler;
import com.rs2.world.clip.*;
import org.apollo.cache.*;
import org.apollo.cache.decoder.*;
import org.apollo.cache.def.*;
import java.nio.file.*;
import java.util.*;
/** Real NPC/object/dialogue entry points, inventory, map collision, drops and save files. */
public final class MonksFriendCheck {
static int checks;
static Path root;
static String original;
static void check(boolean ok,String msg){checks++;if(!ok)throw new AssertionError(msg);}
static class Fixture extends MemberQuestsCheck.Fixture {
Fixture(){playerName=playerName2="monksfriendqa";playerXP[Constants.WOODCUTTING]=0;playerLevel[Constants.WOODCUTTING]=1;}
}
static Fixture reload(Fixture p)throws Exception {
p.saveFile=p.saveCharacter=true;p.newPlayer=false;PlayerHandler.players[1]=p;
System.setProperty("user.dir",root.toString());
try {
check(PlayerSave.saveGame(p),"quest saves");Fixture q=new Fixture();
check(PlayerSave.loadPlayerInfo(q,q.playerName,"",false)==1,"quest reloads");
check(q.monksFriend==p.monksFriend,"quest stage persists");
check(q.questPoints==p.questPoints&&q.playerXP[Constants.WOODCUTTING]==p.playerXP[Constants.WOODCUTTING],"reward XP and QP persist");
check(Arrays.equals(q.playerItems,p.playerItems)&&Arrays.equals(q.playerItemsN,p.playerItemsN),"quest inventory persists");
return q;
} finally {System.setProperty("user.dir",original);PlayerHandler.players[1]=null;}
}
static void omad(Fixture p){p.pos(MonksFriend.OMAD_X,MonksFriend.OMAD_Y,0);p.getNpcs().firstClickNpc(279);}
static void cedric(Fixture p){p.pos(MonksFriend.CEDRIC_X,MonksFriend.CEDRIC_Y,0);p.getNpcs().firstClickNpc(280);}
static void options(Fixture p){omad(p);check(p.nextChat==17420,"Omad introduction continuation");p.getDialogueHandler().sendDialogues(p.nextChat,279);}
static void story()throws Exception {
Fixture p=new Fixture();p.pos(0,0,0);p.getNpcs().firstClickNpc(279);check(p.nextChat==0&&p.monksFriend==0,"remote NPC cannot start");
options(p);DialogueOptions.handleDialogueOptions(p,9158);check(p.monksFriend==0,"declining does not start");
options(p);p.pos(0,0,0);DialogueOptions.handleDialogueOptions(p,9157);check(p.monksFriend==0,"walking away cancels start");
options(p);p.inTrade=true;DialogueOptions.handleDialogueOptions(p,9157);check(p.monksFriend==0,"trade cancels start");p.inTrade=false;
options(p);DialogueOptions.handleDialogueOptions(p,9157);check(p.monksFriend==1,"quest starts through real dialogue option");p=reload(p);
p.item(0,1937);p.item(1,1511);cedric(p);check(p.monksFriend==1&&p.getItemAssistant().playerHasItem(1937),"Cedric cannot skip blanket");
omad(p);check(p.monksFriend==1,"missing blanket does not advance");
p.pos(2562,3222,0);MonksFriend.loadObjects(p);p.getObjects().firstClickObject(1754,2561,3222);
check(p.teleportToX==2562&&p.teleportToY==9622,"surface ladder enters original hideout");
p.pos(2570,9603,0);GlobalDropsHandler.load(p);GlobalDropsHandler.pickup(p,90,2570,9604);
check(p.getItemAssistant().getItemAmount(90)==1,"original blanket ground drop can be picked up");
GlobalDropsHandler.pickup(p,90,2570,9604);check(p.getItemAssistant().getItemAmount(90)==1,"taken blanket cannot duplicate before respawn");
p=reload(p);p.pos(2562,9622,0);p.getObjects().firstClickObject(1755,2561,9622);
check(p.teleportToX==2562&&p.teleportToY==3222,"original exit ladder returns to surface");
omad(p);check(p.monksFriend==2&&!p.getItemAssistant().playerHasItem(90),"Omad consumes blanket and requests Cedric");p=reload(p);
// A full backpack still allows same-slot water -> empty jug replacement.
for(int i=2;i<28;i++)p.item(i,995);check(p.getItemAssistant().freeSlots()==0,"water hand-in fixture backpack full");
cedric(p);check(p.monksFriend==3&&p.playerItems[0]==1936&&p.playerItemsN[0]==1,"full backpack water hand-in preserves empty jug");
check(!p.getItemAssistant().playerHasItem(1937),"water consumed once");p=reload(p);
p.item(1,1521);cedric(p);check(p.monksFriend==3&&p.getItemAssistant().playerHasItem(1521),"oak logs rejected without loss");
p.item(1,1511);p.inDuel=true;cedric(p);check(p.monksFriend==3&&p.getItemAssistant().playerHasItem(1511),"dueling blocks hand-in");p.inDuel=false;
cedric(p);check(p.monksFriend==4&&!p.getItemAssistant().playerHasItem(1511),"normal logs repair cart exactly once");p=reload(p);
p.item(1,995);omad(p);check(p.monksFriend==4&&p.questPoints==0&&p.playerXP[Constants.WOODCUTTING]==0,"full backpack defers all rewards safely");
p.playerItems[1]=p.playerItemsN[1]=0;omad(p);
check(p.monksFriend==5&&p.questPoints==1,"completion grants one QP");
check(p.getItemAssistant().getItemAmount(563)==8,"completion grants eight law runes");
check(p.playerXP[Constants.WOODCUTTING]==8000,"2,000 base Woodcutting XP honors x4 server rate");p=reload(p);
for(int i=0;i<10;i++){omad(p);cedric(p);DialogueOptions.handleDialogueOptions(p,9157);}
check(p.questPoints==1&&p.playerXP[Constants.WOODCUTTING]==8000&&p.getItemAssistant().getItemAmount(563)==8,"relog and replay cannot duplicate rewards");
QuestAssistant.questButtons(p,28201);check(QuestAssistant.Quests.MONKS_FRIEND.questStatus(),"quest enabled in real journal list");
Fixture blocked=new Fixture();blocked.monksFriend=2;blocked.item(0,1937);blocked.pos(2616,3259,1);blocked.getNpcs().firstClickNpc(280);
check(blocked.monksFriend==2&&blocked.getItemAssistant().playerHasItem(1937),"wrong-floor Cedric rejected");
blocked.pos(2616,3259,0);blocked.disconnected=true;blocked.getNpcs().firstClickNpc(280);check(blocked.monksFriend==2,"disconnected hand-in rejected");
}
static void assetsAndRoutes()throws Exception {
for(int[] npc:new int[][]{{279,2606,3205},{280,2616,3259},{282,2570,9606},{282,2572,9603},{283,2568,9605}}) {
check(NpcDefinition.lookup(npc[0])!=null,"original NPC definition "+npc[0]);
check((Region.getClipping(npc[1],npc[2],0)&0x1280100)==0,"NPC footprint open "+Arrays.toString(npc));
}
for(int[] tile:new int[][]{{2562,3222},{2562,9622}})check((Region.getClipping(tile[0],tile[1],0)&0x1280100)==0,"ladder landing open "+Arrays.toString(tile));
check(Region.objectExists(1755,2561,9622,0),"original hideout exit exists");
check(Region.objectExists(595,2570,9604,0),"original blanket table exists");
check(Region.objectExists(306,2615,3261,0)&&Region.objectExists(884,2612,3254,0),"Cedric placed by original cart and well");
com.rs2.game.globalworldobjects.Doors.getSingleton().load();
Fixture door=new Fixture();door.pos(2565,9613,0);
check(QuestRouteCheck.route(2562,9622,2565,9612,0,1),"entrance reaches hideout door");
check(com.rs2.game.globalworldobjects.Doors.getSingleton().handleDoor(door,1530,2565,9612,0),"original hideout door opens");
door.getObjects().firstClickObject(1530,2565,9612);
check(door.teleportToX==2565&&door.teleportToY==9611,"hideout door crosses stale legacy collision");
check(QuestRouteCheck.route(2565,9611,2570,9604,0,1),"inside doorway reaches blanket table");
check(QuestRouteCheck.route(2570,9603,2565,9612,0,1),"blanket room reaches doorway");
door.pos(2565,9611,0);door.getObjects().firstClickObject(1531,2565,9613);
check(door.teleportToX==2565&&door.teleportToY==9613,"door passage also allows exit");
check(QuestRouteCheck.route(2565,9613,2561,9622,0,1),"outside doorway reaches ladder");
check(QuestRouteCheck.route(2606,3205,2561,3222,0,1),"monastery reaches hideout entrance");
check(QuestRouteCheck.route(2606,3205,2616,3259,0,1),"monastery reaches Cedric");
Fixture p=new Fixture();p.pos(0,0,0);MonksFriend.object(p,1754,2561,3222);check(p.teleportToX==0,"remote ladder rejected");
p.pos(2562,3222,1);MonksFriend.object(p,1754,2561,3222);check(p.teleportToX==0,"wrong-floor ladder rejected");
p.pos(2562,3222,0);p.inTrade=true;MonksFriend.object(p,1754,2561,3222);check(p.teleportToX==0,"trade ladder rejected");
}
public static void main(String[] args)throws Exception {
Constants.XP_RATE=4;Constants.VARIABLE_XP_RATE=false;
original=System.getProperty("user.dir");root=Paths.get(original,"../../runtime/monks-friend-save-check").normalize().toAbsolutePath();
check(!Files.exists(root.resolve("data/characters/monksfriendqa.txt")),"cannot overwrite existing save");
IndexedFileSystem cache=new IndexedFileSystem(Paths.get("data/cache"),true);
new ItemDefinitionDecoder(cache).run();ItemDefinitions.load();new NpcDefinitionDecoder(cache).run();new ObjectDefinitionDecoder(cache).run();RegionFactory.load(cache);
try{assetsAndRoutes();story();System.out.println("PASS: "+checks+" Monk's Friend integration, reward, persistence, asset and route checks");}
finally{System.setProperty("user.dir",original);PlayerHandler.players[1]=null;Files.deleteIfExists(root.resolve("data/characters/monksfriendqa.txt"));Files.deleteIfExists(root.resolve("data/characters/monksfriendqa.txt.bak"));}
}
}