
/*
class
level
curhp
maxhp
str
skill
spd
luck
def
res
wpn[8]
items[5]
*/

function encodebits(astate,aout,aoutStart,iter){
 var bit=0
 var pointer=aoutStart
 var i=0
 for(i=0;i<iter;i++){
  var nextbyte=aout[pointer]
  var nextbit=nextbyte>>bit
  if(nextbit&1)
   astate.buf[astate.ptr]|=(1<<astate.bit)
  bit++
  if(bit==8){
   pointer++
   bit=0
  }
  astate.bit++
  if(astate.bit==8){
   astate.ptr++
   astate.bit=0
  }
  astate.totalbits++
 }
}

function decodebits(astate,aout,aoutStart,iter){
 var bit=0
 var pointer=aoutStart
 var i=0
 for(i=0;i<iter;i++){
  if(bit==0){
   aout[pointer]=0
  }
  nextbyte=astate.buf[astate.ptr]
  nextbit=nextbyte>>astate.bit
  if(nextbit&1)
   aout[pointer]|=(1<<bit)
  bit++
  if(bit==8){
   pointer++
   bit=0
  }
  astate.bit++
  if(astate.bit==8){
   astate.ptr++
   astate.bit=0
  }
  astate.totalbits++
 }
}

var wondercode=
"162117071D130F091505100306120E110B"+
"1A1C190C0D04010A14181B1F002002081E"

function c2c(s,c){
 return parseInt(s.substr(c<<1,2),16)
}
function c2w(s,c){
 return c2c(s,c<<1)|(c2c(s,(c<<1)+1)<<8)
}


function CharToCode(c){
 return parseInt(charto.substr(c<<1,2),16)
}

function EncodePassHelper(src,dst,size){
 var i
 var codes=[]
 var astate=new Object()
 for(i=0;i<size;i++){
  var tos=src[i].charCodeAt(0)
  if(tos<=0x20||tos>0x60)return 0;
  codes[i]=tos-0x21
 }
 var r2=size*6+6
 if(r2<0)r2+=7
 r2>>=3
 astate.buf=[]
 astate.bit=astate.ptr=astate.totalbits=0
 for(i=0;i<r2;i++){
  astate.buf[i]=0
 }
 for(i=0;i<size;i++){
  encodebits(astate,codes,i,6);
 }
 for(i=0;i<r2;i++){
  dst[i]=astate.buf[i]
 } 
 return 1
}

function EncodeUnit(unit){
 var i
 var aout=[]
 var codes=[]
 var retpass=[]
 var astate=new Object()
 var astate2=new Object()
 var totalbytes;
 var passlength;
 astate.buf=[]
 astate.bit=astate.ptr=astate.totalbits=0
 aout[0]=unit.cclass
 aout[1]=unit.level
 aout[2]=unit.curhp
 aout[3]=unit.maxhp
 aout[4]=unit.str
 aout[5]=unit.skill
 aout[6]=unit.speed
 aout[7]=unit.luck
 aout[8]=unit.def
 aout[9]=unit.res
 for(i=0;i<5;i++){
  aout[10+i]=unit.items[i]
 }
 for(i=0;i<8;i++){
  aout[15+i]=unit.wpnlevel[i]
 }
 for(i=0;i<26;i++){
  astate.buf[i]=0
 }
 encodebits(astate,aout,0,7)
 encodebits(astate,aout,1,5)
 encodebits(astate,aout,2,7)
 encodebits(astate,aout,3,7)
 for(i=4;i<10;i++){
  encodebits(astate,aout,i,5);
 }
 for(i=10;i<15;i++){
  encodebits(astate,aout,i,8);
 }
 for(i=15;i<23;i++){
  encodebits(astate,aout,i,3);
 }
 for(i=0;i<9;i++){
  var tmp=[]
  if(i<unit.name.length){
   tmp[0]=unit.name.charCodeAt(i)&0xFF
  } else {
   tmp[0]=0
  }
  encodebits(astate,tmp,0,8);
 }
 totalbytes=(astate.totalbits+7)>>3
 var sum=0
 for(i=0;i<totalbytes;i++){
  sum+=astate.buf[i]+(i+1)
  sum&=0xFF
 }
 astate2.buf=[]
 astate2.bit=astate2.ptr=astate2.totalbit=0
 astate2.buf[0]=sum
 for(i=1;i<totalbytes+1;i++){
  astate2.buf[i]=astate.buf[i-1]
 }
 astate2.buf[totalbytes+1]=0;
 passlength=(totalbytes+1)<<3
 passlength=Math.floor((passlength+5)/6)
// alert([totalbytes,passlength])
 for(i=0;i<passlength;i++){
  decodebits(astate2,codes,i,6); 
 }
 for(i=0;i<passlength;i++){
  codes[i]=String.fromCharCode(codes[i]+0x21);
 }
 for(i=0;i<passlength;i++){
  var offset=0
  for(j=0;j<passlength;j++){
   if(c2c(wondercode,j)==i){
    offset=j
    break
   }
  }
  retpass[i]=codes[offset]
 }
 return retpass.join("")
}

