<?
// --------------------------------------------------------------------------------
// bpkm handler class - the Pokécheck VS. Player - https://www.pokecheck.org
// Damage calculation http://www.smogon.com/bw/articles/bw_complete_damage_formula
// Researched by Xfr, Bond697 & Kaphotics
// License: PUBLIC DOMAIN except for all copyrighted names and trademarks
// --------------------------------------------------------------------------------
class BPkm
{
public $hex = "";
public $pid;
public $exp;
public $dex;
public $tid;
public $sid;
public $item;
public $happiness;
public $ability;
public $oability;
public $nature;
public $evs;
public $ivs;
public $riv;
public $rev;
public $moves;
public $pp;
public $gender;
public $form;
public $oform;
public $rnick;
public $nick;
public $rot;
public $ot;
public $ball;
public $lang;
public $lvl;
public $bstats;
public $obstats;
public $base;
public $fateful;
public $shiny;
public $dexinfo;
public $hpt;
public $hpp;
public $player;
public $slot;
public $id;
public $U;
public $hp;
public $planned;
public $params;
public $type1, $type2;
public $statups = array(0,0,0,0,0,0,0);
public $stockpile = 0;
public $critlevel = 0;
public $lastitem = 0;
public $status = 0;
public $volatile = 0;
public $volatile2 = 0;
public $toxiccount = 0;
public $perishcount = 0;
public $sleepcount = 0;
public $sleepturns = 0;
public $confturns = 0;
public $trapturns = 0;
public $encoreturns = 0;
public $disableturns = 0;
public $hbturns = 0;
public $mturns = 0;
public $disabled = 0;
public $trapper = null;
public $tauntturns = 0;
public $grounded = 0;
public $subhp = 0;
public $hadsub = 0;
public $turncount = 0;
public $protectcount = 0;
public $quickclaw = 2;
public $unaware = 0;
public $unburden = 0;
public $mindread = 0;
public $hurtby = array();
public $hasmoved = 0;
public $mcharge = 0;
public $mimic = 0;
public $onfield = 0;
public $switching = 0;
public $history = array();
public $hhistory = array();
public $shistory = array();
public $leecher, $lover;
const F_NONE = 0;
const F_CONFUSED = 1;
const F_INFATUATED = 2;
const F_TRAPPED = 4;
const F_NIGHTMARE = 8;
const F_TAUNTED = 16;
const F_CHARGE = 32;
const F_TORMENT = 64;
const F_DISABLE = 128;
const F_YAWN = 256;
const F_HEALBLOCK = 512;
const F_FLASHFIRE = 1024;
const F_NOIMMUNITY = 2048;
const F_LEECHED = 4096;
const F_EMBARGO = 8192;
const F_PERISH = 16384;
const F_INGRAIN = 32768;
const F_IMPRISON = 65536;
const F_MAGNETRISE = 131072;
const F_POWERTRICK = 262144;
const F_ENCORE = 524288;
const F_CURSE = 1048576;
const F_YAWN2 = 2097152;
const F_AQUARING = 4194304;
const F_DESTINYBOND = 8388608;
public static $vlabel = array
(
self::F_CONFUSED=>"confused",
self::F_INFATUATED => "infatuated",
self::F_TRAPPED=>"trapped",
self::F_NIGHTMARE=>"having a nightmare",
self::F_TAUNTED => "taunted",
self::F_CHARGE => "charging",
self::F_TORMENT=>"tormented",
self::F_DISABLE=>"unable to use its last move again",
self::F_YAWN => "feeling drowsy",
self::F_EMBARGO => "prevented from using its item",
self::F_HEALBLOCK=>"unable to heal",
self::F_LEECHED=>"infected",
self::F_PERISH=>"under the effect of perish song",
self::F_INGRAIN=>"rooted",
self::F_IMPRISON=>"preventing foes from using its moves",
self::F_MAGNETRISE=>"levitating",
self::F_POWERTRICK=>"switching its attack and defense",
self::F_ENCORE=>"forced to use the same move"
);
public static $slabel = array
(
self::S_NONE=>"cured of status problems",
self::S_PARALYZED=>"paralyzed",
self::S_POISON=>"poisoned",
self::S_TOXIC=>"badly poisoned",
self::S_BURNED=>"burned",
self::S_ASLEEP=>"asleep",
self::S_FREEZE=>"frozen",
self::S_FAINTED=>"fainted"
);
public static $stag = array
(
self::S_NONE=>"",
self::S_PARALYZED=>"par",
self::S_POISON=>"psn",
self::S_TOXIC=>"tox",
self::S_BURNED=>"brn",
self::S_ASLEEP=>"slp",
self::S_FREEZE=>"frz",
self::S_FAINTED=>"fnt"
);
const F2_NONE = 0;
const F2_FLINCHED = 1;
const F2_PROTECT = 2;
const F2_WIDEGUARD = 4;
const F2_HELPINGHAND = 8;
const F2_MEFIRST = 16;
const F2_GROUNDED = 32;
const F2_MAGICCOAT = 64;
const F2_RAGEPOWDER = 128;
const F2_QUICKGUARD = 512;
const F2_ENDURE = 1024;
const F2_YAWN1 = 2048;
const F2_YAWN2 = 4096;
const F2_NOTORMENT = 8192;
const S_NONE = 0;
const S_PARALYZED = 1;
const S_ASLEEP = 2;
const S_FREEZE = 3;
const S_BURNED = 4;
const S_POISON = 5;
const S_TOXIC = 6;
const S_FAINTED = 7;
const E_SENDOUT = 0;
const E_TURNSTART = 1;
const E_PREATTACK = 2;
const E_AFFECTED = 3;
const E_ATTACK = 4;
const E_EFFECTS = 5;
const E_FINALEFFECTS = 6;
const E_POSTATTACK = 7;
const E_FAINTED = 8;
const E_TURNEND = 9;
const E_WEATHER = 10;
const E_SWITCHOUT = 11;
const E_FAIL = 12;
const E_ITEMUPK = 13;
const E_LEECHUPK = 14;
const E_STATUSUPK = 15;
const E_FINALUPK = 16;
const E_PREATTACK2 = 17;
// Unified function for output
// Supports alternative codec (JS-style replay)
function O($txt, $js, $raw=false)
{
global $JSOUT, $JS;
if($raw && $raw>0){ echo $txt?($JSOUT?'':$txt):($JSOUT?$js:''); return; }
$u = isset($this->params['user'])?$this->params['user']:$this;
$t = $this;
// Text log output callback
$cb = function($m) use(&$t,&$u)
{
global $players, $partys, $log, $BV, $MOVES, $ABILITIES, $ITEMS, $JSOUT;
if(isset($m[2])) $p = explode(",",substr($m[2],1,-1));
else $p = array();
switch($m[1])
{
case "ab":
if($JSOUT) return $ABILITIES[count($p)?$p[0]:$t->ability];
return "<span class=\"ability\">".$ABILITIES[count($p)?$p[0]:$t->ability]."</span>";
case "n":
if($JSOUT) return $t->rnick;
return "<span class=\"pokemon\">{$t->nick}</span>";
case "lvl": return $t->lvl;
case "species":
if($JSOUT) return $t->dexinfo['name'].($t->oform?"-".$GLOBALS['forms'][$t->dex][$t->oform]:'');
return "<span class=\"pokemon\">{$t->dexinfo['name']}</span>";
case "fspecies":
return $t->dexinfo['name'].((count($p)?$t->dexinfo['form']:$t->oform)?"-".$GLOBALS['forms'][$t->dex][$t->oform]:'')
.", L{$t->lvl}".($t->gender==1?", F":(!$t->gender?", M":"")).($t->shiny?", shiny":"");
case "un":
if($JSOUT) return $u->rnick;
return "<span class=\"pokemon\">{$u->nick}</span>";
case "opp":
if($JSOUT) return "p".(2-($t->player%2)).": ".($players[1-($t->player%2)]->uname);
return "<span class=\"player".(1-($t->player%2))."\">".($players[1-($t->player%2)]->name)."</span>";
case "p":
if($JSOUT) return "p".(1+($t->player%2)).": ".$players[$t->player]->uname;
return "<span class=\"player{$t->player}\">{$players[$t->player]->name}</span>";
case "rp":
return $players[$t->player]->uname;
case "fslot": $full = true; goto _slot;
case "slot": $full = false; _slot:
$tmp = array("a","b","c","d","e","f");
$pk = count($p) ? $players[$p[0]]->party[$p[1]] : $t;
$slot = array_search($pk->id, $partys[$pk->player]);
if($pk->player > 1) $slot++;
return "p".(1+($pk->player%2)).($BV->type?$tmp[$slot]:'')
.($full ? ": {$pk->rnick}": "");
case "hpc":
return ceil($t->getstat(0,1))."/100".($t->status?" ".BPkm::$stag[$t->status]:"");
case "up":
if($JSOUT) return "p".(1+($u->player%2)).": ".$players[$u->player]->uname;
return "<span class=\"player{$u->player}\">{$players[$u->player]->name}</span>";
case "m":
$mid = count($p) ? $p[0] : $t->params['move'];
if($JSOUT) return $MOVES[$mid]["name"];
return "<span class=\"move\">".$MOVES[$mid]["name"]."</span>";
case "status":
if($JSOUT) return BPkm::$stag[count($p)?$p[0]:$t->status];
return BPkm::$slabel[count($p)?$p[0]:$t->status];
case "volatile":
return BPkm::$vlabel[$p[0]];
case "st":
$tmp = array("atk","def","spa","spd","spe","accuracy","evasion");
if($JSOUT) return $tmp[$p[0]-2];
return $GLOBALS['statnames'][$p[0]-1];
case "item":
$p[] = $t->item;
if(isset($ITEMS[$p[0]])) $n = $ITEMS[$p[0]];
else
{
$q = mysql_query("SELECT * FROM items WHERE id='{$p[0]}' AND lang='2'");
if($r = mysql_fetch_assoc($q)) $n = $ITEMS[$p[0]] = $r;
else $n = array("name"=>"Unknown({$p[0]})");
}
$n = $n['name'];
if($JSOUT) return $n;
return "<span class=\"item\">$n</span>";
case "pkn":
$pk = $players[$p[0]]->party[$p[1]];
if($JSOUT) return $pk->rnick;
return "<span class=\"pokemon\">{$pk->nick}</span>";
default: return '';
}
};
if($JSOUT)
{
if($js==="") return;
$tmp = preg_replace_callback("/%([a-z]+)(\([^)]+\))?%?/", $cb, $js);
if($raw) return $tmp;
$JS[] = $tmp;
}
else
{
if($txt==="") return;
$tmp = preg_replace_callback("/%([a-z]+)(\([^)]+\))?%?/", $cb, $txt)."<br />";
if($raw) return $tmp;
echo $tmp;
}
}
// There are many problems with this function (written before research was completed)
// The accuracy system is ironically not accurate. This deserves an addentum to the damage calc article
// Accuracy works like the damage formula, using separate triggers for base value, accuracy and evasion
// Applying some modifiers to the stats rather than base value (e.g. BrightPowder) will ruin the rounding
// when chaining them (typical example: Sand Veil+BrightPowder Garchomp). Left as TODO for now
// (current simulators also implement accuracy mostly wrong, a ref would definitely help)
// The same problem also exists for speed - TODO document 21BC968 and the 0x13 trigger set
// It's interesting to note that global triggers (typically affecting the whole field and located in VRAM)
// such as Tailwind still apply when fainted. They should be implemented separately. TODO
function getstat($stat, $crit=0, $mod=0x1000, $notriggers=false)
{
global $BV;
if($stat == 0) return $crit ? ($this->hp*100/$this->bstats[1]) : $this->hp;
$mul = $this->statups[$stat-1];
$user = isset($this->params['user'])?$this->params['user']:false;
$target = isset($this->params['target'])?$this->params['target']:false;
$mid = isset($this->params['move'])?$this->params['move']:0;
$eid = $mid?getmeta($mid):0; if($eid) $eid=$eid['effect'];
if($user && $target // Unaware, stat boost ignore moves (Sacred Sword)
&& (($user != $this && ($user->ability == 109 || $eid == 304)) // Defense boosts
|| ($user == $this && $target->mbability($this) == 109))) $mul=0; // Attack boosts
if($stat==6 || $stat==7) $base = 100;
else
{
$s = $stat;
if($this->volatile & self::F_POWERTRICK)
{
if($s == 1) $s = 2;
else if($s == 2) $s = 1;
}
$base = $this->bstats[$s+1];
}
if((!$this->hp && $eid!=8) || $crit < 0)
{
// Global triggers still apply when fainted, e.g. tailwind for replacement speed ties (495431100665@5)
// Generally global triggers apply last, which means a refactor is required... TODO TODO TODO
if($stat==5 && $BV->sidee[$this->player%2] & BVideo::S_TAILWIND) $base <<= 1;
return $base;
}
if($stat==6 || $stat==7)
{
if($stat==6 && $BV->gravityt) $mul = min(6, $mul+2);
if($mul >= 0) $res = (int)($base * (6+2*$mul)/6);
else $res = (int)($base*6/(6 - 2*$mul));
}
else if(!($crit && (($mul>0 && in_array($stat, array(2,4)))||($mul<0 && in_array($stat,array(1,3))))))
{
if($mul >= 0) $res = (int)($base*(2+$mul)/2);
else $res = (int)(($base*2)/(2-$mul));
}
else $res = $base;
// GetBPkmStat()
if($notriggers) return $res;
switch($stat)
{
case 1: // Attack
if($BV->fgift[$this->player%2] && $this->mbability()) // Flower Gift
chainmod($mod, 0x1800);
switch($this->ability)
{
case 62: // Guts
if($this->status) chainmod($mod, 0x1800);
break;
case 37: case 74: // Pure/Huge power
chainmod($mod, 0x2000);
break;
case 55: // Hustle
$res = applymod($res, 0x1800);
break;
case 112: // Slow Start
if($this->turncount < 5) chainmod($mod, 0x800);
break;
}
switch($this->getitem())
{
case 220: // Choice band
chainmod($mod, 0x1800);
break;
case 236: // Light Ball
if($this->dex == 25) chainmod($mod, 0x2000);
break;
case 258: // Thick Club
if($this->dex == 104 || $this->dex == 105) chainmod($mod, 0x2000);
break;
}
break;
case 2: // Defense
switch($this->mbability())
{
case 63: // Marvel Scale
if($this->status) chainmod($mod, 0x1800);
break;
}
switch($this->getitem())
{
case 257: // Metal Powder
if($this->dex == 132) chainmod($mod, 0x2000);
break;
case 538: // Evolite
if(mysql_result(mysql_query("SELECT COUNT(*) FROM dex WHERE evo='{$this->dex}'"),0)) chainmod($mod, 0x1800);
break;
}
break;
case 3: // Special Attack
switch($this->ability)
{
case 94: // Solar Power
if($BV->getweather() == BVideo::W_SUN) chainmod($mod, 0x1800);
break;
case 57: case 58: // Plus, Minus
$a = $this->ability==57?58:57;
if(isset($this->params['allies']))
foreach($this->params['allies'] as $p)
if($p->hp && $p->ability == $a)
{
chainmod($mod, 0x1800);
break;
}
break;
}
switch($this->getitem())
{
case 297: // Choice specs
chainmod($mod, 0x1800);
break;
case 236: // Light Ball
if($this->dex == 25) chainmod($mod, 0x2000);
break;
case 225: // Soul Dew
if(in_array($this->dex, array(380, 381))) chainmod($mod, 0x1800);
break;
case 226: // DeepSeaTooth
if($this->dex == 366) chainmod($mod, 0x2000);
break;
}
break;
case 4: // Special Defense
// SD boost in Sandstorm
if($BV->getweather() == BVideo::W_SAND && ($this->type1==6||$this->type2==6)) $res = applymod($res, 0x1800);
// Flower Gift
if($BV->fgift[$this->player%2] && $this->mbability()) chainmod($mod, 0x1800);
switch($this->getitem())
{
case 225: // Soul Dew
if(in_array($this->dex, array(380, 381))) chainmod($mod, 0x1800);
break;
case 227: // DeepSeaScale
if($this->dex == 366) chainmod($mod, 0x2000);
break;
case 538: // Evolite
if(mysql_result(mysql_query("SELECT COUNT(*) FROM dex WHERE evo='{$this->dex}'"),0)) chainmod($mod, 0x1800);
break;
}
break;
case 5: //Speed
switch($this->ability)
{
case 146: // Sand Rush
if($BV->getweather() == BVideo::W_SAND) $res *= 2;
break;
case 95: // Quick Feet
if($this->status) $res = (int)($res*1.5);
break;
case 33: // Swift Swim
if($BV->getweather() == BVideo::W_RAIN) $res <<=1;
break;
case 34: // Chlorophyll
if($BV->getweather() == BVideo::W_SUN) $res <<=1;
break;
case 112: // Slow Start
if($this->turncount < 5) $res >>= 1;
break;
}
switch($this->getitem())
{
case 287: // Choice Scarf
$res = (int)($res*1.5);
break;
}
switch($this->item) // Not affected by Klutz
{
case 278: case 293: case 292: case 290: case 291: case 294: case 289: case 215: // Power items, Iron Ball
$res >>= 1;
break;
}
// Tailwind
if($BV->sidee[$this->player%2] & BVideo::S_TAILWIND) $res <<= 1;
// Unburden
if($this->unburden) $res <<= 1;
// Paralysis
if($this->status == self::S_PARALYZED && $this->ability != 95) $res>>=2;
break;
case 6: // Accuracy
// Victory Star
if(isset($this->params['allies']))
foreach($this->params['allies'] as $pk)
if($pk->ability == 162) $res = (int)($res*1.1);
switch($this->ability)
{
case 55: // Hustle
if($mid && $GLOBALS['MOVES'][$mid]['cat']==2)
$res = (int)($res*0.8);
break;
case 14: // Compoundeyes
$res = (int)($res*1.3);
break;
}
switch($this->getitem())
{
case 265: // Wide Lens
$res = (int)($res*1.1);
break;
}
if($target) // 131966391454 BrightPower lowers accuracy
switch($target->getitem())
{
case 255: // Lax Incense
case 213: // BrightPowder
$res = (int)($res*0.9);
break;
}
break;
case 7: // Evasion
switch($this->mbability())
{
case 81: // Snow Cloak
if($BV->getweather() == BVideo::W_HAIL) $res = (int)($res*1.25);
break;
case 8: // Sand Veil
if($BV->getweather() == BVideo::W_SAND) $res = (int)($res*1.25);
break;
case 77: // Tangled Feet
if($this->volatile & self::F_CONFUSED) $res <<= 1;
break;
case 147: // Wonder Skin
if(isset($this->params['move']) && ($mid=$this->params['move']))
switch($eid = $GLOBALS['META'][$mid]['class'])
{
// Status move, stat moves
case 1: case 2: $res <<= 1;
}
break;
}
break;
}
BVideo::debug("Get stat($stat) on {$this->nick} = $res [$mod]");
return applymod($res, $mod);
}
function statchange($s, $v, $foe=false, $from = "")
{
global $BV;
if($foe && $BV->sidee[$this->player%2] & BVideo::S_MIST)
{
$this->O("%n is protected by the mist!","|-activate|%fslot|mist");
return false;
}
switch($this->mbability())
{
case 51: // Keen Eye
if($s==7) return false;
break;
case 29: // Clear Body
if($v < 0 && $foe) return false;
break;
case 52: // Hyper Cutter
if($s==2 && $v<0) return false;
break;
case 73: // White Smoke
if($foe && $v<0) return false;
break;
case 86: // Simple
$v *= 2;
break;
case 126: // Contrary
$v *= -1;
break;
case 128: // Defiant
if($v < 0 && $foe)
{
$this->statchange(2, 2);
}
break;
case 145: // Big Pecks
if($s==2 && $v<0) return false;
break;
}
if(abs($this->statups[$s-2]) == 6) return false;
$this->statups[$s-2] = min(6, $this->statups[$s-2]+$v);
$this->O("%n's %st($s) ".(abs($v)>2?'drastically':(abs($v)>1?'sharply':''))." ".($v>0?'increased':'fell')."!",
"|-".($v>0?"":"un")."boost|%fslot|%st($s)|".abs($v).($from?"|$from":''));
return true;
}
function setstatus($s, $perm, $from="")
{
global $ITEMS, $BV;
$o_s = $s;
if(!$this->hp && $s) return false;
// Leaf Guard
if($this->mbability() == 102 && $BV->getweather() == BVideo::W_SUN) return false;
// Type and ability canceling status
if($perm || (!$perm && $s==self::F_CONFUSED))
{
// Safegaurd
if($s && $BV->sidee[$this->player%2] & BVideo::S_SAFEGUARD &&
!(isset($this->params['user']) && $this->params['user']->ability==151)) return false;
if($perm)
switch($s)
{
case self::S_TOXIC: // Steel / Poison type
case self::S_POISON: // Immunity
if(count(array_intersect(array($this->type1,$this->type2),array(9,4)))
|| $this->mbability() == 17) return false;
break;
case self::S_BURNED: // Water Veil
if($this->type1 == 10 || $this->type2 == 10 || $this->mbability() == 41) return false;
break;
case self::S_PARALYZED: // Limber
if($this->mbability() == 7) return false;
break;
case self::S_FREEZE: // Magma Armor
if($this->type1 == 15 || $this->type2 == 15 || $this->mbability() == 40 || $BV->getweather() == BVideo::W_SUN)
return false;
break;
case self::S_ASLEEP: // Insomnia
if($this->mbability() == 15 || $this->mbability() == 72) return false;
break;
}
else if($s==self::F_CONFUSED) // Own Tempo
if($this->mbability() == 20) return false;
else if($s==self::F_INFATUATED) // Oblivious
if($this->mbability() == 12) return false;
}
if($perm)
{
if(!$s && $this->status != self::S_FAINTED)
{
$this->toxiccount = 0;
if($this->status)
$this->O("%n is no longer %status!","|-curestatus|%fslot".($from?"|$from":''));
$this->status = $s;
return true;
}
else if($this->status != self::S_NONE && $this->status != self::S_FAINTED && // Can't override status
!(isset($this->params['move']) && $this->params['move'] == 156 && $this->status != self::S_ASLEEP)) // except Rest
{
$this->O("%n is already %status!","|-fail|%fslot|%status");
return false;
}
else
{
if($s == self::S_ASLEEP)
{
$this->sleepturns = isset($this->params['randduration']) ? $this->params['randduration'] : $BV->rand(3)+2;;
$this->sleepcount = $this->sleepturns;
}
$this->O("%n is %status($s)!","|-status|%fslot|%status($s)".($from?"|$from":""));
if($s == self::S_TOXIC)
{
$this->toxiccount = 1;
$s = self::S_POISON;
}
$this->status = $s;
}
// Synchronize
if(isset($this->params['user']) && $this->ability == 28 && !in_array($o_s, array(self::S_ASLEEP, self::S_FREEZE)))
{
$this->O("%n synchronized its status with %un!","");
$this->params['user']->setstatus($o_s, true);
}
$this->checkitem();
return true;
}
if($this->volatile & $s)
{
$this->O("%n is already %volatile($s)!","");
return false;
}
$etag = "";
switch($s)
{
case self::F_LEECHED:
if($this->type1 == 12 || $this->type2 == 12)
{
$this->O("%n is not affected","");
return;
}
global $partys;
$this->leecher = array($p=$this->params['player'], array_search($this->params['pkid'],$partys[$p]));
$etag = "leechseed";
break;
case self::F_INFATUATED:
if(isset($this->params['user']) && (1-$this->gender) != $this->params['user']->gender)
{
$this->O("%n isn't affected.","");
return;
}
$this->lover = array($this->params['player'], $this->params['pkid']);
$etag = "attract";
break;
case self::F_CONFUSED:
$this->confturns = isset($this->params['confturns'])?$this->params['confturns']:$BV->rand(4)+2;
$etag = "confusion";
break;
case self::F_TRAPPED:
$user = $this->params['user'];
if(!$this->params['minduration']) $this->trapturns = -1;
else if($user->getitem() == 286) $this->trapturns = 5;
else $this->trapturns = $this->params['randduration'];
//$this->params['minduration']+$BV->rand($this->params['randduration']);
$this->trapper = $user;
break;
case self::F_HEALBLOCK:
$this->hbturns = isset($this->params['minduration']) ? $this->params['minduration'] : 5;
$etag = "healblock";
break;
case self::F_CHARGE:
$this->O("","|-activate|%fslot|charge");
break;
case self::F_PERISH:
if($this->ability == 43) return false;
$this->perishturns = 4;
break;
case self::F_TAUNTED:
$this->tauntturns = 3;
$etag = "taunt";
break;
case self::F_ENCORE:
if(!($lm=end($this->history)) || in_array($lm, array(227,102,119,166,165,144))
|| !in_array($lm, $this->moves)) return false;
$this->encoreturns = 3;
$etag = "encore";
break;
case self::F_DISABLE:
if(!($lm=end($this->history))) return false;
$this->disabled = $lm;
$this->disableturns = 4;
$etag = "disable";
break;
case self::F_YAWN:
if($this->status) return $this->fail();
$this->volatile2 |= self::F2_YAWN1;
$etag = "yawn";
break;
case self::F_TORMENT:
// 320280985649 Torment only starts after the first used move
$this->volatile2 |= self::F2_NOTORMENT;
break;
}
$this->O("%n is %volatile($s)!",$etag?"|-start|%fslot|$etag":"");
$this->volatile |= $s;
$this->checkitem();
return true;
}
// Mold Breaker helper for abilities affected by it
function mbability($user = false)
{
if(!$user && isset($this->params['user'])) $user = $this->params['user'];
if($user && $user != $this && in_array($user->ability, array(104, 163, 164))) return 0;
return $this->ability;
}
function formchange($form)
{
global $DEX, $forms;
if($form == $this->form) return;
$DEX = mysql_fetch_assoc(mysql_query("SELECT * FROM dex WHERE dex='{$this->dex}' AND form='$form'"));
$this->type1 = $DEX['type1']; $this->type2 = $DEX['type2'];
for($i=2; $i<7; $i++)
{
$this->bstats[$i] = statval($i, $this->ivs[$i-1], $this->evs[$i-1], $this->lvl, $this->nature);
}
$this->form = $form;
$fn = $forms[$this->dex][$form];
$this->O("%n transformed into the $fn form!","|-formechange|%fslot|{$DEX['name']}-$fn");
}
function transform($pk)
{
if($pk->subhp) return false;
$this->bstats = $pk->bstats;
$this->bstats[0] = $this->hp;
$this->bstats[1] = $this->obstats[1];
$this->statups = $pk->statups;
$this->type1 = $pk->type1;
$this->type2 = $pk->type2;
$this->ability = $pk->ability;
$this->O("%n copied %pkn({$pk->U})'s stats.","|-transform|%fslot|%fslot({$pk->U})");
}
// Those trigger at switch in or when ability changes
function triggerability()
{
global $BV, $ABILITIES, $ITEMS, $log, $players, $partys;
if($this->ability == 150) // Imposter
{
if($pk = $this->facingop())
{
$this->transform($pk);
}
}
switch($this->ability)
{
case 36: // Trace
if(($pk=$this->randop()) && !in_array($pk->ability, array(36, 121, 149)))
{
$this->O("%n traced %pkn({$pk->U})'s %ab({$pk->ability})!","");
$this->setability($pk->ability,"|[from] trace|[of] %fslot({$pk->U})");
}
break;
case 88: // Download
if(isset($this->params['opponents']))
{
$def = 0; $sd = 0;
foreach($this->params['opponents'] as $o)
{
$def += $o->getstat(2);
$sd += $o->getstat(4);
}
$this->O("%n downloaded a".($def>=$sd?' Special':'n')." Attack boost.","");
$this->statchange($def >= $sd ? 4 : 2, 1, 0, "[from] download|[of] %fslot({$this->U})");
}
break;
case 22: // Intimidate
$this->O("%n is intimidating its opponents!","");
foreach($this->params['opponents'] as $tmp)
if($tmp->hp && !$tmp->subhp)
{
$this->O("","|-ability|%fslot|%ab|[of] %fslot({$tmp->U})");
$tmp->statchange(2, -1, 1, "[from] intimidate|[of] %fslot({$this->U})");
}
break;
case 46: // Pressure
$this->O("%n is exerting its pressure!","|-ability|%fslot|%ab");
break;
case 104: // Mold Breaker
case 163: // Turboblaze
case 164: // Teravolt
$this->O("%n breaks the mold!","|-ability|%fslot|%ab");
break;
case 127: // Unnerve
$n = $GLOBALS['players'][1-($this->player%2)]->name;
$this->O("%n 's Unnerve makes $n's team too nervous to eat Berries!","|-ability|%fslot|%ab|$n");
break;
case 119: // Frisk
if($pk = $this->randop(1))
$this->O("%n frisked the opponents and found one %item({$pk->item})!","|-item|%fslot({$pk->U})|%item({$pk->item})|[from] Frisk|[of] %fslot");
break;
case 108: // Forewarn
global $MOVES;
$pool = array(); $max=0;
foreach($this->params['opponents'] as $o)
if($o->hp)
foreach($o->moves as $m)
if($MOVES[$m]['bp']>$max)
{
$pool = array(array($o,$m));
$max = $MOVES[$m]['bp'];
}
else if($MOVES[$m]['bp']==$max)
$pool[]=array($o,$m);
if($c=count($pool))
{
list($o,$m) = $pool[$BV->rand($c)];
$this->O("%n foresaw %pkn({$o->nick})'s %m($m)!","|raw|%n foresaw %pkn({$o->nick})'s %m($m)!");
}
break;
case 2: case 70: case 45: case 117: // Auto Weather
$tmp = array(117=>BVideo::W_HAIL, 2=>BVideo::W_RAIN, 70=>BVideo::W_SUN, 45=>BVideo::W_SAND);
$BV->setweather($tmp[$this->ability], 0, $this->O("","ability: %ab|[of] %fslot",-1));
$tmp = array(117=>"summoned a hail storm", 2=>"is making it rain", 70=>"has intensified the sunlight", 45=>"kicked up up a sandstorm");
$this->O("%n's %ab {$tmp[$this->ability]}.","");
break;
case 13: case 76: // Cloud Nine, Air Lock
$this->O("%n's %ab clears the sky.","|-activate|%fslot|ability: %ab");
break;
}
}
function setability($a, $from="")
{
global $ABILITIES, $LANG;
// Multitype
if($a == 121 || $this->ability == 121) return $this->fail();
if(!isset($ABILITIES[$a]))
{
$q = mysql_query("SELECT name FROM abilities WHERE id='{$a}' AND lang='$LANG'");
if($r = mysql_fetch_array($q)) $ABILITIES[$a] = $r[0];
else $ABILITIES[$a] = "None";
}
$this->ability = $a;
$this->O("%n's ability became %ab($a)!","|-ability|%fslot|%ab($a)".$from);
$this->triggerability();
}
function checkitem($launcher = false)
{
$this->berrycheck($launcher);
if($launcher)
switch($this->item)
{
case 29: // Max Revive
$hp = 100; goto revive;
case 28: // Revive
$hp = 50;
revive:
if(!$this->hp)
{
$this->changehp($hp, 1, 1);
$this->status = 0;
$this->resetvolatile();
}
break;
case 17: // Potion
$a = 20; $pc=0; goto _potion;
case 23: // Full Restore
$this->setstatus(0, 1);
$a = 100; $pc = 1; goto _potion;
case 24: // Max Potion
$a = 100; $pc = 1; goto _potion;
case 26: // Super Potion
$a = 50; $pc = 0; goto _potion;
case 33: // Moomoo milk
$a = 100; $pc = 0; goto _potion;
case 25: // Hyper Potion
$a = 200; $pc=0; goto _potion;
_potion:
if(!$this->hp) $this->fail();
else $this->changehp($a,$pc);
break;
case 56: // Dire Hit
$a = 1; goto _direHit;
case 592: // Dire Hit 2
$a = 2; goto _direHit;
case 615: // Dire Hit 3
$a = 3; _direHit:
$this->critlevel += $a;
case 57: // X Attack
$s = 1; $a = 1; goto _xstat;
case 597: // X Attack 2
$s = 1; $a = 2; goto _xstat;
case 603: // X Attack 3
$s = 1; $a = 3; goto _xstat;
case 609: // X Attack 6
$s = 1; $a = 6; goto _xstat;
case 58: // X Defend
$s = 2; $a = 1; goto _xstat;
case 596: // X Defend 2
$s = 2; $a = 2; goto _xstat;
case 602: // X Defend 3
$s = 2; $a = 3; goto _xstat;
case 608: // X Defend 6
$s = 2; $a = 6; goto _xstat;
case 61: // X Special
$s = 3; $a = 1; goto _xstat;
case 594: // X Special 2
$s = 3; $a = 2; goto _xstat;
case 600: // X Special 3
$s = 3; $a = 3; goto _xstat;
case 606: // X Special 6
$s = 3; $a = 6; goto _xstat;
case 62: // X Sp. Def
$s = 4; $a = 1; goto _xstat;
case 595: // X Sp. Def 2
$s = 4; $a = 2; goto _xstat;
case 601: // X Sp. Def 3
$s = 4; $a = 3; goto _xstat;
case 607: // X Sp. Def 6
$s = 4; $a = 6; goto _xstat;
case 59: // X Speed
$s = 5; $a = 1; goto _xstat;
case 593: // X Speed
$s = 5; $a = 2; goto _xstat;
case 599: // X Speed
$s = 5; $a = 3; goto _xstat;
case 605: // X Speed
$s = 5; $a = 6; goto _xstat;
_xstat: $this->statchange($s+1, $a);
break;
case 611: // Ability Urge
$this->triggerability();
break;
case 614: // Reset Urge
$this->statups = array(0,0,0,0,0,0,0);
break;
}
switch($this->getitem())
{
case 214: // White Herb
$l = array();
foreach($this->statups as $i=>$s)
if($s < 0) $l[] = $i;
if(count($l) || $launcher)
{
foreach($l as $i) $this->statups[$i] = 0;
$this->O("%n's White Herb restored its lowered stats.","|-clearboost|%fslot|[silent]");
$this->useitem();
}
break;
case 219: // Mental Herb
$mask = self::F_TAUNTED | self::F_INFATUATED | self::F_ENCORE | self::F_DISABLE | self::F_TORMENT;
if($this->volatile & $mask || $launcher)
{
$this->volatile &= ~$mask;
$this->tauntturns = $this->encoreturns = $this->disableturns = 0;
$this->lover = null;
$this->O("%n's Mental Herb cured its volatile status.","");
$this->useitem();
}
break;
}
}
function resetmulti()
{
global $BV;
if($this->mturns)
{
if($this->mturns==1)
{
$this->params['confturns'] = $BV->rand(4)+2;
$this->setstatus(self::F_CONFUSED, 0);
}
$this->mturns = 0;
}
}
function flinch()
{
if($this->mbability() == 39) return false; // Inner Focus
$this->volatile2 |= self::F2_FLINCHED;
// if($this->ability == 80) $this->statchange(6, 1, 0, "[from] steadfast|[of] %fslot"); // Steadfast
if($this->hp) $this->O("%n flinched!","");
}
function fail($from="")
{
$this->params['fail'] = true;
$this->O("But it failed!","|-fail|%fslot".($from?"|$from":''));
return false;
}
function canloseitem($selfcaused=false)
{
global $ITEMS;
if(!$this->item) return 0;
else if($this->ability == 121) return false; // Multitype
else if($this->mbability() == 60 && !$selfcaused) return false; // Sticky Hold
else if(strpos($ITEMS[$this->item]['name'],"Mail")) return false;
else if($this->item == 112 && $this->dex == 487) return false;
return $this->item;
}
function useitem($more="")
{
// Unburden
if($this->ability == 84) $this->unburden = 1;
$this->lastitem = $this->item;
$this->O("","|-enditem|%fslot|%item".($more?"|$more":''));
$this->item = 0;
}
function getitem()
{
global $BV;
// Klutz, Magic Room
if($this->ability == 103 || $this->volatile & self::F_EMBARGO || $BV->mrcount) return 0;
return $this->item;
}
function getweight()
{
$w = $this->dexinfo['weight'];
switch($this->mbability())
{
case 134: // Heavy Metal;
$w <<= 1;
break;
case 135: // Light Metal
$w >>= 1;
break;
}
switch($this->getitem())
{
case 539: // Float Stone
$w >>= 1;
break;
}
return (int)($w/10);
}
function changehp($amount, $pc=false, $force=false, $fromc=false, $rounding=0)
{
global $ITEMS, $BV, $players;
$oldhp = $this->hp;
// Magic Guard
if($this->ability == 98 && $pc && $amount<0 && !$force) return 0;
if($this->volatile & self::F_HEALBLOCK && $amount>0) return 0;
if($pc>0)
{
$ratio = (100/$amount);
$amount = $this->bstats[1]/$ratio;
// 489454568308 @ 21 (rain dish / leftovers truncated)
// Roost / Revover like rounded up (54-24021-37159 @ 5)
// 739875509625&@10 Moonlight rounded down
$amount = $rounding
? ($rounding > 0 ? round($amount,0) : (int)$amount)
: ($amount <= 0 ? ceil($amount) : ($ratio <= 3 ? round($amount,0) : (int)$amount));
$amount *= $pc;
if(!$amount) $amount = $ratio>0 ? 1 : -1;
}
$confdmg = isset($this->params['move']) && !$this->params['move'];
if(($this->subhp && !$confdmg) && $amount<0 && !$pc)
{
if(abs($amount) >= $this->subhp)
$this->O("%n's substitute faded.","|-end|%fslot|Substitute");
else
$this->O("%n's substitute took the damage.","|-start|%fslot|substitute|[damage]");
$oldshp = $this->subhp;
$this->subhp = max(0, $this->subhp+$amount);
$subdmg = $this->subhp ? abs($amount) : $oldshp;
BVideo::debug("Substitute damage $oldshp->{$this->subhp}");
}
else $this->hp = max(0, min($this->hp+$amount, $this->bstats[1]));
// if($oldhp==$this->hp) return 0;
// False Swipe
if(!$pc && !$this->hp && isset($this->params['move']) && $this->params['move']==206) $this->hp = 1;
if($this->volatile2 & self::F2_ENDURE) $this->hp = 1;
// Sturdy
$sturdy = $this->mbability()==5;
if(!$pc && $oldhp == $this->bstats[1] && ($this->getitem() == 275 || $sturdy) && !$this->hp)
{
$this->hp = 1;
if(!$sturdy) $this->useitem();
$this->O("%n hung on with ".($sturdy?"sturdy":"its focus sash")."!",$sturdy?"|-activate|%fslot|Sturdy":"");
}
if(!$pc && !$this->hp && $this->getitem() == 230) // Focus Band
{
if($BV->rand(100)<10)
{
$this->hp = 1;
$this->O("%n hung on with its focus band!","|-enditem|%fslot|%item");
}
}
$oldpc = ceil(100*$oldhp/$this->bstats[1]);
$newpc = ceil(100*$this->hp/$this->bstats[1]);
$omore = ($oldpc==0 ? " hpf" : ($oldpc <= 20 ? " hpr" : ($oldpc <= 50 ? " hpo" : "")));
$nmore = ($newpc==0 ? " hpf" : ($newpc <= 20 ? " hpr" : ($newpc <= 50 ? " hpo" : "")));
if($oldhp != $this->hp)
{
$this->O("<img src=\"{$this->icon}\" class=\"hpbari\" alt=\"\"> <span class=\"hpbar$omore\" style=\"background-size:{$oldpc}px 100%;\"> $oldhp</span> → <span class=\"hpbar$nmore\" style=\"background-size:{$newpc}px 100%;\"> {$this->hp}</span><br />","",1);
$this->O("","|-".($fromc=="painsplit"?"sethp":($oldpc<$newpc?"heal":"damage"))."|%fslot|".floor(abs($newpc-$oldpc))." (%hpc)".($fromc?"|[from] $fromc":""));
}
if(!$this->hp && $this->status != self::S_FAINTED)
{
$this->status = self::S_FAINTED;
$this->resetvolatile();
$players[$this->player]->hasfainted[0] = true;
$BV->trigger(BVideo::E_FAINT); // To keep scores
$this->trigger(self::E_FAINTED, $this->params);
$this->O("%n fainted!","|faint|%fslot");
return $oldhp;
}
$this->checkitem();
return isset($subdmg) ? $subdmg : ($oldhp-$this->hp);
}
function hpberry($amount, $rel)
{
global $ITEMS;
$x = $this->item; $this->item = 0;
if($this->changehp($amount, $rel))
$this->O("%n's %item($x) restored some HP.","");
$this->item = $x;
$this->useitem("[eat]");
}
function statusberry($s)
{
global $ITEMS;
if((!$s && $this->status) || ($s && $this->status == $s))
{
$this->O("%n's %item clears its status problem.","");
$this->useitem("[eat]");
$this->setstatus(0,1);
}
if((!$s || $s==-1) && $this->volatile & self::F_CONFUSED)
{
$this->volatile &= ~self::F_CONFUSED;
$this->O("%n's %item snapped it out of its confusion.","|-end|%fslot|confusion");
$this->useitem("[eat]");
}
}
function berrycheck($launcher = false)
{
global $BV;
if(isset($this->params['opponents']) && !$launcher)
{
foreach(array_merge($this->params['opponents'],$this->params['allies']) as $o)
if($o->ability == 127 && $o->player%2!=$this->player%2 && $o->hp && $o->onfield) // Unnerve
return;
}
$s = ($this->ability == 82 ? 1 : 2); // Gluttony
$quarter = ($this->hp <= $this->bstats[1]>>$s) || $launcher;
$half = ($this->hp <= $this->bstats[1]>>1) || $launcher;
switch($iid = $launcher ? $this->item : $this->getitem())
{
case 208: // Enigma
if(isset($this->params['supereffective']))
{
$this->hpberry(25, 1);
unset($this->params['supereffective']);
}
break;
case 158: // Sitrus
if($half) $this->hpberry(25, 1);
break;
case 155: // Oran
if($half) $this->hpberry(10, 0);
break;
case 43: // Berry Juice
if($half) $this->hpberry(20, 0);
break;
case 201: // Liechi
$stat = 1; goto pinchStat;
case 202: // Ganlon
$stat = 2; goto pinchStat;
case 203: // Salac
$stat = 5; goto pinchStat;
case 204: // Petaya
$stat = 3; goto pinchStat;
case 205: // Apicot
$stat = 4;
pinchStat:
if($quarter)
{
$this->O("%n ate its pinch berry!","");
$this->statchange($stat+1, 1, 0, "item: %item($iid)");
$this->useitem();
}
break;
case 206: // Lansat Berry
if($quarter)
{
$this->critlevel+=2;
$this->O("%n ate its %item!","");
$this->useitem();
}
break;
case 157: // Lum Berry
$this->statusberry(0);
break;
case 149: // Cheri Berry
$this->statusberry(self::S_PARALYZED);
break;
case 150: // Chesto Berry
$this->statusberry(self::S_ASLEEP);
break;
case 151: // Pecha Berry
$this->statusberry(self::S_POISON);
break;
case 152: // Rawst Berry
$this->statusberry(self::S_BURNED);
break;
case 156: // Persim Berry
$this->statusberry(-1);
break;
case 207: // Starf Berry
if($quarter)
{
$pool = range(0,4);
foreach($pool as $i=>$stat)
if($this->statups[$stat]==6) unset($pool[$i]);
if(count($pool))
{
$stat = $pool[$BV->rand(count($pool))];
$this->O("%n ate its %item!","");
$this->statchange($stat+1, 2, 0, "item: %item($iid)");
$this->useitem();
}
}
break;
}
}
function type_mul($atype)
{
global $TE, $BV;
$t1 = $this->type1;
$t2 = $this->type2;
if($this->volatile2 & self::F2_GROUNDED)
{
if($t1 == 3){ if(!$t2) $t1 = 1; else {$t1 = $t2; $t2 = 0; }}
else if($t2 == 3) $t2 = 0;
}
$t1 = $TE[$atype][$t1];
$t2 = ($t2 ? $TE[$atype][$t2] : 1);
if($atype == 5)
{
// Ingrain, Gravity, Smack Down
$grounded = $this->grounded || ($this->volatile2 & self::F2_GROUNDED)
|| $BV->gravityt || $this->getitem()==278 || ($this->volatile & self::F_INGRAIN);
// Levitate
// Air Balloon
if((($this->mbability() == 26) || $this->getitem() == 541
|| ($this->volatile & self::F_MAGNETRISE)) && !$grounded)
{
return 0;
}
if($grounded && !($t1*$t2)) return max($t1, $t2);
}
// Scrappy
else if(($atype==1 || $atype == 2) && !($t1*$t2)
&& ($this->params['user']->ability == 113 || $this->volatile & self::F_NOIMMUNITY))
{
return max($t1,$t2);
}
if(!($t1*$t2) && $this->getitem() == 543) return 1;
return $t1*$t2;
}
function facingop()
{
global $BV, $log, $players, $partys;
if($BV->pc == 4)
{
$pl = ($this->player+1)%4;
if($pk = $players[$pl]->party[$partys[$pl][0]])
return $pk;
else if($pk = $players[$pl=($pl+2)%4]->party[$partys[$pl][0]])
return $pk;
else return false;
}
else
{
$pl = 1-$this->player;
$pos = array_search($this->id,$partys[$this->player]);
$ct = array_reverse(range(0,$log->leads-1));
while(!($pk=$players[$pl]->party[$partys[$pl][$ct[$pos]]]) && $pk->hp)
$pos = ($pos+1)%count($ct);
if($pk && $pk->hp) return $pk;
}
return false;
}
function randop($single=false)
{
global $BV, $log, $players, $partys;
if($BV->pc == 4)
// Invert==3 219179805159@11 trace
// Invert==2 149738465902@0 frisk
$pl = ($this->player+($log->invert==3?1:3)+2*$BV->rand(2))%4;
else
$pl = 1-$this->player;
$pool = array();
for($i=0; $i<$log->leads; $i++)
{
$pk = $players[$pl]->party[$partys[$pl][$i]];
if($pk->hp) $pool[]=$pk;
}
// 471490498910&@1 Frisk rand
// Trace: no rand
if(count($pool)>1 || ($single && count($pool))) return $pool[$BV->rand(count($pool))];
else if(count($pool)==1) return $pool[0];
else return false;
}
function resetvolatile()
{
$this->subhp = 0;
$this->critlevel = 0;
$this->stockpile = 0;
$this->protectcount = 0;
$this->perishcount = 0;
$this->quickclaw = 2;
$this->unburden = 0;
$this->hurtby = array();
$this->switching = 0;
$this->mcharge = 0;
$this->mturns = 0;
$this->confturns = 0;
$this->trapturns = 0;
$this->encoreturns = 0;
$this->disableturns = 0;
$this->disabled = 0;
$this->sleepcount = $this->sleepturns;
$this->ability = $this->oability;
$this->form = $this->oform;
$this->bstats = $this->obstats;
$this->grounded = 0;
$this->history = array();
$this->hhistory = array();
$this->shistory = array();
$this->volatile = 0;
$this->volatile2 = 0;
$this->type1 = $this->dexinfo['type1'];
$this->type2 = $this->dexinfo['type2'];
}
function effectswitch()
{
global $tn, $partys, $players, $log, $BV;
$i=0; foreach($players[$this->player]->party as $id=>$pk)
if($id != $this->id && $pk->hp && !$pk->onfield) $i++;
if(!$i || !count($log->switchins[$tn])) return false;
for($pool=array(),$i=0; $i < $BV->pc; $i++)
$pool[] = array_shift($log->switchins[$tn]);
$log->switchins[$tn] = array_merge($log->switchins[$tn]);
$id = array_search($this->id, $partys[$this->player]);
foreach($pool as $s)
{
if($s[0]==$this->player && $s[4] == $id)
{
$params = array("uturn"=>1);
// TODO TODO TODO 14-81671-03780
// Bad design here - having to rollback the switch stack in case
// the Pokemon switching out is killed by Pursuit.
// This probably is buggy in some cases - Pursuit check should be
// performed before reading the switch stack
if(!switchin(4, $s[0], $s[3], $s[4], $params))
{
foreach($pool as $tmp) array_unshift($log->switchins[$tn],$tmp);
$log->switchins[$tn] = array_merge($log->switchins[$tn]);
}
return true;
}
}
return false;
}
function trigger($event, &$params)
{
global $BV, $MOVES, $META, $partys, $players, $log, $ITEMS, $ABILITIES;
$affected = false;
$this->params = &$params;
switch($event)
{
case self::E_TURNSTART:
switch($this->getitem())
{
case 217: // Quick Claw
// 875735430109 No quick claw roll if no attack planned
if($this->planned[2] != BattleLog::P_ATTACK) break;
$r = $BV->rand(100);
if($r<20)
{
$this->quickclaw++;
$this->O("%n's %item activated!","|-activate|%fslot|%item");
}
break;
case 210: // Custap Berry
if(4*$this->hp <= $this->bstats[1])
{
$this->O("%n's %item lets it move first!","|-activate|%fslot|custapberry");
$this->quickclaw++;
$this->useitem();
}
break;
case 279: case 316: // Lagging Tail / Full Incense
$this->quickclaw--;
break;
}
if($this->ability == 100) $this->quickclaw--; // Stall
break;
case self::E_SWITCHOUT:
$this->onfield = 0;
$this->bstats = $this->obstats;
$this->statups = array(0,0,0,0,0,0,0);
switch($this->ability)
{
case 144: // Regenerator
if($this->hp)
{
if($this->changehp(100/3, 1, 0, "[silent]"))
if($GLOBALS['JSOUT']) array_pop($GLOBALS['JS']); // Regenerator is internally handled
}
break;
case 127: // Unnerve
if(isset($this->params['opponents']) && is_array($this->params['opponents']))
{
foreach($this->params['opponents'] as $o)
if($o->hp) $o->berrycheck();
}
break;
case 30: // Natural Cure
if($this->hp)
$this->setstatus(self::S_NONE,1, "[silent]");
break;
}
break;
case self::E_SENDOUT:
$this->onfield = 1;
$this->hasmoved = 0;
if(isset($params['batonpass']))
{
$this->turncount = -1;
goto postInitSwitch;
}
$this->turncount = isset($params['replacement']) ? -1 : 0;
$this->toxiccount = ($this->toxiccount ? 1 : 0);
$this->resetvolatile();
postInitSwitch:
$t = $GLOBALS['TE'][6][$this->type1];
$t *= (($tmp=$this->type2) ? $GLOBALS['TE'][6][$tmp] : 1);
// Damn workaround for initializing the structure without
// triggering switch-in effects for #2 and #3 in rotation
if(isset($params['initial']) && $params['initial'] && $BV->rotation && $this->id) return;
// Healing Wish
if($BV->hwish[$this->player])
{
$this->changehp(100, 1);
$this->status = 0;
$BV->hwish[$this->player]--;
}
// Stealth Rocks
if($BV->sidee[$this->player%2] & BVideo::S_ROCKS)
{
$this->O("Pointed rocks dug into %n.","");
$this->changehp(-12.5*$t, 1, 0, "Stealth Rock");
if(!$this->hp) return;
}
// Spikes
if(($BV->sidee[$this->player%2] & BVideo::S_SPIKES1) && $this->type_mul(5))
{
$d = 12.5;
if($BV->sidee[$this->player%2] & BVideo::S_SPIKES2) $d = 16.666;
if($BV->sidee[$this->player%2] & BVideo::S_SPIKES3) $d = 25;
$this->O("%n is hurt by spikes.","");
$this->changehp(-1*$d, 1, 0, "Spikes");
if(!$this->hp) return;
}
// Toxic Spikes
if($BV->sidee[$this->player%2] & BVideo::S_TSPIKES1 && $this->type_mul(4) && $this->type_mul(5))
{
if(in_array(4, array($this->type1, $this->type2)))
{
$this->O("%n absorbed the toxic spikes.","");
if($BV->sidee[$this->player%2] & BVideo::S_TSPIKES1)
$this->O("","|-sideend|%p|Toxic Spikes");
$BV->sidee[$this->player%2] &= ~(BVideo::S_TSPIKES1 | BVideo::S_TSPIKES2);
}
else
{
$s = self::S_POISON;
if($BV->sidee[$this->player%2] & BVideo::S_TSPIKES2) $s = self::S_TOXIC;
$this->O("%n is poisoned by toxic spikes.","");
$this->setstatus($s, 1);
}
}
$tmp = array(298=>10, 299=>11, 300=>13, 301=>12, 302=>15, 303=>2, 304=>4, 305=>5, 306=>3, 307=>14, 308=>7, 309=>6, 310=>8, 311=>16, 312=>17, 313=>9);
if($this->ability == 121 && isset($tmp[$this->getitem()])) // Multitype
{
$this->type1 = $tmp[$this->getitem()];
$this->type2 = 0;
}
switch($this->getitem())
{
case 541: // Air Balloon
$this->O("%n is floating on a balloon.","|-item|%fslot|Air Balloon");
break;
}
$this->triggerability();
$BV->weffects();
break;
case self::E_WEATHER:
if($this->ability == 142) return; // Overcoat
switch($BV->getweather())
{
case BVideo::W_RAIN:
$heal = 0;
if($this->ability == 44) $heal = 6.25; // Rain Dish
else if($this->ability == 87) $heal = 12.5; // Dry Skin
if($heal)
{
$this->O("%n restored some HP from the rain.","");
$this->changehp($heal, 1, 0, "ability: %ab");
}
break;
case BVideo::W_SUN:
$heal = 0;
if($this->ability == 87) $heal = -12.5; // Dry Skin
else if($this->ability == 94) $heal = -12.5; // Solar Power
if($heal)
{
$this->O("%n ".($heal>0?'restored':'lost')." some HP from the sun.","");
$this->changehp($heal, 1, 0, "ability: %ab");
if(!$this->hp) return;
}
break;
case BVideo::W_SAND:
if(in_array($this->ability, array(8,146,159))) return;
else if(!count(array_intersect(array($this->type1,$this->type2),array(5,6,9))))
{
$this->O("%n is buffeted by the sandstorm!","");
$this->changehp(-6.25,1, 0, "Sandstorm");
if(!$this->hp) return;
}
break;
case BVideo::W_HAIL:
if($this->ability == 115)
{
$this->O("%n restored some HP from the hail.","");
$this->changehp(6.25,1, 0, "Hail");
}
else if($this->ability == 81) return;
else if($this->type1 != 15 && $this->type2 != 15)
{
$this->O("%n is buffeted by the hail!","");
$this->changehp(-6.25,1, 0, "Hail");
if(!$this->hp) return;
}
break;
}
break;
case self::E_TURNEND:
$this->turncount++;
// TODO Why the fuck are some effects activated on replacements
if(!$this->turncount) return;
if($this->volatile2 & (self::F2_PROTECT|self::F2_WIDEGUARD|self::F2_QUICKGUARD|self::F2_ENDURE)) $this->protectcount++;
else $this->protectcount = 0;
$yawn = $this->volatile2 & self::F2_YAWN1;
if(($this->volatile2 & self::F2_YAWN2) && !$this->status)
{
$this->setstatus(self::S_ASLEEP, 1);
$this->volatile &= ~self::F_YAWN;
}
if($this->volatile & self::F_HEALBLOCK && !--$this->hbturns)
$this->volatile &= ~self::F_HEALBLOCK;
$this->volatile2 = self::F2_NONE;
$this->quickclaw = 2;
$this->hurtby = array();
$this->hasmoved = 0;
if($this->mindread) $this->mindread--;
if($yawn) $this->volatile2 |= self::F2_YAWN2;
if($this->trapper && !($this->trapper->onfield && $this->trapper->hp))
$this->trapturns = 0;
if($this->trapturns)
{
if(!$this->subhp)
{
$this->O("%n is trapped and lost some health.","");
$this->changehp(-($this->trapper->getitem()==544 ? 12.5 : 6.25), 1);
}
$this->trapturns--;
if(!$this->hp) return;
}
if(!$this->trapturns){ $this->volatile &= ~(self::F_TRAPPED); $this->trapper = null; }
if($this->tauntturns)
{
if(!--$this->tauntturns)
{
$this->volatile &= ~self::F_TAUNTED;
$this->O("%n's taunt wore off.","|-end|%fslot|taunt");
}
}
if($this->encoreturns)
{
if(!--$this->encoreturns)
{
$this->volatile &= ~self::F_ENCORE;
$this->O("%n's encore ended.","|-end|%fslot|encore");
}
}
if($this->disableturns)
{
if(!--$this->disableturns)
{
$this->volatile &= ~self::F_DISABLE;
$this->O("%n is no longer disabled.","|-end|%fslot|disable");
}
}
if($this->volatile & self::F_NIGHTMARE)
{
if($this->status == self::S_ASLEEP)
{
$this->O("%n is locked in a nightmare!","");
$this->changehp(-25, 1, 0, "nightmare");
}
else
{
$this->volatile &= ~self::F_NIGHTMARE;
$this->O("","|-end|%fslot|nightmare");
}
}
// Ingrain
if($this->volatile & self::F_INGRAIN)
{
$this->O("%n is absorbing health from its roots.","");
$r = $this->getitem() == 296 ? 8.125 : 6.25; // Big Root
$this->changehp($r, 1, 0, "ingrain");
}
// Aqua Ring
if($this->volatile & self::F_AQUARING)
{
$this->O("%n's health is restored by the aqua ring.","");
$r = $this->getitem() == 296 ? 8.333 : 6.25; // Big Root
$this->changehp($r, 1,0, "aquaring");
}
// Curse
if($this->volatile & self::F_CURSE)
{
$this->O("%n is afflicted by the curse.","");
$this->changehp(-25, 1);
if(!$this->hp) return;
}
// Charge
if($this->volatile & self::F_CHARGE && end($this->history)!=268)
$this->volatile &= ~self::F_CHARGE;
switch($this->ability)
{
case 139: // Harvest
if(!$this->item && $this->lastitem && strpos($ITEMS[$this->lastitem]['name'], "Berry"))
{
if($BV->getweather() == BVideo::W_SUN || $BV->rand(100)<50)
{
$this->O("%n harvested one %item({$this->lastitem}).","|-item|%fslot|%item({$this->lastitem})");
$this->item = $this->lastitem;
$this->checkitem();
}
}
break;
case 131: // Healer
for($i=0; $i<$log->leads; $i++)
{
if($i == $this->slot) continue;
if(($a=$players[$this->player]->party[$partys[$this->player][$i]])
&& $a->hp && $a->status && $BV->rand(100)<30)
$a->setstatus(0, 1, "Healer|[of] %fslot({$this->U})");
}
break;
case 3: // Speed Boost
$this->O("%n's %ab increases its speed!","|-activate|%fslot|speedboost");
$this->statchange(6,1,0,"[from] speedbost|[of] %fslot");
break;
case 93: // Hydration
if($this->status && $BV->getweather() == BVideo::W_RAIN)
$this->setstatus(0, 1);
break;
case 123: // Bad Dreams
foreach($GLOBALS['SCACHE'] as $t)
{
if($t->player%2 != $this->player%2 && $t->status == self::S_ASLEEP)
{
if($dmg = $t->changehp(-12.5, 1, 0, "baddreams"))
$this->O("%pkn({$t->U}) lost $dmg HP due to %n's %ab.","");
}
}
break;
case 141: // Moody
$pool = array();
foreach(range(1,7) as $u)
{
if($this->statups[$u-1]==6) continue;
foreach(range(1,7) as $d)
if($u != $d) $pool[] = array($u,$d);
}
if($n=count($pool))
{
$r = $pool[$BV->rand($n)];
$this->statchange(1+$r[0], 2);
$this->statchange(1+$r[1], -1);
}
break;
case 61: // Shed Skin
if($this->status && $BV->rand(100) <= 32)
{
$this->O("%n's Shed Skin cured its status problem!","");
$this->setstatus(0,1,"[from] ability: %ab|[of] %fslot");
}
break;
case 161: // Zen Mode
$h = $this->getstat(0,1);
if(($h < 50)^($this->form) && $this->dex == 555)
$this->formchange(1-$this->form);
break;
}
break;
case self::E_ITEMUPK:
### Item healing
$mul = 1;
switch($this->getitem())
{
case 288: // Sticky Barbs
$mul = -2;
case 281: // Black Sludge
if($mul>0 && !in_array(4, array($this->type1, $this->type2))) $mul = -1;
case 234: // Leftovers
if($mul<0 || $this->getstat(0,1)<100)
$this->O("%n ".($mul>0 ? "restored some HP using its" : "lost some health because of")." %item.","");
$this->changehp($mul*6.25, 1, 0, "item: %item");
break;
}
break;
case self::E_LEECHUPK:
if($this->leecher) $leecher = $players[$this->leecher[0]]->party[$partys[$this->leecher[0]][$this->leecher[1]]];
else $leecher = null;
if($this->volatile & self::F_LEECHED && $leecher && $leecher->hp)
{
$this->O("%n's health is leeched towards %pkn({$leecher->U}).","");
$r = $this->changehp(-12.5, 1, 0, "Leech Seed|[of] %fslot({$leecher->U})");
// Big Root
if($leecher->getitem() == 296) $r = (int)(4*$r/3);
if($this->ability == 64) // Liquid Ooze
{
$r *= -1;
$this->O("%n's Liquid Ooze is hurting %pkn({$leecher->nick}).","");
}
$leecher->changehp($r, -1);
$this->checkitem();
}
break;
case self::E_STATUSUPK:
switch($this->status)
{
case self::S_BURNED:
$this->O("%n was hurt by its burn.","");
$this->changehp(($this->ability==85 ? -6.25 : -12.5), 1, 0, "brn"); // Heatproof
if(!$this->hp) return;
break;
case self::S_POISON:
// Poison Heal
if($this->ability == 90)
{
$this->O("%n is healed by poison.","");
$this->changehp(12.5, 1, 0, "ability: Poison Heal");
}
else
{
$this->O("%n was hurt by poison.","");
if(!$this->toxiccount)
$this->changehp(-12.5, 1, 0, "psn");
else
$this->changehp(-6.25, $this->toxiccount++, 0, "psn");
}
break;
}
$this->checkitem();
break;
case self::E_FINALUPK:
switch($this->getitem())
{
case 273: case 272: // Toxic Orb / Flame Orb
$tmp = array(273=>self::S_BURNED, 272=>self::S_TOXIC);
if(!$this->status && $this->turncount) $this->setstatus($tmp[$this->getitem()], true);
break;
}
if($this->volatile & self::F_PERISH && $this->perishturns--) // Perish after leech seed/leftovers
{
$this->O("%n's perish counter went down to {$this->perishturns}!","|-start|%fslot|perish{$this->perishturns}");
if(!$this->perishturns)
{
$this->changehp(-100, 1, 1, "move: Perish Song");
return;
}
}
break;
case self::E_PREATTACK:
$this->volatile &= ~self::F_DESTINYBOND;
if($this->hasmoved && !$params['reflected']){ $params['skip']=true; return false; }
if(!$params['reflected']) $this->hasmoved = 1;
$mid = $params['move'];
// Truant
if($this->ability == 54 && count($this->history) && end($this->history))
{
$this->O("%n is loafing around.","|cant|%fslot|ability: Truant|%m");
$params['move'] = 0; $params['skip'] = true; return false;
}
// Main Sleep check - sleep counter
if($this->status == self::S_ASLEEP)
{
$this->sleepcount--;
if($this->ability == 48) $this->sleepcount--; // Early Bird
if(!$this->sleepcount)
{
$this->O("%n woke up!","");
$this->setstatus(0, 1,"slp");
}
else if($mid != 173 && ($mid != 214 || count($this->moves)==1)) // Sleep Talk
{
$params['skip'] = true;
$this->O("%n is fast asleep.","|cant|%fslot|slp");
return;
}
}
// Freeze status
if($this->status == self::S_FREEZE)
{
if($META[$mid]['flags'] & (1<<11) || ($BV->rand(100) < 20)) // Defrost flag
{
$this->setstatus(0,1,"frz");
$this->O("%n thawed!","");
}
else
{
$this->O("%n is frozen solid.","|cant|%fslot|frz");
$params['skip'] = true;
return;
}
}
// Flinch is after thaw and sleep (242077487794)
if($this->volatile2 & self::F2_FLINCHED)
{
$this->O("%n flinched and cannot use %m($mid).","|cant|%fslot|flinch");
if($this->ability == 80) $this->statchange(6, 1, 0, "[from] steadfast|[of] %fslot"); // Steadfast
$params['skip'] = true; return;
}
// Confusion: after sleep for Sleep Talk
if($this->volatile & self::F_CONFUSED)
{
$this->confturns--;
if(!$this->confturns)
{
$this->O("%n snapped out of its confusion!","|-end|%fslot|confusion");
$this->volatile &= ~self::F_CONFUSED;
}
else
{
$this->O("%n is confused!","|-activate|%fslot|confusion");
$r = $BV->rand(100);
if($r < 50)
{
$this->O("%n hurt itself in its confusion.","");
$params['targets'] = array($this);
$params['move'] = 0;
return;
}
}
}
// Sleep Talk and Snore
if($this->status == self::S_ASLEEP)
{
if($mid == 214 && count($this->moves)>1)
{
$pool = array();
foreach($this->moves as $m) if($m && $m != 214) $pool[]=$m;
$m = $pool[$BV->rand(count($pool))];
$this->O("%n's %m(214) allows it to use %m($m).","");
$a = $this->planned; $a[3] = $m;
do_action($a, array('mcalled'=>1));
$params['skip'] = true; return;
}
}
// Taunt before paralysis 628764403705
if($MOVES[$mid]['cat']==1 && $this->volatile & self::F_TAUNTED)
{
$this->O("%n cannot use %m because of the taunt.","|cant|%fslot|move: Taunt|%m");
$params['skip'] = true; return false;
}
// No Paralysis on self-targeted moves
if($this->status == self::S_PARALYZED)
{
if($BV->rand(100) < 25)
{
$params['skip'] = true;
$this->O("%n is paralyzed and cannot use %m","|cant|%fslot|par");
return;
}
}
if($this->volatile & self::F_INFATUATED)
{
$lover = $players[$this->lover[0]]->party[$this->lover[1]];
if($lover->onfield)
{
$r = $BV->rand(100);
if($r < 50)
{
$this->O("%n's love for %pkn({$lover->U}) prevents it from using %m.","|cant|%fslot|attract");
$params['skip'] = true;
return false;
}
else $this->O("%n is in love with %pkn({$lover->U}).","|-activate|%fslot|attract|[of] %fslot({$lover->U})");
}
else
{
$this->volatile &= ~self::F_INFATUATED;
$this->O("%n snapped out of his infatuation.","|-end|%fslot|attract");
}
}
// Imprison
foreach($params['opponents'] as $o)
{
if($o->volatile & self::F_IMPRISON)
{
if(in_array($mid, $o->moves))
{
$this->O("%pkn({$o->U}) has imprisoned %m","|cant|%fslot|move: Imprison|%m");
$params['skip'] = true; return false;
}
}
}
// Torment
if($this->volatile & self::F_TORMENT && !($this->volatile2 & self::F2_NOTORMENT) && end($this->history)==$mid)
{
$this->O("%n's torment prevents it from using %m again.","|cant|%fslot|move: Torment|%m");
$params['skip'] = true; return false;
}
// Disable
if($this->volatile & self::F_DISABLE && $this->disabled==$mid)
{
$this->O("%n's %m is disabled.","|cant|%fslot|move: Disable|%m");
$params['skip'] = true; return false;
}
// Mimic
if($mid == 102 && $this->mimic) $params['move'] = $mid = $this->mimic;
break;
case self::E_PREATTACK2:
$mid = $params['move'];
// Wide Guard
if(count($params['targets'])>1)
{
foreach($params['targets'] as $tid=>$t)
{
if($t->volatile2 & self::F2_WIDEGUARD)
{
if($t->player%2 == $this->player%2){ unset($params['targets'][$tid]); continue; }
$params['skip'] = true;
$this->O("%pkn({$t->U})'s Wide Guard prevents %n from using %m.","|-singleturn|%fslot|wideguard");
}
}
}
// Having to repick target after para/confusion (78-50401-67211)
// Related ASM: 21CAA78 (why is it called at that time? 21BE0DE/21BDFCC)
if($META[$mid]['target'] == 8)
{
// Invert==0 378343458678@3 Outrage Invert==3 250849576832@5
if(count($params['targets'])>1)
{
$t = $BV->rand(count($params['targets']));
$params['targets'] = array($params['targets'][$t]);
}
else if(count($params['targets']) && ($BV->pc>2 || $log->leads>1))
{
$BV->rand(2); // TODO
}
$params['tc'] = count($params['targets']);
}
// Future Sight
if($mid == 248 && !isset($params['delayed']))
{
$f = false;
foreach($GLOBALS['delayed'] as $d)
if($d[0]==3 && $d[1]==$this) $f=true;
if(!$f)
{
$this->O("","|move|%fslot|%m");
$this->O("%n foresaw an attack!","|-start|%fslot|futuresight");
$BV->lastmove = 248;
$params['delay'] = true;
}
else{ $this->fail(); $params['skip'] = true; }
return false;
}
// Wish
if($mid == 273 && !isset($params['delayed']))
{
$this->O("%n made a wish!","|move|%fslot|%m");
$params['delay'] = true; return false;
}
// Locking moves, slightly bugged when mcalled 221888640056
if($META[$mid]['effect']==28 && !$this->mturns)
{
$duration = 2+$BV->rand(2); // Always roll duration
if(!isset($params['mcalled']))
{
$this->mturns = $duration;
$params['firstmturn'] = true;
BVideo::debug("Move duration: $duration turns.");
}
}
// Charge moves
if($META[$mid]['flags'] & (1<<2))
{
if($this->mcharge)
{
$this->mcharge = 0;
$this->params['nohistory'] = true;
}
else
{
$this->O("%n is preparing to use %m.","|-prepare|%fslot|%m|%fslot({$params['targets'][0]->U})");
if($mid == 76 && $BV->getweather() == BVideo::W_SUN)
{
$this->O("There is no charging turn in Sunlight.","");
}
else if($this->getitem() == 271) // Power Herb
{
$this->O("%n's Power Herb allows it to forego the the charging turn.","");
$this->useitem();
}
else
{
$this->mcharge = $mid;
$params['skip'] = $params['charge'] = true;
return;
}
}
}
break;
case self::E_AFFECTED:
$affected = true;
case self::E_ATTACK:
$user = $params['user'];
$mid = $params['move'];
$crit = false;
$stab = 0x1000;
$destinybond = ($this->volatile & self::F_DESTINYBOND);
if(!$mid)
{
$atype = -1;
$bp = 40;
$tmul = 1;
$sid = 1;
$mod = 0x1000;
goto damageCalc;
}
$atype = $MOVES[$mid]['type'];
$bp = $MOVES[$mid]['bp'];
if($user->ability == 96) $atype=1; // Normalize
// Variable type
switch($mid)
{
case 237: // Hidden Power
$atype = $user->hpt;
break;
case 449: // Judgment
$jt = array(298=>10, 299=>11, 300=>13, 301=>12, 302=>15, 303=>2, 304=>4, 305=>5, 306=>3, 307=>14, 308=>7, 309=>6, 310=>8, 311=>16, 312=>17, 313=>9);
if(isset($jt[$user->item])) $atype = $jt[$user->item];
break;
case 311: // Weather Ball
switch($BV->getweather())
{
case BVideo::W_RAIN: $atype = 11; break;
case BVideo::W_SUN: $atype = 10; break;
case BVideo::W_SAND: $atype = 6; break;
case BVideo::W_HAIL: $atype = 15; break;
}
break;
case 363: // Natural Gift
$NG = array(149=>array(10, 60),150=>array(11, 60),151=>array(13, 60),152=>array(12, 60),153=>array(15, 60),154=>array(2, 60),155=>array(4, 60),156=>array(5, 60),157=>array(3, 60),158=>array(14, 60),159=>array(7, 60),160=>array(6, 60),161=>array(8, 60),162=>array(16, 60),163=>array(17, 60),164=>array(9, 60),165=>array(10, 70),166=>array(11, 70),167=>array(13, 70),168=>array(12, 70),169=>array(15, 70),170=>array(2, 70),171=>array(4, 70),172=>array(5, 70),173=>array(3, 70),174=>array(14, 70),175=>array(7, 70),176=>array(6, 70),177=>array(8, 70),178=>array(16, 70),179=>array(17, 70),180=>array(9, 70),181=>array(10, 80),182=>array(11, 80),183=>array(13, 80),184=>array(10, 60),185=>array(11, 60),186=>array(13, 60),187=>array(12, 60),188=>array(15, 60),189=>array(2, 60),190=>array(4, 60),191=>array(5, 60),192=>array(3, 60),193=>array(14, 60),194=>array(7, 60),195=>array(6, 60),196=>array(8, 60),197=>array(16, 60),198=>array(17, 60),199=>array(9, 60),200=>array(1, 60),201=>array(12, 80),202=>array(15, 80),203=>array(2, 80),204=>array(4, 80),205=>array(5, 80),206=>array(3, 80),207=>array(14, 80),208=>array(7, 80),209=>array(6, 80),210=>array(8, 80),211=>array(16, 80),212=>array(17, 80));
if(($i=$user->getitem()) && isset($NG[$i]))
list($atype, $bp) = $NG[$i];
if(!$affected) $user->item = 0;
break;
}
$tmul = $this->type_mul($atype);
if($tmul>1) $this->params['supereffective'] = 1;
$this->hadsub = ($this->subhp > 0);
if($affected)
{
if($user->player == $this->player && $user->id == $this->id
&& $MOVES[$mid]['cat']==1) return !($params['fail']=0);
// Damp
if($META[$mid]['effect'] == 39 && in_array($this->mbability(),array(5,6)))
{
$this->O("%n is protected by %ab!","|-activate|%fslot|%ab");
return false;
}
switch($mid)
{
case 363: // Natural Gift
if(!(($a = $user->getitem()) && strpos($ITEMS[$a]['name'],"Berry"))) return $this->fail();
break;
case 264: // Focus Punch
if(count($user->hurtby)) return $this->fail();
break;
case 255: // Spit Up
if(!$this->stockpile) return $this->fail();
break;
case 387: // Last Resort
foreach($user->moves as $m)
{
if($m == 387 || !$m) continue;
foreach($user->history as $i=>$tmp)
if($tmp == $m && $user->shistory[$i]) continue 2;
return $this->fail();
}
break;
case 213: // Attract
if((1-$this->gender) != $user->gender) return $this->fail();
break;
case 283: // Endeavour
if($user->hp > $this->hp) return $this->fail();
break;
case 382: // Me First
if($this->hasmoved || !isset($this->planned) || $this->planned[2] != BattleLog::P_ATTACK)
$this->fail();
else
{
$user->hasmoved = 0;
$a = $user->planned;
$a[3] = $this->planned[3];
do_action($a, array('bpmul'=>0x1800, 'mefirst'));
}
return false;
break;
case 389: // Sucker Punch
if($this->planned[2] != BattleLog::P_ATTACK || $MOVES[$this->planned[3]]['cat']==1 || $this->hasmoved)
return $this->fail();
break;
case 252: // Fake Out
if(count($user->history) && $user->turncount) return $this->fail();
break;
case 138: // Dream Eater
if($this->status != self::S_ASLEEP) return $this->fail();
break;
case 173: // Snore
if($user->status != self::S_ASLEEP) return $this->fail();
break;
case 102: // Mimic
if(($lm = end($this->history)) && !in_array($lm, array(448,118,102,166,165)))
{
$user->mimic = $lm;
$user->O("%n learned %m($lm)!","|-start|%fslot|mimic|%m($lm)");
}
else return $this->fail();
break;
case 68: // Counter
$at = 2; goto _counterCheck;
case 243: // Mirror Coat
$at = 3; goto _counterCheck;
case 368: // Metal Burst
$at = 0;
_counterCheck:
foreach($this->hurtby as $h)
{
if($h[2] && (!$at || $h[3]==$at) && ($h[0]%2 != $this->player%2))
{
$tpk = $players[$h[0]]->party[$partys[$h[0]][$h[1]]];
$dmg = (int)($h[2] * ($at ? 2 : 1.5));
if($user==$this)
{
$params['xdmg'] = $dmg;
$params['tpk'] = $tpk;
}
}
}
if(!isset($params['tpk'])) return $this->fail();
if(!$tpk->type_mul($atype)) return $this->fail();
break;
}
// Immunity is before accuracy for damaging moves and Thunder Wave
if((!$tmul && ($MOVES[$mid]['cat']>1 || $mid==86) && $mid != 165) // Struggle ignores immunities
|| ($mid==171 && $this->status != self::S_ASLEEP))
{
$this->O("%un's attack has no effect on %n...","|-immune|%fslot|[msg]");
return false;
}
// Gravity
if($BV->gravityt && ($META[$mid]['flags']>>10)&1)
{
$this->O("%m be used during Gravity!","|cant|%fslot|gravity|%m");
return false;
}
$oor = false;
switch($this->mcharge)
{
case 19: // Fly
$x = array(16, 542, 327, 479, 87, 239, 18); goto _flyBounce;
case 340: // Bounce
$x = array(16, 87, 239, 327);
_flyBounce: if(!in_array($mid, $x)) $oor=true;
else chainmod($params['bpmul'],0x2000);
break;
case 91: // Dig
if(!in_array($mid, array(89,90,220))) $oor=true;
else chainmod($params['bpmul'],0x2000);
break;
case 291: // Dive
if(!in_array($mid, array(57,250))) $oor=true;
else chainmod($params['bpmul'],0x2000);
break;
case 467: // Shadow Force
$oor = true;
break;
}
if($oor && !$this->mindread)
{
$this->O("%un is out of range.","|-miss|%fslot({$user->U})|[from] move: %m({$this->mcharge})");
$this->fixmiss();
return false;
}
if($this->volatile2 & self::F2_PROTECT)
{
if($mid == 364) // Feint
{
$this->volatile2 &= ~(self::F2_PROTECT | self::F2_WIDEGUARD);
$this->O("The feint broke %n's guard!","|-activate|%fslot|feint");
}
else if(in_array($mid, array(367,174,353,364,248,286,195,244,272,467,144)))
{
// Not blocked
}
else if($META[$mid]['flags'] & (1<<4))
{
$this->O("%n protected itself!","|-activate|%fslot|Protect");
return false;
}
}
foreach($params['opponents'] as $p)
{
if($p->volatile2 & self::F2_QUICKGUARD && $META[$mid]['priority']>0)
{
$this->O("%n was protected by %pkn({$p->U})'s Quick Guard!","|-activate|%fslot|quickguard");
return false;
}
}
switch($this->mbability())
{
case 43: // Soundproof
if(($META[$mid]['flags']>>9)&1)
{
$this->O("%n is immune because of %ab!","|-immune|%fslot|%ab");
return false;
}
break;
case 10: // Volt Absorb
if($atype == 13) goto _absorb;
break;
case 87: case 11: if($atype != 11 || $MOVES[$mid]['cat']==1) break; // Water Absorb
_absorb:
$this->O("%n's HP is restored by %ab.","");
$this->changehp(25,1, 0, "ability: %ab");
return false;
break;
case 18: // Flash Fire
if($atype==10)
{
$this->O("%n's fire type moves are boosted by %ab","|-start|%fslot|flashfire");
$this->volatile |= self::F_FLASHFIRE;
return false;
}
break;
case 114: // Storm Drain
if($atype==11 && $MOVES[$mid]['cat']>1)
{
$this->O("%n's Special Attack is boosted by %ab!","");
$this->statchange(4, 1, 0, "[from] ability: %ab|[of] %fslot");
return false;
}
break;
case 25: // Wonder Guard
if($tmul<2 && $MOVES[$mid]['cat']>1)
{
$this->O("%n's %ab makes it immune to the attack!","|-activate|%fslot|wonderguard");
return false;
}
break;
case 78: // Motor Drive
if($MOVES[$mid]['cat']>1 && $atype==13)
{
$this->O("%n's %ab increased its Speed!","");
$this->statchange(6, 1, 0, "[from] ability: %ab");
return false;
}
break;
case 31: // Lightningrod
if($atype==13)
{
$this->O("%n's %ab increased its Special Attack!","");
$this->statchange(4, 1, 0, "[from] ability: %ab");
return false;
}
break;
case 157: // Sap Sipper
if($atype==12)
{
$this->O("%n's %ab increased its Attack!","");
$this->statchange(2, 1, 0, "[from] ability: %ab");
return false;
}
break;
case 140: // Telepathy
if($user->player%2 == $this->player%2)
{
$this->O("%n avoids its allies' attacks with %ab!","|-activate|%fslot|telepathy");
return false;
}
break;
}
if($MOVES[$mid]['acc'])
{
$acc = $MOVES[$mid]['acc'];
$nocheck = false;
// No Guard
if($user->ability == 99 || $this->ability == 99) $nocheck = true;
// Pursuit on switch
if(isset($params['pursuit'])) $nocheck = true;
if($this->mindread) $nocheck = true;
switch($mid)
{
case 59: // Blizzard
if($BV->getweather() == BVideo::W_HAIL) $nocheck = true;
break;
case 542: case 87: // Thunder, Hurricane
if($BV->getweather() == BVideo::W_RAIN) $nocheck = true;
else if($BV->getweather() == BVideo::W_SUN) $acc=50;
break;
}
if(!$nocheck)
{
if($META[$mid]['effect'] != 39)
{
// Zoom Lens
$amod = ($user->getitem()==276 && $this->hasmoved) ? 0x1333 : 0x1000;
$amod = $user->getstat(6, 0, $amod);
$emod = $this->getstat(7);
$acc = round($acc*$amod/$emod,0); //ApplyAccuracyBoost
BVideo::debug("Modified accuracy: $acc {$user->nick}($amod)->{$this->nick}($emod).");
}
else
{
if($user->lvl < $this->lvl) goto _immune;
$acc += $user->lvl-$this->lvl;
}
$r = $BV->rand(100);
if($r >= $acc)
{
$this->O("%n avoided %un's attack!","");
if(count($params['targets'])>1)
$this->O("","|-miss|%fslot|[msg]");
else
$user->O("","|-miss|%fslot");
$this->fixmiss();
return false;
}
}
}
// Immunity is after accuracy for Toxic
if(!$tmul && $mid==92)
{
_immune:
$this->O("%un's attack has no effect on %n","|-immune|%fslot|[msg]");
return false;
}
// Substitute
//$exc = array(213,174,194,50,227,193,288,385,114,391,286,357,316,195,244,46,272,285,180,269,259,18,270);
if($MOVES[$mid]['cat']==1 && $this->subhp && !(($META[$mid]['flags']>>14)&1)) // Use substitute bypass flag
{
$this->O("The attack is blocked by %n's Substitute.","|-start|%fslot|substitute|[block]");
return false;
}
// Multi hit moves (after accuracy)
if($minh = $META[$mid]['minhits'])
{
$maxh = $META[$mid]['maxhits'];
if($maxh > $minh)
{
$r = $BV->rand(100);
$ht = array(0, 0, 35, 70, 85, 100);
for($minh=0; $r >= $ht[$minh]; $minh++);
}
if($user->ability == 92) // Skill Link
$minh = $maxh;
// Beat Up
if($mid == 251)
{
$pool = array();
foreach($players[$user->player]->party as $pk)
if($pk->hp && ($pk==$user || !$pk->status)) $pool[] = $pk;
$params['beatup'] = $pool;
$minh = count($pool);
}
BVideo::debug("Number of hits: $minh");
$params['hits'] = $minh;
}
$params['fail'] = 0;
return true;
}
// OHKO Moves
if($META[$mid]['effect'] == 39)
{
$this->O("It's a one-hit KO!","|-ohko");
$this->changehp(-($dmg=$this->bstats[1]), 0);
$this->hurtby[] = array($user->player, $user->slot, $dmg, $MOVES[$mid]['cat'], $user->id);
return;
}
// Bypass accuracy when damage is set
if(isset($params['dmg']) && in_array($mid, array(68,243,368)))
{
$dmg = $params['dmg'];
goto EndDmg;
}
// Damaging moves
if($MOVES[$mid]['cat']>1)
{
// Shell Armor, Battle Armor, Lucky Chant
if(!in_array($this->mbability(), array(4, 75)) && !($BV->sidee[$this->player%2] & BVideo::S_LUCKYCHANT))
{
if($META[$mid]['effect']== 289)
{
$crit = 1;
}
else
{
$crm = $META[$mid]['critrate']; // Crit level
$crm += $user->critlevel;
if($user->ability == 105) $crm++; // Super Luck
switch($user->getitem())
{
case 232: case 326: // Scope Lens, Razor Claw
$crm++;
break;
case 259: // Stick
if($user->dex==83) $crm+=2;
break;
case 256: // Lucky Punch
if($user->dex==113) $crm+=2;
break;
}
$crl = array(16, 8, 4, 3, 2);
if($crm > 4) $crm = 4;
$crit = ($BV->rand($crl[$crm])==0);
}
}
switch($mid) // Fixed damage
{
case 68: // Counter
case 243: // Mirror Coat
case 368: // Metal Burst
$params['dmg'] = $params['xdmg'];
$params['tpk']->trigger(BPkm::E_ATTACK, $params);
return;
break;
case 162: // Super Fang
$dmg = $this->hp >> 1;
goto EndDmg;
break;
case 149: // Psywave
$dmg = max(1, (int)((($BV->rand(101)+50)*$user->lvl)/100));
goto EndDmg;
break;
case 101: case 69: // Seismic Toss / Night Shade
$dmg = $user->lvl;
goto EndDmg;
break;
case 49: // Sonicboom
$dmg = 20;
goto EndDmg;
break;
case 82: // Dragon Rage
$dmg = 40;
goto EndDmg;
break;
case 283: // Endeavour
$dmg = max(1, $this->hp-$user->hp);
goto EndDmg;
break;
case 515: // Final Gambit
$dmg = $user->hp;
$user->changehp(-100,1,1);
goto EndDmg;
break;
case 280: // Brick Break
if($BV->sidee[$this->player%2]&(BVideo::S_REFLECT|BVideo::S_LIGHTS))
{
$this->O("The barrier around %n shattered!","|-activate|%fslot({$user->U})|brickbreak|[of] %fslot");
$BV->sidee[$this->player%2] &= ~(BVideo::S_REFLECT|BVideo::S_LIGHTS);
}
break;
case 165: // Struggle
$tmul = 1; // Typeless damage
break;
}
if($params['gem'] > 1)
{
$user->O("The power of %m($mid) is increased by %n's %item","|-enditem|%fslot|%item|[from] gem|[move] %m($mid)");
$params['gem'] = 1;
}
if($crit) $this->O("","|-crit|%fslot");
if($tmul!=1)
$this->O("It's ".($tmul<1?'not very':'super')." effective against %n.", "|-".($tmul<1?"resisted":"supereffective")."|%fslot");
###### BASE POWER ######
switch($mid)
{
case 228: // Pursuit
if(isset($params['pursuit'])) $bp *= 2;
break;
case 255: // Spit Up
$bp = 100*$this->stockpile;
$this->stockpile = 0;
$this->statchange(3, -1); $this->statchange(5, -1);
break;
case 514: // Retaliate
$hasf = $GLOBALS['players'][$user->player]->hasfainted;
if($hasf[1] || $hasf[0]) $bp *= 2;
break;
case 237: // Hidden Power
$bp = $user->hpp;
break;
case 76: // SolarBeam
if(($w=$BV->getweather()) && $w!=BVideo::W_SUN) $bp >>=1;
break;
case 512: // Acrobatics
if(!$user->getitem() || $params['gem']) $bp *= 2;
break;
case 500: // Stored Power
foreach($user->statups as $s) if($s>0) $bp += 20*$s;
break;
case 216: // Return
$bp = max(1,(int)($user->happiness*10/25));
break;
case 218: // Frustration
$bp = max(1, (int)((255-$user->happiness)*10/25));
break;
case 372: // Assurance
if(count($this->hurtby)) $bp *= 2;
break;
case 419: // Avalanche
case 279: // Revenge
// 267232308908 Onfield
foreach($user->hurtby as $pk)
if($pk[0]==$this->player && /*$partys[$this->player][$pk[1]]*/ $pk[3] == $this->id)
{
$bp *= 2; break;
}
break;
case 362: // Brine
if($this->getstat(0,1) <= 50) $bp *= 2;
break;
case 462: // Crush Grip
$bp = 1+(int)(1.2*$this->getstat(0,1));
break;
case 378: case 462: // Wring Out, Crush Grip
$bp = (int)(120*$this->getstat(0,1)/100);
break;
case 323: case 284: // Eruption / Water Spout
$bp = (int)(150*$user->getstat(0,1)/100);
break;
case 263: // Facade
if($user->status) $bp *= 2;
break;
case 358: // Wake-Up Slap
if($this->status == self::S_ASLEEP)
{
$bp *= 2;
$this->O("%n woke up!","|-curestatus|%fslot|slp");
$this->status = 0;
}
break;
case 265: // SmellingSalt
if($this->status == self::S_PARALYZED)
{
$bp *= 2;
$this->O("%n was cured of paralysis!","|-curestatus|%fslot|par");
$this->status = 0;
}
break;
case 311: // Weather Ball
if($BV->getweather()) $bp *= 2;
break;
case 360: // Gyro Ball
$bp = min(150, 1+(int)(25*$this->getstat(5)/$user->getstat(5)));
break;
case 486: // Electro Ball
$ratio = (int)($user->getstat(5)/$this->getstat(5));
if($ratio >= 4) $bp = 150;
else if($ratio >=3) $bp = 120;
else if($ratio >= 2) $bp = 80;
else if($ratio >= 1) $bp = 60;
else $bp = 40;
break;
case 371: // Payback
if($this->hasmoved) $bp *= 2;
break;
case 386: // Punishment
$bp = 60;
foreach($this->statups as $v)
if($v>0) $bp += 20*$v;
$bp = min(200, $bp);
break;
case 67: case 447: // Low Kick, Grass Knot
$w = $this->getweight();
if($w >= 200) $bp = 120;
else if($w >= 100) $bp = 100;
else if($w >= 50) $bp = 80;
else if($w >= 25) $bp = 60;
else $bp = 40;
break;
case 484: case 535: // Heavy Slam, Heat Crash
$w = (int)($user->getweight()/$this->getweight());
if($w >= 5) $bp = 120;
else if($w >= 4) $bp = 100;
else if($w >= 3) $bp = 80;
else if($w >= 2) $bp = 60;
else $bp = 40;
break;
case 506: // Hex
if($this->status) $bp *= 2;
break;
case 167: // Truple Kick
$bps = array(10, 20, 30);
$bp = $bps[$params['curhit']];
break;
case 175: case 179: // Flail, Reversal
$p = (int)((48 * $user->getstat(0,1))/100);
if($p > 32) $bp = 20;
else if($p >= 17) $bp = 40;
else if($p >= 10) $bp = 80;
else if($p >= 5) $bp = 100;
else if($p >= 2) $bp = 150;
else $bp = 200;
break;
case 376: // Trump Card
$bps = array(40, 50, 60, 80, 200);
$BV->debug("PP not implemented"); // TODO
$bp = $bps[0];
break;
case 217: // Present
$r = $BV->rand(80);
if($r >= 70) $bp = 120;
else if($r >= 40) $bp = 80;
else $bp = 40;
break;
case 222: // Magnitude
$r = $BV->rand(100);
if($r >= 95) $r = 6;
else if($r >= 85) $r = 5;
else if($r >= 65) $r = 4;
else if($r >= 35) $r = 3;
else if($r >= 16) $r = 2;
else if($r >= 5) $r = 1;
else $r = 0;
$bp = 10+20*$r++;
$this->O("Magnitude $r!","|-activate|%fslot|magnitude|$r");
break;
case 205: case 301: // Rollout
$r = true; $mul = 0;
foreach(array_reverse($user->history) as $m)
{
if($m == $mid)
{
if($r && $mul<=3) $mul++;
else $r=false;
}
if($mul == 111) $mul++; // Defense Curl
}
$bp <<= $mul;
break;
case 251: // Beat Up
$i = $params['curhit'];
$bp = 5+(int)($params['beatup'][$i]->base[1]/10);
break;
case 374: // Fling
if(($user->getitem()) && ($ui=$user->canloseitem(true))
&& ($r=mysql_fetch_assoc(mysql_query("SELECT bp FROM fling WHERE id='$ui' LIMIT 1"))))
{
$bp = $r['bp'];
}
else
{
$this->fail();
$params['fail'] = true;
return false;
}
break;
case 450: // Bug Bite (before damage to avoid berrycheck in changehp)
$a = $this->item;
if($a && strpos($ITEMS[$a]['name'],"Berry") && !$this->hadsub)
{
$this->O("%un ate %n's %item!","");
$this->useitem("[from] stealeat|[of] %fslot({$user->U})");
$i = $user->item;
$user->item = $a; $user->berrycheck(true); $user->item = $i;
}
break;
// Fusion Bolt / Fusion Flare
case 558: $a = 559; goto _fusionMove;
case 559: $a = 558; _fusionMove:
if($BV->lastmove == $a) $bp *= 2;
break;
// TODO TODO TODO
case 16: // Gust
case 239: // Twister
case 210: // Fury Cutter
case 255: // Spit Up
case 497: // Echoed Voice
case 496: // Round
case 518: // Water Pledge
case 519: // Fire Pledge
case 520: // Grass Pledge
$BV->debug("Effect not implemented");
break;
}
$mod = 0x1000;
// Pursuit, Me First...
if($params['bpmul'] != 0x1000) chainmod($mod, $params['bpmul']);
if(($BV->msuser && $atype == 13) || ($BV->wsuser && $atype == 10))
{
chainmod($mod, 0x548);
}
switch($user->ability)
{
case 101: // Technician
if($bp <= 60) chainmod($mod, 0x1800);
break;
case 138: // Flare Boost
if($user->status == self::S_BURNED && $MOVES[$mid]['cat']==3)
chainmod($mod, 0x1800);
break;
case 148: // Analytic
if($this->hasmoved) chainmod($mod, 0x14CD);
break;
case 79: // Rivalry
if($user->gender == $this->gender) chainmod($mod, 0x1400);
else if((1-$user->gender) == $this->gender) chainmod($mod, 0xC00);
break;
case 89: // Iron Fist
if($META[$mid]['flags'] & (1<<8)) chainmod($mod, 0x1333);
break;
case 120: // Reckless
if($META[$mid]['recoil']<0 || $META[$mid]['effect']==46) chainmod($mod, 0x1333);
break;
case 137: // Toxic Boost
if($user->status == self::S_POISON && $MOVES[$mid]['cat']==2)
chainmod($mod, 0x1800);
break;
case 159: // Sand Force
if(in_array($atype, array(5, 6, 9)) && $BV->getweather()==BVideo::W_SAND) chainmod($mod, 0x14CD);
break;
case 125: // Sheer Force
if($META[$mid]['ailmentchance'] || $META[$mid]['flinchrate'] || ($META[$mid]['statchance'] && $META[$mid]['class']!=7))
{
$params['sheerforce'] = true;
chainmod($mod, 0x14CD);
}
break;
}
switch($this->ability)
{
case 85: // Heatproof
if($atype == 10 && $this->mbability()) chainmod($mod, 0x800);
break;
case 87: // Dry Skin
if($atype == 10) chainmod($mod, 0x1400);
break;
case 154: // Justified
if($atype == 17) $this->statchange(2, 1);
break;
case 155: // Rattled
if(in_array($atype, array(7,8,17))) $this->statchange(6, 1);
break;
}
// Gems
if($params['gem']) chainmod($mod, 0x1800);
// Plates & Type items
$tei = array(222=>7, 233=>9, 237=>5, 238=>6, 239=>12, 240=>17, 241=>2, 242=>13, 243=>11, 244=>3, 245=>4, 246=>15, 247=>8, 248=>14, 249=>10, 250=>16, 251=>1, 298=>10, 299=>11, 300=>13, 301=>12, 302=>15, 303=>2, 304=>4, 305=>5, 306=>3, 307=>14, 308=>7, 309=>6, 310=>8, 311=>16, 312=>17, 313=>9);
if(isset($tei[$user->getitem()]) && $atype == $tei[$user->getitem()])
{
chainmod($mod, 0x1333);
}
switch($user->getitem())
{
case 266: // Muscle Band
if($MOVES[$mid]['cat']==2) chainmod($mod, 0x1199);
break;
case 267: // Wise Glasses
if($MOVES[$mid]['cat']==3) chainmod($mod, 0x1199);
break;
case 136: // Lustruous Orb
if($user->dex == 484 && in_array($atype, array(11, 16))) chainmod($mod, 0x1333);
break;
case 135: // Adamant Orb
if($user->dex == 483 && in_array($atype, array(9, 16))) chainmod($mod, 0x1333);
break;
case 112: // Griseous Orb
if($user->dex == 487 && in_array($atype, array(8, 16))) chainmod($mod, 0x1333);
break;
}
// Charge
if($user->volatile & self::F_CHARGE && $atype==13)
chainmod($mod, 0x2000);
// Helping Hand
if($user->volatile2 & self::F2_HELPINGHAND) chainmod($mod, 0x1800);
if($user->volatile2 & self::F2_MEFIRST) chainmod($mod, 0x1800);
switch($mid)
{
case 362: // Brine
if($this->getstat(0,1) <= 50) chainmod($mod, 0x2000);
break;
case 474: // Venoshock
if($this->status == self::S_POISON) chainmod($mod, 0x2000);
break;
}
$bpmod = $mod; $basebp = $bp;
$bp = applymod($bp, $mod);
$mod = 0x1000;
switch($user->ability)
{
// Overgrow, Blaze, Torrent, Swarm
case 65: case 66: case 67: case 68:
$tmp = array(65=>12, 66=>10, 67=>11, 68=>7);
if($tmp[$user->ability]==$atype && (3*$user->hp/$user->bstats[1])<=1)
chainmod($mod, 0x1800);
break;
case 18: // Flash Fire
if($atype==10 && $user->volatile & self::F_FLASHFIRE) chainmod($mod, 0x1800);
break;
case 129: // Defeatist
if($user->getstat(0,1) <= 50) chainmod($mod, 0x800);
break;
}
// Thick Fat
if($this->mbability()==47 && in_array($atype, array(10, 15))) chainmod($mod, 0x800);
$sid = $MOVES[$mid]['cat']==2 ? 1 : 3;
damageCalc:
if($mid == 492) // Foul Play
$at = $this->getstat($sid, $crit, $mod);
else
$at = $user->getstat($sid, $crit, $mod, !$mid);
$sid++; $invert = false;
if($mid)
{
if($META[$mid]['effect']==283) $invert = !$invert;
if($BV->wrcount) $invert = !$invert; // Wonder Room
if($invert) $sid = ($sid==2 ? 4 : 2);
}
$df = $this->getstat($sid, $crit, 0x1000, !$mid);
$dmg = (int)((2*$user->lvl)/5)+2;
$dmg = (int)(($dmg*$bp*$at)/$df);
$dmg = (int)($dmg/50)+2;
if($mid && $params['tc']>1)
{
$dmg = applymod($dmg, 0xC00);
}
if($BV->getweather() == BVideo::W_RAIN)
{
if($atype==10) $dmg = applymod($dmg, 0x800);
if($atype==11) $dmg = applymod($dmg, 0x1800);
}
else if($BV->getweather() == BVideo::W_SUN)
{
if($atype==10) $dmg = applymod($dmg, 0x1800);
if($atype==11) $dmg = applymod($dmg, 0x800);
}
// Crit
if($crit) $dmg *= 2;
// Random mod
$dmgroll = (100-$BV->rand(16))/100;
$dmg = (int)($dmg*$dmgroll);
$mod = 0x1000; // Final Mod
// STAB
if($mid != 165) // Struggle
if($atype == $params['user']->type1 || $atype==$params['user']->type2)
{
$stab = ($user->ability == 91) ? 0x2000 : 0x1800; // Adaptability
$dmg = applymod($dmg, $stab);
}
// Type effectiveness and Burn
$dmg = (int)($dmg*$tmul);
if($mid && $user->status == self::S_BURNED
&& $MOVES[$mid]['cat']==2 && $user->ability != 62) $dmg >>= 1;
if(!$dmg) $dmg++;
if(!$mid) goto EndDmg;
// Reflect / Light Screen
if(!$crit && ($this->ability != 151) // Infiltrator
&& (($BV->sidee[$this->player%2] & BVideo::S_REFLECT && $MOVES[$mid]['cat']==2)
|| ($BV->sidee[$this->player%2] & BVideo::S_LIGHTS && $MOVES[$mid]['cat']==3)))
chainmod($mod, (($log->leads>=2||$BV->pc>2) ? 0xA8F : 0x800));
// Target ability
switch($this->mbability())
{
case 136: // Multiscale
if($this->getstat(0,1)==100) chainmod($mod, 0x800);
break;
case 116: // Solid Rock
case 111: // FIlter
if($tmul > 1) chainmod($mod, 0xC00);
break;
}
// User ability
switch($user->ability)
{
case 97: // Sniper
if($crit) chainmod($mod, 0x1800);
break;
case 110: // Tinted Lens
if($tmul < 1) chainmod($mod, 0x2000);
break;
}
// Friend Guard
foreach(array_merge($params['opponents'], $params['allies']) as $a)
{
if(($a->player%2 == $this->player%2) && $a->mbability($user) == 132
&& ($a->id != $this->id || $a->player != $this->player))
chainmod($mod, 0xC00);
}
// User item
switch($user->getitem())
{
case 270: // Life Orb
chainmod($mod, 0x14CC);
break;
case 268: // Expert Belt
if($tmul > 1) chainmod($mod, 0x1333);
break;
case 277: // Metronome
$met = 0x1000; // Use hhistory but to be accurate should use a success counter (@21D67BC)
// Interestingly, the charging turn of a move is the one that is used for the history
for($i=count($user->history)-1; $i>=0 && $user->history[$i]==$mid && $user->shistory[$i] && $met < 0x1999; $i--, $met+=0x333);
if($met > 0x1000) chainmod($mod, $met);
break;
}
// Type resisting berries
if($this->getitem() >= 184 && $this->getitem() <= 200 && !$this->subhp)
{
$tmp = array(184=>10, 185=>11, 186=>13, 187=>12, 188=>15, 189=>2, 190=>4, 191=>5, 192=>3, 193=>14, 194=>7, 195=>6, 196=>8, 197=>16, 198=>17, 199=>9, 200=>1);
if($atype == $tmp[$this->getitem()])
{
chainmod($mod, 0x800);
$this->O("%n's %item weakens %un's %m power!","");
$this->useitem("[weaken]");
}
}
// Apply final modifier
$dmg = applymod($dmg, $mod);
EndDmg:
$pc = floor((100*$dmg)/($this->bstats[1]));
$this->O(($crit?"It's a critical hit! ":'')."%n lost $dmg HP ($pc%).","|debug|Actual damage was $dmg ($pc%)");
if($dmg)
$admg = $this->changehp(-1*$dmg, false, false, (!$mid?"confusion":''));
else $dmg = $admg = 1;
if($admg && $mid && !$this->hadsub)
$this->hurtby[] = array($user->player, $user->slot, $dmg, $MOVES[$mid]['cat']);
$params['inflicted'] += $admg;
if(!$mid) return;
$basebp = isset($basebp) ? $basebp:0;
$at = isset($at) ? $at : 0;
$df = isset($df) ? $df : 0;
$bpmod = isset($bpmod) ? $bpmod : 0x1000;
$mod = isset($mod) ? $mod : 0x1000;
$dmgroll = isset($dmgroll) ? $dmgroll : 1;
BVideo::debug(sprintf("basebp=$basebp, bpmod=0x%x, stab=0x%x, type=$tmul, random=$dmgroll, at=$at, df=$df, fmod=0x%x.",$bpmod, $stab, $mod));
// Air Balloon
if($this->item == 541)
{
$this->O("%n's Air Balloon popped.","");
$this->useitem();
}
// Destiny Bond
if($this->hp == 0 && $destinybond)
{
$this->O("%n brought down %un with it.","|-activate|%fslot|Destiny Bond");
$user->changehp(-100, 1, 1);
return;
}
// Moxie
if($user->ability == 153 && !$this->hp) $user->statchange(2, 1);
// Fire move thawing
if($atype == 10 && $this->status == self::S_FREEZE)
{
$this->O("%n thawed.","|-curestatus|%fslot|frz");
$this->setstatus(0,1);
}
// Anger Point
if($crit && $this->ability == 83 && $this->hp)
{
$this->statchange(2, 6);
}
switch($itm = $this->getitem())
{
case 545: // Absorb Bulb
if($this->hp && $MOVES[$mid]['type'] == 11)
{
$this->O("%n's %item raised its Special Attack!.","");
$this->statchange(4, 1, 0, "[from] item: %item");
$this->useitem("[silent]");
}
break;
case 546: // Cell Battery
if($this->hp && $MOVES[$mid]['type'] == 13)
{
$this->O("%n's %item raised its Attack!.","");
$this->statchange(2, 1, 0, "[from] item: %item");
$this->useitem("[silent]");
}
break;
}
// Drain effect, Recoil, Rock Head
if($admg && ($recoil = $META[$mid]['recoil']) && !($user->ability==69 && $recoil<0))
{
// Big Root
if($user->getitem() == 296 && $recoil>0) $recoil += 16;
// Be careful with fp emulated rounding!
// var_dump($admg/$div,$recoil);
// 575300734604 float(-47.85)->-48
// 149738464042 93.5 ->94
// $hp2 = ceil(($admg*$recoil)/100); Incorrect but seen in emulators
$div = (100/$recoil);
$hp = round($admg/$div, 0, PHP_ROUND_HALF_UP);
if(!$hp) $hp = ($recoil>0 ? 1 : -1);
if($recoil > 0)
{
if($this->ability == 64)
{
$hp *= -1; $from = "drain";
$this->O("%un is hurt by %n's %ab and lost $hp HP.","");
}
else
{
$from = "drain";
$this->O("%n's health is drained! %un regained $hp HP.","");
}
}
else
{
$from = "recoil";
$this->O("%un is hurt by recoil.","");
}
$user->changehp($hp, -1, 0, $this->O("","$from|[of] %fslot",-1));
}
if(!$this->hp) return;
}
else // Status Move
{
// Self status
if(($ail = $META[$mid]['ailment']) && $META[$mid]['target'] == 7)
{
if($ail < 5)
{
$perm = true;
if($ail==5 && $min && $max && $min==$max) $ail = self::S_TOXIC;
if($ail == self::S_ASLEEP) $params['randduration'] = ($BV->rand(3)+2);
}
else
{
$perm = false;
$ail = 1<<($ail-6);
}
$this->setstatus($ail, $perm);
}
if($heal = $META[$mid]['healing'])
{
$rounding = false;
// Moonlight / Synthesis / Morning Sun
if($META[$mid]['effect'] == 133)
{
switch($BV->getweather())
{
case BVideo::W_NONE: $heal = 50; break;
case BVideo::W_SUN: $heal = 66.66; break;
default: $heal = 25;
}
$rounding = -1;
}
if($mid == 256)
{
$heal *= $this->stockpile;
if($heal==75) $heal = 100;
$this->stockpile = 0;
$this->statchange(3, -1);
$this->statchange(5, -1);
}
$this->changehp($heal, 1, 0, "move: %m($mid)", $rounding);
}
switch($eid = $META[$mid]['effect'])
{
case 80: // Substitute
if($this->subhp) $this->fail("Substitute");
else if(4*$this->hp <= $this->bstats[1])
$this->O("%n is too weak to make a substitute!","|-fail|%fslot|[weak] Substitute");
else
{
$this->changehp(-25, 1, 1);
$this->subhp = $this->bstats[1]>>2;
$this->O("%n made a substitute!","|-start|%fslot|Substitute");
}
break;
case 29: // Roar / Whirlwind / ...
$this->forceswitch();
break;
case 86: // Splash
$this->O("But nothing happened!","|-nothing");
break;
case 175: // Charge
$this->setstatus(self::F_CHARGE, false);
break;
case 95: // Mind Reader
$this->mindread = 2;
break;
case 114: // Foresight
$this->O("%n was identified!","|-start|%fslot|foresight");
$this->statups[6] = 0;
break;
case 243: // Copycat
$a = $this->planned;
if(!($a[3]=$BV->lastmove) || $a[3] == 383)
{
$this->fail();
$params['fail'] = 1;
}
else
{
do_action($a, array('mcalled'=>true));
$params['noeffects'] = true;
}
break;
case 10: // Mirror Move
$a = $user->planned;
if(!($a[3]=end($this->history)) || $a[3] == 119)
{
$user->fail();
$params['fail'] = 1;
}
else
{
do_action($a, array('mcalled'=>true));
$params['noeffects'] = true;
}
break;
case 84: // Metronome
$a = $this->planned;
$a[3] = metronome($BV->rand(518));
do_action($a, array('mcalled'=>true));
$params['noeffects'] = true;
break;
case 181: // Assist
$bl = array(0,68,102,118,119,144,165,166,168,182,194,197,203,214,243,253,264,266,267,270,271,274,289,343,364,382,383,415,448,476,509,516,525); // Uncallable moves
$pool = array();
foreach($partys[$this->player] as $id)
{
if($id >= $players[$this->player]->partyc) continue;
$pk = $players[$this->player]->party[$id];
if($pk==$this) continue;
foreach($pk->moves as $mid)
if(!in_array($mid,$bl)) $pool[]=$mid;
}
if(count($pool))
{
$a = $this->planned;
$a[3] = $pool[$BV->rand(count($pool))];
do_action($a, array('mcalled'=>true));
}
else $this->fail();
$params['noeffects'] = true;
break;
// Protect, Wideguard, Detect, Quick Guard, Endure
case 307: $pflag = self::F2_QUICKGUARD; goto _protect;
case 279: $pflag = self::F2_WIDEGUARD; goto _protect;
case 117: $pflag = self::F2_ENDURE; goto _protect;
case 112: $pflag = self::F2_PROTECT;
_protect:
$allmoved = true;
foreach(array_merge($params['opponents'], $params['allies']) as $a)
{
if(!$a->hasmoved){ $allmoved = false; break; }
}
if((!$this->protectcount || !$BV->rand(1<<$this->protectcount)) && !$allmoved)
{
$this->volatile2 |= $pflag;
$this->O("","|-singleturn|%fslot|".($pflag==self::F2_PROTECT?"Protect":"%m"));
}
else
$this->fail();
break;
// Rain Dance, Sunny Day, Sandstorm, Hail
case 137: case 138: case 116: case 165:
$tmp = array(137=>BVideo::W_RAIN, 138=>BVideo::W_SUN, 116=>BVideo::W_SAND, 165=>BVideo::W_HAIL);
$tmp2 = array(137=>285, 138=>284, 116=>283, 165=>282);
$BV->setweather($tmp[$eid], ($this->getitem()==$tmp2[$eid]?8:5));
break;
case 26: // Haze
foreach(array_merge($params['allies'], $params['opponents']) as $pk)
{
if($pk->hp)
{
$pk->statups = array(0,0,0,0,0,0,0);
}
}
echo "All stat changes were eliminated!<br />";
break;
case 226: // Tailwind
case 66: // Reflect
case 36: // Light Screen
case 125: // Safeguard
case 241: // Lucky Chant
case 47: // Mist
$const = array(226 => BVideo::S_TAILWIND, 66=>BVideo::S_REFLECT, 36=>BVideo::S_LIGHTS,
125=>BVideo::S_SAFEGUARD, 241=>BVideo::S_LUCKYCHANT, 47=>BVideo::S_MIST);
$ename = array(226 => "tailwind", 66=>"reflect", 36=>"lightscreen", 125=>"safeguard", 241=>"", 47=>"mist");
$vn = array(226=>"twcount", 66=>"pcount", 36=>"lscount", 125=>"sgcount", 241=>"lccount",47=>"mcount");
$lab = array(226 => "speed", 66=>"defense", 36=>"special defense");
$len = ($user->getitem() == 269 ? 8 : 5);
$dur = array(226=>4, 66=>$len, 36=>$len, 125=>5, 241=>5);
if($BV->sidee[$this->player%2] & $const[$eid])
$this->fail();
else
{
$BV->sidee[$this->player%2] |= $const[$eid];
$BV->{$vn[$eid]}[$this->player%2] = $dur[$eid];
if($eid==125) $this->O("A protective veil appeared around %n's allies.","");
else if($eid==241) $this->O("%n's allies are protected against critical hits!","");
else if($eid == 47) $this->O("%n's allies became shrouded in mist!","");
else $this->O("It raised its allies' {$lab[$eid]}!","");
if($e=$ename[$eid]) $this->O("","|-sidestart|%p|$e");
}
break;
case 267: // Stealth Rock
$n = "Rocks";
$BV->sidee[1-$this->player%2] |= BVideo::S_ROCKS;
goto _entryHazMsg;
case 250: // Toxic Spikes
$n = "Toxic spikes";
if($BV->sidee[1-$this->player%2] & BVideo::S_TSPIKES1)
$BV->sidee[1-$this->player%2] |= BVideo::S_TSPIKES2;
else $BV->sidee[1-$this->player%2] |= BVideo::S_TSPIKES1;
goto _entryHazMsg;
case 113: // Spikes
$n = "Spikes";
if($BV->sidee[1-$this->player%2] & BVideo::S_SPIKES2)
$BV->sidee[1-$this->player%2] |= BVideo::S_SPIKES3;
else if($BV->sidee[1-$this->player%2] & BVideo::S_SPIKES1)
$BV->sidee[1-$this->player%2] |= BVideo::S_SPIKES2;
else $BV->sidee[1-$this->player%2] |= BVideo::S_SPIKES1;
_entryHazMsg:
$this->O("%m were scattered around %opp's team feet!","|-sidestart|%opp|move: %m");
break;
case 58: // Transform
$user->transform($this);
break;
case 144: // Psych Up
$this->O("%un copied %n's stat changes!","|-swapboost|%fslot({$user->U})|%fslot");
$user->statups = $this->statups;
break;
case 316: // Quash
if($this->hasmoved) $this->fail();
else
{
$t = &$GLOBALS['turn']; $last = count($t)-1;
$myid = array_search($this->id,$GLOBALS['partys'][$this->player]);
foreach($t as $i=>$al)
if($al[0]==$this->player && $al[1]==$myid) break;
if($i != $last)
{
$tmp = array_splice($t, $i, 1); $t[] = $tmp[0]; // Move last
}
$this->O("%n's move was postponed","|-message|%n's move was postponed!");
}
break;
case 295: // Soak
$this->type1 = 11; $this->type2 = 0;
$this->O("%n's type changed to Water.","|-start|%fslot|typechange|Water");
break;
case 252: // Aqua Ring
$this->volatile |= self::F_AQUARING;
$this->O("%n is surrounded by a ring of water.","|-start|%fslot|aquaring");
break;
case 103: // Aromatherapy
$this->O("","|-cureteam|%fslot");
foreach(array_unique(array($this->player, ($this->player+2)%($BV->pc))) as $pl)
foreach($players[$pl]->party as $p)
{
if($p->hp && $p->status)
$p->setstatus(0, 1);
}
break;
case 143: // Belly Drum
if(2*$this->hp > $this->bstats[1])
{
$this->changehp(-50, 1, 1);
$this->statchange(2, 6);
}
else $this->fail();
break;
case 169: // Memento
$this->statchange(2, -2);
$this->statchange(4, -2);
break;
case 92: // Pain Split
$a = ($this->hp+$user->hp)>>1;
$this->changehp($a-$this->hp, -1, 1, "painsplit");
$user->changehp($a-$user->hp, -1, 1, "|[silent]");
break;
case 280: // Guard Split
$stat = 3; goto _split;
case 281: // Power Split
$stat = 2; _split:
$a = ($this->bstats[$stat]+$user->bstats[$stat])>>1;
$b = ($this->bstats[$stat+2]+$user->bstats[$stat+2])>>1;
$this->bstats[$stat] = $user->bstats[$stat] = $a;
$this->bstats[$stat+2] = $user->bstats[$stat+2] = $b;
break;
case 244: // Power Swap
$stat = 0; goto _statSwap;
case 245: // Guard Swap
$stat = 1;
_statSwap:
$a = $user->statups; $b = $this->statups;
$this->O("%un's and %n's ".($stat?'defensive':'offensive')." stat boosts are swapped.",
"|-swapboost|%fslot({$user->U})|%fslot|".($stat?"def, spd":"atk, spa")."|[from] %m");
list($a[$stat],$a[$stat+2],$b[$stat],$b[$stat+2]) = array($b[$stat],$b[$stat+2],$a[$stat],$a[$stat+2]);
$user->statups = $a; $this->statups = $b;
break;
case 235: // Psycho Shift
if($this->status || !$user->status) $this->fail();
else
{
$this->O("%un transfered its status problem to %n.","|-curestatus|%fslot({$user->U})|psychoshift|[of] %fslot");
$this->setstatus($user->status, true);
$user->setstatus(self::S_NONE, true);
}
break;
case 110: // Curse
if(in_array(8, array($user->type1, $user->type2)))
{
$this->volatile |= self::F_CURSE;
$this->O("%un cut its own HP and laid a curse on %n!","|-start|%fslot|curse|[of] %fslot({$user->U})");
$user->changehp(-50, 1, 1);
}
else
{
$user->statchange(6, -1);
$user->statchange(2, 1);
$user->statchange(3, 1);
}
break;
case 260: // Trick Room
if($BV->trcount)
{
$BV->trcount = 0;
$this->O("The dimensions returned to normal.","|-fieldend|trickroom|[of] %fslot");
}
else
{
$BV->trcount = 5;
$this->O("%s twisted the dimensions!","|-fieldstart|trickroom|[of] %fslot");
}
break;
case 287: // Magic Room
if($BV->mrcount)
{
$BV->mrcount = 0;
$this->O("The use of items returned to normal.","|-fieldend|magicroom|[of] %fslot");
}
else
{
$BV->mrcount = 5;
$this->O("Held items cannot be used!","|-fieldstart|magicroom|[of] %fslot");
}
break;
case 287: // Wonder Room
if($BV->wrcount)
{
$BV->wrcount = 0;
$this->O("Wonder Room wore off.","|-fieldend|wonderroom|[of] %fslot");
}
else
{
$BV->wrcount = 5;
$this->O("It created a bizarre area in which the Defense and Sp. Def stats are swapped!","|-fieldstart|wonderroom|[of] %fslot");
}
break;
case 216: // Gravity
if($BV->gravityt)
{
$this->O("Gravity is already in effect.","|-fail|%fslot");
}
else
{
$BV->gravityt = 5;
$this->O("%n intensified the effect of gravity!","|-fieldstart|gravity");
}
break;
case 173: // Rage Powder / Follow Me
$this->volatile2 |= self::F2_RAGEPOWDER;
break;
case 177: // Helping Hand
if($this->player%2 != $user->player%2) $this->fail();
else
{
$this->O("%un is ready to help %n!","|-singleturn|%fslot|%m|[of]%fslot({$user->U})");
$this->volatile2 |= self::F2_HELPINGHAND;
}
break;
case 99: // Destiny Bond
$this->O("%n is trying to take its foe down with it!","|-singlemove|%fslot|%m");
$this->volatile |= self::F_DESTINYBOND;
break;
case 184: // Magic Coat
$this->O("%n shrouded itself with Magic Coat!","|-singleturn|%fslot|%m");
$this->volatile2 |= self::F2_MAGICCOAT;
break;
case 239: // Power Trick
$this->volatile ^= self::F_POWERTRICK;
$this->O("%n switched its Attack and Defense!","|-start|%fslot|powertrick");
break;
case 107: // Mean Look
// TODO XXX
// Do not use damage trapping mechanism for this
$this->O("%n cannot escape!","|-message|%n cannot escape!");
break;
case 91: // Encore
// Magic Bounce Encore fails. There is a deeper cause to it
// in move history, TODO investigate (708633036034)
if($params['reflected'] || !$this->setstatus(self::F_ENCORE, 0))
$this->fail();
break;
case 308: // Ally Switch
$pos = array_search($this->id, $partys[$this->player]);
$p = $partys[$this->player][1-$pos];
$pk = $players[$this->player]->party[$p];
if($pk->onfield)
{
$partys[$this->player][1-$pos] = $this->id;
$partys[$this->player][$pos] = $p;
echo "<span class=\"pokemon\">{$this->nick}</span> switched place with <span class=\"pokemon\">{$pk->nick}</span>.<br />";
}
else $this->fail();
break;
case 119: // Swagger
$this->statchange(2, 2);
break;
case 167: // Flatter
$this->statchange(4, 1);
break;
case 185: // Recycle
if(!$this->item && $this->lastitem)
{
$this->item = $this->lastitem;
$this->O("%n found one %item!","|-item|%fslot|%item|[from] recycle");
$this->checkitem();
}
else $this->fail();
break;
case 178: // Trick
$a = $user->canloseitem(true);
$b = $this->canloseitem();
if(($a==0&&$b==0) || $a===false || $b===false)
$this->fail();
else
{
$user->O("","|-activate|%fslot|trick|[of] %fslot({$this->U})");
$user->item = $b; $this->item = $a;
if($a) $this->O("%n obtained one %item($a)!","|-item|%fslot|%item($a)|[from] trick");
if($b) $user->O("%n obtained one %item($b)!","|-item|%fslot|%item($b)|[from] trick");
$user->checkitem();
$this->checkitem();
}
break;
case 299: // Simple Beam
$this->setability(86);
break;
case 240: // Gastro Acid
$this->O("%n's ability is disabled!","|-endability|%fslot|%ab");
$this->ability = 0;
break;
case 192: // Skill Swap
$this->O("%un switched ability with %n!","|-activate|%fslot({$user->U})|skillswap");
$a = $this->ability;
$this->setability($user->ability ?: $user->oability);
$user->setability($a ?: $this->oability);
break;
case 179: // Role Play
$user->setability($this->ability?:$this->oability, "|[from] roleplay|[of] %fslot($this->U)");
break;
case 300: // Entrainment
$this->setability($user->oability);
break;
case 227: // Acupressure
$stat = 1+$BV->rand(7);
if($stat==3) $stat=5;
else if($stat==4||$stat==5) $stat--;
$this->statchange(1+$stat, 2);
break;
case 161: // Stockpile
if($this->stockpile < 3)
{
$i = ++$this->stockpile;
$this->O("%n stockpiled $i!","|-start|%fslot|stockpile$i");
$this->statchange(3, 1);
$this->statchange(5, 1);
}
else $this->fail();
break;
case 38: // Rest
$params['randduration'] = 3;
if($this->setstatus(self::S_ASLEEP, 1))
$this->changehp(100, 1);
break;
case 194: // Refreshi
$this->setstatus(self::S_NONE,1);
break;
case 215: // Roost
$this->volatile2 |= self::F2_GROUNDED;
$this->O("%n landed on the ground!","|-singleturn|%fslot|%m");
break;
case 94: // Conversion 2
if($lastm = end($this->hhistory))
{
$at = $MOVES[$lastm[0]]['type'];
$pool = array();
for($i=1; $i<=17; $i++)
if($GLOBALS['TE'][$at][$i] < 1)
$pool[] = $i;
$t = $pool[$BV->rand(count($pool))];
echo "<span class=\"pokemon\">{$this->nick}</span> became a <img src=\"i/typ/2/$t.png\" alt=\"\" /> type!<br />";
$this->O("%n became a {$GLOBALS['HPT'][$t]} type!","|-start|%fslot|typechange|{$GLOBALS['HPT'][$t]}|[from] %m");
$this->type1 = $t; $this->type2 = 0;
} else $this->fail();
break;
case 319: // Reflect Type
$user->type1 = $this->type1;
$user->type2 = $this->type2;
$this->O("%un's type changed to match %n!","|-start|%fslot({$user->U})|typechange|[of] %fslot");
break;
case 128: // Baton Pass
$i=0; foreach($players[$this->player]->party as $id=>$pk)
if($id != $this->id && $pk->hp) $i++;
if(!$i){ $this->fail(); return; }
$pool = array(); global $tn;
for($i=0; $i < $BV->pc; $i++)
$pool[] = array_shift($log->switchins[$tn]);
$log->switchins[$tn] = array_merge($log->switchins[$tn]);
$id = array_search($this->id, $partys[$this->player]);
foreach($pool as $s)
{
if($s[0]==$this->player && $s[4] == $id)
{
$newp = array("batonpass"=>1);
switchin(6, $s[0], $s[3], $s[4], $newp);
return;
}
}
break;
}
}
break;
case self::E_FAIL: // Move failed to complete
// Ugly workaround for granularity difference in triggers
// Interrupted multi turn moves still trip the confusion trigger
// TODO Possibly not correct in some corner cases
// Reset multi turn moves
$this->resetmulti();
// Reset charging move
if(!isset($params['charge']))
$this->mcharge = 0;
$this->O("","|debug|%n was trying to use %m");
// 273219967425: Truant/Entrainment/Mimic/Dig battle - very good for testing failure mechanisms
break;
case self::E_POSTATTACK:
$mid = $params['move'];
if($this->mturns)
{
if(!--$this->mturns)
{
$this->params['confturns'] = $BV->rand(4)+2;
$this->setstatus(self::F_CONFUSED, 0);
}
}
switch($META[$mid]['effect'])
{
case 330: // Relic Song
$this->formchange(1-$this->form);
break;
case 221: // Healing Wish
case 271: // Lunar Dance
$BV->hwish[$this->player]++;
case 169: // Memento
$this->changehp(-100, 1, 1);
return;
case 255: // Struggle
$this->changehp(-25, 1, 1);
if(!$this->hp) return;
break;
case 130: // Rapid Spin
$tmp = $BV->sidee[$this->player%2];
$tmp = array($tmp&BVideo::S_ROCKS, $tmp&BVideo::S_SPIKES1, $tmp&BVideo::S_TSPIKES1);
$tmp2 = array("Stealth Rock","Spikes","Toxic Spikes");
$BV->sidee[$this->player%2] &= ~63;
$this->O("%p's side of the field is cleared of entry hazards.","");
foreach($tmp as $i=>$t)
if($t) $this->O("","|-sideend|%p|".$tmp2[$i]);
if($this->trapturns > 0)
{
$this->volatile &= ~self::F_TRAPPED; $this->trapturns = 0;
}
if($this->volatile & self::F_LEECHED)
{
$this->volatile &= ~self::F_LEECHED;
$this->O("","|-end|%fslot|leechseed|[from] rapidspin");
}
break;
case 234: // Fling
$this->useitem("[from] fling");
break;
}
if(!isset($params['sheerforce'])) // Sheer Force
{
// Flame Burst
if($mid == 481 && count($params['opponents'])>1)
{
foreach($params['opponents'] as $p)
{
if(in_array($p, $params['targets']) || !$p->hp) continue;
$p->O("The burstling flame hit %n!","");
$p->changehp(-6.25, 1);
}
}
// Life Orb
if($this->getitem() == 270 && $MOVES[$mid]['cat']>1)
{
$this->O("%n some HP because of its %item.","");
$this->changehp(-10,1,0,"item: Life Orb");
}
// Shell Bell
if($this->getitem() == 253)
{
if($r = $params['inflicted'] >> 3)
{
$this->O("%n's %item restored some of its health.","");
$this->changehp($r, 0, 0, "%item");
}
}
} // Sheer Force
if($r = $params['inflicted'])
{
$this->hhistory[] = array($mid, $r);
}
// U-turn and Volt Switch
if(in_array($mid, array(521, 369)) && $this->hp)
{
$this->effectswitch();
}
break;
case self::E_EFFECTS:
$mid = $params['move'];
$user = $params['user'];
if(!$mid) return;
// Sheer Force, Shield Dust
if(isset($params['sheerforce']) || ($this->mbability()==19 && $MOVES[$mid]['cat']>1)) goto _foeTriggered;
if(!$this->hadsub)
switch($eid = $META[$mid]['effect'])
{
case 189: // Knock Off
if($a=$this->canloseitem())
{
$this->O("%n lost its %item($a)!","");
$this->useitem("[from] %m|[of] %fslot({$user->U})");
}
break;
case 106: // Covet / Thief
if(($a=$this->canloseitem()) && !$user->item)
{
$this->O("%un stole %n's %item($a)!","|-item|%fslot({$user->U})|%item($a)|[from] %m|[of] %fslot");
$this->useitem(); $user->item = $a; $user->checkitem();
}
break;
case 234: // Fling
switch($ui=$user->item)
{
case 236: // Light Ball
case 245: // Poison Barb
case 272: // Toxic Orb
case 273: // Flame Orb
$tmp = array(236=>self::S_PARALYZED,245=>self::S_POISON,273=>self::S_BURNED, 272=>self::S_TOXIC);
$this->setstatus($tmp[$ui], true);
break;
case 327: // Razor Fang
case 221: // King's Rock
$BV->rand(100); // 27-32199-77575 extra flinch call
$this->flinch();
break;
case 214: // White Herb
case 219: // Mental Herb
$a = $this->item; $this->item = $ui;
$this->checkitem(true);
$this->item = $a;
break;
}
if($ui && strpos($ITEMS[$ui]['name'],"Berry"))
{
$a = $this->item; $this->item = $ui;
$this->berrycheck(true);
$this->item =