Implement all the known stadium mechanics for Stadium subgen. Stadium mechanics discussion is here: http://www.smogon.com/forums/threads/stadium-format-is-now-available-on-ps.3526616/ Would be nice if the main server then added Stadium Ubers and Stadium OU (same rules as RBY but with team preview) if the mechanics are implemented. I'd like to host a Supreme Stadium OU tournament at some point :p
Just reading on that forum link you posted. I've played Pokemon Stadium 1 and 2 long ago and never heard about speed effecting critical hit ratio.
I'll be happy to implement it, but I'm very lazy. I know we have a lot of stadium mechanics already in the code, but can you make a list of what we don't have? I'm going to assume there's also a formula behind the CH ratio, which would be nice to have as I'm not using hard values just in case someone doesn't want to use max speed for some reason. I should be able to add the HP displays as the max value though, so for most intents and purposes it would work. But then the calculation wouldn't be perfect, so the HP would have an error range of about 1% of max hp (as in, Chansey can be +/- 6, Charmander would be like +/- 3). Maybe I can get creative and add a server side message that only works in Stadium to send the damage dealt, then take max HP and subtract the damage as it adds up. Perhaps I can even access the pokemon's HP and send as well, since the server should have that information. By restricting it to only stadium gens too, it wouldn't be abusable if the server controls it.
Formula for Stadium Crit Ratio (from Bulba): Spoiler In Pokemon Stadium, critical hit probabilities are determined with a different formula, (BaseSpeed + 76) / 1024. Compared to the handheld games, this causes Pokémon with a base speed of lower than 76 to land critical hits more often and Pokémon with a base speed of higher than 76 to land critical hits less often. As in the handheld games, moves with a high critical-hit ratio are eight times more likely to land a critical hit, giving a formula of (BaseSpeed + 76) / 128. The effect of Focus Energy now correctly increases the critical hit rate, by changing the formula to (BaseSpeed + 236) / 512; this stacks with the use of a high critical hit rate move ((BaseSpeed + 236) / 64). While the 1/256 move accuracy error was fixed in Stadium, the probability of landing a critical hit is still capped at 255/256.
I think only substitute blocking status and boom, no recoil when ko and haze are done at the moment, looking through this: https://github.com/po-devs/pokemon-online/search?p=1&q=Stadium&utf8=✓ edit: sleep was done also I think (I assume it was in a .txt which is why it wasn't commented) Which leaves: + 1/256 accuracy misses shouldn't happen + recovery -255/511 glitch is removed ================================================ On a slightly different subject, I think ideally we should have these subgens for 1st gen: RBY RBY Tradebacks Stadium Stadium Tradebacks As nice as it is to have symmetry between all gens, I don't think anyone cares about the Red/Blue movepool differences. Are any OU/Ubers pokes even affected?
I looked into the code and it seems like the formula for Focus Energy in Stadium is already implemented. Spoiler: Code /* In RBY, Focus Energy reduces crit by 75% */ if (tmove(player).critRaise & 2) { if (gen() <= Pokemon::gen(Gen::Yellow)) { baseSpeed /=4; } else { baseSpeed *= 4; } }
Bulba is slightly incorrect, I kept doing modifications to fix it but someone doesn't give up and replace it with a wrong simplified formula, and I didn't want to start an edit war. As a general rule, don't follow Bulbapedia for accurate formulas. The correct formula is there: http://datacrystal.romhacking.net/wiki/Pokémon_Stadium:ROM_map#.C2.A1Un_Golpe_Cr.C3.ADtico.21 Simplified explanation of the code: http://datacrystal.romhacking.net/wiki/User_talk:Zowayix So, this is incorrect: Also, Effect chance for Acid is 51/256, Effect chance for Aurora Beam is 77/256, and Effect chance for Bubblebeam is 77/256 in Stadium.
Hyper Beam already does the recharge, I added that awhile back iirc. Code (text): /*Hyper Beam always needs to recharge in Stadium *KOs and sub breaks dont cause recharge in RBY*/ if (b.gen() <= Pokemon::gen(Gen::Yellow) && (b.koed(t) || b.hadSubstitute(t))) return; ----------------------- This looks like some code for draining moves. Recoil < 0 means you heal damage instead of taking it. So essentially what this code is saying is if you kill a pokemon with a draining move in stadium you don't recover HP. Is this correct? Code (text): // If move KOs opponent's pokemon, no recoil damage is applied in stadium. if (koed(target) && recoil < 0 && gen() > Pokemon::gen(Gen::Yellow)) { return; } ------------------------- As far as accuracy goes, do you know if it calculates it out of 255 instead of 256? Or if 100% Acc moves are now 256 acc instead of 255? -------------------------- As far as CH goes then, our formula should read something like this then just for Stadium? Code (text): int ch = (baseSpeed + 76) >> 2; if (focus energy) { ch << 2 + 160 >> 1; } So for bulbasaur it would return CH = 30; With Focus Energy CH = 140; Then that's out of 256 so Bulbasaur = 11.71875% Bulbasaur with FE = 54.6875% Which seems to match that Smogon thread
The hyper beam recharge isn't working on the server for stadium. edit: explosion user survives against sub as well.
I would assume the moves still cap at 255 accuracy. 256 accuracy would take up two bytes, while 255 would only take one, so I'd imagine accuracy is still the same to save space on the cartridges. Although I guess Pokemon mechanics can be weird and I don't have a source \o/ (most likely the coders typo'd in RBY and did /256 instead of /255).
In RBY the game checks if a pseudo-random number between 0 and 255 is < 255. If random number == 255, the attack fails because 255 is not < 255. Hence the 255/256 accuracy. In Stadium, if random number == 255, the attack skips the accuracy check. This means every attack has an additional 1/256 accuracy. http://datacrystal.romhacking.net/wiki/Pokémon_Stadium:ROM_map#Move_Accuracy_check This does not affect Effects chances though, as Stadium simply skips the check if Effect chance == 255. So the Effects chances are [chance]/256. Edited my previous post for Acid / Aurora Beam / Bubblebeam Effects chances, the /255 was a typo. That is correct.
our crit formula is a mess for Gen 1... Code (text): void BattleBase::testCritical(int player, int target) { (void) target; /* In RBY, Focus Energy reduces crit by 75%; in statium, it's * 4 */ int up (1), down(1); if (tmove(player).critRaise & 1) { up *= 8; } if (tmove(player).critRaise & 2) { if (gen() == Gen::RedBlue || gen() == Gen::Yellow) { down = 4; } else { up *= 4; } } PokeFraction critChance(up, down); int randnum = randint(512); int baseSpeed = PokemonInfo::BaseStats(fpoke(player).id, gen()).baseSpeed(); bool critical = randnum < std::min(510, baseSpeed * critChance); if (critical) { turnMem(player).add(TM::CriticalHit); notify(All, CriticalHit, player); } else { turnMem(player).remove(TM::CriticalHit); } } From what it looks like is, excluding Focus energy or high crit moves we do: Find a random number between 0 and 511. Find the base speed of a pokemon The lesser of 510 or the base speed of the pokemon is compared against the random number If the random number is smaller, its a crit (if not, its not a crit) If high crit move is used (like Slash) it does the same as above, but multiplies the base speed by 8. So Persian gets nearly 100% chance to crit because 8*115 > 510, so it compares if 510 is greater than or equal to random int from 0 to 511 inclusive (99.8% apparently from what I calculate). Using a move like Scratch or any other basic move will return a crit rate of about 22.46% ~~~ It should be like Base: CH% rate = (User's base Speed) × 100 ÷ 512 High crit move: CH% rate = (User's base Speed) × 100 ÷ 64 RBY Focus Energy: ???? Stadium Focus Energy: CH% rate = (User's base Speed) × 100 ÷ 128 right? Idk what I'm doing at this point
Code (text): void BattleBase::testCritical(int player, int target) { (void) target; int randnum = randint(256); int baseSpeed = PokemonInfo::BaseStats(fpoke(player).id, gen()).baseSpeed(); /* Focus Energy */ if (gen() == Gen::RedBlue || gen() == Gen::Yellow) { int criticalRatio = baseSpeed >> 1; if (tmove(player).critRaise & 2) { criticalRatio = criticalRatio >> 1; } else { criticalRatio = criticalRatio << 1; } } else { int criticalRatio = (baseSpeed + 76) >> 2; if (tmove(player).critRaise & 2) { criticalRatio = (criticalRatio << 2) + 160; } else { criticalRatio = criticalRatio << 1; } } /* Karate Chop, Razor Leaf, Crabhammer, Slash */ if (tmove(player).critRaise & 1) { criticalRatio = criticalRatio << 2; } else { criticalRatio = criticalRatio >> 1; } /* Critical Hit check */ bool critical = randnum < std::min(255, criticalRatio); if (critical) { turnMem(player).add(TM::CriticalHit); notify(All, CriticalHit, player); } else { turnMem(player).remove(TM::CriticalHit); } } I don't know how Pokémon Online handles Pseudo-Random Number Generation. Does randint(n) generates a pseudo-random number between 0 and n ?
I fixed this. It will be implemented after the next battle server update. I can confirm that the user survives against sub, so our code to fix this doesn't work as it should Code (text): /* Explosion doesn't faint the user if it breaks a sub. * However, it faints all the time in Stadium. */ if (b.gen() <= Pokemon::gen(Gen::Yellow) && b.hadSubstitute(t)) return; b.selfKoer() = s; b.koPoke(s, s);
our randint does 0 to n-1 inclusive because the implementation is something like: Generate random integer, A, with a mersenne twister algorithm. Return A%n.
By using scientific calculator from hex to dec conversion with only 2 values. The minimum 00 (hex) = 00 (dec). The maximum FF (hex) = 255 (dec). You can't have anything greater than 255 with 2 values. I don't know about Pokemon Stadium code but I thought I just mention the 1 to 256 might be 0 to 255.
So I edited the code snippet in my previous post with randint(256) Nightfall Alicorn > ikr, Stadium and RBY use a pseudo-random between 0 and 255
I think this is the todo at the moment: Fix doesn't work: Fix needs testing: Fixes need doing: Fuzzy edit: striking out what is done
I looked into the code and it seems like this is already implemented. @Lutra Are you sure that it isn't working correctly?
It seems I misread a part of the code about Acid. http://datacrystal.romhacking.net/wiki/Pokémon_Stadium:ROM_map#Acid.2C_Aurora_Beam.2C_Bubble.2C_Bubblebeam.2C_Psychic Acid, Aurora Beam, Bubble, Bubblebeam and Psychic have a 33.2% chance to lower the target's stat one level. My apologies about this. I have tested each of these moves to be 100% sure, just in case.
I fixed some RBY sprites too since they had transparency issues (Charizard, Dragonite, etc. Tails, arms, legs, and so on that created loops had white space in there, so I fixed them) Hyper Beam Spoiler: Tier: Stadium Start of turn 1 The foe's Dragonite used Hyper Beam! Charmander lost 218 HP! (100% of its health) Charmander fainted! Fuzzysqurl sent out Golbat! Start of turn 2 The foe's Dragonite must recharge! Golbat used Double-Edge! Spoiler: Tier: Red/Blue Start of turn 1 Dragonite used Hyper Beam! The foe's Charmander lost 100% of its health! The foe's Charmander fainted! Fuzzysqurl sent out Golbat! Start of turn 2 Dragonite used Hyper Beam! The foe's Golbat lost 49% of its health! Explosion: Spoiler: Tier: Stadium Start of turn 1 The foe's Golem used Explosion! Porygon lost 301 HP! (100% of its health) Porygon fainted! The foe's Golem fainted! Fuzzysqurl sent out Golem! Fuzzysqurl2 sent out Lapras! Start of turn 2 The foe's Lapras used Substitute! The foe's Lapras made a substitute! Golem used Explosion! The foe's Lapras's substitute faded! Golem fainted! Spoiler: Tier: Red/Blue Start of turn 1 The foe's Lapras used Substitute! The foe's Lapras made a substitute! Golem used Explosion! The foe's Lapras's substitute faded! Start of turn 2 Fuzzysqurl called Lapras back! Fuzzysqurl sent out Charmander! Golem used Explosion! The foe's Charmander lost 100% of its health! The foe's Charmander fainted! Golem fainted! Also as far as Critical hits and Focus energy needing testing, I'll let someone that knows what they are doing address those. I'll get around to the Partial Trapping and stat/status interaction (provided the latter wasn't fixed already) eventually.
Thrash was broken too. If you broke a sub with it, the duration reset. The bug shared its cause with Explosion breaking a sub. If a sub was broken in an attack, the attack didn't call the ending effects. This affected only 2 (4) moves: Explosion (and Selfdestruct) and Thrash (and Petal Dance). Spoiler: log Fuzzysqurl sent out Porygon! Developer sent out Nidoking! Start of turn 1 The foe's Porygon used Substitute! The foe's Porygon made a substitute! Nidoking used Thrash! The foe's Porygon's substitute faded! Start of turn 2 The foe's Porygon used Substitute! The foe's Porygon made a substitute! Nidoking used Thrash! A critical hit! The foe's Porygon's substitute faded! Start of turn 3 The foe's Porygon used Substitute! The foe's Porygon made a substitute! Nidoking used Thrash! The foe's Porygon's substitute faded! Start of turn 4 The foe's Porygon used Substitute! The foe's Porygon made a substitute! Nidoking used Thrash! The foe's Porygon's substitute faded! Start of turn 5 The foe's Porygon used Substitute! The foe's Porygon hasn't enough energy left! Nidoking used Thrash! A critical hit! The foe's Porygon lost 0% of its health! The foe's Porygon fainted! Fuzzysqurl sent out Nidoking! Developer: still can't select a move Developer: thrash is still automatically attacking Start of turn 6 Fuzzysqurl called Nidoking back! Fuzzysqurl sent out Dragonite! Nidoking used Thrash! A critical hit! The foe's Dragonite lost 34% of its health! Start of turn 7 Fuzzysqurl called Dragonite back! Fuzzysqurl sent out Golem! Nidoking used Thrash! It's not very effective... The foe's Golem lost 7% of its health! Start of turn 8 Fuzzysqurl called Golem back! Fuzzysqurl sent out Lapras! Nidoking used Thrash! The foe's Lapras lost 18% of its health! Nidoking became confused! Developer: now it ends Developer: and i can select a move 8 turns of thrash before I was able to select a move again @Lutra @Crystal_ this was a bug right? (double posting for notify)
I don't know anything about the game code unfortunately. Maybe it's worth using a bit shift for the RBY stuff too though? Also is the *= needed for changing the up variable? It isn't used for changing down.
int randnum = randint(255) + 1; // randint [1; 256] no idea which way this line should be, but it's contradictory. randint(255) produces a random int from 0 inclusive to 255 exclusive. So that line is actually producing a random int [1, 255], not [1, 256] as the comment says it does.
I did a quick test of my formula by writing a test program and checking the results with the ones Fuzzy had and I got the same results. However, I am still not sure if the next part of my fix is 100% correct. Any opinions on this part?
You're applying High Crit Ratio twice if both Focus Energy and High Crit Ratio are active. Also, randint(256) is the correct way to do it ; if it produces a 255 and ch >= 255, then randnum < std::min(255, ch) will be false, thus the move will not produce a critical. This would be correct way to implement it: Code (text): else if (gen() == Gen::Stadium) { int ch = (baseSpeed + 76) >> 2; if (tmove(player).critRaise & 2) // Focus Energy ch = (ch << 2) + 160; else ch = ch << 1; if (tmove(player).critRaise & 1) // Move with high crit ratio ch = ch << 2; else ch = ch >> 1; int randnum = randint(256); // randint [0; 255] critical = randnum < std::min(255, ch); // highest possible crit chance is 255/256 }
Can we get an updated list of mechanics that are still incorrect? If possible, someone compile the list and start a new thread. Thanks.