Emberveil sourcetests/MemberQuestsCheck.java
import com.rs2.Constants;
import com.rs2.game.content.quests.QuestAssistant;
import com.rs2.game.content.quests.impl.MemberQuests;
import com.rs2.game.dialogues.DialogueOptions;
import com.rs2.game.items.ItemDefinitions;
import com.rs2.game.players.*;
import com.rs2.world.clip.*;
import org.apollo.cache.IndexedFileSystem;
import org.apollo.cache.decoder.*;
import java.nio.file.*;
import java.util.Arrays;
/** Real cache, inventory, dialogue handlers and serializer; disposable local players only. */
public final class MemberQuestsCheck {
static int checks;
static Path saveRoot;
static String originalRoot;
static class Fixture extends Client {
Fixture(){super(null,1);playerName=playerName2="memberquestqa";playerPass="disposable-quest-test";
tutorialProgress=36;isActive=true;respawnTimer=-6;randomEventsEnabled=false;
Arrays.fill(playerLevel,99);Arrays.fill(playerXP,13034431);
getOutStream().packetEncryption=new org.apollo.util.security.IsaacRandom(new int[]{1,2,3,4});}
public void flushOutStream(){getOutStream().currentOffset=0;}
void pos(int x,int y,int z){absX=x;absY=y;heightLevel=z;teleportToX=teleportToY=0;}
void clear(){Arrays.fill(playerItems,0);Arrays.fill(playerItemsN,0);}
void item(int slot,int id){playerItems[slot]=id+1;playerItemsN[slot]=1;}
}
static void check(boolean value,String label){checks++;if(!value)throw new AssertionError(label);}
static Fixture reload(Fixture p)throws Exception {
p.saveFile=p.saveCharacter=true;p.newPlayer=false;PlayerHandler.players[1]=p;
System.setProperty("user.dir",saveRoot.toString());
try {
check(PlayerSave.saveGame(p),"save succeeds");
Fixture q=new Fixture();check(PlayerSave.loadPlayerInfo(q,q.playerName,"",false)==1,"reload succeeds");
check(q.druidicRitual==p.druidicRitual&&q.junglePotion==p.junglePotion&&q.jungleHerbsFound==p.jungleHerbsFound,"quest stages and found herbs persist");
check(q.questPoints==p.questPoints&&q.playerXP[15]==p.playerXP[15],"QP and Herblore XP persist");
check(Arrays.equals(p.playerItems,q.playerItems)&&Arrays.equals(p.playerItemsN,q.playerItemsN),"inventory persists");
return q;
} finally {System.setProperty("user.dir",originalRoot);PlayerHandler.players[1]=null;}
}
static void accept(Fixture p,int npc){p.getNpcs().firstClickNpc(npc);check(p.nextChat!=0,"quest NPC introduction has continuation");p.getDialogueHandler().sendDialogues(p.nextChat,npc);DialogueOptions.handleDialogueOptions(p,9157);}
static void druidic()throws Exception {
Fixture p=new Fixture();p.playerXP[15]=0;p.playerLevel[15]=1;
p.pos(0,0,0);p.getNpcs().firstClickNpc(455);check(p.druidicRitual==0,"cannot start remotely");
p.pos(2925,3485,0);p.getNpcs().firstClickNpc(455);p.getDialogueHandler().sendDialogues(p.nextChat,455);
DialogueOptions.handleDialogueOptions(p,9158);check(p.druidicRitual==0,"declining leaves quest unstarted");
accept(p,455);check(p.druidicRitual==1,"accept starts Druidic Ritual");p=reload(p);
p.pos(2896,3428,0);p.getNpcs().firstClickNpc(454);check(p.druidicRitual==1,"wrong floor cannot talk to Sanfew");
p.pos(2896,3428,1);p.getNpcs().firstClickNpc(454);check(p.druidicRitual==2,"Sanfew requests four meats");p=reload(p);
int[] raw={2132,2134,2136,2138};
p.clear();for(int i=0;i<28;i++)p.item(i,995);for(int i=0;i<4;i++)p.item(i,raw[i]);
p.pos(2894,9832,0);
MemberQuests.useItem(p,2142,2893,9831,379);check(p.playerItems[0]==2133,"wrong meat unchanged");
MemberQuests.useItem(p,2142,3000,3000,2132);check(p.playerItems[0]==2133,"forged cauldron rejected");
p.pos(2894,9832,1);MemberQuests.useItem(p,2142,2893,9831,2132);check(p.playerItems[0]==2133,"wrong-floor cauldron rejected");
p.pos(2894,9832,0);p.inTrade=true;MemberQuests.useItem(p,2142,2893,9831,2132);check(p.playerItems[0]==2133,"trade blocks conversion");p.inTrade=false;
for(int i=0;i<4;i++) {MemberQuests.useItem(p,2142,2893,9831,raw[i]);check(p.playerItems[i]==523+i&&p.playerItemsN[i]==1,"exact-slot full-backpack conversion "+i);p=reload(p);p.pos(2894,9832,0);}
check(p.getItemAssistant().freeSlots()==0,"enchanting works with full backpack");
p.pos(2925,3485,0);p.getNpcs().firstClickNpc(455);check(p.druidicRitual==2&&p.questPoints==0,"cannot skip Sanfew hand-in");
p.pos(2896,3428,1);p.playerItems[3]=0;p.playerItemsN[3]=0;p.getNpcs().firstClickNpc(454);
check(p.druidicRitual==2&&p.getItemAssistant().playerHasItem(522),"missing meat consumes nothing");p.item(3,525);
p.getNpcs().firstClickNpc(454);check(p.druidicRitual==3,"all ingredients hand in");for(int id=522;id<=525;id++)check(!p.getItemAssistant().playerHasItem(id),"hand-in consumes enchanted meat "+id);p=reload(p);
p.pos(2925,3485,0);p.getNpcs().firstClickNpc(455);check(p.druidicRitual==4&&p.questPoints==4&&p.playerXP[15]==1000,"Druidic reward exactly four QP and 250x4 XP");
p=reload(p);p.pos(2925,3485,0);for(int i=0;i<10;i++)p.getNpcs().firstClickNpc(455);
check(p.questPoints==4&&p.playerXP[15]==1000,"Druidic reward cannot repeat after reload");
QuestAssistant.questButtons(p,28187);check(QuestAssistant.Quests.DRUDIC_RITUAL.questStatus(),"Druidic quest enabled with journal");
}
static void jungle()throws Exception {
Fixture p=new Fixture();p.pos(2809,3086,0);p.getNpcs().firstClickNpc(740);check(p.nextChat==0&&p.junglePotion==0,"Druidic prerequisite enforced");
p.druidicRitual=4;p.playerXP[15]=1000;p.playerLevel[15]=9;accept(p,740);check(p.junglePotion==1,"Trufitus starts Jungle Potion");p=reload(p);
int[][] sites={{2575,2757,3041},{2577,2870,3115},{2579,2789,3047},{2581,2850,3035},{2583,2829,9493}};
int[] herbs={1525,1527,1529,1531,1533};
for(int i=0;i<5;i++) {
int[] site=sites[i];p.clear();p.pos(site[1]+1,site[2],0);
MemberQuests.object(p,site[0],site[1]+30,site[2]);check(!p.getItemAssistant().playerHasItem(herbs[i]),"remote/forged herb rejected "+i);
p.disconnected=true;MemberQuests.object(p,site[0],site[1],site[2]);check(p.jungleHerbsFound==((1<<i)-1),"disconnected collection rejected "+i);p.disconnected=false;
for(int j=0;j<28;j++)p.item(j,995);MemberQuests.object(p,site[0],site[1],site[2]);check(!p.getItemAssistant().playerHasItem(herbs[i]),"full backpack collection rejected "+i);p.clear();
p.getObjects().firstClickObject(site[0],site[1],site[2]);check(p.getItemAssistant().getItemAmount(herbs[i])==1,"real map herb found "+i);
p.getObjects().secondClickObject(site[0],site[1],site[2]);check(p.getItemAssistant().getItemAmount(herbs[i])==1,"repeat search does not duplicate "+i);p=reload(p);
p.pos(2809,3086,0);p.getNpcs().firstClickNpc(740);check(p.junglePotion==i+1,"unclean herb cannot advance "+i);
int slot=p.getItemAssistant().getItemSlot(herbs[i]);p.playerLevel[15]=1;MemberQuests.clean(p,herbs[i],slot);check(p.getItemAssistant().playerHasItem(herbs[i]),"low-level identification blocked "+i);p.playerLevel[15]=9;
int xp=p.playerXP[15];MemberQuests.clean(p,herbs[i],slot);check(p.playerXP[15]-xp==10&&p.getItemAssistant().playerHasItem(herbs[i]+1),"identification converts once and awards 2.5x4 XP "+i);
MemberQuests.clean(p,herbs[i],slot);check(p.playerXP[15]-xp==10,"replayed identification cannot grant XP "+i);p=reload(p);
p.pos(2809,3086,0);p.getNpcs().firstClickNpc(740);check(p.junglePotion==i+2&&!p.getItemAssistant().playerHasItem(herbs[i]+1),"Trufitus consumes correct identified herb "+i);p=reload(p);
}
check(p.junglePotion==6&&p.questPoints==1&&p.playerXP[15]==4150,"Jungle reward: 775x4 plus five identification awards");
p.pos(2809,3086,0);for(int i=0;i<10;i++)p.getNpcs().firstClickNpc(740);check(p.questPoints==1&&p.playerXP[15]==4150,"Jungle rewards cannot repeat after reload");
QuestAssistant.questButtons(p,28197);check(QuestAssistant.Quests.JUNGLE_POTION.questStatus(),"Jungle quest enabled with journal");
p=new Fixture();p.druidicRitual=4;p.junglePotion=5;p.item(0,1534);p.pos(2809,3086,0);p.getNpcs().firstClickNpc(740);check(p.junglePotion==5,"Rogue's purse from other sources cannot bypass cave collection");
}
static void routes()throws Exception {
Fixture p=new Fixture();p.pos(2823,3118,0);p.getObjects().firstClickObject(2584,2824,3118);check(p.teleportToX==2831&&p.teleportToY==9521,"Jogre cave entrance destination");
p.pos(2831,9522,0);p.getObjects().firstClickObject(2585,2830,9522);check(p.teleportToX==2823&&p.teleportToY==3118,"Jogre cave exit destination");
p.pos(0,0,0);MemberQuests.object(p,2584,2824,3118);check(p.teleportToX==0,"remote cave teleport rejected");
p=new Fixture();p.pos(2925,3485,0);p.getNpcs().firstClickNpc(455);p.getDialogueHandler().sendDialogues(p.nextChat,455);p.pos(0,0,0);DialogueOptions.handleDialogueOptions(p,9157);check(p.druidicRitual==0,"walk-away dialogue rejected");
p=new Fixture();p.pos(2925,3485,0);p.getNpcs().firstClickNpc(455);p.getDialogueHandler().sendDialogues(p.nextChat,455);p.disconnected=true;DialogueOptions.handleDialogueOptions(p,9157);check(p.druidicRitual==0,"disconnected dialogue rejected");
p=new Fixture();p.pos(2925,3485,0);accept(p,455);DialogueOptions.handleDialogueOptions(p,9157);check(p.druidicRitual==1,"stale option cannot repeat start");
check(QuestAssistant.MAXIMUM_QUESTPOINTS==Integer.getInteger("emberveil.test.expectedQuestPoints",32),"maximum QP matches the tested release");
}
public static void main(String[] args)throws Exception {
Constants.XP_RATE=4;Constants.VARIABLE_XP_RATE=false;
originalRoot=System.getProperty("user.dir");saveRoot=Paths.get(originalRoot,"../../runtime/member-quest-save-check").normalize().toAbsolutePath();
check(!Files.exists(saveRoot.resolve("data/characters/memberquestqa.txt")),"test cannot overwrite existing save");
IndexedFileSystem cache=new IndexedFileSystem(Paths.get("data/cache"),true);
new ItemDefinitionDecoder(cache).run();ItemDefinitions.load();new ObjectDefinitionDecoder(cache).run();RegionFactory.load(cache);
try {druidic();jungle();routes();System.out.println("PASS: "+checks+" members' quest integration checks");}
finally {System.setProperty("user.dir",originalRoot);PlayerHandler.players[1]=null;Files.deleteIfExists(saveRoot.resolve("data/characters/memberquestqa.txt"));Files.deleteIfExists(saveRoot.resolve("data/characters/memberquestqa.txt.bak"));}
}
}