completely restructured and refactored the front end.
83
web/assets/css/creatures.css
Normal file
@ -0,0 +1,83 @@
|
||||
|
||||
#resultSection {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
background-color: #abf1f1;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.house-note {
|
||||
background-color: rgba(246, 246, 167, 0.93);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 1fr 0.9fr;
|
||||
grid-template-rows: 1.2fr 0.5fr 1.3fr;
|
||||
gap: 0 0;
|
||||
grid-auto-flow: row;
|
||||
}
|
||||
|
||||
.container.android {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
/* Container for each type (thinker, worker, warrior) */
|
||||
.typeContainer {
|
||||
display: flex; /* Use flexbox for layout */
|
||||
justify-content: space-between; /* Space out the child elements evenly */
|
||||
margin-bottom: 20px; /* Add some bottom margin for separation between types */
|
||||
}
|
||||
|
||||
/* Profile and abilities within each type */
|
||||
.creature_id,
|
||||
.creature_profile,
|
||||
.creature_abilities {
|
||||
width: 45%; /* Give them each about half the container's width */
|
||||
height: 345px;
|
||||
}
|
||||
|
||||
.creature_attacks {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.creature_mutations {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.creature_description {
|
||||
width: 90%;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.creature_id {
|
||||
grid-area: 1 / 1 / 2 / 2;
|
||||
}
|
||||
|
||||
.creature_profile {
|
||||
grid-area: 1 / 2 / 2 / 3;
|
||||
}
|
||||
|
||||
.creature_abilities {
|
||||
grid-area: 1 / 3 / 2 / 4;
|
||||
}
|
||||
|
||||
.creature_attacks {
|
||||
grid-area: 2 / 1 / 3 / 4;
|
||||
}
|
||||
|
||||
.creature_mutations {
|
||||
grid-area: 2 / 2 / 3 / 2;
|
||||
}
|
||||
|
||||
.creature_description {
|
||||
grid-area: 3 / 1 / 4 / 4;
|
||||
}
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 412 KiB After Width: | Height: | Size: 412 KiB |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 434 KiB After Width: | Height: | Size: 434 KiB |
Before Width: | Height: | Size: 382 KiB After Width: | Height: | Size: 382 KiB |
Before Width: | Height: | Size: 440 KiB After Width: | Height: | Size: 440 KiB |
Before Width: | Height: | Size: 566 KiB After Width: | Height: | Size: 566 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 4.8 MiB After Width: | Height: | Size: 4.8 MiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 200 KiB After Width: | Height: | Size: 200 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 636 KiB After Width: | Height: | Size: 636 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 8.8 MiB After Width: | Height: | Size: 8.8 MiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
47
web/assets/js/androidResultTable.js
Normal file
@ -0,0 +1,47 @@
|
||||
import {createProfileSection} from "./createProfileSection.js";
|
||||
import {createAbilitiesSection} from "./createAbilitiesSection.js";
|
||||
import {createDescriptionSection} from "./createDescriptionSection.js";
|
||||
|
||||
export function androidResultTable(data, creatureName) {
|
||||
let container = document.createElement('div');
|
||||
container.className = 'container android';
|
||||
|
||||
let creatureTitle = document.createElement('h2');
|
||||
creatureTitle.textContent = creatureName.toUpperCase();
|
||||
container.appendChild(creatureTitle)
|
||||
|
||||
let image = document.createElement('img');
|
||||
image.src = '/assets/img/android.jpg';
|
||||
image.onerror = () => { image.src = '/assets/img/404.jpg' };
|
||||
image.style.width = '275px';
|
||||
image.style.height = '275px';
|
||||
container.appendChild(image);
|
||||
|
||||
let description = createDescriptionSection(data);
|
||||
container.appendChild(description);
|
||||
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
|
||||
if (key === 'thinker' || key === 'worker' || key === 'warrior') {
|
||||
let typeTitle = document.createElement('h2');
|
||||
typeTitle.textContent = key.toUpperCase();
|
||||
container.appendChild(typeTitle);
|
||||
|
||||
// Create a div for each type (thinker, worker, or warrior)
|
||||
let typeContainer = document.createElement('div');
|
||||
typeContainer.className = 'typeContainer'; // we'll use this in css
|
||||
|
||||
let profile = createProfileSection(value);
|
||||
let abilities = createAbilitiesSection(value);
|
||||
|
||||
// Append profile and abilities to the typeContainer, instead of the main container
|
||||
typeContainer.appendChild(profile);
|
||||
typeContainer.appendChild(abilities);
|
||||
|
||||
// Finally, append the typeContainer to the main container.
|
||||
container.appendChild(typeContainer);
|
||||
}
|
||||
});
|
||||
|
||||
resultSection.appendChild(container);
|
||||
}
|
44
web/assets/js/bestiary.js
Normal file
@ -0,0 +1,44 @@
|
||||
import {loadingGif} from "./loading.js";
|
||||
import {androidResultTable} from "./androidResultTable.js";
|
||||
import {setCreatureTable} from "./setCreatureTable.js";
|
||||
|
||||
|
||||
document.getElementById('searchForm').addEventListener('submit', function (event) {
|
||||
event.preventDefault();
|
||||
const creatureName = document.getElementById('creature').value;
|
||||
const queryParams = new URLSearchParams({
|
||||
creature: creatureName,
|
||||
});
|
||||
|
||||
// Insert the loading.gif before making the fetch request
|
||||
let resultSection = document.getElementById('resultSection');
|
||||
let imgElement = loadingGif(resultSection);
|
||||
|
||||
setTimeout(() => {
|
||||
fetch(`${window.BASE_URL}/rules/creature?${queryParams}`)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw response;
|
||||
}
|
||||
return response.json(); // we only get here if there is no error
|
||||
})
|
||||
.then((json) => {
|
||||
if (json === null) { // if json is null update the image and stop execution
|
||||
imgElement.src = '/assets/img/404.jpg';
|
||||
throw new Error('No data found'); // this will stop the execution and go to the catch block
|
||||
}
|
||||
resultSection.innerHTML = ''; // Clear previous content
|
||||
if (creatureName === 'android') {
|
||||
androidResultTable(json, creatureName);
|
||||
} else {
|
||||
setCreatureTable(json, creatureName);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if (!resultSection.hasChildNodes()) { // Only update the image if it wasn't already updated
|
||||
imgElement.src = '/assets/img/404.jpg';
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
});
|
@ -4,5 +4,10 @@ if (window.location.hostname === 'localhost' || window.location.hostname === '12
|
||||
} else {
|
||||
window.BASE_URL = 'https://gammaworld.gmgauthier.com';
|
||||
}
|
||||
window.IMG = "/assets/img";
|
||||
window.CSS = "/assets/css";
|
||||
window.JS = "/assets/js";
|
||||
window.DOC = "/pages";
|
||||
|
||||
console.log(window.location.hostname)
|
||||
console.log(window.BASE_URL)
|
19
web/assets/js/createAbilitiesSection.js
Normal file
@ -0,0 +1,19 @@
|
||||
import {formatDiceMnemonic} from "./formatDiceMnemonic.js";
|
||||
|
||||
export function createAbilitiesSection(data) {
|
||||
// Abilities section
|
||||
let abilities = document.createElement('div');
|
||||
abilities.className = 'creature_abilities';
|
||||
abilities.innerHTML = `
|
||||
<h3>Abilities</h3>
|
||||
<table>
|
||||
<tr><td><b>MS</b></td> <td>${formatDiceMnemonic(data.ms)}</td></tr>
|
||||
<tr><td><b>IN</b></td> <td>${formatDiceMnemonic(data.in)}</td></tr>
|
||||
<tr><td><b>DX</b></td> <td>${formatDiceMnemonic(data.dx)}</td></tr>
|
||||
<tr><td><b>CH</b></td> <td>${formatDiceMnemonic(data.ch)}</td></tr>
|
||||
<tr><td><b>CN</b></td> <td>${formatDiceMnemonic(data.cn)}</td></tr>
|
||||
<tr><td><b>PS</b></td> <td>${formatDiceMnemonic(data.ps)}</td></tr>
|
||||
</table>
|
||||
`;
|
||||
return abilities;
|
||||
}
|
20
web/assets/js/createAttacksSection.js
Normal file
@ -0,0 +1,20 @@
|
||||
import {formatDiceMnemonic} from "./formatDiceMnemonic.js";
|
||||
|
||||
export function createAttacksSection(data) {
|
||||
// Attacks section
|
||||
let attacks = document.createElement('div');
|
||||
attacks.className = 'creature_attacks';
|
||||
let attacksHTML = '<h3>Attacks</h3>';
|
||||
let attacksTable = '<table>';
|
||||
for (let attack in data.attacks) {
|
||||
let mnemonic_display;
|
||||
mnemonic_display = formatDiceMnemonic(data.attacks[attack]);
|
||||
if (mnemonic_display === "0d0") {
|
||||
mnemonic_display = 'See Description';
|
||||
}
|
||||
attacksTable += `<tr><td><b>${attack}</b></td><td>${mnemonic_display}</td></tr>`
|
||||
}
|
||||
attacksTable += '</table>';
|
||||
attacks.innerHTML = attacksHTML + attacksTable;
|
||||
return attacks
|
||||
}
|
11
web/assets/js/createDescriptionSection.js
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
export function createDescriptionSection(data) {
|
||||
// Description section
|
||||
let description = document.createElement('div');
|
||||
description.className = 'creature_description';
|
||||
description.innerHTML = `
|
||||
<h3>Description</h3>
|
||||
${data.description}
|
||||
`;
|
||||
return description;
|
||||
}
|
10
web/assets/js/createMutationsSection.js
Normal file
@ -0,0 +1,10 @@
|
||||
export function createMutationsSection(mutarray) {
|
||||
// Mutations section
|
||||
let mutations = document.createElement('div');
|
||||
mutations.className = 'creature_mutations';
|
||||
let mutationsHTML = '<h3>Mutations</h3>';
|
||||
let mutationsList = mutarray.reduce((result, mutation) => result + `<li>${mutation}</li>`, '');
|
||||
mutationsHTML += `<ul>${mutationsList}</ul>`;
|
||||
mutations.innerHTML = mutationsHTML
|
||||
return mutations
|
||||
}
|
29
web/assets/js/createProfileSection.js
Normal file
@ -0,0 +1,29 @@
|
||||
import {formatDiceMnemonic} from "./formatDiceMnemonic.js";
|
||||
import {formatSpeedMnemonic} from "./formatSpeedMnemonic.js";
|
||||
|
||||
export function createProfileSection(data) {
|
||||
let profile = document.createElement('div');
|
||||
profile.className = 'creature_profile';
|
||||
let profileHTML = `
|
||||
<h3>Profile</h3>
|
||||
<table>
|
||||
<tr><td><b>NUMBER</b></td> <td>${formatDiceMnemonic(data.number)}</td></tr>
|
||||
<tr><td><b>MORALE</b></td> <td>${formatDiceMnemonic(data.morale)}</td></tr>
|
||||
<tr><td><b>HIT DICE</b></td> <td>${formatDiceMnemonic(data['hit dice'])}</td></tr>
|
||||
<tr><td><b>ARMOUR</b></td> <td>${data.armour}</td></tr>
|
||||
<tr><td><b>ENVIRON</b></td> <td>${data.environ.join(', ')}</td></tr>
|
||||
`;
|
||||
if(data['land speed']){
|
||||
profileHTML += `<tr><td><b>LAND SPEED</b></td> <td>${formatSpeedMnemonic(data['land speed'])}</td></tr>`;
|
||||
}
|
||||
if(data['water speed']){
|
||||
profileHTML += `<tr><td><b>WATER SPEED</b></td> <td>${formatSpeedMnemonic(data['water speed'])}</td></tr>`;
|
||||
}
|
||||
if(data['air speed']){
|
||||
profileHTML += `<tr><td><b>AIR SPEED</b></td> <td>${formatSpeedMnemonic(data['air speed'])}</td></tr>`;
|
||||
}
|
||||
profileHTML += '</table>';
|
||||
|
||||
profile.innerHTML = profileHTML;
|
||||
return profile;
|
||||
}
|
48
web/assets/js/encounter.js
Normal file
@ -0,0 +1,48 @@
|
||||
import {loadingGif, setResultImage} from "./loading.js";
|
||||
|
||||
import {setCreatureTable} from "./setCreatureTable.js";
|
||||
import {androidResultTable} from "./androidResultTable.js";
|
||||
|
||||
document.getElementById('terrainForm').addEventListener('submit', function (event) {
|
||||
event.preventDefault();
|
||||
const terrain = document.getElementById('terrainType').value;
|
||||
const queryParams = new URLSearchParams({
|
||||
terrain: terrain,
|
||||
});
|
||||
|
||||
// Insert the loading.gif before making the fetch request
|
||||
let resultSection = document.getElementById('resultSection');
|
||||
let imgElement = loadingGif(resultSection);
|
||||
|
||||
setTimeout(() => {
|
||||
fetch(`${window.BASE_URL}/gameplay/encounter?${queryParams}`)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw response;
|
||||
}
|
||||
return response.json(); // we only get here if there is no error
|
||||
})
|
||||
.then((json) => {
|
||||
resultSection.innerHTML = ''; // Clear previous content
|
||||
if (Object.keys(json).length === 1) { // Short version of response
|
||||
let name = json.name !== null ? json.name : 'No Encounter';
|
||||
setResultImage(name);
|
||||
} else { // Long version of the response
|
||||
if (json.name === 'android') {
|
||||
androidResultTable(json, json.name);
|
||||
} else {
|
||||
setCreatureTable(json, json.name);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
err.text().then(() => {
|
||||
imgElement.src = '/assets/img/404.jpg';
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}, 1000);
|
||||
});
|
5
web/assets/js/formatDiceMnemonic.js
Normal file
@ -0,0 +1,5 @@
|
||||
export function formatDiceMnemonic(data) {
|
||||
return Array.isArray(data) ?
|
||||
data[0] + "d" + data[1] + (data[2] === 0 ? "" : (data[2] > 0 ? "+" : "") + data[2]) :
|
||||
data.toString();
|
||||
}
|
9
web/assets/js/formatSpeedMnemonic.js
Normal file
@ -0,0 +1,9 @@
|
||||
export function formatSpeedMnemonic(data){
|
||||
if (data[0] === 0 && data[1] === 0 && data[2] === 0){
|
||||
return "Immobile";
|
||||
}
|
||||
if (data[0] === 1 && data[1] === 1 && data[2] === 1) {
|
||||
return "See Description"
|
||||
}
|
||||
return data[0] + '/' + data[1] + '/' + data[2];
|
||||
}
|
@ -3,9 +3,9 @@ export function loadingGif(resultSection) {
|
||||
// Insert the loading.gif before making the fetch request
|
||||
resultSection.innerHTML = '';
|
||||
let imgElement = document.createElement('img');
|
||||
imgElement.src = 'img/checking.gif';
|
||||
imgElement.style.width = '500px';
|
||||
imgElement.style.height = '500px';
|
||||
imgElement.src = `/assets/img/checking.gif`;
|
||||
imgElement.style.width = '275px';
|
||||
imgElement.style.height = '275px';
|
||||
imgElement.style.display = 'block';
|
||||
imgElement.style.marginLeft = 'auto';
|
||||
imgElement.style.marginRight = 'auto';
|
||||
@ -15,10 +15,10 @@ export function loadingGif(resultSection) {
|
||||
export function setResultImage(document, name) {
|
||||
let imgElement = document.createElement('img');
|
||||
let resultSection = document.getElementById('resultSection');
|
||||
imgElement.src = 'img/' + name + ".jpg";
|
||||
imgElement.src = `/assets/img/` + name + ".jpg";
|
||||
console.log(imgElement.src);
|
||||
imgElement.onerror = function () {
|
||||
this.src = 'img/404.jpg';
|
||||
this.src = `/assets/img/404.jpg`;
|
||||
};
|
||||
resultSection.appendChild(imgElement);
|
||||
}
|
33
web/assets/js/setCreatureTable.js
Normal file
@ -0,0 +1,33 @@
|
||||
import {createProfileSection} from "./createProfileSection.js";
|
||||
import {createAbilitiesSection} from "./createAbilitiesSection.js";
|
||||
import {createAttacksSection} from "./createAttacksSection.js";
|
||||
import {createMutationsSection} from "./createMutationsSection.js";
|
||||
import {createDescriptionSection} from "./createDescriptionSection.js";
|
||||
|
||||
export function setCreatureTable(data, creatureName) {
|
||||
let container = document.createElement('div');
|
||||
container.className = 'container';
|
||||
|
||||
// Creature ID section
|
||||
let creatureId = document.createElement('div');
|
||||
creatureId.className = 'creature_id';
|
||||
creatureId.innerHTML = `
|
||||
<h2>${creatureName.toUpperCase()}</h2>
|
||||
<img src='/assets/img/${creatureName}.jpg' onerror='this.src="/assets/img/404.jpg";' style='width: 275px; height: 275px;'>
|
||||
`;
|
||||
|
||||
let profile = createProfileSection(data);
|
||||
let abilities = createAbilitiesSection(data);
|
||||
let attacks = createAttacksSection(data);
|
||||
let mutations = createMutationsSection(data.mutations); // Notice the data difference
|
||||
let description = createDescriptionSection(data);
|
||||
|
||||
container.appendChild(creatureId);
|
||||
container.appendChild(profile);
|
||||
container.appendChild(abilities);
|
||||
container.appendChild(attacks);
|
||||
container.appendChild(mutations);
|
||||
container.appendChild(description);
|
||||
|
||||
resultSection.appendChild(container);
|
||||
}
|
11
web/assets/js/setResultImage.js
Normal file
@ -0,0 +1,11 @@
|
||||
export function setResultImage(name) {
|
||||
let imgElement = document.createElement('img');
|
||||
imgElement.src = '/assets/img/' + name + ".jpg";
|
||||
console.log(imgElement.src);
|
||||
imgElement.onerror = function () {
|
||||
this.src = '/assets/img/404.jpg';
|
||||
};
|
||||
resultSection.appendChild(imgElement);
|
||||
return imgElement;
|
||||
}
|
||||
|
@ -1,98 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" type="text/css" href="styles.css">
|
||||
<style>
|
||||
#resultSection {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
background-color: #abf1f1;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.house-note{
|
||||
background-color: rgba(246, 246, 167, 0.93);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 1fr 0.9fr;
|
||||
grid-template-rows: 1.2fr 0.5fr 1.3fr;
|
||||
gap: 0px 0px;
|
||||
grid-auto-flow: row;
|
||||
}
|
||||
|
||||
.container.android {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
/* Container for each type (thinker, worker, warrior) */
|
||||
.typeContainer {
|
||||
display: flex; /* Use flexbox for layout */
|
||||
justify-content: space-between; /* Space out the child elements evenly */
|
||||
margin-bottom: 20px; /* Add some bottom margin for separation between types */
|
||||
}
|
||||
|
||||
/* Profile and abilities within each type */
|
||||
.creature_id,
|
||||
.creature_profile,
|
||||
.creature_abilities {
|
||||
width: 45%; /* Give them each about half the container's width */
|
||||
height: 345px;
|
||||
}
|
||||
|
||||
.creature_attacks {
|
||||
width: 40%;
|
||||
}
|
||||
.creature_mutations {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.creature_description {
|
||||
width: 90%;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.creature_id { grid-area: 1 / 1 / 2 / 2; }
|
||||
.creature_profile { grid-area: 1 / 2 / 2 / 3; }
|
||||
.creature_abilities { grid-area: 1 / 3 / 2 / 4; }
|
||||
.creature_attacks { grid-area: 2 / 1 / 3 / 4; }
|
||||
.creature_mutations { grid-area: 2 / 2 / 3 / 2; }
|
||||
.creature_description { grid-area: 3 / 1 / 4 / 4; }
|
||||
</style>
|
||||
<title>Gamma World Bestiary</title>
|
||||
<script src="config.js"></script>
|
||||
<script type="module" src="bestiary.js" defer></script>
|
||||
<script type="module" src="catalog.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Look Up A Creature</h2>
|
||||
|
||||
<form id="searchForm">
|
||||
<div class="form-row">
|
||||
<div class="form-field">
|
||||
<label for="creature">Beast</label>
|
||||
<input type="string" name="creature" id="creature">
|
||||
<button id="search-button" type="submit">Search</button>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label for="catalog-button">Retrieve Catalog</label>
|
||||
<button id="catalog-button" type="button">Catalog</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
<div id="resultSection"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
226
web/bestiary.js
@ -1,226 +0,0 @@
|
||||
import {loadingGif} from "./loading.js";
|
||||
|
||||
document.getElementById('searchForm').addEventListener('submit', function (event) {
|
||||
event.preventDefault();
|
||||
const creatureName = document.getElementById('creature').value;
|
||||
const queryParams = new URLSearchParams({
|
||||
creature: creatureName,
|
||||
});
|
||||
|
||||
// Insert the loading.gif before making the fetch request
|
||||
let resultSection = document.getElementById('resultSection');
|
||||
let imgElement = loadingGif(resultSection);
|
||||
|
||||
setTimeout(() => {
|
||||
fetch(`${window.BASE_URL}/rules/creature?${queryParams}`)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw response;
|
||||
}
|
||||
return response.json(); // we only get here if there is no error
|
||||
})
|
||||
.then((json) => {
|
||||
if (json === null) { // if json is null update the image and stop execution
|
||||
imgElement.src = 'img/404.jpg';
|
||||
throw new Error('No data found'); // this will stop the execution and go to the catch block
|
||||
}
|
||||
resultSection.innerHTML = ''; // Clear previous content
|
||||
if (creatureName === 'android') {
|
||||
androidResultTable(json);
|
||||
} else {
|
||||
setCreatureTable(json);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if(!resultSection.hasChildNodes()) { // Only update the image if it wasn't already updated
|
||||
imgElement.src = 'img/404.jpg';
|
||||
}
|
||||
});
|
||||
|
||||
function setResultImage(name) {
|
||||
let imgElement = document.createElement('img');
|
||||
imgElement.src = 'img/' + name + ".jpg";
|
||||
console.log(imgElement.src);
|
||||
imgElement.onerror = function () {
|
||||
this.src = 'img/404.jpg';
|
||||
};
|
||||
resultSection.appendChild(imgElement);
|
||||
}
|
||||
|
||||
function formatDiceMnemonic(data) {
|
||||
return Array.isArray(data) ?
|
||||
data[0] + "d" + data[1] + (data[2] === 0 ? "" : (data[2] > 0 ? "+" : "") + data[2]) :
|
||||
data.toString();
|
||||
}
|
||||
|
||||
function formatSpeedMnemonic(data){
|
||||
if (data[0] === 0 && data[1] === 0 && data[2] === 0){
|
||||
return "Immobile";
|
||||
}
|
||||
if (data[0] === 1 && data[1] === 1 && data[2] === 1) {
|
||||
return "See Description"
|
||||
}
|
||||
return data[0] + '/' + data[1] + '/' + data[2];
|
||||
}
|
||||
|
||||
function androidResultTable(data) {
|
||||
let container = document.createElement('div');
|
||||
container.className = 'container android';
|
||||
|
||||
let creatureTitle = document.createElement('h2');
|
||||
creatureTitle.textContent = creatureName.toUpperCase();
|
||||
container.appendChild(creatureTitle)
|
||||
|
||||
let image = document.createElement('img');
|
||||
image.src = 'img/android.jpg';
|
||||
image.onerror = () => { image.src = 'img/404.jpg' };
|
||||
image.style.width = '275px';
|
||||
image.style.height = '275px';
|
||||
container.appendChild(image);
|
||||
|
||||
let description = createDescriptionSection(data);
|
||||
container.appendChild(description);
|
||||
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
|
||||
if (key === 'thinker' || key === 'worker' || key === 'warrior') {
|
||||
let typeTitle = document.createElement('h2');
|
||||
typeTitle.textContent = key.toUpperCase();
|
||||
container.appendChild(typeTitle);
|
||||
|
||||
// Create a div for each type (thinker, worker, or warrior)
|
||||
let typeContainer = document.createElement('div');
|
||||
typeContainer.className = 'typeContainer'; // we'll use this in css
|
||||
|
||||
let profile = createProfileSection(value);
|
||||
let abilities = createAbilitiesSection(value);
|
||||
|
||||
// Append profile and abilities to the typeContainer, instead of the main container
|
||||
typeContainer.appendChild(profile);
|
||||
typeContainer.appendChild(abilities);
|
||||
|
||||
// Finally, append the typeContainer to the main container.
|
||||
container.appendChild(typeContainer);
|
||||
}
|
||||
});
|
||||
|
||||
resultSection.appendChild(container);
|
||||
}
|
||||
|
||||
function createProfileSection(data) {
|
||||
let profile = document.createElement('div');
|
||||
profile.className = 'creature_profile';
|
||||
let profileHTML = `
|
||||
<h3>Profile</h3>
|
||||
<table>
|
||||
<tr><td><b>NUMBER</b></td> <td>${formatDiceMnemonic(data.number)}</td></tr>
|
||||
<tr><td><b>MORALE</b></td> <td>${formatDiceMnemonic(data.morale)}</td></tr>
|
||||
<tr><td><b>HIT DICE</b></td> <td>${formatDiceMnemonic(data['hit dice'])}</td></tr>
|
||||
<tr><td><b>ARMOUR</b></td> <td>${data.armour}</td></tr>
|
||||
<tr><td><b>ENVIRON</b></td> <td>${data.environ.join(', ')}</td></tr>
|
||||
`;
|
||||
if(data['land speed']){
|
||||
profileHTML += `<tr><td><b>LAND SPEED</b></td> <td>${formatSpeedMnemonic(data['land speed'])}</td></tr>`;
|
||||
}
|
||||
if(data['water speed']){
|
||||
profileHTML += `<tr><td><b>WATER SPEED</b></td> <td>${formatSpeedMnemonic(data['water speed'])}</td></tr>`;
|
||||
}
|
||||
if(data['air speed']){
|
||||
profileHTML += `<tr><td><b>AIR SPEED</b></td> <td>${formatSpeedMnemonic(data['air speed'])}</td></tr>`;
|
||||
}
|
||||
profileHTML += '</table>';
|
||||
|
||||
profile.innerHTML = profileHTML;
|
||||
return profile;
|
||||
}
|
||||
|
||||
function createAbilitiesSection(data) {
|
||||
// Abilities section
|
||||
let abilities = document.createElement('div');
|
||||
abilities.className = 'creature_abilities';
|
||||
abilities.innerHTML = `
|
||||
<h3>Abilities</h3>
|
||||
<table>
|
||||
<tr><td><b>MS</b></td> <td>${formatDiceMnemonic(data.ms)}</td></tr>
|
||||
<tr><td><b>IN</b></td> <td>${formatDiceMnemonic(data.in)}</td></tr>
|
||||
<tr><td><b>DX</b></td> <td>${formatDiceMnemonic(data.dx)}</td></tr>
|
||||
<tr><td><b>CH</b></td> <td>${formatDiceMnemonic(data.ch)}</td></tr>
|
||||
<tr><td><b>CN</b></td> <td>${formatDiceMnemonic(data.cn)}</td></tr>
|
||||
<tr><td><b>PS</b></td> <td>${formatDiceMnemonic(data.ps)}</td></tr>
|
||||
</table>
|
||||
`;
|
||||
return abilities;
|
||||
}
|
||||
|
||||
function createAttacksSection(data) {
|
||||
// Attacks section
|
||||
let attacks = document.createElement('div');
|
||||
attacks.className = 'creature_attacks';
|
||||
let attacksHTML = '<h3>Attacks</h3>';
|
||||
let attacksTable = '<table>';
|
||||
for (let attack in data.attacks) {
|
||||
let mnemonic_display;
|
||||
mnemonic_display = formatDiceMnemonic(data.attacks[attack]);
|
||||
if (mnemonic_display === "0d0") {
|
||||
mnemonic_display = 'See Description';
|
||||
}
|
||||
attacksTable += `<tr><td><b>${attack}</b></td><td>${mnemonic_display}</td></tr>`
|
||||
}
|
||||
attacksTable += '</table>';
|
||||
attacks.innerHTML = attacksHTML + attacksTable;
|
||||
return attacks
|
||||
}
|
||||
|
||||
function createMutationsSection(mutarray) {
|
||||
// Mutations section
|
||||
let mutations = document.createElement('div');
|
||||
mutations.className = 'creature_mutations';
|
||||
let mutationsHTML = '<h3>Mutations</h3>';
|
||||
let mutationsList = mutarray.reduce((result, mutation) => result + `<li>${mutation}</li>`, '');
|
||||
mutationsHTML += `<ul>${mutationsList}</ul>`;
|
||||
mutations.innerHTML = mutationsHTML
|
||||
return mutations
|
||||
}
|
||||
|
||||
function createDescriptionSection(data) {
|
||||
// Description section
|
||||
let description = document.createElement('div');
|
||||
description.className = 'creature_description';
|
||||
let descriptionHTML = `
|
||||
<h3>Description</h3>
|
||||
${data.description}
|
||||
`;
|
||||
description.innerHTML = descriptionHTML;
|
||||
return description;
|
||||
}
|
||||
|
||||
function setCreatureTable(data) {
|
||||
let container = document.createElement('div');
|
||||
container.className = 'container';
|
||||
|
||||
// Creature ID section
|
||||
let creatureId = document.createElement('div');
|
||||
creatureId.className = 'creature_id';
|
||||
creatureId.innerHTML = `
|
||||
<h2>${creatureName.toUpperCase()}</h2>
|
||||
<img src='img/${creatureName}.jpg' onerror='this.src="img/404.jpg";' style='width: 275px; height: 275px;'>
|
||||
`;
|
||||
|
||||
let profile = createProfileSection(data);
|
||||
let abilities = createAbilitiesSection(data);
|
||||
let attacks = createAttacksSection(data);
|
||||
let mutations = createMutationsSection(data.mutations); // Notice the data difference
|
||||
let description = createDescriptionSection(data);
|
||||
|
||||
container.appendChild(creatureId);
|
||||
container.appendChild(profile);
|
||||
container.appendChild(abilities);
|
||||
container.appendChild(attacks);
|
||||
container.appendChild(mutations);
|
||||
container.appendChild(description);
|
||||
|
||||
resultSection.appendChild(container);
|
||||
}
|
||||
}, 1000);
|
||||
});
|