Add replay database code to version control (#1301)

This required a new architecture for serving replays from inside the
client repository, because that seems like a better call than
introducing yet another PS repository.

(Experience gives me the impression that separating repositories wasn't
a good idea, and we should be working to make PS more mono-repo-like,
rather than less.)

License for replay code is tentatively AGPLv3, although feel free to ask
for a more permissive license if you have plans to use it in an
open-source project that requires it.
This commit is contained in:
Guangcong Luo 2019-06-14 17:48:34 +09:00 committed by GitHub
parent a746edb4aa
commit 029b691be1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 23870 additions and 5 deletions

3
.gitignore vendored
View File

@ -36,4 +36,7 @@ package-lock.json
/js/panel-teambuilder.js
/js/panel-teamdropdown.js
/replays/caches/
/replays/replay-config.inc.php
.vscode

6
build
View File

@ -49,7 +49,7 @@ case 'sprites':
execSync(`node ./build-tools/build-minidex`, options);
break;
case 'replays':
execSync(`node ../replay.pokemonshowdown.com/build`, options);
execSync(`node ./replays/build`, options);
process.exit();
break;
case 'test-only':
@ -67,7 +67,7 @@ execSync(`node ./build-tools/update` + full, options);
if (process.argv[2] === 'full') {
try {
fs.statSync('../replay.pokemonshowdown.com/build');
execSync(`node ../replay.pokemonshowdown.com/build`, options);
fs.statSync('../replays/build');
execSync(`node ../replays/build`, options);
} catch (e) {}
}

View File

@ -373,7 +373,7 @@ class DefaultActionHandler {
}
$reqData['serverid'] = $server['id'];
include_once __DIR__.'/../../replay.pokemonshowdown.com/replays.lib.php';
include_once __DIR__.'/../replays/replays.lib.php';
$out = $GLOBALS['Replays']->prepUpload($reqData);
$dispatcher->setPrefix(''); // No need for prefix since only usable by server.
@ -384,7 +384,7 @@ class DefaultActionHandler {
header('Content-Type: text/plain; charset=utf-8');
include __DIR__.'/../../replay.pokemonshowdown.com/replays.lib.php';
include __DIR__.'/../replays/replays.lib.php';
die($GLOBALS['Replays']->upload($reqData));
}

39
replays/.htaccess Normal file
View File

@ -0,0 +1,39 @@
# -FrontPage-
IndexIgnore .htaccess */.??* *~ *# */HEADER* */README* */_vti*
<Limit GET POST>
order deny,allow
deny from all
allow from all
</Limit>
<Limit PUT DELETE>
order deny,allow
deny from all
</Limit>
AddType text/plain .phps
AddType application/x-tgz .tgz
AddType application/x-chrome-extension .crx
AddType application/x-web-app-manifest+json .webapp
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTP:X-Forwarded-Proto} ^http$
RewriteRule ^(.*)$ https://replay.pokemonshowdown.com/$1 [R=302,NE,L]
RewriteRule ^replay\/(.*)$ /$1 [R=302,L]
RewriteRule ^battle-([A-Za-z0-9-]+)?$ /$1 [R=302,L]
RewriteRule ^search/?$ search.php [L,QSA]
RewriteRule ^([A-Za-z0-9-]+)$ battle.php?name=$1 [L,QSA]
RewriteRule ^([A-Za-z0-9-]+)/manage$ battle.php?name=$1&manage [L,QSA]
RewriteRule ^([A-Za-z0-9-]+)\.log$ battle.log.php?name=$1 [L,QSA]
RewriteRule ^([A-Za-z0-9-]+)\.json$ battle.log.php?json&name=$1 [L,QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(apple-touch-icon-precomposed\.png)$ - [R=404,L]
</IfModule>

46
replays/404.php Normal file
View File

@ -0,0 +1,46 @@
<?php
error_reporting(0);
header('HTTP/1.1 404 Not Found');
include_once 'theme/panels.lib.php';
$page = '404';
$pageTitle = "Replay not found";
$panels->setPageTitle("Replay not found");
$panels->noScripts();
$panels->start();
?>
<div class="pfx-panel"><div class="pfx-body pfx-body-padded" style="max-width:8in;">
<div class="main">
<h1>Not Found</h1>
<p>
The battle you're looking for has expired. Battles expire after 15 minutes of inactivity unless they're saved.
</p>
<p>
In the future, remember to click <strong>Save replay</strong> to save a replay permanently.
</p>
<script type="text/javascript"><!--
google_ad_client = "ca-pub-6535472412829264";
/* PS replay */
google_ad_slot = "8206205605";
google_ad_width = 728;
google_ad_height = 90;
//-->
</script>
<script type="text/javascript"
src="//pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
</div></div>
<?php
$panels->end();
?>

43
replays/503.php Normal file
View File

@ -0,0 +1,43 @@
<?php
error_reporting(0);
header('HTTP/1.1 503 Service Unavailable');
include_once 'theme/panels.lib.php';
$page = '503';
$pageTitle = "Service Unavailable";
$panels->setPageTitle("Replay not found");
$panels->noScripts();
$panels->start();
?>
<div class="pfx-panel"><div class="pfx-body pfx-body-padded" style="max-width:8in;">
<div class="main">
<h1>Technical difficulties</h1>
<p>
<?= @$Replays->offlineReason ? $Replays->offlineReason : "We're currently experiencing some technical difficulties. Please try again later. Sorry." ?>
</p>
<script type="text/javascript"><!--
google_ad_client = "ca-pub-6535472412829264";
/* PS replay */
google_ad_slot = "8206205605";
google_ad_width = 728;
google_ad_height = 90;
//-->
</script>
<script type="text/javascript"
src="//pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
</div></div>
<?php
$panels->end();
?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

50
replays/battle.log.php Normal file
View File

@ -0,0 +1,50 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
$manage = false;
// if (@$_REQUEST['name'] === 'smogtours-ou-509') {
// header('Content-type: text/plain');
// readfile('js/smogtours-ou-509.log');
// die();
// }
// if (@$_REQUEST['name'] === 'ou-305002749') {
// header('Content-type: text/plain');
// readfile('js/ou-305002749.log');
// die();
// }
require_once 'replays.lib.php';
$replay = null;
if (@$_REQUEST['name']) {
$replay = $Replays->get($_REQUEST['name']);
}
if (!$replay) {
header('HTTP/1.1 404 Not Found');
die();
}
$replay['log'] = str_replace("\r","",$replay['log']);
if ($replay['inputlog']) {
if (substr($replay['formatid'], -12) === 'randombattle' || substr($replay['formatid'], -19) === 'randomdoublesbattle' || $replay['formatid'] === 'gen7challengecup' || $replay['formatid'] === 'gen7challengecup1v1' || $replay['formatid'] === 'gen7battlefactory' || $replay['formatid'] === 'gen7bssfactory' || $replay['formatid'] === 'gen7hackmonscup' || $manage) {
// ok
} else {
unset($replay['inputlog']);
}
}
if (isset($_REQUEST['json'])) {
header('Content-type: application/json');
header('Access-Control-Allow-Origin: *');
die(json_encode($replay));
die();
}
header('Content-type: text/plain');
header('Access-Control-Allow-Origin: *');
echo $replay['log'];

179
replays/battle.php Normal file
View File

@ -0,0 +1,179 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
include_once 'theme/panels.lib.php';
if (!@$_REQUEST['name']) {
include '404.php';
die();
}
$manage = false;
$csrfOk = false;
if (isset($_REQUEST['manage'])) {
require_once '../lib/ntbb-session.lib.php';
if ($curuser['group'] != 2 && $curuser['group'] != 6) die("access denied");
$csrfOk = !!$users->csrfCheck();
$manage = true;
}
if (preg_match('/[^A-Za-z0-9-]/', $_REQUEST['name'])) die("access denied");
$replay = null;
$cached = false;
// $forcecache = isset($_REQUEST['forcecache8723']);
$forcecache = false;
if (file_exists('caches/' . $_REQUEST['name'] . '.inc.php')) {
include 'caches/' . $_REQUEST['name'] . '.inc.php';
$replay['formatid'] = '';
$cached = true;
} else {
require_once 'replays.lib.php';
if (!$Replays->db && !$forcecache) {
include '503.php';
die();
}
$replay = $Replays->get($_REQUEST['name'], $forcecache);
}
if (!$replay) {
include '404.php';
die();
}
if (@$replay['private']) header('X-Robots-Tag: noindex');
if ($forcecache) {
file_put_contents('caches/' . $_REQUEST['name'] . '.inc.php', '<?php $replay = ' . var_export($replay, true) . ';');
}
function userid($username) {
if (!$username) $username = '';
$username = strtr($username, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz");
return preg_replace('/[^A-Za-z0-9]+/','',$username);
}
$replay['log'] = str_replace("\r","",$replay['log']);
// $matchSuccess = preg_match('/\\|player\\|p1\\|([^|]*)(\\|[^|]*)?\\n\\|player\\|p2\\|([^|]+)(\\|[^|]*)?\\n(\\|gametype\\|[^|]*\\n)?\\|tier\\|([^|]*)\\n/', $replay['log'], $matches);
$matchSuccess = preg_match('/\\n\\|tier\\|([^|]*)\\n/', $replay['log'], $matches);
$format = $replay['format'];
if ($matchSuccess) $format = $matches[1];
$panels->setPageTitle($format.' replay: '.$replay['p1'].' vs. '.$replay['p2']);
$panels->setPageDescription('Watch a replay of a Pokémon battle between ' . $replay['p1'] . ' and ' . $replay['p2'] . ' (' . $format . ')');
$panels->setTab('replay');
$panels->start();
?>
<div class="pfx-panel"><div class="pfx-body" style="max-width:1180px">
<div class="wrapper replay-wrapper">
<div class="battle"><div class="playbutton"><button disabled>Loading...</button></div></div>
<div class="battle-log"></div>
<div class="replay-controls">
<button data-action="start"><i class="fa fa-play"></i> Play</button>
</div>
<div class="replay-controls-2">
<div class="chooser leftchooser speedchooser">
<em>Speed:</em>
<div><button value="fast">Fast</button><button value="normal" class="sel">Normal</button><button value="slow">Slow</button><button value="reallyslow">Really Slow</button></div>
</div>
<div class="chooser colorchooser">
<em>Color&nbsp;scheme:</em>
<div><button class="sel" value="light">Light</button><button value="dark">Dark</button></div>
</div>
<div class="chooser soundchooser" style="display:none">
<em>Music:</em>
<div><button class="sel" value="on">On</button><button value="off">Off</button></div>
</div>
</div>
<!--[if lte IE 8]>
<div class="error"><p>&#3232;_&#3232; <strong>You're using an old version of Internet Explorer.</strong></p>
<p>We use some transparent backgrounds, rounded corners, and other effects that your old version of IE doesn't support.</p>
<p>Please install <em>one</em> of these: <a href="http://www.google.com/chrome">Chrome</a> | <a href="http://www.mozilla.org/en-US/firefox/">Firefox</a> | <a href="http://windows.microsoft.com/en-US/internet-explorer/products/ie/home">Internet Explorer 9</a></p></div>
<![endif]-->
<?php if (@$replay['private']) echo '<strong>THIS REPLAY IS PRIVATE</strong> - make sure you have the owner\'s permission to share<br />'; ?>
<pre class="urlbox" style="word-wrap: break-word;"><?php echo htmlspecialchars('http://replay.pokemonshowdown.com/'.$_REQUEST['name']); ?></pre>
<h1 style="font-weight:normal;text-align:left"><strong><?= htmlspecialchars($format) ?></strong>: <a href="//pokemonshowdown.com/users/<?= userid($replay['p1']) ?>" class="subtle"><?= htmlspecialchars($replay['p1']) ?></a> vs. <a href="//pokemonshowdown.com/users/<?= userid($replay['p2']) ?>" class="subtle"><?= htmlspecialchars($replay['p2']) ?></a></h1>
<p style="padding:0 1em;margin-top:0">
<small class="uploaddate" data-timestamp="<?= @$replay['uploadtime'] ?? @$replay['date'] ?>"><em>Uploaded:</em> <?php echo date("M j, Y", @$replay['uploadtime'] ?? @$replay['date']); ?><?= @$replay['rating'] ? ' | <em>Rating:</em> ' . $replay['rating'] : '' ?></small>
</p>
<div id="loopcount"></div>
<?php
if ($manage) {
if ($csrfOk && isset($_POST['private'])) {
$replay['private'] = intval($_POST['private']);
$Replays->edit($replay);
echo '<p>Edited.</p>';
}
?>
<form action="/<?= $replay['id'] ?>/manage" method="post" style="margin-top:1em" data-target="replace">
<?php $users->csrfData(); ?>
<input type="hidden" name="private" value="1" />
<button type="submit" name="private" value="1">Private</button>
</form>
<form action="/<?= $replay['id'] ?>/manage" method="post" style="margin-top:1em" data-target="replace">
<?php $users->csrfData(); ?>
<input type="hidden" name="private" value="0" />
<button type="submit" name="private" value="0">Public</button>
</form>
<?php
}
?>
</div>
<input type="hidden" name="replayid" value="<?php echo htmlspecialchars($replay['id']); ?>" />
<!--
You can get this log directly at https://replay.pokemonshowdown.com/<?php echo $replay['id']; ?>.log
Or with metadata at https://replay.pokemonshowdown.com/<?php echo $replay['id']; ?>.json
Most PS pages you'd want to scrape will have a .json version!
-->
<script type="text/plain" class="log"><?php if ($replay['id'] === 'smogtours-ou-509') readfile('js/smogtours-ou-509.log'); else if ($replay['id'] === 'ou-305002749') readfile('js/ou-305002749.log'); else echo str_replace('/','\\/',$replay['log']); ?></script>
<?php
if (substr($replay['formatid'], -12) === 'randombattle' || substr($replay['formatid'], -19) === 'randomdoublesbattle' || $replay['formatid'] === 'gen7challengecup' || $replay['formatid'] === 'gen7challengecup1v1' || $replay['formatid'] === 'gen7battlefactory' || $replay['formatid'] === 'gen7bssfactory' || $replay['formatid'] === 'gen7hackmonscup' || $manage) {
?>
<script type="text/plain" class="inputlog"><?php echo str_replace('</','<\\/',$replay['inputlog']); ?></script>
<?php
}
?>
<?php
if ($panels->output === 'normal') {
?>
<div><script type="text/javascript"><!--
google_ad_client = "ca-pub-6535472412829264";
/* PS replay */
google_ad_slot = "6865298132";
google_ad_width = 728;
google_ad_height = 90;
//-->
</script>
<script type="text/javascript"
src="//pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>
<?php
}
?>
<a href="/" class="pfx-backbutton" data-target="back"><i class="fa fa-chevron-left"></i> <?= $cached ? 'Other' : 'More' ?> replays</a>
</div></div>
<?php
$panels->end();
?>

39
replays/build Executable file
View File

@ -0,0 +1,39 @@
#!/usr/bin/env node
/**
* This script parses index.html and sets the version query string of each
* resource to be the MD5 hash of that resource.
*/
const fs = require('fs');
const crypto = require('crypto');
process.chdir(__dirname);
function updateIndex() {
let indexContents = fs.readFileSync('theme/wrapper.inc.template.php', {encoding: 'utf8'});
// add hashes to js and css files
process.stdout.write("Updating hashes... ");
indexContents = indexContents.replace(/(src|href)="\/(.*?)\?[a-z0-9]*?"/g, function (a, b, c) {
let hash = Math.random(); // just in case creating the hash fails
try {
var filepath = c;
if (c.includes('/play.pokemonshowdown.com/')) {
const filename = c.replace('/play.pokemonshowdown.com/', '');
filepath = '../' + filename;
}
const fstr = fs.readFileSync(filepath, {encoding: 'utf8'});
hash = crypto.createHash('md5').update(fstr).digest('hex').substr(0, 8);
} catch (e) {}
return b + '="/' + c + '?' + hash + '"';
});
console.log("DONE");
process.stdout.write("Writing new `wrapper.inc.php` file... ");
fs.writeFileSync('theme/wrapper.inc.php', indexContents);
console.log("DONE");
}
updateIndex();

1620
replays/converter.lib.php Normal file

File diff suppressed because it is too large Load Diff

BIN
replays/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

276
replays/index.php Normal file
View File

@ -0,0 +1,276 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
include 'theme/panels.lib.php';
// require_once '../../pokemonshowdown.com/lib/ntbb-database.lib.php';
require_once '../../pokemonshowdown.com/config/servers.inc.php';
$panels->setPageTitle('Replays');
$panels->setPageDescription('Watch replays of battles on Pokémon Showdown!');
$panels->setTab('replay');
$panels->start();
?>
<div class="pfx-panel"><div class="pfx-body pfx-body-padded">
<h1>Upload replays</h1>
<p>
To upload a replay, click "Share" or use the command <code>/savereplay</code> in a Pok&eacute;mon Showdown battle!
</p>
<h1>Search replays</h1>
<!--p>Username search temporarily down, will be back up soon</p-->
<form action="/search/" method="get" data-target="replace">
<p style="text-align:center">
<label><input type="text" name="user" class="textbox" placeholder="Username" size="24" /></label>
<button type="submit"><strong>Search for user</strong></button>
</p>
</form>
<form action="/search/" method="get" data-target="replace">
<p style="text-align:center">
<label><input type="text" name="format" class="textbox" placeholder="Format" size="24" /></label>
<button type="submit"><strong>Search by format</strong></button>
</p>
</form>
<h1>Featured replays</h1>
<ul class="linklist" style="max-width:480px;margin:0 auto;text-align:center">
<h3>Fun</h3>
<li><a href="/smogtours-ou-39893" data-target="push">
<small>[ou]<br /></small>
<strong>Get Backer</strong> vs. <strong>CrashinBoomBang</strong>
<small><br />Shadow Tag + Shadow Tag</small>
</a></li>
<li><a href="/oumonotype-82345404" data-target="push">
<small>[oumonotype]<br /></small>
<strong>kdarewolf</strong> vs. <strong>Onox</strong>
<small><br />Protean + prediction</small>
</a></li>
<li><a href="/anythinggoes-218380995" data-target="push">
<small>[anythinggoes]<br /></small>
<strong>Anta2</strong> vs. <strong>dscottnew</strong>
<small><br />Cheek Pouch</small>
</a></li>
<li><a href="/uberssuspecttest-147833524" data-target="push">
<small>[ubers]<br /></small>
<strong>Metal Brellow</strong> vs. <strong>zig100</strong>
<small><br />Topsy-Turvy</small>
</a></li>
<li><a href="/smogondoublesuu-223559888" data-target="push">
<small>[smogondoublesuu]<br /></small>
<strong>KBthealmighty</strong> vs. <strong>big hit on campus</strong>
<small><br />Skill Swap</small>
</a></li>
<li><button class="button" onclick="$('.older2').show();$(this).parent().hide()">Older</button></li>
<li class="older2" style="display:none"><a href="/smogondoubles-75588440" data-target="push">
<small>[smogondoubles]<br /></small>
<strong>jamace6</strong> vs. <strong>DubsWelder</strong>
<small><br />Garchomp sweeps 11 pokemon</small>
</a></li>
<li class="older2" style="display:none"><a href="/ou-20651579" data-target="push">
<small>[ou]<br /></small>
<strong>RainSeven07</strong> vs. <strong>my body is regi</strong>
<small><br />An entire team based on Assist V-create</small>
</a></li>
<li class="older2" style="display:none"><a href="/exeggutorboldalakazam" data-target="push">
<small>[ou]<br /></small>
<strong>Exeggutor</strong> vs. <strong>Minato4thHokage</strong>
<small><br />An unlikely victory with an unusual Alakazam set</small>
</a></li>
<li class="older2" style="display:none"><a href="/ou2258347" data-target="push">
<small>[ou]<br /></small>
<strong>V4</strong> vs. <strong>TryHardPony</strong>
<small><br />A successful sweep with Entrainment Truant + Arena Trap</small>
</a></li>
<li class="older2" style="display:none"><a href="/balancedhackmons7322360" data-target="push">
<small>[balancedhackmons]<br /></small>
<strong>a ver</strong> vs. <strong>Shuckie</strong>
<small><br />To a ver's frustration, PP stall is viable in Balanced Hackmons</small>
</a></li>
<h3>Competitive</h3>
<li><a href="/doublesou-232753081" data-target="push" style="white-space:normal">
<small>[doubles ou]<br /></small>
<strong>Electrolyte</strong> vs. <strong>finally</strong>
<small><br />finally steals Electrolyte's spot in the finals of the Doubles Winter Seasonal by outplaying Toxic Aegislash.</small>
</a></li>
<li><a href="/smogtours-gen5ou-59402" data-target="push" style="white-space:normal">
<small>[bw ou]<br /></small>
<strong>Reymedy</strong> vs. <strong>Leftiez</strong>
<small><br />Reymedy's superior grasp over BW OU lead to his claim of victory over Leftiez in the No Johns Tournament.</small>
</a></li>
<li><a href="/smogtours-gen3ou-56583" data-target="push" style="white-space:normal">
<small>[adv ou]<br /></small>
<strong>pokebasket</strong> vs. <strong>Alf'</strong>
<small><br />pokebasket proved Blissey isn't really one to take a Focus Punch well in his victory match over Alf' in the Fuck Trappers ADV OU tournament.</small>
</a></li>
<li><a href="/smogtours-ou-55891" data-target="push" style="white-space:normal">
<small>[oras ou]<br /></small>
<strong>Marshall.Law</strong> vs. <strong>Malekith</strong>
<small><br />In a "match full of reverses", Marshall.Law takes on Malekith in the finals of It's No Use.</small>
</a></li>
<li><a href="/smogtours-ubers-54583" data-target="push" style="white-space:normal">
<small>[custom]<br /></small>
<strong>hard</strong> vs. <strong>panamaxis</strong>
<small><br />Dark horse panamaxis proves his worth as the rightful winner of The Walkthrough Tournament in this exciting final versus hard.</small>
</a></li>
<li><button class="button" onclick="$('.older1').show();$(this).parent().hide()">Older</button></li>
<li class="older1" style="display:none"><a href="/smogtours-ubers-34646" data-target="push" style="white-space:normal">
<small>[oras ubers]<br /></small>
<strong>steelphoenix</strong> vs. <strong>Jibaku</strong>
<small><br />In this SPL Week 4 battle, Jibaku's clever plays with Mega Sableye keep the momentum mostly in his favor.</small>
</a></li>
<li class="older1" style="display:none"><a href="/smogtours-uu-36860" data-target="push" style="white-space:normal">
<small>[oras uu]<br /></small>
<strong>IronBullet93</strong> vs. <strong>Laurel</strong>
<small><br />Laurel outplays IronBullet's Substitute Tyrantrum with the sly use of a Shuca Berry Cobalion, but luck was inevitably the deciding factor in this SPL Week 6 match.</small>
</a></li>
<li class="older1" style="display:none"><a href="/smogtours-gen5ou-36900" data-target="push" style="white-space:normal">
<small>[bw ou]<br /></small>
<strong>Lowgock</strong> vs. <strong>Meridian</strong>
<small><br />This SPL Week 6 match features impressive plays, from Jirachi sacrificing itself to paralysis to avoid a burn to some clever late-game switches.</small>
</a></li>
<li class="older1" style="display:none"><a href="/smogtours-gen4ou-36782" data-target="push" style="white-space:normal">
<small>[dpp ou]<br /></small>
<strong>Heist</strong> vs. <strong>liberty32</strong>
<small><br />Starting out as an entry hazard-filled stallfest, this close match is eventually decided by liberty32's efficient use of Aerodactyl.</small>
</a></li>
<li class="older1" style="display:none"><a href="/tiershift-210206137" data-target="push" style="white-space:normal">
<small>[tiershift]<br /></small>
<strong>Kingslayer2779</strong> vs. <strong>Adrian Marin</strong>
<small><br />Mega Heracross thrives with a power boost from UU, and this stall team fares decently in the face of Trick Room offense.</small>
</a></li>
<li class="older1" style="display:none"><a href="/randombattle-213274483" data-target="push" style="white-space:normal">
<small>[randombattle]<br /></small>
<strong>The Immortal</strong> vs. <strong>Amphinobite</strong>
<small><br />Substitute Lugia and Rotom-Fan take advantage of Slowking's utility and large HP stat, respectively, in this high ladder match.</small>
</a></li>
<li class="older1" style="display:none"><a href="/capaurumothplaytest4630310" data-target="push">
<small>[cap]<br /></small>
<strong>jas61292</strong> vs. <strong>bugmaniacbob</strong>
<small><br />The CAP Aurumoth playtest's inaugural battle</small>
</a></li>
</ul>
<h1>Recent replays</h1>
<ul class="linklist" style="max-width:480px;margin:0 auto;text-align:center">
<?php
// $replays = [];
// echo "<p>";
// echo "Recent replays are currently unavailable due to database load. They'll be back very soon!";
// echo "</p>";
require_once 'replays.lib.php';
if (!$Replays->db) {
echo "<p>";
echo @$Replays->offlineReason ? $Replays->offlineReason : "Replays are currently offline due to technical difficulties. We'll be back up soon!";
echo "</p>";
} else {
$replays = $Replays->recent();
$time = time();
$timeoffset = 60;
foreach ($replays as $replay) {
$timetext = '';
while ($timeoffset >= 0 && $replay['uploadtime'] < $time - $timeoffset) {
switch ($timeoffset) {
case 60:
$timetext = '<h3>1 minute ago</h3>';
$timeoffset = 120;
break;
case 120:
$timetext = '<h3>2 minutes ago</h3>';
$timeoffset = 180;
break;
case 180:
$timetext = '<h3>3 minutes ago</h3>';
$timeoffset = 240;
break;
case 240:
$timetext = '<h3>4 minutes ago</h3>';
$timeoffset = 300;
break;
case 300:
$timetext = '<h3>5 minutes ago</h3>';
$timeoffset = 600;
break;
case 600:
$timetext = '<h3>10 minutes ago</h3>';
$timeoffset = 1200;
break;
case 1200:
$timetext = '<h3>20 minutes ago</h3>';
$timeoffset = 1800;
break;
case 1800:
$timetext = '<h3>30 minutes ago</h3>';
$timeoffset = 3600;
break;
case 3600:
$timetext = '<h3>1-2 hours ago</h3>';
$timeoffset = 2*3600;
break;
case 2*3600:
$timetext = '<h3>2-3 hours ago</h3>';
$timeoffset = 3*3600;
break;
case 3*3600:
$timetext = '<h3>3-4 hours ago</h3>';
$timeoffset = 4*3600;
break;
case 4*3600:
$timetext = '<h3>4-8 hours ago</h3>';
$timeoffset = 8*3600;
break;
case 8*3600:
$timetext = '<h3>8-12 hours ago</h3>';
$timeoffset = 12*3600;
break;
case 12*3600:
$timetext = '<h3>12-24 hours ago</h3>';
$timeoffset = 24*3600;
break;
case 24*3600:
$timetext = '<h3>1-2 days ago</h3>';
$timeoffset = 2*24*3600;
break;
case 2*24*3600:
$timetext = '<h3>2-3 days ago</h3>';
$timeoffset = 3*24*3600;
break;
case 3*24*3600:
$timetext = '<h3>3 days ago</h3>';
$timeoffset = 5*24*3600;
break;
default:
$timeoffset = -1;
break;
}
}
echo $timetext;
$server = '';
if (preg_match('/^([a-z0-9]+)-[a-z0-9]+-[0-9]+$/', $replay['id'], $matches)) {
$serverid = $matches[1];
if (!isset($PokemonServers[$serverid])) {
// This should be impossible.
$server = 'unknown server';
} else {
$server = strtolower($PokemonServers[$serverid]['name']);
}
}
?>
<li><a href="/<?php echo htmlspecialchars($replay['id']); ?>" data-target="push"><small>[<?php echo htmlspecialchars($replay['format']); ?><?php if ($server) echo ' - ' . htmlspecialchars($server) ?>]<br /></small> <strong><?php echo htmlspecialchars($replay['p1']); ?></strong> vs. <strong><?php echo htmlspecialchars($replay['p2']); ?></strong></a></li>
<?php
}
}
?>
</ul>
</div></div>
<?php
$panels->end();
?>

14927
replays/js/ou-305002749.log Normal file

File diff suppressed because it is too large Load Diff

242
replays/js/replay.js Normal file
View File

@ -0,0 +1,242 @@
/* function updateProgress(done, a, b)
{
if (!battle.paused) return;
//$('#battle').html('<div class="playbutton"><button data-action="start">Play</button>'+(done?'':'<br />Buffering: '+parseInt(100*a/b)+'%')+'</div>');
} */
//setTimeout(function(){updateProgress(true)}, 10000);
if (window.soundManager) {
soundManager.onready(function(){
soundManager.ready = true;
$('.soundchooser, .startsoundchooser').show();
});
}
// Panels
var Topbar = Panels.Topbar.extend({
height: 51
});
var ReplaySidebarPanel = Panels.StaticPanel.extend({
minWidth: 300,
maxWidth: 600,
events: {
'click button[name=moreResults]': 'moreResults'
},
moreResults: function(e) {
var elem = e.currentTarget;
elem.disabled = true;
// var offset = elem.value;
var page = Number(elem.value);
if (!page) page = 2;
var user = elem.dataset.user;
var format = elem.dataset.format;
var private = !!elem.dataset.private;
var self = this;
elem.innerHTML = 'Loading...<br /><i class="fa fa-caret-down"></i>'
$.get('/search', Object.assign({
user: user,
format: format,
page: page,
output: 'html'
}, private ? {private: 1} : {}), function (data) {
self.$('ul.linklist').append(data);
// var $nextOffset = self.$('input.offset');
// var val = $nextOffset.val();
// $nextOffset.remove();
var $more = self.$('input.more');
if (!$more.length) {
$(elem).remove();
return;
} else {
$more.remove();
}
elem.innerHTML = 'More<br /><i class="fa fa-caret-down"></i>'
elem.disabled = false;
elem.value = page + 1;
});
}
});
var ReplayPanel = Panels.StaticPanel.extend({
minWidth: 960,
maxWidth: 1180,
moveTo: function() {
Panels.Panel.prototype.moveTo.apply(this, arguments);
this.$el.css('top', $(window).height() > 495 ? '51px' : '0');
},
events: {
'click .chooser button': 'clickChangeSetting',
'click .replayDownloadButton': 'clickReplayDownloadButton'
},
clickChangeSetting: function(e) {
e.preventDefault();
var $chooser = $(e.currentTarget).closest('.chooser');
var value = e.currentTarget.value;
this.changeSetting($chooser, value, $(e.currentTarget));
},
changeSetting: function(type, value, valueElem) {
var $chooser;
if (typeof type === 'string') {
$chooser = this.$('.'+type+'chooser');
} else {
$chooser = type;
type = '';
if ($chooser.hasClass('colorchooser')) {
type = 'color';
} else if ($chooser.hasClass('soundchooser')) {
type = 'sound';
} else if ($chooser.hasClass('speedchooser')) {
type = 'speed';
}
}
if (!valueElem) valueElem = $chooser.find('button[value='+value+']');
$chooser.find('button').removeClass('sel');
valueElem.addClass('sel');
switch (type) {
case 'color':
if (value === 'dark') {
$(document.body).addClass('dark');
} else {
$(document.body).removeClass('dark');
}
break;
case 'sound':
var muteTable = {
on: false, // this is kind of backwards: sound[on] === muted[false]
off: true
};
this.battle.setMute(muteTable[value]);
break;
case 'speed':
var fadeTable = {
fast: 50,
normal: 300,
slow: 500,
reallyslow: 1000
};
var delayTable = {
fast: 1,
normal: 1,
slow: 1000,
reallyslow: 3000
};
this.battle.messageShownTime = delayTable[value];
this.battle.messageFadeTime = fadeTable[value];
break;
}
},
battle: null,
errorCallback: function() {
var replayid = this.$('input[name=replayid]').val();
var m = /^([a-z0-9]+)-[a-z0-9]+-[0-9]+$/.exec(replayid);
if (m && m[1] !== 'smogtours') {
this.battle.log('<hr /><div class="chat">This replay was uploaded from a third-party server (<code>' + Tools.escapeHTML(m[1]) + '</code>). It contains errors and cannot be viewed.</div><div class="chat">Replays uploaded from third-party servers can contain errors if the server is running custom code, or the server operator has otherwise incorrectly configured their server.</div>', true);
this.battle.pause();
}
},
updateContent: function() {
this.$el.css('overflow-x', 'hidden');
var $battle = this.$('.battle');
if (!$battle.length) return;
this.battle = new Battle($battle, this.$('.battle-log'));
//this.battle.preloadCallback = updateProgress;
this.battle.errorCallback = this.errorCallback.bind(this);
this.battle.resumeButton = this.resume.bind(this);
this.battle.setQueue((this.$('script.log').text()||'').replace(/\\\//g,'/').split('\n'));
this.battle.reset();
this.battle.fastForwardTo(0);
$battle.append('<div class="playbutton"><button data-action="start"><i class="fa fa-play"></i> Play</button><br /><br /><button data-action="startMuted" class="startsoundchooser" style="font-size:10pt;display:none">Play (music off)</button></div>');
this.$('.urlbox').css('margin-right', 120).before('<a class="button replayDownloadButton" style="float:right;margin-top:7px;margin-right:7px" href="#"><i class="fa fa-download"></i> Download</a>');
// this works around a WebKit/Blink bug relating to float layout
var rc2 = this.$('.replay-controls-2')[0];
if (rc2) rc2.innerHTML = rc2.innerHTML;
if (window.soundManager && soundManager.ready) this.$('.soundchooser, .startsoundchooser').show();
},
clickReplayDownloadButton: function (e) {
var filename = (this.battle.tier || 'Battle').replace(/[^A-Za-z0-9]/g, '');
// ladies and gentlemen, JavaScript dates
var timestamp = parseInt(this.$('.uploaddate').data('timestamp'), 10) * 1000;
var date = new Date(timestamp);
filename += '-' + date.getFullYear();
filename += (date.getMonth() >= 9 ? '-' : '-0') + (date.getMonth() + 1);
filename += (date.getDate() >= 10 ? '-' : '-0') + date.getDate();
filename += '-' + toId(this.battle.p1.name);
filename += '-' + toId(this.battle.p2.name);
e.currentTarget.href = BattleLog.createReplayFileHref(this);
e.currentTarget.download = filename + '.html';
e.stopPropagation();
},
pause: function() {
this.$('.replay-controls').html('<button data-action="play"><i class="fa fa-play"></i> Play</button><button data-action="reset"><i class="fa fa-undo"></i> Reset</button> <button data-action="rewind"><i class="fa fa-step-backward"></i> Last turn</button><button data-action="ff"><i class="fa fa-step-forward"></i> Next turn</button> <button data-action="ffto"><i class="fa fa-fast-forward"></i> Go to turn...</button> <button data-action="switchSides"><i class="fa fa-random"></i> Switch sides</button>');
this.battle.pause();
},
play: function() {
this.$('.battle .playbutton').remove();
this.$('.replay-controls').html('<button data-action="pause"><i class="fa fa-pause"></i> Pause</button><button data-action="reset"><i class="fa fa-undo"></i> Reset</button> <button data-action="rewind"><i class="fa fa-step-backward"></i> Last turn</button><button data-action="ff"><i class="fa fa-step-forward"></i> Next turn</button> <button data-action="ffto"><i class="fa fa-fast-forward"></i> Go to turn...</button> <button data-action="switchSides"><i class="fa fa-random"></i> Switch sides</button>');
this.battle.play();
},
resume: function() {
this.play();
},
reset: function() {
this.battle.reset();
this.battle.fastForwardTo(0);
this.$('.battle').append('<div class="playbutton"><button data-action="start"><i class="fa fa-play"></i> Play</button></div>');
// this.$('.battle-log').html('');
this.$('.replay-controls').html('<button data-action="start"><i class="fa fa-play"></i> Play</button><button data-action="reset" disabled="disabled"><i class="fa fa-undo"></i> Reset</button>');
},
ff: function() {
this.battle.skipTurn();
},
rewind: function() {
if (this.battle.turn) {
this.battle.fastForwardTo(this.battle.turn - 1);
}
},
ffto: function() {
var turn = prompt('Turn?');
if (!turn) return;
turn = parseInt(turn);
this.battle.fastForwardTo(turn);
},
switchSides: function() {
this.battle.switchSides();
},
start: function() {
this.battle.play();
this.$('.replay-controls').html('<button data-action="pause"><i class="fa fa-pause"></i> Pause</button><button data-action="reset"><i class="fa fa-undo"></i> Reset</button> <button data-action="rewind"><i class="fa fa-step-backward"></i> Last turn</button><button data-action="ff"><i class="fa fa-step-forward"></i> Next turn</button> <button data-action="ffto"><i class="fa fa-fast-forward"></i> Go to turn...</button> <button data-action="switchSides"><i class="fa fa-random"></i> Switch sides</button>');
},
startMuted: function() {
this.changeSetting('sound', 'off');
this.start();
}
});
var App = Panels.App.extend({
topbarView: Topbar,
states: {
'*path': ReplaySidebarPanel, // catch-all default
':replay': ReplayPanel,
':replay/manage': ReplayPanel,
'search': ReplaySidebarPanel,
'search/': ReplaySidebarPanel,
'search:query': ReplaySidebarPanel,
'search/:query': ReplaySidebarPanel
},
});
var app = new App();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
<?php
$config_replay_database = [
'server' => '127.0.0.1',
'username' => 'username',
'password' => 'password',
'database' => 'replays',
'prefix' => 'ps_',
'charset' => 'utf8mb4',
];

225
replays/replays.lib.php Normal file
View File

@ -0,0 +1,225 @@
<?php
require __DIR__.'/replay-config.inc.php';
class Replays {
var $db;
var $offlineReason;
var $config;
function __construct($config_replay_database) {
$this->config = $config_replay_database;
// $this->offlineReason = "Sorry, we're experiencing technical difficulties! Check back later!";
$this->init();
}
function init() {
if ($this->offlineReason) return;
if ($this->db) return;
try {
$this->db = new PDO(
'mysql:dbname='.$this->config['database'].';host='.$this->config['server'].';charset='.$this->config['charset'],
$this->config['username'],
$this->config['password']
);
} catch (PDOException $e) {
// this error message contains the database password for some reason :|
die("Could not connect");
}
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$this->db->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
}
function get($id, $force = false) {
if (!$this->db) {
// if (!$force) return false;
$this->init();
}
$res = $this->db->prepare("SELECT * FROM ps_replays WHERE id = ? LIMIT 1");
$res->execute([$id]);
if (!$res) return [];
$replay = $res->fetch();
if (!$replay) return $replay;
if ($replay['p1'][0] === '!') $replay['p1'] = substr($replay['p1'], 1);
if ($replay['p2'][0] === '!') $replay['p2'] = substr($replay['p2'], 1);
$res = $this->db->prepare("UPDATE ps_replays SET views = views + 1 WHERE id = ? LIMIT 1");
$res->execute([$id]);
return $replay;
}
function edit($replay) {
$res = $this->db->prepare("UPDATE ps_replays SET private = ? WHERE id = ? LIMIT 1");
$res->execute([$replay['private'], $replay['id']]);
return;
}
function toId($name) {
if (!$name) $name = '';
$name = strtr($name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz");
return preg_replace('/[^a-z0-9]+/','',$name);
}
function search($term, $page = 0, $isPrivate) {
if (!$this->db) return [];
if ($page > 100) return [];
$limit1 = intval(50*($page-1));
if ($limit1 < 0) $limit1 = 0;
$term = $this->toId($term);
$isPrivate = $isPrivate ? 1 : 0;
// $res = $this->db->prepare("SELECT uploadtime, id, format, p1, p2 FROM ps_replays WHERE private = 0 AND (p1id = ? OR p2id = ?) ORDER BY uploadtime DESC LIMIT ?, 51");
$res = $this->db->prepare("(SELECT uploadtime, id, format, p1, p2 FROM ps_replays FORCE INDEX (p1) WHERE private = ? AND p1id = ? ORDER BY uploadtime DESC) UNION (SELECT uploadtime, id, format, p1, p2 FROM ps_replays FORCE INDEX (p2) WHERE private = ? AND p2id = ? ORDER BY uploadtime DESC) ORDER BY uploadtime DESC LIMIT ?, 51;");
$res->execute([$isPrivate, $term, $isPrivate, $term, $limit1]);
return $res->fetchAll();
}
function fullSearch($term, $page = 0) {
if (!$this->db) return [];
if ($page > 0) return [];
$patterns = [];
foreach (explode(',', $term) as $subterm) {
$pattern = '%' . str_replace('_', '\\_', str_replace('%', '\\%', $subterm)) . '%';
$patterns[] = trim($pattern);
}
// $pattern = '%' . str_replace('_', '\\_', str_replace('%', '\\%', $term)) . '%';
// $res = $this->db->prepare("SELECT uploadtime, id, format, p1, p2 FROM ps_replays WHERE private = 0 AND (p1id = ? OR p2id = ?) ORDER BY uploadtime DESC LIMIT ?, 51");
// $this->db->query("SET SESSION max_execution_time=5000");
$res = null;
switch (count($patterns)) {
case 1:
$res = $this->db->prepare("SELECT /*+ MAX_EXECUTION_TIME(10000) */ uploadtime, id, format, p1, p2 FROM ps_replays FORCE INDEX (recent) WHERE private = 0 AND log LIKE ? ORDER BY uploadtime DESC LIMIT 10;");
$res->execute($patterns);
break;
case 2:
$res = $this->db->prepare("SELECT /*+ MAX_EXECUTION_TIME(10000) */ uploadtime, id, format, p1, p2 FROM ps_replays FORCE INDEX (recent) WHERE private = 0 AND log LIKE ? AND log LIKE ? ORDER BY uploadtime DESC LIMIT 10;");
$res->execute($patterns);
break;
default;
return [];
}
return $res->fetchAll();
}
function searchFormat($term, $page = 0, $byRating = false) {
if (!$this->db) return [];
if ($page > 100) return [];
$limit1 = intval(50*($page-1));
if ($limit1 < 0) $limit1 = 0;
$term = $this->toId($term);
$res = null;
if ($byRating) {
$res = $this->db->prepare("SELECT uploadtime, id, format, p1, p2, rating FROM ps_replays FORCE INDEX (top) WHERE private = 0 AND formatid = ? ORDER BY rating DESC LIMIT ?, 51");
} else {
$res = $this->db->prepare("SELECT uploadtime, id, format, p1, p2 FROM ps_replays FORCE INDEX (format) WHERE private = 0 AND formatid = ? ORDER BY uploadtime DESC LIMIT ?, 51");
}
$res->execute([$term, $limit1]);
return $res->fetchAll();
}
function recent() {
if (!$this->db) return [];
$res = $this->db->query("SELECT uploadtime, id, format, p1, p2 FROM ps_replays FORCE INDEX (recent) WHERE private = 0 ORDER BY uploadtime DESC LIMIT 50");
return $res->fetchAll();
}
function prepUpload(&$reqData) {
if (!$this->db) return false;
global $users;
$id = $reqData['id'];
$private = (@$reqData['hidden'] ? 1 : 0);
$p1 = $users->wordfilter($reqData['p1']);
$p2 = $users->wordfilter($reqData['p2']);
$format = $reqData['format'];
if ($private) {
$p1 = '!' . $p1;
$p2 = '!' . $p2;
}
$loghash = $reqData['loghash'];
$rating = intval(@$reqData['rating']);
if ($reqData['serverid'] !== 'showdown') $rating = 0;
$inputlog = null;
if (@$reqData['inputlog']) $inputlog = $reqData['inputlog'];
$res = $this->db->prepare("REPLACE INTO ps_prepreplays (id,loghash,p1,p2,format,private,uploadtime,rating,inputlog) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
$out = !!$res->execute([$id, $loghash, $p1, $p2, $format, $private, time(), $rating, $inputlog]);
// $res = $this->db->prepare("REPLACE INTO ps_prepreplays (id,loghash,p1,p2,format,private,uploadtime,rating) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
// $out = !!$res->execute([$id, $loghash, $p1, $p2, $format, $private, time(), $rating]);
// throw new Exception(var_export($res->errorInfo(), true));
return $out;
}
function stripNonAscii($str) { return preg_replace('/[^(\x20-\x7F)]+/','', $str); }
function upload(&$reqData) {
if (!$this->db) return $this->offlineReason ? $this->offlineReason : 'replays are down due to technical difficulties, will be back soon';
global $users;
$id = @$reqData['id'];
if (!$id) return 'ID needed';
$res = $this->db->prepare("SELECT * FROM ps_prepreplays WHERE id = ?");
if (!$res->execute([$id])) {
// throw new Exception($this->db->error);
return 'database error for ' . $id . ': ' . $res->errorInfo();
}
$replay = $res->fetch();
if (!$replay) {
$res = $this->db->prepare("SELECT id FROM ps_replays WHERE id = ?");
$res->execute([$id]);
if ($res->fetch()) {
// Someone else uploaded a replay while we were trying to upload it
return 'success';
}
if (!preg_match('/^[a-z0-9]+-[a-z0-9]+-[0-9]+$/', $id)) {
return 'invalid id';
}
return 'not found';
}
if (md5($this->stripNonAscii($reqData['log'])) !== $replay['loghash']) {
$reqData['log'] = str_replace("\r",'', $reqData['log']);
if (md5($this->stripNonAscii($reqData['log'])) !== $replay['loghash']) {
// Hashes don't match.
// Someone else tried to upload a replay of the same battle,
// while we were uploading this
// ...pretend it was a success
return 'success';
}
}
$p1id = $this->toId($replay['p1']);
$p2id = $this->toId($replay['p2']);
$formatid = $this->toId($replay['format']);
$res = $this->db->prepare("INSERT INTO ps_replays (id, p1, p2, format, p1id, p2id, formatid, uploadtime, private, rating, log, inputlog) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE log = ?, inputlog = ?, rating = ?");
$res->execute([$id, $replay['p1'], $replay['p2'], $replay['format'], $p1id, $p2id, $formatid, $replay['uploadtime'], $replay['private'], $replay['rating'], $reqData['log'], $replay['inputlog'], $reqData['log'], $replay['inputlog'], $replay['rating']]);
$res = $this->db->prepare("DELETE FROM ps_prepreplays WHERE id = ? AND loghash = ?");
$res->execute([$id, $replay['loghash']]);
return 'success';
}
function userid($username) {
if (!$username) $username = '';
$username = strtr($username, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz");
return preg_replace('/[^A-Za-z0-9]+/','',$username);
}
}
$GLOBALS['Replays'] = new Replays($config_replay_database);

272
replays/search.php Normal file
View File

@ -0,0 +1,272 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
include 'theme/panels.lib.php';
$username = @$_REQUEST['user'];
$format = ($username ? '' : @$_REQUEST['format']);
$contains = (@$_REQUEST['contains']);
$byRating = isset($_REQUEST['rating']);
$isPrivate = isset($_REQUEST['private']);
$noRequest = (!$username && !$format && !$contains);
if ($username) {
$panels->setPageTitle($username."'s replays");
} else if ($format) {
$panels->setPageTitle($username." replays");
} else {
$panels->setPageTitle("Search replays");
}
$panels->setTab('replay');
$panels->start();
$page = 0;
if (@$_REQUEST['page']) $page = intval($_REQUEST['page']);
if (!$page) {
?>
<div class="pfx-panel"><div class="pfx-body pfx-body-padded">
<?php
if (!$contains) {
?>
<h1>Search replays</h1>
<form action="/search/" method="get" data-target="replace">
<p style="text-align:center">
<label><input type="text" name="user" class="textbox" placeholder="Username" size="24"<?= !$format ? ' autofocus="autofocus"' : '' ?> value="<?php echo htmlspecialchars($username) ?>" /></label>
<button type="submit"><strong>Search</strong></button>
</p>
</form>
<form action="/search/" method="get" data-target="replace">
<p style="text-align:center">
<label><input type="text" name="format" class="textbox" placeholder="Format" size="24"<?= $format ? ' autofocus="autofocus"' : '' ?> value="<?php echo htmlspecialchars($format) ?>" /></label>
<button type="submit"><strong>Search</strong></button>
</p>
</form>
<?php
}
if ($noRequest || $contains) {
?>
<h1>Experimental full-text search</h1>
<form action="/search/" method="get" data-target="replace">
<p style="text-align:center">
<label><input type="text" name="contains" class="textbox" placeholder="EXPERIMENTAL FULL TEXT SEARCH" size="24"<?= $format ? ' autofocus="autofocus"' : '' ?> value="<?php echo htmlspecialchars($contains) ?>" /></label>
<button type="submit"><strong>Search</strong></button>
</p>
</form>
<?php
}
}
if ($username || $format || $contains) {
require_once 'replays.lib.php';
include_once '../lib/ntbb-session.lib.php';
$username = $users->userid($username);
$isPrivateAllowed = ($username === $curuser['userid'] || $curuser['userid'] === 'zarel');
if (!$page) {
?>
<h1>Search results for "<?php echo htmlspecialchars($username ? $username : ($contains ? $contains : $format)); ?>"</h1>
<?php
if ($format) {
$format = $Replays->toId($format);
?>
<ul class="tabbar centered" style="margin-bottom: 18px"><li><a class="button nav-first<?= $byRating ? '' : ' cur' ?>" href="/search/?format=<?= $format ?>" data-target="replace">Newest</a></li><li><a class="button nav-last<?= $byRating ? ' cur' : '' ?>" href="/search/?format=<?= $format ?>&amp;rating" data-target="replace">Highest rated</a></li></ul>
<?php
} else if ($isPrivateAllowed) {
?>
<ul class="tabbar centered" style="margin-bottom: 18px"><li><a class="button nav-first<?= $isPrivate ? '' : ' cur' ?>" href="/search/?user=<?= $username ?>" data-target="replace">Public</a></li><li><a class="button nav-last<?= $isPrivate ? ' cur' : '' ?>" href="/search/?user=<?= $username ?>&amp;private" data-target="replace">Private</a></li></ul>
<?php
}
?>
<ul class="linklist" style="max-width:480px;margin:0 auto;text-align:center">
<?php
}
$replays = null;
if ($page > 25) {
// no
} else if ($username) {
if (!$isPrivate || $isPrivateAllowed) {
$replays = $Replays->search($username, $page, $isPrivate);
}
} else if ($contains) {
$replays = $Replays->fullSearch($contains, $page);
} else {
$replays = $Replays->searchFormat($format, $page, $byRating);
}
$time = time();
$timeoffset = 600;
$count = 0;
// $newoffset = 0;
if ($isPrivate && !$isPrivateAllowed) {
?>
<li>Error: You must be logged into account "<?= htmlspecialchars($username) ?>" to see their private replays (You're currently "<?= $curuser['userid'] ?>").</li>
<?php
} else if (!$replays || !count($replays)) {
if ($page > 25) {
?>
<li>Can't search any further back</li>
<?php
} else if (!$Replays->db) {
?>
<li><?= $Replays->offlineReason ?></li>
<?php
} else {
?>
<li>No results found</li>
<?php
}
} else foreach ($replays as $replay) {
if ($count == 50) {
$count++;
// $newoffset = $replay['num'];
break;
}
$timetext = '';
if (!$byRating) while ($timeoffset >= 0 && $replay['uploadtime'] < $time - $timeoffset) {
$prevtimeoffset = $timeoffset;
switch ($timeoffset) {
case 600:
$timetext = '<h3>10 minutes ago</h3>';
$timeoffset = 1200;
break;
case 1200:
$timetext = '<h3>20 minutes ago</h3>';
$timeoffset = 1800;
break;
case 1800:
$timetext = '<h3>30 minutes ago</h3>';
$timeoffset = 3600;
break;
case 3600:
$timetext = '<h3>1-2 hours ago</h3>';
$timeoffset = 2*3600;
break;
case 2*3600:
$timetext = '<h3>2-3 hours ago</h3>';
$timeoffset = 3*3600;
break;
case 3*3600:
$timetext = '<h3>3-4 hours ago</h3>';
$timeoffset = 4*3600;
break;
case 4*3600:
$timetext = '<h3>4-8 hours ago</h3>';
$timeoffset = 8*3600;
break;
case 8*3600:
$timetext = '<h3>8-12 hours ago</h3>';
$timeoffset = 12*3600;
break;
case 12*3600:
$timetext = '<h3>12-24 hours ago</h3>';
$timeoffset = 24*3600;
break;
case 24*3600:
$timetext = '<h3>1-2 days ago</h3>';
$timeoffset = 2*24*3600;
break;
case 2*24*3600:
$timetext = '<h3>2-3 days ago</h3>';
$timeoffset = 3*24*3600;
break;
case 3*24*3600:
$timetext = '<h3>3-7 days ago</h3>';
$timeoffset = 7*24*3600;
break;
case 7*24*3600:
$timetext = '<h3>1 week ago</h3>';
$timeoffset = 14*24*3600;
break;
case 14*24*3600:
$timetext = '<h3>2 weeks ago</h3>';
$timeoffset = 21*24*3600;
break;
case 21*24*3600:
$timetext = '<h3>3 weeks ago</h3>';
$timeoffset = 30*24*3600;
break;
case 30*24*3600:
$timetext = '<h3>1 month ago</h3>';
$timeoffset = 2*30*24*3600;
break;
case 2*30*24*3600:
$timetext = '<h3>2 months ago</h3>';
$timeoffset = 3*30*24*3600;
break;
case 3*30*24*3600:
$timetext = '<h3>3-6 months ago</h3>';
$timeoffset = 183*24*3600;
break;
case 183*24*3600:
$timetext = '<h3>6-9 months ago</h3>';
$timeoffset = 274*24*3600;
break;
case 274*24*3600:
$timetext = '<h3>9-12 months ago</h3>';
$timeoffset = 365*24*3600;
break;
case 365*24*3600:
$timetext = '<h3>more than 1 year ago</h3>';
$timeoffset = 2*365*24*3600;
break;
case 2*365*24*3600:
$timetext = '<h3>more than 2 year ago</h3>';
$timeoffset = 3*365*24*3600;
break;
case 3*365*24*3600:
$timetext = '<h3>more than 3 years ago</h3>';
$timeoffset = 4*365*24*3600;
break;
case 4*365*24*3600:
$timetext = '<h3>more than 4 years ago</h3>';
$timeoffset = 5*365*24*3600;
break;
case 5*365*24*3600:
$timetext = '<h3>more than 5 years ago</h3>';
$timeoffset = -1;
break;
}
if ($prevtimeoffset === $timeoffset) break;
}
echo $timetext;
?>
<li><a href="/<?php echo $replay['id']; ?>" data-target="push"><small>[<?php echo $replay['format']; ?>]<?php if (@$replay['rating']) echo ' rating: '.$replay['rating']; ?><br /></small> <strong><?php echo htmlspecialchars($replay['p1']); ?></strong> vs. <strong><?php echo htmlspecialchars($replay['p2']); ?></strong></a></li>
<?php
$count++;
}
if (!$page) {
?>
</ul>
<?php
if ($count === 51) {
?>
<p style="text-align: center"><button class="button" name="moreResults" value="<?= /* $newoffset */ '' ?>" data-user="<?= htmlspecialchars($username) ?>" data-format="<?= htmlspecialchars($format) ?>" data-private="<?= $isPrivate ? '1' : '' ?>">More<br /><i class="fa fa-caret-down"></i></button></p>
<?php
}
} else {
// echo '<input type="hidden" name="offset" class="offset" value="' . $newoffset . '" />';
if ($count === 51) {
echo '<input type="hidden" name="more" class="more" value="1" />';
}
}
}
if (!$page) {
?>
</div></div>
<?php
}
$panels->end();
?>

View File

@ -0,0 +1,77 @@
<?php
include_once dirname(__FILE__).'/wrapper.inc.php';
class Panels {
var $output = 'normal';
var $tab = false;
var $root;
var $name;
function __construct() {
global $psconfig;
$this->root = '/';
$this->name = 'Showdown';
switch (@$_REQUEST['output']) {
case 'html':
case 'json':
$this->output = $_REQUEST['output'];
break;
}
}
var $pagetitle = false;
function setPageTitle($title) {
if ($this->pagetitle === false) {
$this->pagetitle = $title;
}
}
var $pagedescription = false;
function setPageDescription($description) {
if ($this->pagedescription === false) {
$this->pagedescription = $description;
}
}
function setTab($tab) {
if ($this->tab === false) {
$this->tab = $tab;
}
}
var $depth = 0;
var $scriptsIncluded = false;
function start() {
$this->depth++;
if ($this->depth != 1) {
return;
}
if ($this->output === 'normal') {
ThemeHeaderTemplate();
}
}
function noScripts() {
$this->scriptsIncluded = true;
}
function scripts() {
if ($this->output !== 'normal') return;
if ($this->scriptsIncluded) return;
$this->scriptsIncluded = true;
ThemeScriptsTemplate();
}
function end() {
$this->depth--;
if ($this->depth != 0) {
return;
}
if ($this->output === 'normal') {
ThemeFooterTemplate();
}
}
}
$panels = new Panels();

View File

@ -0,0 +1,110 @@
<?php
if ((substr($_SERVER['REMOTE_ADDR'],0,11) === '69.164.163.') ||
(substr(@$_SERVER['HTTP_X_FORWARDED_FOR'],0,11) === '69.164.163.')) {
die('website disabled');
}
/********************************************************************
* Header
********************************************************************/
function ThemeHeaderTemplate() {
global $panels;
?>
<!DOCTYPE html>
<html><head>
<meta charset="utf-8" />
<title><?php if ($panels->pagetitle) echo htmlspecialchars($panels->pagetitle).' - '; ?>Pok&eacute;mon Showdown</title>
<?php if ($panels->pagedescription) { ?>
<meta name="description" content="<?php echo htmlspecialchars($panels->pagedescription); ?>" />
<?php } ?>
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=IE8" />
<link rel="stylesheet" href="//play.pokemonshowdown.com/style/font-awesome.css" />
<link rel="stylesheet" href="//pokemonshowdown.com/forum/theme/panels.css" />
<link rel="stylesheet" href="//pokemonshowdown.com/theme/main.css" />
<link rel="stylesheet" href="//play.pokemonshowdown.com/style/battle.css?24e88eca" />
<link rel="stylesheet" href="//play.pokemonshowdown.com/style/replay.css?cfa51183" />
<!-- Workarounds for IE bugs to display trees correctly. -->
<!--[if lte IE 6]><style> li.tree { height: 1px; } </style><![endif]-->
<!--[if IE 7]><style> li.tree { zoom: 1; } </style><![endif]-->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-26211653-1']);
_gaq.push(['_setDomainName', 'pokemonshowdown.com']);
_gaq.push(['_setAllowLinker', true]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head><body>
<div class="pfx-topbar">
<div class="header">
<ul class="nav">
<li><a class="button nav-first<?php if ($panels->tab === 'home') echo ' cur'; ?>" href="//pokemonshowdown.com/"><img src="//pokemonshowdown.com/images/pokemonshowdownbeta.png" alt="Pok&eacute;mon Showdown! (beta)" /> Home</a></li>
<li><a class="button<?php if ($panels->tab === 'pokedex') echo ' cur'; ?>" href="//dex.pokemonshowdown.com/">Pok&eacute;dex</a></li>
<li><a class="button<?php if ($panels->tab === 'replay') echo ' cur'; ?>" href="/">Replays</a></li>
<li><a class="button<?php if ($panels->tab === 'ladder') echo ' cur'; ?>" href="//pokemonshowdown.com/ladder/">Ladder</a></li>
<li><a class="button nav-last" href="//pokemonshowdown.com/forums/">Forum</a></li>
</ul>
<ul class="nav nav-play">
<li><a class="button greenbutton nav-first nav-last" href="http://play.pokemonshowdown.com/">Play</a></li>
</ul>
<div style="clear:both"></div>
</div>
</div>
<?php
}
/********************************************************************
* Footer
********************************************************************/
function ThemeScriptsTemplate() {
?>
<script src="//play.pokemonshowdown.com/js/lib/jquery-1.11.0.min.js"></script>
<script src="//play.pokemonshowdown.com/js/lib/lodash.core.js"></script>
<script src="//play.pokemonshowdown.com/js/lib/backbone.js"></script>
<script src="//dex.pokemonshowdown.com/js/panels.js"></script>
<?php
}
function ThemeFooterTemplate() {
global $panels;
?>
<?php $panels->scripts(); ?>
<script src="//play.pokemonshowdown.com/js/lib/jquery-cookie.js"></script>
<script src="//play.pokemonshowdown.com/js/lib/html-sanitizer-minified.js"></script>
<script src="//play.pokemonshowdown.com/js/lib/soundmanager2-nodebug-jsmin.js?2de76b4b"></script>
<script>
soundManager.setup({url: '//play.pokemonshowdown.com/swf/'});
</script>
<script src="//play.pokemonshowdown.com/config/config.js?2eb1b9d3"></script>
<script src="//play.pokemonshowdown.com/js/battledata.js?590e5ef9"></script>
<script src="//play.pokemonshowdown.com/data/pokedex-mini.js?d43512b9"></script>
<script src="//play.pokemonshowdown.com/data/pokedex-mini-bw.js?616fd068"></script>
<script src="//play.pokemonshowdown.com/data/graphics.js?df20bee4"></script>
<script src="//play.pokemonshowdown.com/data/pokedex.js?7f2bd4f0"></script>
<script src="//play.pokemonshowdown.com/data/items.js?bbd626f5"></script>
<script src="//play.pokemonshowdown.com/data/moves.js?5c0fdec9"></script>
<script src="//play.pokemonshowdown.com/data/abilities.js?ab314611"></script>
<script src="//play.pokemonshowdown.com/data/teambuilder-tables.js?88ee7998"></script>
<script src="//play.pokemonshowdown.com/js/battle-tooltips.js?0f1f5fa6"></script>
<script src="//play.pokemonshowdown.com/js/battle.js?f2797548"></script>
<script src="/js/replay.js?f7d435c0"></script>
</body></html>
<?php
}

View File

@ -0,0 +1,110 @@
<?php
if ((substr($_SERVER['REMOTE_ADDR'],0,11) === '69.164.163.') ||
(substr(@$_SERVER['HTTP_X_FORWARDED_FOR'],0,11) === '69.164.163.')) {
die('website disabled');
}
/********************************************************************
* Header
********************************************************************/
function ThemeHeaderTemplate() {
global $panels;
?>
<!DOCTYPE html>
<html><head>
<meta charset="utf-8" />
<title><?php if ($panels->pagetitle) echo htmlspecialchars($panels->pagetitle).' - '; ?>Pok&eacute;mon Showdown</title>
<?php if ($panels->pagedescription) { ?>
<meta name="description" content="<?php echo htmlspecialchars($panels->pagedescription); ?>" />
<?php } ?>
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=IE8" />
<link rel="stylesheet" href="//play.pokemonshowdown.com/style/font-awesome.css" />
<link rel="stylesheet" href="//pokemonshowdown.com/forum/theme/panels.css" />
<link rel="stylesheet" href="//pokemonshowdown.com/theme/main.css" />
<link rel="stylesheet" href="//play.pokemonshowdown.com/style/battle.css?" />
<link rel="stylesheet" href="//play.pokemonshowdown.com/style/replay.css?" />
<!-- Workarounds for IE bugs to display trees correctly. -->
<!--[if lte IE 6]><style> li.tree { height: 1px; } </style><![endif]-->
<!--[if IE 7]><style> li.tree { zoom: 1; } </style><![endif]-->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-26211653-1']);
_gaq.push(['_setDomainName', 'pokemonshowdown.com']);
_gaq.push(['_setAllowLinker', true]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head><body>
<div class="pfx-topbar">
<div class="header">
<ul class="nav">
<li><a class="button nav-first<?php if ($panels->tab === 'home') echo ' cur'; ?>" href="//pokemonshowdown.com/"><img src="//pokemonshowdown.com/images/pokemonshowdownbeta.png" alt="Pok&eacute;mon Showdown! (beta)" /> Home</a></li>
<li><a class="button<?php if ($panels->tab === 'pokedex') echo ' cur'; ?>" href="//dex.pokemonshowdown.com/">Pok&eacute;dex</a></li>
<li><a class="button<?php if ($panels->tab === 'replay') echo ' cur'; ?>" href="/">Replays</a></li>
<li><a class="button<?php if ($panels->tab === 'ladder') echo ' cur'; ?>" href="//pokemonshowdown.com/ladder/">Ladder</a></li>
<li><a class="button nav-last" href="//pokemonshowdown.com/forums/">Forum</a></li>
</ul>
<ul class="nav nav-play">
<li><a class="button greenbutton nav-first nav-last" href="http://play.pokemonshowdown.com/">Play</a></li>
</ul>
<div style="clear:both"></div>
</div>
</div>
<?php
}
/********************************************************************
* Footer
********************************************************************/
function ThemeScriptsTemplate() {
?>
<script src="//play.pokemonshowdown.com/js/lib/jquery-1.11.0.min.js"></script>
<script src="//play.pokemonshowdown.com/js/lib/lodash.core.js"></script>
<script src="//play.pokemonshowdown.com/js/lib/backbone.js"></script>
<script src="//dex.pokemonshowdown.com/js/panels.js"></script>
<?php
}
function ThemeFooterTemplate() {
global $panels;
?>
<?php $panels->scripts(); ?>
<script src="//play.pokemonshowdown.com/js/lib/jquery-cookie.js"></script>
<script src="//play.pokemonshowdown.com/js/lib/html-sanitizer-minified.js"></script>
<script src="//play.pokemonshowdown.com/js/lib/soundmanager2-nodebug-jsmin.js?"></script>
<script>
soundManager.setup({url: '//play.pokemonshowdown.com/swf/'});
</script>
<script src="//play.pokemonshowdown.com/config/config.js?"></script>
<script src="//play.pokemonshowdown.com/js/battledata.js?"></script>
<script src="//play.pokemonshowdown.com/data/pokedex-mini.js?"></script>
<script src="//play.pokemonshowdown.com/data/pokedex-mini-bw.js?"></script>
<script src="//play.pokemonshowdown.com/data/graphics.js?"></script>
<script src="//play.pokemonshowdown.com/data/pokedex.js?"></script>
<script src="//play.pokemonshowdown.com/data/items.js?"></script>
<script src="//play.pokemonshowdown.com/data/moves.js?"></script>
<script src="//play.pokemonshowdown.com/data/abilities.js?"></script>
<script src="//play.pokemonshowdown.com/data/teambuilder-tables.js?"></script>
<script src="//play.pokemonshowdown.com/js/battle-tooltips.js?"></script>
<script src="//play.pokemonshowdown.com/js/battle.js?"></script>
<script src="/js/replay.js?"></script>
</body></html>
<?php
}

227
replays/turn-image.php Normal file
View File

@ -0,0 +1,227 @@
<?php
if ($_GET['data']) {
$datasplit = explode('_',$_GET['data']);
$step = 1;
foreach ($datasplit as $data) {
$st = '';
if (substr($data,0,4)==='fnt.') {
$st = 'fnt';
$data = substr($data,4);
} else if (substr($data,0,3)==='dr.') {
$st = 'dr';
$data = substr($data,3);
} else if (substr($data,0,3)==='sw.') {
$st = 'sw';
$data = substr($data,3);
}
$row = explode('.',$data);
if ($step == 1 && !$st) $step = 2;
if ($step > 1 && $st) $step = 4;
switch ($step) {
case 1:
$_GET['oa'] = $row[0];
$_GET['oahp'] = $row[1];
$_GET['oat'] = $st;
break;
case 2:
$_GET['a'] = $row[0];
$_GET['ahp'] = $row[1];
break;
case 3:
$_GET['b'] = $row[0];
$_GET['bhp'] = $row[1];
break;
case 4:
$_GET['ob'] = $row[0];
$_GET['obhp'] = $row[1];
$_GET['obt'] = $st;
break;
}
$step++;
}
}
//die(var_export($_GET));
$A = $_GET['a'];
$B = $_GET['b'];
$A = preg_replace('/[^a-z0-9-]+/','', strtolower($A));
$B = preg_replace('/[^a-z0-9-]+/','', strtolower($B));
$Ahp = intval($_GET['ahp']);
$Bhp = intval($_GET['bhp']);
$oA = $_GET['oa'];
$oB = $_GET['ob'];
$oA = preg_replace('/[^a-z0-9-]+/','', strtolower($oA));
$oB = preg_replace('/[^a-z0-9-]+/','', strtolower($oB));
$oAhp = intval($_GET['oahp']);
$oBhp = intval($_GET['obhp']);
$oAt = $_GET['oat'];
$oBt = $_GET['obt'];
$im = imagecreatetruecolor(500,100);
$transparent = imagecolorallocate($im, 246, 234, 252);
$blue = imagecolorallocate($im, 20, 40, 90);
$white = imagecolorallocate($im, 255, 255, 255);
$hpborder = imagecolorallocate($im, 119, 119, 119);
$hpcolor = imagecolorallocate($im, 10, 170, 102);
$hpshadowcolor = imagecolorallocate($im, 5, 119, 68);
$t_hpborder = imagecolorallocatealpha($im, 119, 119, 119,89);
$t_hpcolor = imagecolorallocatealpha($im, 10, 170, 102,89);
$t_hpshadowcolor = imagecolorallocatealpha($im, 5, 119, 68,89);
imagecolortransparent($im, $transparent);
imagefilledrectangle($im, 0,0, 499, 99, $transparent);
$srcim = imagecreatefrompng('../fx/versus.png');
imagecopymerge($im, $srcim, 250-29, 39, 0,0, 58,23, 100);
imagedestroy($srcim);
// Pokemon A
if ($Ahp)
{
$srcim = @imagecreatefrompng('../sprites/bw/'.$A.'.png') or $srcim = imagecreatefrompng('../sprites/bw/0.png');
//imagecopymerge($im, $srcim, 124, 4, 0,0, 96,96, $Ahp?100:30);
imagecopyresampled($im, $srcim, 124,4, 96,0, 96,96, -96,96);
imagedestroy($srcim);
}
else
{
$srcim = @imagecreatefrompng('../sprites/bw/'.$A.'.png') or $srcim = imagecreatefrompng('../sprites/bw/0.png');
$srcim2 = imagecreatetruecolor(96,96);
$s2t = imagecolorallocate($srcim2, 246, 234, 252);
imagealphablending($srcim2, false);
imagealphablending($srcim, false);
imagefilledrectangle($srcim2, 0,0, 95, 95, $s2t);
imagecolortransparent($srcim2, $s2t);
//imagealphablending($srcim2, true);
imagealphablending($srcim2, true);
imagealphablending($srcim, true);
imagecopyresampled($srcim2, $srcim, 0,0, 96,0, 96,96, -96,96);
imagecopymerge($im, $srcim2, 124, 4, 0,0, 96,96, 30);
imagedestroy($srcim);
imagedestroy($srcim2);
}
$hpwidth = intval((186-114) * $Ahp/100);
if ($Ahp)
{
imagefilledrectangle($im, 132,90, 208, 98, $hpborder);
imagefilledrectangle($im, 133,91, 207, 97, $white);
imagefilledrectangle($im, 134,92, 134+$hpwidth, 96, $hpcolor);
imagefilledrectangle($im, 134,95, 134+$hpwidth, 96, $hpshadowcolor);
}
else
{
//imagefilledrectangle($im, 132,90, 208, 98, $t_hpborder);
//imagefilledrectangle($im, 133,91, 207, 97, $white);
}
// Pokemon A switched
if ($oA)
{
$srcim = @imagecreatefrompng('../sprites/bw/'.$oA.'.png') or $srcim = imagecreatefrompng('../sprites/bw/0.png');
$srcim2 = imagecreatetruecolor(96,96);
$s2t = imagecolorallocate($srcim2, 246, 234, 252);
imagealphablending($srcim2, false);
imagealphablending($srcim, false);
imagefilledrectangle($srcim2, 0,0, 95, 95, $s2t);
imagecolortransparent($srcim2, $s2t);
//imagealphablending($srcim2, true);
imagealphablending($srcim2, true);
imagealphablending($srcim, true);
imagecopyresampled($srcim2, $srcim, 0,0, 96,0, 96,96, -96,96);
imagecopymerge($im, $srcim2, 24, 4, 0,0, 96,96, 30);
imagedestroy($srcim);
imagedestroy($srcim2);
$hpwidth = intval((186-114) * $oAhp/100);
if ($oAhp)
{
imagefilledrectangle($im, 32,90, 108, 98, $t_hpborder);
imagefilledrectangle($im, 33,91, 107, 97, $white);
imagefilledrectangle($im, 34,92, 34+$hpwidth, 96, $t_hpcolor);
imagefilledrectangle($im, 34,95, 34+$hpwidth, 96, $t_hpshadowcolor);
}
if ($oAt === 'fnt' || $oAt === 'faint')
{
$srcim = imagecreatefrompng('../fx/faintright.png');
}
else
{
$srcim = imagecreatefrompng('../fx/switchright.png');
}
imagecopymerge($im, $srcim, 120-29-10, 39, 0,0, 58,23, 100);
imagedestroy($srcim);
}
// Pokemon B
$srcim = @imagecreatefrompng('../sprites/bw/'.$B.'.png') or $srcim = imagecreatefrompng('../sprites/bw/0.png');
imagecopymerge($im, $srcim, 284, 4, 0,0, 96,96, $Bhp?100:30);
imagedestroy($srcim);
$hpwidth = intval((186-114) * $Bhp/100);
if ($Bhp)
{
imagefilledrectangle($im, 292,90, 368, 98, $hpborder);
imagefilledrectangle($im, 293,91, 367, 97, $white);
imagefilledrectangle($im, 294,92, 294+$hpwidth, 96, $hpcolor);
imagefilledrectangle($im, 294,95, 294+$hpwidth, 96, $hpshadowcolor);
}
else
{
//imagefilledrectangle($im, 292,90, 368, 98, $t_hpborder);
//imagefilledrectangle($im, 293,91, 367, 97, $white);
}
// Pokemon B switched
if ($oB)
{
$srcim = @imagecreatefrompng('../sprites/bw/'.$oB.'.png') or $srcim = imagecreatefrompng('../sprites/bw/0.png');
imagecopymerge($im, $srcim, 384, 4, 0,0, 96,96, 30);
imagedestroy($srcim);
$hpwidth = intval((186-114) * $oBhp/100);
if ($oBhp)
{
imagefilledrectangle($im, 392,90, 468, 98, $t_hpborder);
imagefilledrectangle($im, 393,91, 467, 97, $white);
imagefilledrectangle($im, 394,92, 394+$hpwidth, 96, $t_hpcolor);
imagefilledrectangle($im, 394,95, 394+$hpwidth, 96, $t_hpshadowcolor);
}
if ($oBt === 'fnt' || $oBt === 'faint')
{
$srcim = imagecreatefrompng('../fx/faintleft.png');
}
else
{
$srcim = imagecreatefrompng('../fx/switchleft.png');
}
imagecopymerge($im, $srcim, 380-29+10, 39, 0,0, 58,23, 100);
imagedestroy($srcim);
}
// k
header('Content-Type: image/png');
imagepng($im);
imagedestroy($im);

306
replays/warstory.php Normal file
View File

@ -0,0 +1,306 @@
<?php
$name = $_REQUEST['name'];
if (!$name)
{
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<?php if (!isset($_REQUEST['dev'])) { ?><meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=IE8" /><?php } ?>
<title>Warstory maker</title>
<link rel="stylesheet" href="/style/replayer.css" />
<script src="/js/jquery-1.9.1.min.js"></script>
</head>
<body>
<div id="battle" class="battle" style="display:none"></div>
<div id="battle-log" class="battle-log" style="display:none"></div>
<div class="wrapper">
<noscript>turn on javascript</noscript>
<form id="form" onsubmit="return doSubmit()">
1. if you haven't done so, upload a replay <a href="https://replay.pokemonshowdown.com/">https://replay.pokemonshowdown.com/</a>, get its URL, and come back here.
<br />
2. Paste the URL: <input type="text" size="90" id="text" onkeyup="update()" onchange="update()" />
<br />
3. <button type="submit" disabled="disabled" id="button">Convert</button>
</form>
<script>
<!--
function doSubmit()
{
var val = document.getElementById('text').value;
var ok = false;
if (val.substr(0,7) === 'http://') val = val.substr(7);
if (val.substr(0,8) === 'http://') val = val.substr(8);
if (val.substr(0,4) === 'www.') val = val.substr(4);
if (val.substr(0,'replay.pokemonshowdown.com/'.length) === 'replay.pokemonshowdown.com/')
{
val = val.substr('replay.pokemonshowdown.com/'.length);
ok = true;
}
if (!ok) return false;
else
{
document.location.href = 'http://replay.pokemonshowdown.com/warstory-'+val;
return false;
}
}
function update()
{
var val = document.getElementById('text').value;
var ok = false;
if (val.substr(0,7) === 'http://') val = val.substr(7);
if (val.substr(0,8) === 'http://') val = val.substr(8);
if (val.substr(0,4) === 'www.') val = val.substr(4);
if (val.substr(0,'replay.pokemonshowdown.com/'.length) === 'replay.pokemonshowdown.com/')
{
val = val.substr('replay.pokemonshowdown.com/'.length);
ok = true;
}
document.getElementById('button').disabled = !ok;
}
setInterval(update, 300);
-->
</script>
<?php
die();
}
else if ($_REQUEST['name'])// && $REPLAYS[$_REQUEST['name']])
{
include '../../pokemonshowdown.com/lib/ntbb-database.lib.php';
$res = $psdb->query("SELECT * FROM `ntbb_replays` WHERE `id` = '".$psdb->escape($_REQUEST['name'])."'");
$replay = $psdb->fetch_assoc($res);
if (!$replay) {
header('HTTP/1.1 404 Not Found');
echo '<h1>404 Not Found</h1>';
die();
}
$replay['log'] = str_replace("\r","",$replay['log']);
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<?php if (!isset($_REQUEST['dev'])) { ?><meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=IE8" /><?php } ?>
<title>Viewing Pokemon replay: <?php echo htmlspecialchars($_REQUEST['name']); ?></title>
<link rel="shortcut icon" href="http://aesoft.org/favicon.ico" />
<link rel="stylesheet" href="/style/replayer.css" />
<script src="/js/jquery-1.9.1.min.js"></script>
<script src="/js/jquery-cookie.js"></script>
<script src="/js/soundmanager2.js"></script>
<script src="/js/battledata.js"></script>
<script src="/data/pokedex-mini.js"></script>
<script src="/data/graphics.js"></script>
<script src="/js/battle.js"></script>
<script>
<!--
var newlog = <?php echo json_encode(explode("\n", $replay['log'])); ?>;
//-->
</script>
<?php
if (false && !$_REQUEST['manage'])
{
?>
<script type="text/javascript">
<!--
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25200696-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
-->
</script>
<?php
}
?>
</head>
<body>
<div id="battle" class="battle" style="display:none"></div>
<div id="battle-log" class="battle-log" style="display:none"></div>
<div class="wrapper">
1. Done
<br />
2. Done
<br />
3. Done
<br />
4. Here's the code. Use it as you wish.
<div id="warstory"></div>
<script>
<!--
var battle;
function start()
{
$('#battle-log').html('');
$('#warstory').html('');
battle.reset();
battle.fastForwardTo(-2);
}
battle = new Battle($('#battle'), $('#battle-log'));
battle.setQueue(newlog);
var Aswitch = null;
var Bswitch = null;
function turnCallback(battle)
{
var A = battle.mySide.activePokemon;
var B = battle.yourSide.activePokemon;
if (!A)
{
A = Aswitch?Aswitch[1]:{};
Aswitch = null;
}
if (!B)
{
B = Bswitch?Bswitch[1]:{};
Bswitch = null;
}
// var imagestring = 'a='+A.speciesid+'&ahp='+(100*A.hp/A.maxhp)+'&ast='+A.status+'&b='+B.speciesid+'&bhp='+(100*B.hp/B.maxhp)+'&bst='+B.status;
// if (Aswitch)
// {
// var oA = Aswitch[1];
// imagestring += '&oa='+oA.speciesid+'&oahp='+(100*oA.hp/oA.maxhp)+'&oast='+oA.status+'&oat='+Aswitch[0];
// Aswitch = null;
// }
// if (Bswitch)
// {
// var oB = Bswitch[1];
// imagestring += '&ob='+oB.speciesid+'&obhp='+(100*oB.hp/oB.maxhp)+'&obst='+oB.status+'&obt='+Bswitch[0];
// Bswitch = null;
// }
var imagestring = '';
if (Aswitch) {
var oA = Aswitch[1];
imagestring += ''+Aswitch[0]+'.'+oA.spriteid+'.'+(100*oA.hp/oA.maxhp)+(oA.status?'.'+oA.status:'')+'_';
Aswitch = null;
}
imagestring += ''+A.spriteid+'.'+(100*A.hp/A.maxhp)+(A.status?'.'+A.status:'')+'_'+B.spriteid+'.'+(100*B.hp/B.maxhp)+(B.status?'.'+B.status:'');
if (Bswitch) {
var oB = Bswitch[1];
imagestring += '_'+Bswitch[0]+'.'+oB.spriteid+'.'+(100*oB.hp/oB.maxhp)+(oB.status?'.'+oB.status:'');
Bswitch = null;
}
imagestring = '<div><img src="http://play.pokemonshowdown.com/replay/turn_'+imagestring+'.png" /></div>';
$('#warstory').append($('#battle-log .inner').html());
$('#battle-log .inner').html('');
var lastchild = $('#warstory').children().last();
if (lastchild[0].tagName.toLowerCase() === 'h2')
{
lastchild.before(imagestring+'<div><br /></div><div><br /></div>');
}
else
{
$('#warstory').append(imagestring);
}
}
function switchCallback(battle, side)
{
var switched = ['sw', side.lastPokemon];
if (!side.lastPokemon) return;
if (side.n === 0)
{
if (Aswitch && Aswitch[0] === 'fnt') return;
Aswitch = switched;
}
else
{
if (Bswitch && Bswitch[0] === 'fnt') return;
Bswitch = switched;
}
}
function dragCallback(battle, side)
{
var switched = ['dr', side.lastPokemon];
if (!side.lastPokemon) return;
if (side.n === 0) Aswitch = switched;
else Bswitch = switched;
}
function faintCallback(battle, side)
{
var switched = ['fnt', side.lastPokemon];
if (side.n === 0) Aswitch = switched;
else Bswitch = switched;
}
function endCallback(battle)
{
turnCallback(battle);
$('#warstory').find('div.chat').remove();
var bbcode = $('#warstory').html();
bbcode = bbcode.replace(/<br( )?(\/)?>/g, '');
bbcode = bbcode.replace(/<div class="spacer">/g, '');
bbcode = bbcode.replace(/<div>/g, '');
bbcode = bbcode.replace(/<\/div>/g, "\n");
bbcode = bbcode.replace(/<h2>/g, '[b][color="#002299"][size="3"]');
bbcode = bbcode.replace(/<\/h2>/g, "[/size][/color][/b]\n");
bbcode = bbcode.replace(/<em>/g, '[i]');
bbcode = bbcode.replace(/<\/em>/g, '[/i]');
bbcode = bbcode.replace(/<strong>/g, '[b]');
bbcode = bbcode.replace(/<\/strong>/g, '[/b]');
bbcode = bbcode.replace(/<small>/g, '[size="1"]');
bbcode = bbcode.replace(/<\/small>/g, '[/size]');
bbcode = bbcode.replace(/<img[^>]* src="([^"]*)"[^>]*\/?>/g, "[img]$1[/img]\n");
bbcode = bbcode.replace(/&amp;/g, '&');
var teamcode = '[hide="'+battle.mySide.name+'\'s team"]';
for (var i=0; i<battle.mySide.pokemon.length && i<6; i++)
{
teamcode += '[img]http://play.pokemonshowdown.com/sprites/bw/'+battle.mySide.pokemon[i].spriteid+'.png[/img]';
}
teamcode += '[/hide]'+"\n";
teamcode += '[hide="'+battle.yourSide.name+'\'s team"]';
for (var i=0; i<battle.yourSide.pokemon.length && i<6; i++)
{
teamcode += '[img]http://play.pokemonshowdown.com/sprites/bw/'+battle.yourSide.pokemon[i].spriteid+'.png[/img]';
}
teamcode += '[/hide]'+"\n\n";
bbcode = teamcode + bbcode;
bbcode = "[indent]>>>[url=\"http://play.pokemonshowdown.com/replay/battle-<?php echo $name ?>\"][size=\"3\"]Click to [b]watch this replay[/b]![/size][/url]<<<[/indent]\n\n"+bbcode;
bbcode += "\n\n[indent]>>>[url=\"http://play.pokemonshowdown.com/replay/battle-<?php echo $name ?>\"][size=\"3\"]Click to [b]watch this replay[/b]![/size][/url]<<<[/indent]";
$('#warstory').prepend('<textarea rows="20" cols="90"></textarea>');
$('#warstory textarea').text(bbcode);
}
battle.turnCallback = turnCallback;
battle.endCallback = endCallback;
battle.switchCallback = switchCallback;
battle.dragCallback = dragCallback;
battle.faintCallback = faintCallback;
start();
-->
</script>
</div>
</body>
</html>
<?php
die();
}
header('HTTP/1.1 404 Not Found');
?><h1>404 Not Found</h1>