function ItemImage(item){
 var num=("000"+(item-1))
 num=num.substr(num.length-3)
 return "<img src=\"../images/fe7/item"+num+".gif\" width=\"16\" height=\"16\" alt=\"\" />"
}

function ItemLabel(item){
 var ret="<span title=\""
 var range=ItemData[item][5]
 var amax=range&0x0F;
 var amin=range>>4;
 var typenames=["Sword","Lance","Axe","Bow","Staff","Anima","Light","Dark"]
 var type=ItemData[item][0]
 var rangestr=""
 if(type>=8){
  return ItemImage(item)+ItemNames[item]
 }
 ret+=typenames[type]+"; Range: "
 if(range==0x10){
  rangestr="MP/2"
 } else if(amax==amin){
  rangestr=amax
 } else {
  rangestr=amin+"-"+amax
 }
 ret+=rangestr
 ret+="; Wt: "+ItemData[item][1]
 ret+="; Mt: "+ItemData[item][2]
 ret+="; Hit: "+ItemData[item][3]
 ret+="; Crit: "+ItemData[item][4]
 ret+="\">"+ItemImage(item)+ItemNames[item]+"</span>"
 return ret
}

function UnitLabel(unit){
 var ret=""
 var num=("000"+(unit.cclass-1))
 num=num.substr(num.length-3)
 ret+="<img src=\"../images/fe7/class"+num+".gif\" alt=\"\" />"
 ret+=" <b>"+unit.name+"</b> ("+ClassNames[unit.cclass]+")"
 return ret 
}

function UnitToHtml(unit){
 var ret=""
 var i
// ret+="<xmp>"
 ret+=UnitLabel(unit)+"<br/>"
 ret+="&nbsp;&nbsp;Level <b>"+unit.level+"</b>; HP: <b>"+unit.curhp+"/"+MaxHP(unit)+"</b><br/>"
 ret+="&nbsp;&nbsp;&nbsp;&nbsp;"
 ret+="Str: "+unit.str
 if(Str(unit)!=unit.str){
  ret+="+"+(Str(unit)-unit.str)
 }
 ret+="; Skill: "+unit.skill
 if(Skill(unit)!=unit.skill){
  ret+="+"+(Skill(unit)-unit.skill)
 }
 ret+="; Speed: "+unit.speed
 if(Speed(unit)!=unit.speed){
  ret+="+"+(Speed(unit)-unit.speed)
 }
 ret+="<br/>&nbsp;&nbsp;&nbsp;&nbsp;"
 ret+="Luck: "+unit.luck
 if(Luck(unit)!=unit.luck){
  ret+="+"+(Luck(unit)-unit.luck)
 }
 ret+="; Def: "+unit.def
 if(Def(unit)!=unit.def){
  ret+="+"+(Def(unit)-unit.def)
 }
 ret+="; Res: "+unit.res
 if(Res(unit)!=unit.res){
  ret+="+"+(Res(unit)-unit.res)
 }
 ret+="<br/>";
 for(i=0;i<5;i++){
  if(unit.items[i]){
   ret+=ItemLabel(unit.items[i])+"<br/>"
  }
 }
 ret+="&nbsp;&nbsp;&nbsp;&nbsp;"
 ret+="Atk: "+Atk(unit,null)
 ret+="; Hit: "+Hit(unit)
 ret+="; Crit: "+Crit(unit)
 ret+="<br/>&nbsp;&nbsp;&nbsp;&nbsp;"
 ret+="Avoid: "+Avoid(unit)
// ret+="</xmp>"
 ret+="</xmp>"
 return ret
}


