summaryrefslogtreecommitdiff
path: root/views
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2021-05-01 00:48:35 +0200
committerTor Andersson <tor@ccxvii.net>2021-05-01 00:48:35 +0200
commit652852e3104ce4020de53231ee7691a4970439d6 (patch)
tree57a053cb2104520e240cd44a4dfb9f92fd5ede07 /views
parent156f7f8546890c6406001061dae199f8320ca83b (diff)
downloadserver-652852e3104ce4020de53231ee7691a4970439d6.tar.gz
Add server and lobby code.
Diffstat (limited to 'views')
-rw-r--r--views/about.ejs83
-rw-r--r--views/banned.ejs3
-rw-r--r--views/change_password.ejs15
-rw-r--r--views/create.ejs22
-rw-r--r--views/error.ejs1
-rw-r--r--views/header.ejs32
-rw-r--r--views/index.ejs19
-rw-r--r--views/info.ejs83
-rw-r--r--views/join.ejs85
-rw-r--r--views/login.ejs15
-rw-r--r--views/profile.ejs84
-rw-r--r--views/signup.ejs18
-rw-r--r--views/users.ejs16
13 files changed, 476 insertions, 0 deletions
diff --git a/views/about.ejs b/views/about.ejs
new file mode 100644
index 0000000..ed1b54d
--- /dev/null
+++ b/views/about.ejs
@@ -0,0 +1,83 @@
+<%- include('header', { title: "Rally the Troops!" }) %>
+<style>li img{height:1.0em;vertical-align:middle}</style>
+
+<p>
+Rally the Troops! is created and maintained by Tor Andersson.
+It is an open source project, and you can find the code on <a href="https://github.com/ccxvii/rally-the-troops">GitHub</a>.
+
+<h2>
+Tips &amp; Tricks
+</h2>
+
+<ul>
+
+<li>
+Open a separate browser tab or window for each side when playing solo.
+
+<li>
+Use the middle mouse button to drag and scroll around the map.
+
+<li>
+The <i>Enter</i> and <i>Escape</i> keys open and close the chat box.
+
+<li>
+To invite your friends to a private game, send them the address of the join page.
+
+<li>
+Chat messages can only be seen by players who are part of a game.
+They are hidden from observers.
+
+<li>
+The <img src="/images/cog.svg"> menu has links to rules, player aids and other reference material.
+In some games you can also choose between alternative graphics and layout options.
+
+<li>
+The <img src="/images/earth-africa-europe.svg"> button hides all counters and markers,
+if you need to check something on the map that is obscured.
+
+<li>
+The <img src="/images/scroll-quill.svg"> button hides the game log and player status displays, so you can
+see more of the map.
+
+<li>
+The <img src="/images/chat-bubble.svg"> button lights up if you have unread chat messages.
+
+</ul>
+
+<h2>
+Licensing
+</h2>
+
+<p>
+All games are used with consent from their respective rights holders.
+
+<p>
+Icons are sourced from <a href="https://game-icons.net">game-icons.net</a>
+by Delapouite, Lorc, and others under the
+<a href="https://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a> license.
+
+<!--
+<h2>
+Other Games
+</h2>
+
+<p>
+Here are some web sites where you can play other historical games:
+
+<dl>
+<dt><a href="https://www.boardgamearena.com/">boardgamearena.com</a>
+<dd>Polis
+<dd>Unconditional Surrender
+<dt><a href="http://civ.rol-play.com/">civ.rol-play.com</a>
+<dd>Advanced Civilization
+<dt><a href="https://paronglans.com/">paronglans.com</a>
+<dd>Julius Caesar
+<dt><a href="http://playfriedrich.com/">playfriedrich.com</a>
+<dd>Friedrich
+<dt><a href="https://www.yucata.de/">yucata.de</a>
+<dd>A Few Acres of Snow
+<dd>Pax Porfiriana
+<dd>Polis
+<dd>Sekigahara
+</dl>
+-->
diff --git a/views/banned.ejs b/views/banned.ejs
new file mode 100644
index 0000000..4c08bc2
--- /dev/null
+++ b/views/banned.ejs
@@ -0,0 +1,3 @@
+<%- include('header', { title: "Banned" }) %>
+<p>
+Sorry, but this IP or account has been banned.
diff --git a/views/change_password.ejs b/views/change_password.ejs
new file mode 100644
index 0000000..ab15a4a
--- /dev/null
+++ b/views/change_password.ejs
@@ -0,0 +1,15 @@
+<%- include('header', { title: "Change password" }) %>
+<form action="/change_password" method="post">
+<p>
+Name: <%= user.name %>
+<p>
+Mail: <%= user.mail %>
+<p>
+<label for="password">Old Password: </label><br>
+<input type="password" id="password" name="password" required>
+<p>
+<label for="newpass">New Password: </label><br>
+<input type="password" id="newpass" name="newpass" required>
+<p>
+<button type="submit">Change password</button>
+</form>
diff --git a/views/create.ejs b/views/create.ejs
new file mode 100644
index 0000000..a23fab5
--- /dev/null
+++ b/views/create.ejs
@@ -0,0 +1,22 @@
+<%- include('header', { title: title.title_name }) %>
+<style>form{display:block;margin-left:200px;}</style>
+<a href="/info/<%= title.title_id %>"><img class="logo" src="/<%= title.title_id %>/cover.jpg"></a>
+<form action="/create/<%= title.title_id %>" method="post">
+<p>
+Scenario:<br>
+<select id="scenario" name="scenario">
+<% scenarios.forEach((scenario) => { %>
+<option value="<%= scenario %>"><%= scenario %></option>
+<% }); %>
+</select>
+<p>
+Description:<br>
+<input type="text" autocomplete="off" id="description" name="description" size="50">
+<p>
+<label>
+<input type="checkbox" id="private" name="private" value="private">
+<span>Private</span>
+</label>
+<p>
+<button type="submit">Create</button>
+</form>
diff --git a/views/error.ejs b/views/error.ejs
new file mode 100644
index 0000000..b7d9632
--- /dev/null
+++ b/views/error.ejs
@@ -0,0 +1 @@
+<%- include('header', { title: "Error" }) %>
diff --git a/views/header.ejs b/views/header.ejs
new file mode 100644
index 0000000..9e101dd
--- /dev/null
+++ b/views/header.ejs
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1">
+<% if (typeof refresh != 'undefined' && refresh > 0) { %><meta http-equiv="refresh" content="<%= refresh %>"><% } %>
+<link rel="icon" href="/images/rally-the-troops.png">
+<link rel="stylesheet" href="/fonts/fonts.css">
+<link rel="stylesheet" href="/style.css">
+<title><%= title %></title>
+</head>
+<body>
+<div class="header">
+<div><a href="/"><img src="/images/rally-the-troops.svg" width="48" height="48"></a></div>
+<div>
+<span><a href="/about">About</a></span>
+<%
+ if (user) {
+ %><span><a href="/profile">Profile (<%= user.name %>)</a></span><%
+ } else {
+ %><span><a href="/signup">Signup</a></span><%
+ %><span><a href="/login">Login</a></span><%
+ }
+%>
+</div>
+</div>
+<div class="main">
+<h1><%= title %></h1>
+<%
+ if (typeof message != 'undefined' && message.length > 0) {
+ %><p class="error"><%= Array.isArray(message) ? message.join("\n") : message %></p><%
+ }
+%>
diff --git a/views/index.ejs b/views/index.ejs
new file mode 100644
index 0000000..a9abeff
--- /dev/null
+++ b/views/index.ejs
@@ -0,0 +1,19 @@
+<%- include('header', { title: "Rally the Troops!" }) %>
+<style>
+.list { display: flex; flex-wrap: wrap; justify-content: left; }
+.list a { margin: 1em; display: block; }
+.list img { box-shadow: 2px 2px 4px 0px rgba(0,0,0,0.5); }
+</style>
+
+<p>
+Rally the Troops! is a website where you can play historic games with other
+players.
+
+<p>
+Registration and use is free, and there are no ads.
+
+<div class="list">
+</div>
+
+<p>
+Join the <a href="https://discord.gg/CBrTh8k84A">Discord</a> server to find players or report bugs.
diff --git a/views/info.ejs b/views/info.ejs
new file mode 100644
index 0000000..27cb543
--- /dev/null
+++ b/views/info.ejs
@@ -0,0 +1,83 @@
+<%- include('header', { title: title.title_name, refresh: (user ? 300 : 0) }) %>
+<img class="logo" src="/<%= title.title_id %>/cover.jpg">
+<%- include('../public/' + title.title_id + '/about.html') %>
+<br clear=left>
+<p>
+Read more about the game on
+<a href="https://boardgamegeek.com/boardgame/<%= title.bgg %>">boardgamegeek.com</a>.
+
+<% if (user) { %>
+
+<h2>Open Games</h2>
+<table class="wide">
+<tr><th>ID<th>Scenario<th>Owner<th>Description<th>Created<th>
+<% if (open_games.length > 0) { %>
+<% open_games.forEach((row) => { %>
+<tr>
+<td><%= row.game_id %>
+<td><%= row.scenario %>
+<td><%= row.owner_name %>
+<td><%= row.description %>
+<td class="nowrap"><%= row.ctime %>
+<td><a href="/join/<%= row.game_id %>">Join</a>
+<% }); } else { %>
+<tr><td colspan="6">No open games.
+<% } %>
+</table>
+
+<p>
+<a href="/create/<%= title.title_id %>">Create a new game</a>.
+
+<% if (active_games.length > 0) { %>
+<h2>Active Games</h2>
+<table class="wide">
+<tr><th>ID<th>Scenario<th>Players<th>Description<th>Changed<th>Turn<th>
+<% active_games.forEach((row) => { %>
+<tr>
+<td><%= row.game_id %>
+<td><%= row.scenario %>
+<td><%= row.players.join(", ") %>
+<td><%= row.description %>
+<td class="nowrap"><%= row.mtime %>
+<%
+ if (row.your_turn) {
+ %><td class="your_turn"><%= row.active %><%
+ } else {
+ %><td><%= row.active %><%
+ }
+ let me = row.players.reduce((n,p) => n + (p === user.name ? 1 : 0), 0);
+ if (me == 1) {
+ %><td><a href="/play/<%= row.game_id %>">Play</a><%
+ } else if (me > 1) {
+ %><td><a href="/join/<%= row.game_id %>">Play</a><%
+ } else {
+ %><td><a href="/join/<%= row.game_id %>">View</a><%
+ }
+%>
+<% }); %>
+</table>
+<% } %>
+
+<% if (finished_games.length > 0) { %>
+<h2>Finished Games</h2>
+<table class="wide">
+<tr><th>ID<th>Scenario<th>Players<th>Description<th>Finished<th>Result<th>
+<% finished_games.forEach((row) => { %>
+<tr>
+<td><%= row.game_id %>
+<td><%= row.scenario %>
+<td><%= row.players.join(", ") %>
+<td><%= row.description %>
+<td class="nowrap"><%= row.mtime %>
+<td><%= row.result %>
+<td><a href="/join/<%= row.game_id %>">View</a>
+<% }); %>
+</table>
+<% } %>
+
+<% } else { %>
+
+<p>
+Login to create or join a game.
+
+<% } %>
diff --git a/views/join.ejs b/views/join.ejs
new file mode 100644
index 0000000..810e0f4
--- /dev/null
+++ b/views/join.ejs
@@ -0,0 +1,85 @@
+<%- include('header', { title: game.title_name, refresh: game.status == 0 ? 15 : 0 }) %>
+<script>
+function confirm_delete(status) {
+ let warning = "Are you sure you want to DELETE this game?";
+ if (window.confirm(warning))
+ window.open("/delete/<%= game.game_id %>");
+}
+</script>
+
+<a href="/info/<%= game.title_id %>"><img class="logo" src="/<%= game.title_id %>/cover.jpg"></a>
+
+<p>
+Owner: <%= game.owner_name %>
+<p>
+Scenario: <%= game.scenario %>
+<p>
+Description: <%= game.description || "No description." %>
+
+<br clear=left>
+
+<p>
+<table>
+<tr>
+<%
+ roles.forEach((role) => {
+ %><th><%= role.role %><%
+ });
+%>
+<tr>
+<%
+ roles.forEach((role) => {
+ if (game.active == role.role || game.active == "Both" || game.active == "All") {
+ %><td style="min-width:9em" class="your_turn"><%
+ } else {
+ %><td style="min-width:9em"><%
+ }
+ let p = players.find(p => p.role == role.role);
+ if (game.status == 0) {
+ if (p) {
+ if ((p.user_id == user.user_id) || (game.owner_id == user.user_id)) {
+ %><a style="color:red;text-decoration:none" href="/part/<%= game.game_id %>/<%= p.user_id %>/<%= p.role %>">&#x274c;</a> <%
+ %><%= p.name %><%
+ } else {
+ %><%= p.name %><%
+ }
+ } else {
+ %><a href="/join/<%= game.game_id %>/<%= role.role %>">Join</a><%
+ }
+ } else {
+ if (p) {
+ if (p.user_id == user.user_id) {
+ %><a href="/play/<%= game.game_id %>/<%= p.role %>">Play</a><%
+ } else {
+ %><%= p.name %><%
+ }
+ } else {
+ %><i>Empty</i><%
+ }
+ }
+ });
+ if (game.status > 0 && !players.some(p => p.user_id == user.user_id)) {
+ %>
+ <tr><td colspan="<%= roles.length %>"><a href="/play/<%= game.game_id %>/Observer">View</a>
+ <%
+ }
+%>
+</table>
+
+<p>
+<%
+ if (game.status == 0) {
+ if (players.length == roles.length) {
+ if (game.owner_id == user.user_id) {
+ %><form action="/start/<%= game.game_id %>"><button type="submit">Start!</button></form><%
+ } else {
+ %>Waiting for <%= game.owner_name %> to start the game.<%
+ }
+ } else {
+ %>Waiting for players to join the game.<%
+ }
+ }
+ if (game.owner_id == user.user_id && (game.status == 0 || solo)) {
+ %><p><br><button onclick="confirm_delete()">Delete</button><%
+ }
+%>
diff --git a/views/login.ejs b/views/login.ejs
new file mode 100644
index 0000000..a5e2546
--- /dev/null
+++ b/views/login.ejs
@@ -0,0 +1,15 @@
+<%- include('header', { title: "Login" }) %>
+<% if (user) { %>
+<p>You're already logged in!
+<% } else { %>
+<form action="/login" method="post">
+<p>
+<label for="username">Name: </label><br>
+<input type="text" id="username" name="username" required>
+<p>
+<label for="password">Password: </label><br>
+<input type="password" id="password" name="password" required>
+<p>
+<button type="submit">Login</button>
+</form>
+<% } %>
diff --git a/views/profile.ejs b/views/profile.ejs
new file mode 100644
index 0000000..60f1ff9
--- /dev/null
+++ b/views/profile.ejs
@@ -0,0 +1,84 @@
+<%- include('header', { title: "Rally the Troops!", refresh: (active_games.length > 0 ? 300 : 0) }) %>
+<style>td.nowrap a { color: black; text-decoration: none; }</style>
+
+<img class="logo" src="<%= avatar %>" width="80" height="80">
+<p>
+Welcome, <%= user.name %>!
+<p>
+Your mail address is <%= user.mail %>.
+
+<br clear=left>
+
+<p>
+<a href="/change_password">Change password</a>
+
+<p>
+<a href="/logout">Logout</a>
+
+<% if (open_games.length > 0) { %>
+<h2>Open Games</h2>
+<table class="wide">
+<tr><th>ID<th>Game<th>Scenario<th>Players<th>Description<th>Created<th>
+<% open_games.forEach((row) => { %>
+<tr>
+<td><%= row.game_id %>
+<td class="nowrap"><a href="/info/<%= row.title_id %>"><%= row.title_name %></a>
+<td><%= row.scenario %>
+<td><%= row.players.join(", ") %>
+<td><%= row.description %>
+<td class="nowrap"><%= row.ctime %>
+<td><a href="/join/<%= row.game_id %>">Join</a>
+<% }); %>
+</table>
+<% } %>
+
+<% if (active_games.length > 0) { %>
+<h2>Active Games</h2>
+<table class="wide">
+<tr><th>ID<th>Game<th>Scenario<th>Players<th>Description<th>Changed<th>Turn<th>
+<% active_games.forEach((row) => { %>
+<tr>
+<td><%= row.game_id %>
+<td class="nowrap"><a href="/info/<%= row.title_id %>"><%= row.title_name %></a>
+<td><%= row.scenario %>
+<td><%= row.players.join(", ") %>
+<td><%= row.description %>
+<td class="nowrap"><%= row.mtime %>
+<%
+ if (row.your_turn) {
+ %><td class="your_turn"><%= row.active %><%
+ } else {
+ %><td><%= row.active %><%
+ }
+ if (row.players.reduce((n,p) => n + (p === user.name ? 1 : 0), 0) == 1) {
+ %><td><a href="/play/<%= row.game_id %>">Play</a><%
+ } else {
+ %><td><a href="/join/<%= row.game_id %>">Play</a><%
+ }
+%>
+<% }); %>
+</table>
+<% } %>
+
+<% if (finished_games.length > 0) { %>
+<h2>Finished Games</h2>
+<table class="wide">
+<tr><th>ID<th>Game<th>Scenario<th>Players<th>Description<th>Finished<th>Result<th>
+<% finished_games.forEach((row) => { %>
+<tr>
+<td><%= row.game_id %>
+<td class="nowrap"><a href="/info/<%= row.title_id %>"><%= row.title_name %></a>
+<td><%= row.scenario %>
+<td><%= row.players.join(", ") %>
+<td><%= row.description %>
+<td class="nowrap"><%= row.mtime %>
+<td><%= row.result %>
+<td><a href="/join/<%= row.game_id %>">View</a>
+<% }); %>
+</table>
+<% } %>
+
+<% if (open_games.length == 0 && active_games.length == 0 && finished_games.length == 0) { %>
+<p>
+You don't have any current or finished games.
+<% } %>
diff --git a/views/signup.ejs b/views/signup.ejs
new file mode 100644
index 0000000..819da3f
--- /dev/null
+++ b/views/signup.ejs
@@ -0,0 +1,18 @@
+<%- include('header', { title: "Signup" }) %>
+<% if (user) { %>
+<p>You're already logged in!
+<% } else { %>
+<form action="/signup" method="post">
+<p>
+<label for="username">Name: </label><br>
+<input type="text" id="username" name="username" required>
+<p>
+<label for="mail">Mail: </label><br>
+<input type="text" id="mail" name="mail" required>
+<p>
+<label for="password">Password: </label><br>
+<input type="password" id="password" name="password" required>
+<p>
+<button type="submit">Create Account</button>
+</form>
+<% } %>
diff --git a/views/users.ejs b/views/users.ejs
new file mode 100644
index 0000000..a8f5c7d
--- /dev/null
+++ b/views/users.ejs
@@ -0,0 +1,16 @@
+<%- include('header', { title: "User list" }) %>
+<style>
+td.avatar{padding:0;width:80px;}
+td.avatar img{display:block;width:80px;height:80px;}
+</style>
+
+<table class="wide">
+<tr><th>Avatar<th>Name<th>Member since<th>Last seen
+
+<% userList.forEach((row) => { %>
+<tr>
+<td class="avatar"><img src="<%= row.avatar %>">
+<td><%= row.name %>
+<td><%= row.ctime %>
+<td><%= row.atime %>
+<% }); %>