<!doctype html>
<html>
- <head>
- <title>Tick City</title>
- <meta charset="utf-8">
- <script src="https://unpkg.com/htmx.org@1.7.0" integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous" defer></script>
- <link rel="stylesheet" href="https://fonts.xz.style/serve/inter.css">
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css">
- <link rel="stylesheet" href="/stylesheet.css">
- </head>
- <body hx-trigger="load" hx-get="/city">
- </body>
+
+<head>
+ <title>Tick City</title>
+ <meta charset="utf-8">
+ <link rel="preconnect" href="https://fonts.googleapis.com">
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+ <link href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@300;400;700&display=swap"
+ rel="stylesheet">
+ <script src="https://unpkg.com/htmx.org@1.7.0"
+ integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous"
+ defer></script>
+ <link rel="stylesheet" type="text/css" href="https://unpkg.com/augmented-ui@2/augmented-ui.min.css">
+ <link rel="stylesheet" href="/scifi.css">
+ <link rel="stylesheet" href="/stylesheet.css">
+
+</head>
+
+<body>
+ <div class="row">
+ <div class="col sidebar">
+ <div class="row">
+ <div class="col pane" data-augmented-ui="tl-clip tr-2-clip-x br-2-clip-y bl-clip-x border">
+ <h2 data-augmented-ui="tl-clip bl-clip none">Nav</h2>
+ <ul class="nav">
+ <li>
+ <a href="#" hx-target="#main" hx-get="/poll/overview" hx-trigger="load, click">Home</a>
+ </li>
+ <li>
+ <a href="#" hx-target="#main" hx-get="/poll/construction" hx-trigger="click">Construction</a>
+ </li>
+ <li>
+ <a href="#" hx-target="#main" hx-get="/poll/unit-training" hx-trigger="click">Unit Training</a>
+ </li>
+ <li>
+ <a href="#" hx-target="#main" hx-get="/poll/map" hx-trigger="click">Map</a>
+ </li>
+ <li>
+ <a href="#" hx-target="#main" hx-get="/poll/mailroom" hx-trigger="click">Mail</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ <div class="col" id="main" data-augmented-ui="tl-clip tr-clip br-clip-x bl-clip border">
+
+ </div>
+ </div>
+ <footer>
+ A project by <a href="https://xangelo.ca">xangelo</a>
+ </footer>
+</body>
</html>
\ No newline at end of file
<!doctype html>
<html>
- <head>
- <title>Tick City</title>
- <meta charset="utf-8">
- <script src="https://unpkg.com/htmx.org@1.7.0" integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous" defer></script>
- <link rel="stylesheet" href="https://fonts.xz.style/serve/inter.css">
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css">
- <link rel="stylesheet" href="/stylesheet.css">
- </head>
- <body id="home">
- <h2>Alpha Kingdom Management</h2>
- <p>Hey, welcome to this alpha version of a kingdom management game. I've always loved building browser based games and this is
- one in a long line. There's little to no branding just yet, just a simple time-based kingdom management game
- </p>
- <table>
- <tr>
- <td>
- <h4>Signup</h4>
- <form hx-post="/accounts">
- <div>
- <label>Username: </label>
- <input type="text" name="username">
- </div>
- <div>
- <label>Password: </label>
- <input type="password" name="password">
- </div>
- <button type="submit">Sign Up</button>
- </form>
+<head>
+ <title>Tick City</title>
+ <meta charset="utf-8">
+ <link rel="preconnect" href="https://fonts.googleapis.com">
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+ <link href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@300;400;700&display=swap"
+ rel="stylesheet">
+ <script src="https://unpkg.com/htmx.org@1.7.0"
+ integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous"
+ defer></script>
+ <link rel="stylesheet" type="text/css" href="https://unpkg.com/augmented-ui@2/augmented-ui.min.css">
+ <link rel="stylesheet" href="/scifi.css">
+</head>
+
+<body id="home">
+ <h1 data-augmented-ui="tl-clip bl-clip none">Antares</h1>
+ <div class="row">
+ <div class="col">
+ <p>Welcome to <b>Antares</b>, a free-to-play browser based strategy game.</p>
+ <p>We're very early in development, but are always looking for new players to sign up and test things out. Just note
+ that the likelihood of you running into a broken feature or a bug is quite high at this point. If you do
+ encouter a bug or issue, feel free to post in our forums and one of the devs will take a look.
+ </p>
- </td>
- <td>
- <h4>Login</h4>
- <form hx-post="/login">
- <div>
- <label>Username: </label>
- <input type="text" name="username">
+ <h2 data-augmented-ui="tl-clip bl-clip none">Upcoming Features</h2>
+ <h3 data-augmented-ui="tl-clip bl-clip none">Species</h3>
+ <p>
+ Work is being done to add different Species to the game to allow further customization and strategy options.
+ </p>
+ <h3 data-augmented-ui="tl-clip bl-clip none">More Buildings</h3>
+ <p>We're pretty light on buildings at the moment but we have plans for a few more:</p>
+ <ul>
+ <li>Homes</li>
+ <li>Banks</li>
+ <li>Mills</li>
+ <li>Quarries</li>
+ <li>Spy Hideouts</li>
+ <li>Mage Towers</li>
+ </ul>
+ </div>
+ <div class="col">
+ <div class="row">
+ <div class="col pane" data-augmented-ui="tl-clip tr-2-clip-x br-2-clip-y bl-clip-x border">
+ <h3 data-augmented-ui="tl-clip bl-clip none">Signup</h3>
+ <form hx-post="/accounts">
+ <div>
+ <label>Username:</label>
+ <input type="text" name="username">
+ </div>
+ <div>
+ <label>Password:</label>
+ <input type="password" name="password">
+ </div>
+ <button type="submit" class="success">Sign Up</button>
+ </form>
+ </div>
</div>
- <div>
- <label>Password: </label>
- <input type="password" name="password">
+ <div class="row">
+ <div class="col pane" style="margin-top: 2rem; " data-augmented-ui="tl-clip tr-2-clip-x br-2-clip-y bl-clip-x border">
+ <h3 data-augmented-ui="tl-clip bl-clip none">Login</h3>
+ <form hx-post="/login">
+ <div>
+ <label>Username:</label>
+ <input type="text" name="username">
+ </div>
+ <div>
+ <label>Password:</label>
+ <input type="password" name="password">
+ </div>
+ <button type="submit">Log In</button>
+ </form>
+ </div>
+
</div>
- <button type="submit">Log In</button>
- </form>
- </td>
- </tr>
- </table>
+ </div>
+ </div>
+
+ <div class="row">
+ </div>
- <h2>Upcoming Features</h2>
- <h3>Fantasy Races</h3>
- <p>All good kingdom management games need to give you the ability to pick your fantasy race. Once you decide on the race of your leader you'll be given various perks/units/buildings that differ from all the others. You'll also have access to a "Race Chat" where only other members of your chosen race can see each other speak. </p>
- <h3>More Buildings</h3>
- <p>We're pretty light on buildings at the moment but we have plans for a few more:</p>
- <ul>
- <li>Banks</li>
- <li>Mills</li>
- <li>Quarries</li>
- <li>Spy Hideouts</li>
- <li>Mage Towers</li>
- </ul>
- <footer>
- A project by <a href="https://xangelo.ca">xangelo</a>
- </footer>
- </body>
+
+ <footer>
+ A project by <a href="https://xangelo.ca">xangelo</a>
+ </footer>
+</body>
</html>
\ No newline at end of file
--- /dev/null
+:root {
+ --border: #1c7282;
+ --page-bg: #061619;
+ --green-bg: #193818;
+ --green-border: #32821c;
+}
+
+input {
+ outline: none;
+}
+
+body {
+ background-color: var(--page-bg);
+ color: #fff;
+ margin: 50px auto 20px;
+ width: 1024px;
+ font-family: 'Roboto Condensed', sans-serif;
+ font-weight: normal;
+ font-size: 16px;
+ background-image: url('/assets/bg/green_nebula_4.png');
+ background-blend-mode: soft-light;
+}
+hr {
+ border-color: var(--border);
+ margin-bottom: 1.2rem;
+}
+
+h1, h2, h3 {
+ text-transform: uppercase;
+ font-weight: 300;
+ line-height: 2.2rem;
+ border: solid 0 var(--border);
+ border-left-width: 6px;
+ border-bottom-width: 2px;
+ padding-left: 10px;
+ margin-top: 0;
+ color :#6ac9db;
+ text-shadow: 0 0 2px #6ac9db;
+}
+h1 {
+ font-size: 2.2rem;
+}
+h2 {
+ font-size: 1.7rem;
+}
+h3 {
+ font-size: 1.3rem;
+}
+table {
+ width: 100%;
+ margin-bottom: 2rem;
+ border-spacing: 0;
+ border: solid 1px var(--border);
+}
+tr:nth-child(odd) td, tr:nth-child(odd) th {
+ background-color: #0d2329;
+}
+th, td {
+ padding: 0.5rem;
+}
+p, form, ul, ol {
+ line-height: 1.3rem;
+ margin: 0 2rem 2rem 2rem;
+ flex: inherit;
+}
+
+label {
+ font-weight: bold;
+ margin-right: 5px;
+}
+
+button, .btn {
+ border: solid 1px var(--border);
+ background-color: #183238;
+ color: #fff;
+ font-size: 0.8rem;
+ cursor: pointer;
+ padding: 5px 5px 5px 25px;
+ text-transform: uppercase;
+ text-align: center;
+ font-weight: bold;
+ min-width: 150px;
+}
+button::after, .btn::after {
+ content: '\27EB';
+ float: right;
+ color: var(--border);
+}
+button.danger, .btn.danger {
+ background-color: #381818;
+ border-color: #821c1c;
+}
+button.danger::after, .btn.danger::after {
+ color: #821c1c
+}
+button.success, .btn.success {
+ background-color: var(--green-bg);
+ border-color: var(--green-border);
+}
+button.success::after, .btn.success::after{
+ color: #32821c;
+}
+button:active, .btn:active, button:hover, .btn:hover {
+ background-color: #1a444c;
+}
+button.success:active, .btn.success:active, button.success:hover, .btn.success:hover {
+ background-color: #1e4c1a;
+}
+
+input[type="text"], input[type="password"], input[type="number"] {
+ border: solid 1px var(--border);
+ background-color: #183238;
+ color: #fff;
+ font-size: 0.8rem;
+ cursor: pointer;
+ min-width: 120px;
+ padding: 5px;
+}
+
+a {
+ font-weight: bold;
+ color: #fff;
+ text-decoration: none;
+}
+a::before {
+ content: '\27EA';
+}
+a::after {
+ content: '\27EB';
+}
+footer {
+ text-align: center;
+ border-top: solid 1px var(--border);
+ padding-top: 1rem;
+ margin-top: 2rem;
+}
+
+.nav {
+ padding: 0;
+ margin: 0;
+}
+.nav li {
+ list-style: none;
+}
+.nav li a {
+ display: block;
+ padding: 10px 25px;
+}
+.nav li a:hover {
+ background-color: #0d2329;
+}
+.nav li a::before {
+ content: '';
+}
+.nav li a::after {
+ float: right;
+}
+
+/** Grid Stuff **/
+.row {
+ display: flex;
+}
+.col {
+ flex-grow: 1;
+ flex-basis: 50%;
+ margin-right: 5rem;
+}
+.col:last-child {
+ margin-right: 0;
+}
+.col p {
+ margin-right: 0;
+}
+
+/** Utility **/
+.right {
+ float: right;
+}
+.left {
+ float: left;
+}
+.clear {
+ clear: both;
+}
+.hidden {
+ display: none;
+}
+.text-right {
+ text-align: right;
+}
+.text-left {
+ text-align: left;
+}
+.text-center {
+ text-align: center;
+}
+
+/** CUSTOMIZATIONS **/
+form > div {
+ margin-bottom: 1rem;
+}
+.sidebar {
+ flex-basis: 100px;
+}
+.pane {
+ background-color: var(--page-bg);
+ padding: 1rem;
+ --aug-border-all: 1px;
+ --aug-border-bg: var(--border);
+}
+.nav {
+ margin-bottom: 1rem;
+}
+#main {
+ background-color: var(--page-bg);
+ padding: 1rem;
+ --aug-border-all: 1px;
+ --aug-border-bg: var(--border);
+}
+#corp-ads {
+ margin-top: 1rem;
+}
\ No newline at end of file
});
-server.get<{}, string>('/city', async req => {
- const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
- const city = await cityRepo.FindOne({ owner: account.id });
-
-
- const buildQueues = await cityRepo.getBuildQueues(account.id);
- const unitTrainingQueues = await cityRepo.getUnitTrainingQueues(account.id);
-
- const buildings = await cityRepo.buildingRepository.list();
- const units = await cityRepo.unitRepository.list();
-
- let html = `
- <h2>Kingom Overview</h2>
- ${renderKingomOverview(city, account)}
- <hr>
- <h2>Land Development</h2>
- ${renderLandDevelopment(city, buildings, buildQueues)}
- <h2>Unit Training</h2>
- ${renderUnitTraining(city, units, unitTrainingQueues)},
- <h2>Map</h2>
- ${renderOverworldMap(await cityRepo.FindAll(), city)}
- <h2>Mail</h2>
- ${renderMailroom(await mailRepo.listReceivedMessages(account.id))}
- `;
- return html;
-});
-
server.post<{body: {
soldiers: number,
attackers: number,
return renderKingomOverview(city, account);
});
-server.get<{}, string>('/queue/construction', async req => {
+server.get<{}, string>('/poll/construction', async req => {
const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
const city = await cityRepo.FindOne({ owner: account.id });
const buildings = await cityRepo.buildingRepository.list();
return renderLandDevelopment(city, buildings, buildQueues);
});
-server.get<{}, string>('/queue/units', async req => {
+server.get<{}, string>('/poll/unit-training', async req => {
const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
const city = await cityRepo.FindOne({ owner: account.id });
return renderUnitTraining(city, units, unitTrainingQueues);
});
+server.get<{}, string>('/poll/map', async req => {
+ const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
+ const city = await cityRepo.FindOne({ owner: account.id });
+
+ return renderOverworldMap(await cityRepo.FindAll(), city);
+});
+
+server.get<{}, string>('/poll/mailroom', async req => {
+ const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
+
+ return renderMailroom(await mailRepo.listReceivedMessages(account.id));
+});
+
server.post<{
body: {
rows[y] += '</tr>';
}
let html = `
+ <h2>Map</h2>
<div id="city-offence"></div>
<div id="outgoing-attacks" hx-trigger="reload-outgoing-attacks, every 600s, load" hx-get="/attacks/outgoing"></div>
<table id="overworld-map">
import * as _ from 'lodash';
export function renderKingomOverview(city: City, account: Account): string {
- return `<div hx-trigger="reload-construction-queue from:body, reload-unit-training from:body, every 600s" hx-get="/poll/overview">
+ return `
+ <div hx-trigger="every 600s" hx-get="/poll/overview">
+ <h2 data-augmented-ui="tl-clip bl-clip none">Kingdom Overview</h2>
<table>
<tr>
<th>Lord</th>
<td>${city.bushels.toLocaleString()}</td>
</tr>
</table>
- </div>`;
+ </div>
+ `;
}
\ No newline at end of file
function progressBar(current, max): string {
const percent = Math.ceil((current/max) * 100);
return `
- <div class="progress-bar construction" style="background: background: rgb(0,102,0);
- background: linear-gradient(90deg, rgba(0,102,0,1) 0%, rgba(0,102,0,1) ${percent}%, rgba(0,0,0,1) ${percent}%);">
+ <div class="progress-bar construction" style="background: background: var(--green-bg);
+ background: linear-gradient(90deg, var(--green-bg) 0%, var(--green-bg) ${percent}%, #000 ${percent}%); border-color: var(--green-border); ">
${percent}%
</div>
`;
export function renderLandDevelopment(city: City, buildings: Building[], buildQueues: BuildQueue[]): string {
const freeSpace = city.totalSpace - city.usedSpace;
let html = `
- <div hx-trigger="reload-construction-queue, every 600s" hx-get="/queue/construction">
+ <div hx-trigger="reload-construction-queue, every 600s" hx-get="/poll/construction">
+ <h2 data-augmented-ui="tl-clip bl-clip none">Construction</h2>
<table>
<tr>
<th>Free Land</th>
<td>${city[building.slug]}</td>
<td>
<form hx-post="/build">
- <input type="number" name="amount" hx-post="/cost/construction" hx-trigger="change" hx-target="#${building.slug}-cost">
+ <input type="number" size="6" name="amount" hx-post="/cost/construction" hx-trigger="change" hx-target="#${building.slug}-cost">
<input type="hidden" name="building_type" value="${building.slug}">
<button type="submit">Build</button>
</form>
const queues = `
<hr>
- <h2>Build Queues</h2>
+ <h2 data-augmented-ui="tl-clip bl-clip none">Build Queues</h2>
<table>
<tr>
<th>Building</th>
<th>Amount Expected</th>
<th>Progress</th>
</tr>
- <tr>
${buildQueues.sort((a, b) => {
return a.due - b.due;
}).map(queue => {
export function renderMailroom(mail: MessageWithNames[]): string {
return `
+ <div hx-trigger="every 600s" hx-get="/poll/mailroom">
+ <h2 data-augmented-ui="tl-clip bl-clip none">Mail</h2>
<table>
<tr>
<th>From</th>
}).join("\n")}
</table>
<div id="individual-message"></div>
+ </div>
`;
}
</tr>
</table>
`;
- console.log(msg);
- return '';
}
\ No newline at end of file
function progressBar(current, max): string {
const percent = Math.ceil((current/max) * 100);
return `
- <div class="progress-bar unit-training" style="background: background: rgb(0,102,0);
- background: linear-gradient(90deg, rgba(0,102,0,1) 0%, rgba(0,102,0,1) ${percent}%, rgba(0,0,0,1) ${percent}%);">
+ <div class="progress-bar unit-training" style="background: background: var(--green-bg);
+ background: linear-gradient(90deg, var(--green-bg) 0%, var(--green-bg) ${percent}%, #000 ${percent}%); border-color: var(--green-border);">
${percent}%
</div>
`;
export function renderUnitTraining(city: City, units: Unit[], trainingQueues: UnitTrainingQueue[]): string {
const unit = _.keyBy(units, 'slug');
let html = `
- <div hx-trigger="reload-unit-training, every 600s" hx-get="/queue/units">
+ <div hx-trigger="reload-unit-training, every 600s" hx-get="/poll/unit-training">
+ <h2 data-augmented-ui="tl-clip bl-clip none">Unit Training</h2>
<table>
+ <tr>
+ <th>Unit</th>
+ <th>Avail.</td>
+ <th></th>
+ </tr>
<tr>
<th>Soldiers</th>
- <td>${city.population} Avail.</td>
+ <td>${city.population}</td>
<td>
<form hx-post="/units">
- <input type="number" name="amount" max="${city.population}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.soldiers.slug}-cost">
+ <input type="number" name="amount" size="6" max="${city.population}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.soldiers.slug}-cost">
<input type="hidden" name="type" value="${unit.soldiers.slug}">
- <button type="submit" ${city.population ? '' : 'disabled'}>Train Units</button>
+ <button type="submit" ${city.population ? '' : 'disabled'}>Train</button>
</form>
<span id="${unit.soldiers.slug}-cost"></span>
</td>
</tr>
<tr>
<th>Attackers</th>
- <td>${city.soldiers} Avail.</td>
+ <td>${city.soldiers}</td>
<td>
<form hx-post="/units">
- <input type="number" name="amount" max="${city.soldiers}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.attackers.slug}-cost">
+ <input type="number" name="amount" size="6" max="${city.soldiers}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.attackers.slug}-cost">
<input type="hidden" name="type" value="${unit.attackers.slug}">
- <button type="submit" ${city.soldiers ? '' : 'disabled'}>Train Units</button>
+ <button type="submit" ${city.soldiers ? '' : 'disabled'}>Train</button>
</form>
<span id="${unit.attackers.slug}-cost"></span>
</td>
</tr>
<tr>
<th>Defenders</th>
- <td>${city.soldiers} Avail.</td>
+ <td>${city.soldiers}</td>
<td>
<form hx-post="/units">
- <input type="number" name="amount" max="${city.soldiers}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.defenders.slug}-cost">
+ <input type="number" name="amount" size="6" max="${city.soldiers}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.defenders.slug}-cost">
<input type="hidden" name="type" value="${unit.defenders.slug}">
- <button type="submit" ${city.soldiers ? '' : 'disabled'}>Train Units</button>
+ <button type="submit" ${city.soldiers ? '' : 'disabled'}>Train</button>
</form>
<span id="${unit.defenders.slug}-cost"></span>
</td>
</tr>
<tr>
<th>Special Attackers</th>
- <td>${city.attackers} Avail.</td>
+ <td>${city.attackers}</td>
<td>
<form hx-post="/units">
- <input type="number" name="amount" max="${city.attackers}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.sp_attackers.slug}-cost">
+ <input type="number" name="amount" size="6" max="${city.attackers}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.sp_attackers.slug}-cost">
<input type="hidden" name="type" value="${unit.sp_attackers.slug}">
- <button type="submit" ${city.attackers ? '': 'disabled'}>Train Units</button>
+ <button type="submit" ${city.attackers ? '': 'disabled'}>Train</button>
</form>
<span id="${unit.sp_attackers.slug}-cost"></span>
</td>
</tr>
<tr>
<th>Special Defenders</th>
- <td>${city.defenders} Avail.</td>
+ <td>${city.defenders}</td>
<td>
<form hx-post="/units">
- <input type="number" name="amount" max="${city.defenders}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.sp_defenders.slug}-cost">
+ <input type="number" name="amount" size="6" max="${city.defenders}" hx-trigger="change" hx-post="/cost/training" hx-target="#${unit.sp_defenders.slug}-cost">
<input type="hidden" name="type" value="${unit.sp_defenders.slug}">
- <button type="submit" ${city.defenders ? '': 'disabled'}>Train Units</button>
+ <button type="submit" ${city.defenders ? '': 'disabled'}>Train</button>
</form>
<span id="${unit.sp_defenders.slug}-cost"></span>
</td>
const queues = `
<hr>
- <h2>Training Queues</h2>
+ <h2 data-augmented-ui="tl-clip bl-clip none">Training Queues</h2>
<table>
<tr>
<th>Unit Type</th>
<th>Amount Expected</th>
<th>Progress</th>
</tr>
- <tr>
${trainingQueues.sort((a, b) => {
return a.due - b.due;
}).map(queue => {