function DecodeUnit(pass){
 var i=0
 var aout=[]
 if(pass.length<34)
  return null
 var newpass=[]
 for(i=0;i<34;i++){
  var offset=c2c(wondercode,i)
  var k=0,j=0
  if(offset!=k){
   k=offset
  }
  newpass[i]=pass.charAt(k)
 }
 var code=[]
 if(!EncodePassHelper(newpass,code,34))
  return null
 var sum=0
 for(i=1;i<25;i++){
  sum+=code[i]+i
  sum&=0xFF
 }
 if(sum!=code[0]){
  return null;
 }
 var astate=new Object()
 var name=""
 astate.buf=code.slice(1)
 astate.bit=astate.ptr=astate.totalbit=0
 for(i=0;i<25;i++){
  aout[i]=0
 }
 decodebits(astate,aout,0,7)
 decodebits(astate,aout,1,5)
 decodebits(astate,aout,2,7)
 decodebits(astate,aout,3,7)
 for(i=4;i<10;i++){
  decodebits(astate,aout,i,5);
 }
 for(i=10;i<15;i++){
  decodebits(astate,aout,i,8);
 }
 for(i=15;i<23;i++){
  decodebits(astate,aout,i,3);
 }
 for(i=0;i<9;i++){
  var tmp=[]
  decodebits(astate,tmp,0,8);
  if(tmp[0]!=0){
   name+=String.fromCharCode(tmp[0])
  }
 }
 var unit=new FEUnit(aout[0])
 unit.level=aout[1]
 unit.curhp=aout[2]
 unit.maxhp=aout[3]
 unit.str=aout[4]
 unit.skill=aout[5]
 unit.speed=aout[6]
 unit.luck=aout[7]
 unit.def=aout[8]
 unit.res=aout[9]
 unit.name=name
 for(i=0;i<5;i++){
  unit.items[i]=aout[10+i]
 }
 for(i=0;i<8;i++){
  unit.wpnlevel[i]=aout[15+i]
 }
 return unit
}


function FEUnit(cclass){
 var i=0
 var skills=ClassData[cclass][0]
 this.cclass=cclass
 this.wpnlevel=[]
 this.items=[]
 for(i=0;i<8;i++){
  if(skills&(1<<i)){
   this.wpnlevel[i]=6//"S" level
  } else {
   this.wpnlevel[i]=0
  }
 }
 for(i=0;i<5;i++){
  this.items[i]=0
 }
 this.name=""
 this.curhp=1
 this.maxhp=1
 this.str=1
 this.skill=1
 this.speed=1
 this.luck=1
 this.def=1
 this.res=1
 this.level=20
}



function HasItem(unit,finditem){
 var i=0
 for(i=0;i<5;i++){
  var item=unit.items[i]
  if(item==finditem){
   return 1;
  }
 }
 return 0
}

function SetEquipItem(unit,pos){
 var i=0
 var eqppos=-1
 if(pos>=5||pos<0)return
 if(unit){
  var tmp=unit.items[pos]
  unit.items[pos]=unit.items[0]
  unit.items[0]=tmp
 }
}


function EquipItem(unit){
 var i=0
 if(unit){
  for(i=0;i<5;i++){
   var item=unit.items[i]
   if(item>0){
    var type=ItemData[item][0]
    if(type<8)return item
   }
  }
 }
 return 0
}

function MaxHP(unit,item){
 var eqp=(item)?item:EquipItem(unit)
 var ret=unit.maxhp
 if(eqp==0x77)ret+=17;
 return ret;
}


function Str(unit,item){
 var eqp=(item)?item:EquipItem(unit)
 var ret=unit.str
 if(eqp==0x77)ret+=5;
 if(eqp==0x84)ret+=5;
 if(eqp==0x8F)ret+=10;
 return ret;
}

