aboutsummaryrefslogtreecommitdiffstats
path: root/gaming/runescape
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gaming/runescape/_lib.php182
-rw-r--r--gaming/runescape/data.json95
-rw-r--r--gaming/runescape/index.php139
-rw-r--r--gaming/runescape/index_jp.php66
-rw-r--r--gaming/runescape/index_zh.php66
-rw-r--r--gaming/runescape/runescape.pngbin0 -> 206939 bytes
6 files changed, 548 insertions, 0 deletions
diff --git a/gaming/runescape/_lib.php b/gaming/runescape/_lib.php
new file mode 100644
index 0000000..c8fbbab
--- /dev/null
+++ b/gaming/runescape/_lib.php
@@ -0,0 +1,182 @@
+<?php
+// Shared read-only helpers for the /gaming/runescape/ page.
+// Data is edited via /admin.php?section=runescape; this file just renders it.
+
+function runescape_valid_url_or_path(string $url): bool {
+ if ($url === '') return false;
+ if ($url[0] === '/') return !str_starts_with($url, '//') && !str_contains($url, "\0");
+ $scheme = strtolower(parse_url($url, PHP_URL_SCHEME) ?? '');
+ if (in_array($scheme, ['http', 'https', 'gemini', 'gopher'], true)) return filter_var($url, FILTER_VALIDATE_URL) !== false;
+ return false;
+}
+
+function runescape_data(?string $path = null): array {
+ $path = $path ?? (__DIR__ . '/data.json');
+ $empty = [
+ 'welcome' => [],
+ 'screenshots' => [],
+ 'eras' => [],
+ 'links_section' => ['title' => 'Server Lists', 'disclaimer' => '', 'items' => []],
+ ];
+ if (!is_file($path)) {
+ return $empty;
+ }
+ $raw = file_get_contents($path);
+ if ($raw === false || trim($raw) === '') {
+ return $empty;
+ }
+ $data = json_decode($raw, true);
+ if (!is_array($data)) {
+ return $empty;
+ }
+ $welcome = [];
+ foreach (($data['welcome'] ?? []) as $line) {
+ $line = trim((string)$line);
+ if ($line !== '') $welcome[] = $line;
+ }
+ $screenshots = [];
+ foreach (($data['screenshots'] ?? []) as $shot) {
+ if (!is_array($shot)) continue;
+ $filename = trim((string)($shot['filename'] ?? ''));
+ if ($filename === '' || str_contains($filename, '..') || str_contains($filename, '/')) continue;
+ $screenshots[] = [
+ 'filename' => $filename,
+ 'caption' => (string)($shot['caption'] ?? ''),
+ ];
+ }
+ $eras = [];
+ foreach (($data['eras'] ?? []) as $era) {
+ if (!is_array($era)) continue;
+ $title = trim((string)($era['title'] ?? ''));
+ if ($title === '') continue;
+ $servers = [];
+ foreach (($era['servers'] ?? []) as $srv) {
+ if (!is_array($srv)) continue;
+ $name = trim((string)($srv['name'] ?? ''));
+ if ($name === '') continue;
+ $url = trim((string)($srv['url'] ?? ''));
+ if ($url !== '' && !runescape_valid_url_or_path($url)) $url = '';
+ $highlights = [];
+ foreach (($srv['highlights'] ?? []) as $hl) {
+ $hl = trim((string)$hl);
+ if ($hl !== '') $highlights[] = $hl;
+ }
+ $links = [];
+ foreach (($srv['links'] ?? []) as $lk) {
+ if (!is_array($lk)) continue;
+ $label = trim((string)($lk['label'] ?? ''));
+ $lurl = trim((string)($lk['url'] ?? ''));
+ if ($label === '' || $lurl === '' || !runescape_valid_url_or_path($lurl)) continue;
+ $links[] = ['label' => $label, 'url' => $lurl];
+ }
+ $srvImage = trim((string)($srv['image'] ?? ''));
+ if ($srvImage !== '' && str_contains($srvImage, '..')) $srvImage = '';
+ $servers[] = [
+ 'id' => (string)($srv['id'] ?? ''),
+ 'name' => $name,
+ 'url' => $url,
+ 'featured' => !empty($srv['featured']),
+ 'desc' => (string)($srv['desc'] ?? ''),
+ 'highlights' => $highlights,
+ 'links' => $links,
+ 'image' => $srvImage,
+ ];
+ }
+ $eraImage = trim((string)($era['image'] ?? ''));
+ if ($eraImage !== '' && str_contains($eraImage, '..')) $eraImage = '';
+ $eras[] = [
+ 'id' => (string)($era['id'] ?? ''),
+ 'title' => $title,
+ 'subtitle' => (string)($era['subtitle'] ?? ''),
+ 'image' => $eraImage,
+ 'servers' => $servers,
+ ];
+ }
+ $linksSection = ['title' => 'Server Lists', 'disclaimer' => '', 'items' => []];
+ if (is_array($data['links_section'] ?? null)) {
+ $ls = $data['links_section'];
+ $linksSection['title'] = trim((string)($ls['title'] ?? '')) ?: 'Server Lists';
+ $linksSection['disclaimer'] = (string)($ls['disclaimer'] ?? '');
+ foreach (($ls['items'] ?? []) as $item) {
+ if (!is_array($item)) continue;
+ $label = trim((string)($item['label'] ?? ''));
+ $lurl = trim((string)($item['url'] ?? ''));
+ if ($label === '' || $lurl === '' || !runescape_valid_url_or_path($lurl)) continue;
+ $linksSection['items'][] = ['label' => $label, 'url' => $lurl, 'desc' => (string)($item['desc'] ?? '')];
+ }
+ }
+ return ['welcome' => $welcome, 'screenshots' => $screenshots, 'eras' => $eras, 'links_section' => $linksSection];
+}
+
+function runescape_screenshot_src(string $filename): string {
+ $filename = trim($filename);
+ if ($filename === '' || str_contains($filename, '..') || str_contains($filename, '/')) return '';
+ return '/gaming/runescape/images/' . rawurlencode($filename);
+}
+
+/** Public URL for an era/server image (uploaded filename, site path, or absolute URL). */
+function runescape_image_src(string $image): string {
+ $image = trim($image);
+ if ($image === '') return '';
+ if (str_starts_with($image, 'http://') || str_starts_with($image, 'https://') || str_starts_with($image, '/')) {
+ return $image;
+ }
+ if (str_contains($image, '..') || str_contains($image, "\0")) return '';
+ return '/gaming/runescape/images/' . rawurlencode(basename($image));
+}
+
+/** Render the Welcome list — lines like "::Label::" become bold headings, everything else is a bullet. */
+function runescape_render_welcome(array $lines): void {
+ $he = fn($s) => htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8');
+ $inList = false;
+ foreach ($lines as $line) {
+ if (preg_match('/^::(.+)::$/', trim($line), $m)) {
+ if ($inList) { echo '</ul>'; $inList = false; }
+ echo '<p class="machine-title machine-title--sub">' . $he(trim($m[1])) . '</p>';
+ continue;
+ }
+ if (!$inList) { echo '<ul class="contact-list">'; $inList = true; }
+ echo '<li>' . $he($line) . '</li>';
+ }
+ if ($inList) echo '</ul>';
+}
+
+/** Render one server card (used inside an era section). */
+function runescape_render_server(array $srv): void {
+ $he = fn($s) => htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8');
+ $cardClass = $srv['featured'] ? 'machine machine--notes' : 'machine';
+ $img = runescape_image_src((string)($srv['image'] ?? ''));
+ ?>
+ <div class="<?= $cardClass ?>">
+ <div class="<?= $img !== '' ? 'machine-row' : '' ?>">
+ <?php if ($img !== ''): ?>
+ <img class="machine-thumb" src="<?= $he($img) ?>" alt="<?= $he($srv['name']) ?>" loading="lazy" decoding="async">
+ <?php endif; ?>
+ <div class="machine-info<?= $img !== '' ? ' machine-facts' : '' ?>">
+ <div class="machine-title">
+ <?php if ($srv['url'] !== ''): ?>
+ <a href="<?= $he($srv['url']) ?>" target="_blank" rel="noopener noreferrer"><?= $he($srv['name']) ?></a>
+ <?php else: ?>
+ <?= $he($srv['name']) ?>
+ <?php endif; ?>
+ </div>
+ <?php if ($srv['desc'] !== ''): ?>
+ <?= nl2br($he($srv['desc'])) ?>
+ <?php endif; ?>
+ <?php if ($srv['highlights']): ?>
+ <ul class="contact-list">
+ <?php foreach ($srv['highlights'] as $hl): ?><li><?= $he($hl) ?></li><?php endforeach; ?>
+ </ul>
+ <?php endif; ?>
+ <?php if ($srv['links']): ?>
+ <p>
+ <?php foreach ($srv['links'] as $i => $lk): ?>
+ <?= $i > 0 ? ' &middot; ' : '' ?><a href="<?= $he($lk['url']) ?>" target="_blank" rel="noopener noreferrer"><?= $he($lk['label']) ?></a>
+ <?php endforeach; ?>
+ </p>
+ <?php endif; ?>
+ </div>
+ </div>
+ </div>
+ <?php
+}
diff --git a/gaming/runescape/data.json b/gaming/runescape/data.json
new file mode 100644
index 0000000..3b81b94
--- /dev/null
+++ b/gaming/runescape/data.json
@@ -0,0 +1,95 @@
+{
+ "welcome": [],
+ "screenshots": [],
+ "eras": [
+ {
+ "id": "2004-lost-city",
+ "title": "2004 · Lost City Era",
+ "subtitle": "Preservation servers recreating RuneScape as it was in 2004.",
+ "servers": [
+ {
+ "id": "lost-city",
+ "name": "Lost City",
+ "url": "https://2004.lostcity.rs/",
+ "featured": true,
+ "desc": "Open-source recreation of RuneScape 2 as it launched in May 2004, rebuilt from decompiled client/server code and old cache dumps rather than guesswork. Runs live, incrementally-patched revisions instead of one static snapshot.",
+ "highlights": [
+ "Play straight in the browser, no client install required",
+ "Progresses through real 2004–2005 content updates over time, at the original pace",
+ "Active dev team, open GitHub repo, public roadmap"
+ ],
+ "links": [
+ {"label": "Play", "url": "https://2004.lostcity.rs/"},
+ {"label": "Website", "url": "https://lostcity.rs/"}
+ ]
+ }
+ ]
+ },
+ {
+ "id": "other-era-servers",
+ "title": "Other Era Servers",
+ "subtitle": "",
+ "servers": [
+ {
+ "id": "openrsc",
+ "name": "OpenRSC",
+ "url": "https://rsc.vet/",
+ "featured": false,
+ "desc": "Preservation of RuneScape Classic, the isometric pre-RS2 client from 2001–2003, before the switch to the 3D engine.",
+ "highlights": [
+ "Original 2D isometric client and UI, not a reskin of a later revision",
+ "Slower, grindier economy typical of the Classic era",
+ "Small dedicated community focused on historical accuracy"
+ ],
+ "links": []
+ },
+ {
+ "id": "2006scape",
+ "name": "2006Scape",
+ "url": "https://2006scape.org/",
+ "featured": false,
+ "desc": "Targets the RS2 client roughly a year after Lost City's starting point — post-Lost City the quest, pre-Evolution of Combat.",
+ "highlights": [
+ "Wilderness and old-style PvP still intact",
+ "Content built out toward the mid-2000s quest/skill lineup",
+ "Fan-maintained, community-driven development"
+ ],
+ "links": []
+ },
+ {
+ "id": "2009scape",
+ "name": "2009scape",
+ "url": "https://2009scape.org/",
+ "featured": false,
+ "desc": "Open-source continuation of the old RS2 client around the HD graphics upgrade era, before Evolution of Combat and the Grand Exchange changes that followed.",
+ "highlights": [
+ "HD-era graphics with the old tick-based combat system",
+ "Fully open source, community forks welcome",
+ "Long-running project with regular content patches"
+ ],
+ "links": []
+ },
+ {
+ "id": "darkan",
+ "name": "Darkan",
+ "url": "https://darkan.org/",
+ "featured": false,
+ "desc": "Solo-developed remake targeting the 2011–2012 client, later than the other entries here and closer to modern OSRS's starting point.",
+ "highlights": [
+ "One-person passion project, built from scratch rather than a leaked source",
+ "Development notes and progress posted publicly",
+ "Smaller playerbase, actively maintained"
+ ],
+ "links": []
+ }
+ ]
+ }
+ ],
+ "links_section": {
+ "title": "Server Lists",
+ "disclaimer": "Unofficial private servers, not affiliated with Jagex Ltd.",
+ "items": [
+ {"label": "Rune-Server", "url": "https://rune-server.org/", "desc": "private server development community"}
+ ]
+ }
+}
diff --git a/gaming/runescape/index.php b/gaming/runescape/index.php
new file mode 100644
index 0000000..e8aacfa
--- /dev/null
+++ b/gaming/runescape/index.php
@@ -0,0 +1,139 @@
+<?php
+require_once __DIR__ . '/_lib.php';
+$rsData = runescape_data();
+?>
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
+ <meta name="description" content="RuneScape / OSRS fan page by SillyLaird." />
+ <meta property="og:title" content="RuneScape — SillyLaird" />
+ <meta property="og:description" content="RuneScape / OSRS fan page by SillyLaird." />
+ <meta property="og:image" content="https://www.sillylaird.ca/assets/img/lain.png" />
+ <meta property="og:type" content="website" />
+ <meta property="og:url" content="https://www.sillylaird.ca/gaming/runescape/" />
+ <meta property="og:locale" content="en_US" />
+ <meta property="og:locale:alternate" content="ja_JP" />
+ <meta property="og:locale:alternate" content="zh_CN" />
+ <meta name="twitter:card" content="summary_large_image" />
+ <meta name="twitter:title" content="RuneScape — SillyLaird" />
+ <meta property="og:description" content="RuneScape / OSRS fan page by SillyLaird." />
+ <meta name="twitter:image" content="https://www.sillylaird.ca/assets/img/lain.png" />
+ <link rel="canonical" href="https://www.sillylaird.ca/gaming/runescape/" />
+ <link rel="alternate" hreflang="x-default" href="https://www.sillylaird.ca/gaming/runescape/" />
+ <link rel="alternate" hreflang="en" href="https://www.sillylaird.ca/gaming/runescape/" />
+ <link rel="alternate" hreflang="zh" href="https://www.sillylaird.ca/gaming/runescape/index_zh.php" />
+ <link rel="alternate" hreflang="ja" href="https://www.sillylaird.ca/gaming/runescape/index_jp.php" />
+ <title>RuneScape — SillyLaird</title>
+ <link rel="icon" href="https://www.sillylaird.ca/assets/img/lain.png" />
+
+ <link rel="stylesheet" href="/assets/css/fonts.css" />
+ <link rel="stylesheet" href="/assets/css/site.css" />
+ <link rel="stylesheet" href="/assets/css/skeleton.css" />
+ <link rel="stylesheet" href="/assets/css/pages/computers.css" />
+</head>
+
+<body>
+ <a class="skip-link" href="#main">Skip to content</a>
+ <?php include '../../partials/header.php'; ?>
+
+ <main id="main" class="wrap">
+ <section aria-labelledby="rs-title">
+ <h1 id="rs-title">RuneScape (OSRS)</h1>
+
+ <nav class="colon-nav" aria-label="RuneScape page sections">
+ <strong>::Servers::</strong>
+ <?php foreach ($rsData['eras'] as $era): ?>
+ <a href="#era-<?= htmlspecialchars($era['id'], ENT_QUOTES, 'UTF-8') ?>-title"><?= htmlspecialchars($era['title'], ENT_QUOTES, 'UTF-8') ?></a> |
+ <?php endforeach; ?>
+ <a href="#servers-links-title">Links</a>
+ <strong>::Scrapbook::</strong>
+ <a href="#servers-bank-title">Bank Pics</a>
+ <strong>::Official::</strong>
+ <a href="https://oldschool.runescape.wiki/" target="_blank" rel="noopener noreferrer">OSRS Wiki</a>
+ </nav>
+
+ <div class="machine">
+ <div class="machine-row">
+ <img class="machine-thumb" src="/assets/img/runescape.png" alt="RuneScape" loading="lazy" decoding="async">
+ <div class="machine-facts">
+ <div class="machine-info">
+ <strong>Page added:</strong> 2026-07-25<br>
+ <strong>Last revised:</strong> 2026-07-25
+ </div>
+ <?php if ($rsData['welcome']): ?>
+ <?php runescape_render_welcome($rsData['welcome']); ?>
+ <?php else: ?>
+ <div class="machine-title machine-title--sub">Welcome</div>
+ <ul class="contact-list">
+ <li>A small fan page / launch pad &mdash; official OSRS plus private servers, grouped by era.</li>
+ <li>Servers listed below are unofficial preservation projects, not affiliated with Jagex Ltd.</li>
+ <li>Bookmark <a href="/gaming/runescape/">/gaming/runescape/</a> directly &mdash; server hosts change over time.</li>
+ </ul>
+ <?php endif; ?>
+ </div>
+ </div>
+ </div>
+ </section>
+
+ <?php foreach ($rsData['eras'] as $era): ?>
+ <section aria-labelledby="era-<?= htmlspecialchars($era['id'], ENT_QUOTES, 'UTF-8') ?>-title">
+ <h2 id="era-<?= htmlspecialchars($era['id'], ENT_QUOTES, 'UTF-8') ?>-title" class="section-accent-title"><?= htmlspecialchars($era['title'], ENT_QUOTES, 'UTF-8') ?></h2>
+ <?php if ($era['subtitle'] !== ''): ?><p class="muted"><?= htmlspecialchars($era['subtitle'], ENT_QUOTES, 'UTF-8') ?></p><?php endif; ?>
+ <?php $eraImgSrc = runescape_image_src($era['image']); ?>
+ <?php if ($eraImgSrc !== ''): ?>
+ <img class="rs-era-banner" src="<?= htmlspecialchars($eraImgSrc, ENT_QUOTES, 'UTF-8') ?>" alt="<?= htmlspecialchars($era['title'], ENT_QUOTES, 'UTF-8') ?>" loading="lazy" decoding="async">
+ <?php endif; ?>
+
+ <?php foreach ($era['servers'] as $srv): ?>
+ <?php runescape_render_server($srv); ?>
+ <?php endforeach; ?>
+ </section>
+ <?php endforeach; ?>
+
+ <!-- Screenshots are managed at /admin.php?section=runescape — placeholder-box slots
+ show until real ones are uploaded there; no manual editing needed. -->
+ <section aria-labelledby="servers-bank-title">
+ <h2 id="servers-bank-title" class="section-accent-title">My Bank / Screenshots</h2>
+ <?php if ($rsData['screenshots']): ?>
+ <div class="placeholder-grid">
+ <?php foreach ($rsData['screenshots'] as $shot): ?>
+ <figure class="rs-shot">
+ <img src="<?= htmlspecialchars(runescape_screenshot_src($shot['filename']), ENT_QUOTES, 'UTF-8') ?>"
+ alt="<?= htmlspecialchars($shot['caption'] !== '' ? $shot['caption'] : 'RuneScape screenshot', ENT_QUOTES, 'UTF-8') ?>"
+ loading="lazy" decoding="async">
+ <?php if ($shot['caption'] !== ''): ?>
+ <figcaption class="muted"><?= htmlspecialchars($shot['caption'], ENT_QUOTES, 'UTF-8') ?></figcaption>
+ <?php endif; ?>
+ </figure>
+ <?php endforeach; ?>
+ </div>
+ <?php else: ?>
+ <p class="muted">Placeholder &mdash; swap these for real bank/inventory screenshots later.</p>
+ <div class="placeholder-grid">
+ <div class="placeholder-box">Add screenshot 1</div>
+ <div class="placeholder-box">Add screenshot 2</div>
+ <div class="placeholder-box">Add screenshot 3</div>
+ </div>
+ <?php endif; ?>
+ </section>
+
+ <section aria-labelledby="servers-links-title">
+ <h2 id="servers-links-title" class="section-accent-title"><?= htmlspecialchars($rsData['links_section']['title'], ENT_QUOTES, 'UTF-8') ?></h2>
+ <?php if ($rsData['links_section']['items']): ?>
+ <ul class="contact-list">
+ <?php foreach ($rsData['links_section']['items'] as $item): ?>
+ <li><a href="<?= htmlspecialchars($item['url'], ENT_QUOTES, 'UTF-8') ?>" target="_blank" rel="noopener noreferrer"><?= htmlspecialchars($item['label'], ENT_QUOTES, 'UTF-8') ?></a><?= $item['desc'] !== '' ? ' &mdash; ' . htmlspecialchars($item['desc'], ENT_QUOTES, 'UTF-8') : '' ?></li>
+ <?php endforeach; ?>
+ </ul>
+ <?php endif; ?>
+ <?php if ($rsData['links_section']['disclaimer'] !== ''): ?>
+ <p class="muted"><?= htmlspecialchars($rsData['links_section']['disclaimer'], ENT_QUOTES, 'UTF-8') ?></p>
+ <?php endif; ?>
+ </section>
+ </main>
+
+ <?php include '../../partials/footer.php'; ?>
+</body>
+</html>
diff --git a/gaming/runescape/index_jp.php b/gaming/runescape/index_jp.php
new file mode 100644
index 0000000..629dca9
--- /dev/null
+++ b/gaming/runescape/index_jp.php
@@ -0,0 +1,66 @@
+<!doctype html>
+<html lang="ja">
+<head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
+ <meta name="description" content="RuneScape / OSRS fan page by SillyLaird." />
+ <meta property="og:title" content="RuneScape — SillyLaird" />
+ <meta property="og:description" content="RuneScape / OSRS fan page by SillyLaird." />
+ <meta property="og:image" content="https://www.sillylaird.ca/assets/img/lain.png" />
+ <meta property="og:type" content="website" />
+ <meta property="og:url" content="https://www.sillylaird.ca/gaming/runescape/index_jp.php" />
+ <meta property="og:locale" content="ja_JP" />
+ <meta property="og:locale:alternate" content="en_US" />
+ <meta property="og:locale:alternate" content="zh_CN" />
+ <meta name="twitter:card" content="summary_large_image" />
+ <meta name="twitter:title" content="RuneScape — SillyLaird" />
+ <meta name="twitter:description" content="RuneScape / OSRS fan page by SillyLaird." />
+ <meta name="twitter:image" content="https://www.sillylaird.ca/assets/img/lain.png" />
+ <link rel="canonical" href="https://www.sillylaird.ca/gaming/runescape/index_jp.php" />
+ <link rel="alternate" hreflang="x-default" href="https://www.sillylaird.ca/gaming/runescape/" />
+ <link rel="alternate" hreflang="en" href="https://www.sillylaird.ca/gaming/runescape/" />
+ <link rel="alternate" hreflang="zh" href="https://www.sillylaird.ca/gaming/runescape/index_zh.php" />
+ <link rel="alternate" hreflang="ja" href="https://www.sillylaird.ca/gaming/runescape/index_jp.php" />
+ <meta name="keywords" content="sillylaird, runescape, osrs, gielinor" />
+ <title>RuneScape — SillyLaird</title>
+ <link rel="icon" href="https://www.sillylaird.ca/assets/img/lain.png" />
+
+ <link rel="stylesheet" href="/assets/css/fonts.css" />
+ <link rel="stylesheet" href="/assets/css/site.css" />
+ <link rel="stylesheet" href="/assets/css/skeleton.css" />
+</head>
+
+<body>
+ <a class="skip-link" href="#main">本文へ移動</a>
+ <?php include '../../partials/header.php'; ?>
+
+ <main id="main" class="wrap">
+ <section aria-labelledby="rs-title">
+ <h1 id="rs-title">RuneScape (OSRS)</h1>
+ <p class="muted">A small fan page / launch pad.</p>
+
+ <img
+ src="https://oldschool.runescape.wiki/images/thumb/Old_School_RuneScape_logo.png/1200px-Old_School_RuneScape_logo.png?d047b"
+ alt="Old School RuneScape Logo"
+ loading="lazy"
+ decoding="async" class="sb83d4c5326"
+ />
+
+ <p class="sd1a2ffa0b4">
+ <a class="header-button" href="https://oldschool.runescape.wiki/" target="_blank" rel="noopener noreferrer">Visit OSRS Wiki</a>
+ </p>
+ </section>
+
+ <section aria-labelledby="guides-title">
+ <h2 id="guides-title">Essential Guides</h2>
+ <ul class="contact-list">
+ <li><a href="#">Combat XP Calculator</a></li>
+ <li><a href="#">Quest Item Database</a></li>
+ <li><a href="#">Grand Exchange Tracker</a></li>
+ </ul>
+ </section>
+ </main>
+
+ <?php include '../../partials/footer.php'; ?>
+</body>
+</html>
diff --git a/gaming/runescape/index_zh.php b/gaming/runescape/index_zh.php
new file mode 100644
index 0000000..a2cf16f
--- /dev/null
+++ b/gaming/runescape/index_zh.php
@@ -0,0 +1,66 @@
+<!doctype html>
+<html lang="zh">
+<head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
+ <meta name="description" content="RuneScape / OSRS fan page by SillyLaird." />
+ <meta property="og:title" content="RuneScape — SillyLaird" />
+ <meta property="og:description" content="RuneScape / OSRS fan page by SillyLaird." />
+ <meta property="og:image" content="https://www.sillylaird.ca/assets/img/lain.png" />
+ <meta property="og:type" content="website" />
+ <meta property="og:url" content="https://www.sillylaird.ca/gaming/runescape/index_zh.php" />
+ <meta property="og:locale" content="zh_CN" />
+ <meta property="og:locale:alternate" content="en_US" />
+ <meta property="og:locale:alternate" content="ja_JP" />
+ <meta name="twitter:card" content="summary_large_image" />
+ <meta name="twitter:title" content="RuneScape — SillyLaird" />
+ <meta name="twitter:description" content="RuneScape / OSRS fan page by SillyLaird." />
+ <meta name="twitter:image" content="https://www.sillylaird.ca/assets/img/lain.png" />
+ <link rel="canonical" href="https://www.sillylaird.ca/gaming/runescape/index_zh.php" />
+ <link rel="alternate" hreflang="x-default" href="https://www.sillylaird.ca/gaming/runescape/" />
+ <link rel="alternate" hreflang="en" href="https://www.sillylaird.ca/gaming/runescape/" />
+ <link rel="alternate" hreflang="zh" href="https://www.sillylaird.ca/gaming/runescape/index_zh.php" />
+ <link rel="alternate" hreflang="ja" href="https://www.sillylaird.ca/gaming/runescape/index_jp.php" />
+ <meta name="keywords" content="sillylaird, runescape, osrs, gielinor" />
+ <title>RuneScape — SillyLaird</title>
+ <link rel="icon" href="https://www.sillylaird.ca/assets/img/lain.png" />
+
+ <link rel="stylesheet" href="/assets/css/fonts.css" />
+ <link rel="stylesheet" href="/assets/css/site.css" />
+ <link rel="stylesheet" href="/assets/css/skeleton.css" />
+</head>
+
+<body>
+ <a class="skip-link" href="#main">跳至内容</a>
+ <?php include '../../partials/header.php'; ?>
+
+ <main id="main" class="wrap">
+ <section aria-labelledby="rs-title">
+ <h1 id="rs-title">RuneScape (OSRS)</h1>
+ <p class="muted">A small fan page / launch pad.</p>
+
+ <img
+ src="https://oldschool.runescape.wiki/images/thumb/Old_School_RuneScape_logo.png/1200px-Old_School_RuneScape_logo.png?d047b"
+ alt="Old School RuneScape Logo"
+ loading="lazy"
+ decoding="async" class="sb83d4c5326"
+ />
+
+ <p class="sd1a2ffa0b4">
+ <a class="header-button" href="https://oldschool.runescape.wiki/" target="_blank" rel="noopener noreferrer">Visit OSRS Wiki</a>
+ </p>
+ </section>
+
+ <section aria-labelledby="guides-title">
+ <h2 id="guides-title">Essential Guides</h2>
+ <ul class="contact-list">
+ <li><a href="#">Combat XP Calculator</a></li>
+ <li><a href="#">Quest Item Database</a></li>
+ <li><a href="#">Grand Exchange Tracker</a></li>
+ </ul>
+ </section>
+ </main>
+
+ <?php include '../../partials/footer.php'; ?>
+</body>
+</html>
diff --git a/gaming/runescape/runescape.png b/gaming/runescape/runescape.png
new file mode 100644
index 0000000..2eb0c44
--- /dev/null
+++ b/gaming/runescape/runescape.png
Binary files differ