Emberveil source

tests/GodWarsCheck.java

import com.rs2.Constants;
import com.rs2.GameEngine;
import com.rs2.event.CycleEventHandler;
import com.rs2.game.content.emberveil.*;
import com.rs2.game.content.combat.melee.MeleeData;
import com.rs2.game.npcs.*;
import com.rs2.game.players.*;
import com.rs2.game.items.ItemDefinitions;
import org.apollo.cache.IndexedFileSystem;
import org.apollo.cache.decoder.ItemDefinitionDecoder;
import org.apollo.cache.decoder.ObjectDefinitionDecoder;
import org.apollo.cache.def.ItemDefinition;
import com.rs2.world.clip.RegionFactory;
import com.rs2.world.clip.Region;
import java.nio.file.Paths;
import java.util.*;
import java.io.FileReader;
import com.google.gson.Gson;

/** Runs against disposable staged data. Does not connect to or save live accounts. */
public final class GodWarsCheck {
    static int checks;
    static void check(boolean value,String label){if(!value)throw new AssertionError(label);checks++;System.out.println("PASS: "+label);}
    static class Fighter extends Client {
        Fighter(int id){super(null,id);getOutStream().packetEncryption=new org.apollo.util.security.IsaacRandom(new int[]{1,2,3,4});
            playerName=playerName2="gwdqa"+id;tutorialProgress=36;Arrays.fill(playerEquipment,-1);Arrays.fill(playerLevel,99);Arrays.fill(playerXP,13034431);specAccuracy=specDamage=1;specAmount=10;}
        public void flushOutStream(){getOutStream().currentOffset=0;}
    }
    static void place(Fighter p,int x,int y,int h){p.absX=x;p.absY=y;p.heightLevel=h;p.teleportToX=p.teleportToY=-1;p.stopPlayerPacket=false;p.isDead=false;p.disconnected=false;p.playerLevel[3]=99;}
    static void tick(){CycleEventHandler.getSingleton().process();}
    static void arrived(Fighter p,int x,int y,int h,String name){tick();tick();check(p.teleportToX==x&&p.teleportToY==y&&p.teleHeight==h&&!p.stopPlayerPacket,name);place(p,x,y,h);}
    static void inventory(Fighter p){Arrays.fill(p.playerItems,0);Arrays.fill(p.playerItemsN,0);}
    static int amount(Fighter p,int id){return p.getItemAssistant().getItemAmount(id);}
    static int slot(int type){for(int i=1;i<NpcHandler.npcs.length;i++)if(NpcHandler.npcs[i]!=null&&NpcHandler.npcs[i].npcType==type)return i;throw new AssertionError("No spawn "+type);}
    static final class NeedRoll extends RuntimeException {int bound;NeedRoll(int bound){this.bound=bound;}}
    static Map<Integer,Double> probabilities=new HashMap<>();
    static void enumerate(int npc,List<Integer> path,double weight){
        final int[] index={0};
        try{
            List<GodWarsLoot.Drop> drops=GodWarsLoot.roll(npc,bound->{if(index[0]==path.size())throw new NeedRoll(bound);int v=path.get(index[0]++);if(v<0||v>=bound)throw new AssertionError("Out of range RNG");return v;});
            Set<Integer> found=new HashSet<>();
            for(GodWarsLoot.Drop drop:drops){if(drop.id<0||drop.id>=ItemDefinition.count()||drop.min<1||drop.max<drop.min)throw new AssertionError("Invalid drop "+drop.id);if(found.add(drop.id))probabilities.merge(drop.id,weight,Double::sum);}
        }catch(NeedRoll need){for(int r=0;r<need.bound;r++){path.add(r);enumerate(npc,path,weight/need.bound);path.remove(path.size()-1);}}
    }
    static void rate(int item,double expected,String label){check(Math.abs(probabilities.getOrDefault(item,0.0)-expected)<1e-10,label);}
    public static void main(String[] args)throws Exception {
        IndexedFileSystem cache=new IndexedFileSystem(Paths.get("data/cache"),true);
        new ItemDefinitionDecoder(cache).run();new ObjectDefinitionDecoder(cache).run();RegionFactory.load(cache);ItemDefinitions.load();
        check(GameEngine.npcHandler!=null&&GodWars.enabled(),"staged world and God Wars definitions load");
        check(Region.objectExists(GodWars.objectType(26341),2915,3745,0),"entrance object registered in server map");
        for(int[] tile:new int[][]{{2882,5311,2},{2916,5300,1},{2919,5273,0},{2925,5330,2},{2839,5297,2},{2906,5265,0},{2865,5354,2},{2898,3715,0},{2898,3719,0},{2903,3720,0},{2899,3713,0},{2914,3744,0},{2919,5276,1},{2912,5300,2},{2872,5279,2},{2872,5269,2},{2852,5333,2},{2850,5333,2},{2885,5332,2},{2885,5345,2}})check((Region.getClipping(tile[0],tile[1],tile[2])&0x200100)==0,"route destination is walkable "+Arrays.toString(tile));
        Fighter p=new Fighter(1),v=new Fighter(2);PlayerHandler.players[1]=p;PlayerHandler.players[2]=v;
        for(int npc:new int[]{3900,3919,3944,3957}){
            int start=npc==3900?3900:npc;int[] types={start,start+1,start+3,start+5};
            for(int type:types){check(slot(type)>0,"boss/bodyguard spawned "+type);probabilities.clear();enumerate(type,new ArrayList<>(),1.0);
                for(int item:new int[]{11710,11712,11714})rate(item,1.0/(type==npc?762:1524),"exact shard rate "+type+" / "+item);
                if(type==npc){int hilt=npc==3900?11708:npc==3919?11702:npc==3944?11706:11704;rate(hilt,1.0/508,"exact boss hilt rate "+type);
                    if(npc==3957||npc==3919)for(int item:npc==3957?new int[]{11724,11726,11728}:new int[]{11718,11720,11722})rate(item,1.0/381,"exact boss armour rate "+item);
                    if(npc==3900){rate(11716,1.0/127,"original Zamorak spear rate");rate(11736,1.0/127,"original steam staff rate");}
                    if(npc==3944)rate(11730,1.0/127,"original Saradomin sword rate");
                }else if(npc==3957)for(int item:new int[]{11724,11726,11728})rate(item,1.0/16256,"exact bodyguard armour rate "+item);
                else if(npc==3944)rate(11730,1.0/16256,"exact bodyguard sword rate");
                check(!probabilities.containsKey(11791)&&!probabilities.containsKey(11785),"modern weapons absent "+type);
            }
        }
        GodWars.Definition[] definitions;
        try(FileReader reader=new FileReader("data/cfg/godwars-npcs.json")){definitions=new Gson().fromJson(reader,GodWars.Definition[].class);}
        for(GodWars.Definition definition:definitions)if(definition.name.toLowerCase().contains("spiritual mage")){
            probabilities.clear();enumerate(definition.id,new ArrayList<>(),1.0);rate(11732,1.0/128,"spiritual mage dragon boots rate "+definition.id);
        }
        place(p,2915,3745,0);inventory(p);p.gwdRopes=0;
        GodWars.object(p,GodWars.objectType(26341),2914,3744,false);check(!p.stopPlayerPacket&&p.gwdRopes==0,"forged entrance location rejected");
        GodWars.object(p,GodWars.objectType(26341),2915,3745,false);check(!p.stopPlayerPacket&&p.gwdRopes==0,"entry without rope rejected");
        p.getItemAssistant().addItem(954,3);GodWars.object(p,GodWars.objectType(26341),2915,3745,false);check(amount(p,954)==2&&p.gwdRopes==1,"entrance consumes exactly one rope");
        GodWars.object(p,GodWars.objectType(26341),2915,3745,false);check(amount(p,954)==2,"repeat click cannot consume another rope");arrived(p,2882,5311,2,"entrance reaches dungeon floor");
        place(p,2913,5300,2);GodWars.object(p,GodWars.objectType(26296),2913,5300,false);arrived(p,2916,5300,1,"upper Saradomin rope descends");
        place(p,2920,5276,1);GodWars.object(p,GodWars.objectType(26300),2920,5276,false);arrived(p,2919,5273,0,"lower Saradomin rope descends");check(p.gwdRopes==7&&amount(p,954)==0,"three ropes stored independently");
        GodWars.object(p,GodWars.objectType(26301),2919,5274,false);arrived(p,2919,5276,1,"lower Saradomin rope ascends");
        place(p,2915,5300,1);GodWars.object(p,GodWars.objectType(26297),2915,5300,false);arrived(p,2912,5300,2,"upper Saradomin rope ascends");
        int[][] doors={{GodWars.objectType(26428),2925,5332,2,2925,5330},{GodWars.objectType(26426),2839,5295,2,2839,5297},{GodWars.objectType(26427),2908,5265,0,2906,5265},{GodWars.objectType(26425),2863,5354,2,2865,5354}};
        for(int faction=0;faction<4;faction++){int[] d=doors[faction];place(p,d[1],d[2],d[3]);p.gwdKillCount[faction]=39;
            GodWars.object(p,d[0],d[1],d[2],false);check(!p.stopPlayerPacket&&p.gwdKillCount[faction]==39,"boss door rejects 39 kills "+faction);
            p.gwdKillCount[faction]=40;GodWars.object(p,d[0],d[1],d[2],false);GodWars.object(p,d[0],d[1],d[2],false);check(p.gwdKillCount[faction]==0,"boss door deducts 40 kills once "+faction);
            arrived(p,d[4],d[5],d[3],"boss door reaches correct chamber "+faction);
            check(GodWars.room(p)==GodWars.Faction.values()[faction],"room detection "+faction);
        }
        place(p,2885,5333,2);p.playerLevel[5]=99;GodWars.object(p,GodWars.objectType(26439),2885,5333,false);arrived(p,2885,5345,2,"Zamorak bridge crosses");check(p.playerLevel[5]==0,"Zamorak bridge drains prayer");
        place(p,2872,5269,2);p.playerEquipment[3]=9185;p.playerEquipment[p.playerArrows]=9419;
        GodWars.object(p,GodWars.objectType(26303),2871,5270,false);arrived(p,2872,5279,2,"Armadyl grapple crosses north");
        GodWars.object(p,GodWars.objectType(26303),2871,5270,false);arrived(p,2872,5269,2,"Armadyl grapple returns south from full object footprint");
        int[][] altars={{26286,2937,5323,2936,5323,2},{26288,2821,5301,2824,5301,2},{26287,2885,5267,2889,5267,0},{26289,2869,5370,2869,5369,2}};
        for(int f=0;f<4;f++){int[] a=altars[f];place(p,a[3],a[4],a[5]);p.gwdAltarRecharge=0;p.logoutDelay=0;p.playerLevel[5]=50;
            GodWars.object(p,GodWars.objectType(a[0]),a[1],a[2],false);check(p.playerLevel[5]==99&&p.gwdAltarRecharge>System.currentTimeMillis(),"altar restores prayer "+f);
            p.playerLevel[5]=50;GodWars.object(p,GodWars.objectType(a[0]),a[1],a[2],false);check(p.playerLevel[5]==50,"altar cooldown prevents repeated recharge "+f);
            GodWars.object(p,GodWars.objectType(a[0]),a[1],a[2],true);GodWars.Faction faction=GodWars.Faction.values()[f];arrived(p,faction.exitX,faction.exitY,faction.plane,"altar exits correct room "+f);
        }
        p.playerEquipment[3]=11694;p.usingMagic=false;check(MeleeData.getAttackDelay(p)==6,"godsword speed is six ticks");p.usingMagic=true;p.spellId=0;check(MeleeData.getAttackDelay(p)==5,"holding godsword preserves spell speed");p.usingMagic=false;
        int[] weapons={11694,11696,11698,11700,11730,11716};double[] costs={5,10,5,6,10,2.5};
        for(int n=0;n<weapons.length;n++){p.playerEquipment[3]=weapons[n];p.usingSpecial=false;p.specAmount=10;p.getItemAssistant().addSpecialBar(weapons[n]);
            check(p.specBarId==(weapons[n]==11716?7686:7711),"correct visible special bar "+weapons[n]);
            com.rs2.game.content.combat.Specials.specialClicking(p,weapons[n]==11716?29238:30007);check(p.usingSpecial,"actual special button enables weapon "+weapons[n]);
            check(p.getCombatAssistant().checkSpecAmount(weapons[n])&&p.specAmount==10-costs[n],"original special cost "+weapons[n]);p.getOutStream().currentOffset=0;
        }
        p.playerEquipment[3]=11694;GodWarsEquipment.button(p,18103);check(p.fightMode==0,"godsword Chop trains Attack");GodWarsEquipment.button(p,18104);check(p.fightMode==1,"godsword Block trains Defence");GodWarsEquipment.button(p,18105);check(p.fightMode==2&&MeleeData.getWeaponAnimation(p)==17049,"godsword Smash trains Strength with crush animation");GodWarsEquipment.button(p,18106);check(p.fightMode==2&&!p.gwdCrushStyle,"godsword Slash trains Strength");
        place(p,2870,5360,2);place(v,2871,5360,2);p.playerLevel[3]=40;p.playerLevel[5]=40;p.specEffect=101;GodWarsEquipment.effect(p,v,null,40);check(p.playerLevel[3]==60&&p.playerLevel[5]==50,"Saradomin godsword restores half damage and quarter prayer");
        p.specEffect=101;GodWarsEquipment.effect(p,v,null,0);check(p.playerLevel[3]==60&&p.specEffect==0,"miss cannot heal or leak special to next hit");
        p.specEffect=105;Arrays.fill(v.playerLevel,5);GodWarsEquipment.effect(p,v,null,17);check(v.playerLevel[1]==0&&v.playerLevel[2]==0&&v.playerLevel[5]==0&&v.playerLevel[0]==3,"Bandos drain follows original skill order");
        p.specEffect=102;v.freezeTimer=-3;GodWarsEquipment.effect(p,v,null,10);check(v.freezeTimer==33,"Zamorak godsword freezes for twenty seconds");
        p.specEffect=102;v.freezeTimer=-1;GodWarsEquipment.effect(p,v,null,10);check(v.freezeTimer==-1,"Zamorak godsword respects freeze immunity");
        inventory(p);p.getItemAssistant().addItem(11690,1);p.getItemAssistant().addItem(11702,1);GodWarsEquipment.combine(p,11690,11702);check(amount(p,11694)==1&&amount(p,11690)==0&&amount(p,11702)==0,"blade and hilt combine without duplication");
        GodWarsEquipment.dismantle(p,11694);check(amount(p,11694)==0&&amount(p,11690)==1&&amount(p,11702)==1,"godsword dismantles into exact components");
        place(p,2835,5300,2);check(!GodWars.canAttack(p,3919,false)&&GodWars.canAttack(p,3919,true),"flying boss requires ranged or magic");
        place(p,2865,5354,2);check(NpcHandler.npcs[slot(3957)].inMulti(),"God Wars NPCs permit group combat");
        p.npcIndex=slot(3957);p.playerEquipment[3]=11698;p.playerBonus[10]=150;p.playerLevel[3]=40;p.playerLevel[5]=40;
        Npc sgsTarget=NpcHandler.npcs[p.npcIndex];sgsTarget.defence=0;
        for(int trial=0;trial<20&&p.playerLevel[3]==40;trial++){p.specEffect=101;p.getCombatAssistant().applyNpcMeleeDamage(p.npcIndex,1);}
        check(p.playerLevel[3]>40&&p.playerLevel[5]>40&&p.specEffect==0,"Saradomin godsword effect runs through actual melee hit callback");
        int bossSlot=slot(3957);Npc boss=NpcHandler.npcs[bossSlot];boss.HP=0;GodWars.recordDamage(p,bossSlot,255);GodWars.processNpc(bossSlot);check(boss.applyDead&&boss.killedBy==1,"boss death chooses damage contributor");
        for(int i=0;i<5;i++)GodWars.processNpc(bossSlot);check(boss.needRespawn&&boss.absX==0,"boss corpse disappears after death animation");int kills=p.gwdKillCount[3];
        for(int i=0;i<199;i++)GodWars.processNpc(bossSlot);check(boss.isDead&&p.gwdKillCount[3]==kills,"dead boss grants no duplicate loot or kills");
        GodWars.processNpc(bossSlot);check(!boss.isDead&&boss.HP==boss.MaxHP&&boss.absX==boss.makeX,"boss respawns at original position with full health");
        for(int type:new int[]{3900,3919,3944,3957}){
            Npc leader=NpcHandler.npcs[slot(type)];place(p,leader.makeX+leader.size,leader.makeY,leader.heightLevel);place(v,3222,3218,0);
            Arrays.fill(p.getPrayer().prayerActive,false);p.npcIndex=slot(type);int dealt=0,attacks=0;
            for(int t=0;t<400;t++){p.playerLevel[3]=99;GodWars.processNpc(slot(type));tick();dealt+=99-p.playerLevel[3];if(leader.animUpdateRequired)attacks++;leader.animUpdateRequired=false;p.getOutStream().currentOffset=0;}
            check(dealt>0&&attacks>20,"400-tick boss combat run "+type+" attacks="+attacks+" damage="+dealt);
            place(p,3222,3218,0);int hp=p.playerLevel[3];GodWars.processNpc(slot(type));tick();check(p.playerLevel[3]==hp,"leaving room cancels boss hits "+type);
        }
        place(p,3222,3218,0);place(v,3222,3218,0);
        Npc first=null,second=null;
        for(Npc n:NpcHandler.npcs)if(n!=null&&GodWars.isNpc(n.npcType)&&GodWars.chamber(n.npcType)==null){if(first==null)first=n;else if((GodWars.original(n.npcType)<=6221)!=(GodWars.original(first.npcType)<=6221)){second=n;break;}}
        check(first!=null&&second!=null,"opposing army NPCs exist");
        first.absX=2882;first.absY=5311;second.absX=2883;second.absY=5311;first.heightLevel=second.heightLevel=3;first.HP=second.HP=100;first.attackTimer=second.attackTimer=0;
        for(int t=0;t<60&&first.HP>0&&second.HP>0;t++){GodWars.processNpc(first.npcId);GodWars.processNpc(second.npcId);}
        check(first.HP<100||second.HP<100,"opposing factions fight without players present");
        // Disposable characters for separate real-client rendering checks.
        if(args.length>0&&args[0].equals("prepare-clients")){
            int[][] positions={{2882,5311,2},{2862,5354,2},{2839,5294,2},{2925,5333,2},{2909,5265,0},{2914,3744,0}};
            for(int i=0;i<positions.length;i++){
                Fighter qa=new Fighter(10+i);qa.playerName=qa.playerName2="gwdqa"+i;qa.playerPass="gwd-local-qa";
                qa.saveFile=qa.saveCharacter=true;qa.newPlayer=false;qa.gwdRopes=7;Arrays.fill(qa.gwdKillCount,40);
                qa.absX=positions[i][0];qa.absY=positions[i][1];qa.heightLevel=positions[i][2];
                qa.playerEquipment[3]=i==0?11716:11694;qa.playerEquipmentN[3]=1;qa.playerEquipment[4]=11724;qa.playerEquipmentN[4]=1;qa.playerEquipment[0]=11718;qa.playerEquipmentN[0]=1;
                qa.playerEquipment[1]=2412;qa.playerEquipmentN[1]=1;qa.playerEquipment[2]=1724;qa.playerEquipmentN[2]=1;
                PlayerHandler.players[qa.playerId]=qa;
                check(PlayerSave.saveGame(qa),"staged client fixture saved "+i);
                PlayerHandler.players[qa.playerId]=null;
            }
        }
        PlayerHandler.players[1]=PlayerHandler.players[2]=null;
        System.out.println("GOD WARS CHECKS: "+checks+" passed");
    }
}