function Skill(unit,item){
 var eqp=(item)?item:EquipItem(unit)
 var ret=unit.skill
 if(eqp==0x77)ret+=4;
 if(eqp==0x8F)ret+=10;
 return ret;
}

function Speed(unit,item){
 var eqp=(item)?item:EquipItem(unit)
 var ret=unit.speed
 if(eqp==0x77)ret+=9;
 return ret;
}

function Luck(unit,item){
 var eqp=(item)?item:EquipItem(unit)
 var ret=unit.luck
 if(eqp==0x3C)ret+=5;
 return ret;
}

function Def(unit,item){
 var eqp=(item)?item:EquipItem(unit)
 var ret=unit.def
 if(eqp==0x77)ret+=4;
 if(eqp==0x85)ret+=5;
 if(eqp==0x8F)ret+=20;
 return ret;
}

function Res(unit,item){
 var eqp=(item)?item:EquipItem(unit)
 var ret=unit.res
 if(eqp==0x77)ret+=14;
 if(eqp==0x86)ret+=5;
 if(eqp==0x8C)ret+=5;
 if(eqp==0x8F)ret+=10;
 return ret;
}

function Avoid(unit,item){
 var eqp=(item)?item:EquipItem(unit)
 var spd=AttackSpeed(unit,eqp)
 var luck=Luck(unit,eqp)
 var ret=(spd<<1)+luck;
 if(HasItem(unit,0x8A)){
  ret+=10;
 }
 return Math.max(ret,0);
}

function NegotiateRange(itemAtt,itemOpp){
 var rangeAtt=ItemData[itemAtt][5]
 var rangeOpp=ItemData[itemOpp][5]
 var amax=rangeAtt&0x0F;
 var amin=rangeAtt>>4;
 var omax=rangeOpp&0x0F;
 var omin=rangeOpp>>4;
 var isectMin,isectMax
 var usable=0
 if(amax==0||amin>2){
  return [0,1]
 }
 if(itemOpp==0){
  return [amin,0]
 }
 if(omax==0||omin>2){
  return [0,1]
 }
 isectMin=Math.max(omin,amin);
 isectMax=Math.min(omax,amax);
 if(isectMin<=isectMax){
  return [isectMin,1]
 } else {
  return [amin,0]
 }
}


function DamageExp(attacker,opponent){
 var aclass=attacker.cclass;
 var oclass=opponent.cclass;
 var alevel=attacker.level;
 var olevel=opponent.level;
 var aupper=ClassData[aclass][1]?20:0;
 var oupper=ClassData[oclass][1]?20:0;
 var astr=ClassData[aclass][4]
 var ret=Math.floor((oupper+31+olevel-aupper-alevel)/astr)
 return Math.max(0,ret)
}

function BaseDefeatExp(attacker,opponent){
 var aclass=attacker.cclass;
 var oclass=opponent.cclass;
 var alevel=attacker.level;
 var olevel=opponent.level;
 var aupper=ClassData[aclass][1]?60:0;
 var oupper=ClassData[oclass][1]?60:0;
 var astr=ClassData[aclass][4]
 var ostr=ClassData[oclass][4]
 var playmode=1
 var ret
 var aresult,oresult
 if(aclass==31||aclass==62||aclass==45){
  aupper=40;
 }
 if(oclass==31||oclass==62||oclass==45){
  oupper=40;
 }
 aresult=(aupper+alevel*astr)
 oresult=(oupper+olevel*ostr)
 if(oresult<=aresult)
  aresult/=2;
 ret=Math.floor((oresult-aresult)/playmode)
 return ret
}

function DefeatExp(attacker,opponent,crit){
 var dmgexp=DamageExp(attacker,opponent)
 var defexp=BaseDefeatExp(attacker,opponent)+20
 var oclass=opponent.cclass;
 var ret
 if(oclass==60||oclass==61||oclass==62){
  defexp+=20;
 }
 ret=Math.max(0,dmgexp+Math.max(0,defexp))
 if(crit){
  ret+=ret;
 }
 return ret;
}

function Reverse(i){
 return (i==0x12||i==0x1e||i==0x2A||i==0x2B)?1:0;
}

