Emberveil source

tests/QuestRouteCheck.java

import com.rs2.world.clip.*;
import org.apollo.cache.*;
import org.apollo.cache.decoder.*;
import java.nio.file.*;
import java.util.*;
public class QuestRouteCheck {
 static int checks;
 static void check(boolean ok,String msg){if(!ok)throw new AssertionError(msg);checks++;}
 static boolean route(int sx,int sy,int tx,int ty,int z,int radius){
  Set<Long> seen=new HashSet<>();ArrayDeque<int[]> q=new ArrayDeque<>();q.add(new int[]{sx,sy});seen.add(((long)sx<<32)|sy);
  while(!q.isEmpty()){
   int[] t=q.remove();if(Math.abs(t[0]-tx)<=radius&&Math.abs(t[1]-ty)<=radius)return true;
   for(int dx=-1;dx<=1;dx++)for(int dy=-1;dy<=1;dy++){
    if(dx==0&&dy==0)continue;int x=t[0]+dx,y=t[1]+dy;
    if(x<Math.min(sx,tx)-96||x>Math.max(sx,tx)+96||y<Math.min(sy,ty)-96||y>Math.max(sy,ty)+96)continue;
    long key=((long)x<<32)|y;if(!seen.contains(key)&&Region.canMove(t[0],t[1],x,y,z,1,1)){seen.add(key);q.add(new int[]{x,y});}
   }
  }int best=99999,bx=0,by=0;for(long key:seen){int x=(int)(key>>32),y=(int)key;int d=Math.max(Math.abs(x-tx),Math.abs(y-ty));if(d<best){best=d;bx=x;by=y;}}System.out.println("NO ROUTE "+sx+","+sy+" -> "+tx+","+ty+" reachable="+seen.size()+" closest="+bx+","+by+" distance="+best);return false;
 }
 public static void main(String[] args)throws Exception {
  IndexedFileSystem cache=new IndexedFileSystem(Paths.get("data/cache"),true);new ObjectDefinitionDecoder(cache).run();RegionFactory.load(cache);
  for(int[] o:new int[][]{{2584,2824,3118},{2575,2757,3041},{2577,2870,3115},{2579,2789,3047},{2581,2850,3035},{2583,2829,9493},{2142,2893,9831}})System.out.println("SIZE "+o[0]+" "+Arrays.toString(Region.getObject(o[0],o[1],o[2],0).getObjectSize()));
  for(int[] tile:new int[][]{{2831,9521,0},{2823,3118,0},{2809,3086,0},{2896,3429,1},{2925,3486,0}}){
   int clip=Region.getClipping(tile[0],tile[1],tile[2]);
   if((clip&0x1280100)!=0)for(int x=tile[0]-2;x<=tile[0]+2;x++)for(int y=tile[1]-2;y<=tile[1]+2;y++)System.out.println("NEAR "+x+","+y+","+tile[2]+"="+Integer.toHexString(Region.getClipping(x,y,tile[2])));
   check((clip&0x1280100)==0,"destination blocked: "+Arrays.toString(tile)+" clip="+Integer.toHexString(clip));
  }
  check(route(2831,9521,2829,9493,0,1),"cave entrance can reach Rogue's purse wall");
  check(route(2829,9494,2830,9522,0,1),"herb wall can reach cave exit");
  check(route(2809,3086,2824,3118,0,1),"Trufitus area can reach cave entrance");
  check(route(2809,3086,2757,3041,0,1),"village can reach Snake weed");
  check(route(2809,3086,2870,3115,0,1),"village can reach Ardrigal palm");
  check(route(2809,3086,2789,3047,0,1),"village can reach Sito foil");
  check(route(2809,3086,2850,3035,0,1),"village can reach Volencia moss");
  check(route(2884,9798,2898,9831,0,1),"dungeon ladder can reach cauldron gate");
  check(route(2897,9831,2893,9831,0,1),"inside gate can reach cauldron");
  com.google.gson.JsonArray spawns=new com.google.gson.Gson().fromJson(new java.io.FileReader("data/cfg/spawns.json"),com.google.gson.JsonArray.class);
  int addedEnemies=0;
  for(com.google.gson.JsonElement row:spawns){com.google.gson.JsonObject s=row.getAsJsonObject();int id=s.get("id").getAsInt(),x=s.get("x").getAsInt(),y=s.get("y").getAsInt();
   if((id==113&&y>=9470&&y<=9530)||(id==453&&y>=9800&&y<=9840)){
    addedEnemies++;int size=id==113?2:1;
    boolean valid=true;for(int dx=0;dx<size;dx++)for(int dy=0;dy<size;dy++)if((Region.getClipping(x+dx,y+dy,0)&0x1280100)!=0)valid=false;
    if(!valid){int printed=0;for(int xx=x-3;xx<=x+3;xx++)for(int yy=y-3;yy<=y+3;yy++){boolean safe=true;for(int dx=0;dx<size;dx++)for(int dy=0;dy<size;dy++)if((Region.getClipping(xx+dx,yy+dy,0)&0x1280100)!=0)safe=false;if(safe&&printed++<5)System.out.println("SAFE NPC "+id+" "+xx+","+yy);}}
    for(int dx=0;dx<size;dx++)for(int dy=0;dy<size;dy++)check((Region.getClipping(x+dx,y+dy,0)&0x1280100)==0,"NPC footprint blocked: "+id+" "+(x+dx)+","+(y+dy));
   }
  }check(addedEnemies==7,"five Jogres and two armour guards have usable footprints");
  MemberQuestsCheck.Fixture p=new MemberQuestsCheck.Fixture();p.pos(2899,9831,0);p.objectX=2898;p.objectY=9831;
  p.getObjects().firstClickObject(1557,2898,9831);
  check(p.getGateHandler().gateStatus!=p.getGateHandler().CLOSED,"cauldron gate opens through real handler");
  check(p.teleportToX==2896&&p.teleportToY==9831,"cauldron gate crosses stale legacy collision");
  check(route(p.teleportToX,p.teleportToY,2893,9831,0,1),"gate passage permits cauldron approach");
  p.pos(2896,9831,0);p.getObjects().firstClickObject(1557,2897,9831);
  check(p.teleportToX==2899&&p.teleportToY==9831,"cauldron gate also allows exit");
  System.out.println("PASS: "+checks+" quest destination and collision-route checks");
 }
}