function Advantage(tUnit,tOpp){
 return ((tUnit==0&&tOpp==2)  //swords over axes
       ||(tUnit==1&&tOpp==0)  //lances over swords
       ||(tUnit==2&&tOpp==1)  //axes over lances
       ||(tUnit==5&&tOpp==6)  //anima over light
       ||(tUnit==6&&tOpp==7)  //light over dark
       ||(tUnit==7&&tOpp==5)) //dark over anima
}

function AttackType(item,range){
 var type=ItemData[item][0]
 if(item==0x11){
  return 7
 } else if(item==0x10){
  return (range==2)?6:0
 } else if(item==0x99){
  return (range==2)?5:0
 }
 return type
}

function WeaponAdvantage(eqp,oEqp,range){
 var typeUnit=AttackType(eqp,range)
 var typeOpp=AttackType(oEqp,range)
 var reverse=Reverse(eqp)^Reverse(oEqp);
 var advant=0;
 var advObv=Advantage(typeUnit,typeOpp);
 var advRev=Advantage(typeOpp,typeUnit);
 if(!advObv&&!advRev)return 0;
 if(reverse){
  return (advRev)?4:3;
 } else {
  return (advObv)?2:1;
 }
 return 0;
}

function ClassAdvantage(item,opponent){
 var eff=ItemData[item][7]
 var type=ItemData[item][0]
 var cclass=opponent.cclass
 if(type==3&&HasItem(opponent,0x70)){
  //bow, opponent has Delphi shield
  return 0;
 }
 if(eff){
  eff-=1;
  for(var i=eff;;i++){
   if(ClassAdv[i]==0)break;
   if(ClassAdv[i]==cclass)return 1
  }
 }
 return 0
}

function Hit(unit){
 return HitEx(EquipItem(unit),0,unit,null,0)
}

function Atk(unit,opponent,range){
 return AtkEx(EquipItem(unit),EquipItem(opponent),unit,opponent,range);
}

function AtkEx(eqpUnit,eqpOpp,unit,opponent,range){
 if(eqpUnit){
  var mt=ItemData[eqpUnit][2]
  if(opponent){
   if(eqpOpp){
    var adv=WeaponAdvantage(eqpUnit,eqpOpp,range);
    if(adv==2||adv==4){
     mt+=(adv==4)?2:1;
    } else if(adv==1||adv==3){
     mt-=(adv==3)?2:1;
    }
   }
   if(ClassAdvantage(eqpUnit,opponent)){
    mt*=2;
   }
  }
  return mt+Str(unit,eqpUnit);
 }
 return 0;
}


function HitEx(eqpUnit,eqpOpp,unit,opponent,range){
 if(eqpUnit){
  var type=ItemData[eqpUnit][0]
  var skill=Skill(unit,eqpUnit)
  var luck=Luck(unit,eqpUnit)
  var ret=ItemData[eqpUnit][3]+(skill<<1)+(luck>>1);
  if(unit.wpnlevel[type]==6){
   ret+=5;
  }
  if(opponent&&eqpOpp){
   var adv=WeaponAdvantage(eqpUnit,eqpOpp,range);
    if(adv==2||adv==4){
     ret+=(adv==4)?30:15;
    } else if(adv==1||adv==3){
     ret-=(adv==3)?30:15;
    }
  }
  return ret;  
 }
 return 0;
}

function AttackSpeed(unit,item){
 var eqp=(item)?item:EquipItem(unit)
 var spd=Speed(unit,eqp)
 if(eqp){
  var wt=ItemData[eqp][1]
  var con=ClassData[unit.cclass][2];
  if(wt>con){
   spd-=(wt-con);
  }
 }
 return spd
}

function Crit(unit,item,range){
 var eqp=(item)?item:EquipItem(unit)
 if(eqp){
  var type=ItemData[eqp][0]
  var skill=Skill(unit,eqp)
  var ret=ItemData[eqp][4]+(skill>>1);
  var cclass=unit.cclass
  if(type!=AttackType(eqp,range)){
   return 0
  }
  if(unit.wpnlevel[type]==6){
   ret+=5;
  }
  if(cclass==16||cclass==17||cclass==59){
   ret+=15;
  }
  return ret;
 }
 return 0
}



