diff --git a/app/routes/get_creature.py b/app/routes/get_creature.py
index eb31429..65edd80 100644
--- a/app/routes/get_creature.py
+++ b/app/routes/get_creature.py
@@ -4,15 +4,16 @@ from app.tables.creature import Creatures
namespace = Namespace('rules', description='Gamma World Rules')
parser = reqparse.RequestParser()
-parser.add_argument('creature', type=str, help='the name of a Gamma World creature to search for.')
+parser.add_argument('creature', type=str, required=False, help='the name of a Gamma World creature to search for.')
@namespace.route('/creature') # resolves to: /rules/creature
class RollEncounter(Resource):
@namespace.expect(parser)
def get(self):
+ creatures = Creatures()
creature = request.args.get('creature', default=None, type=str)
if creature is None:
- return {'error': 'Provide the name of a Gamma World creature to search for'}, 400
- creatures = Creatures()
+ return creatures.get_creature_list(), 200
return creatures.get_creature(creature), 200
+
diff --git a/app/tables/creature.py b/app/tables/creature.py
index 318708b..5c3d967 100644
--- a/app/tables/creature.py
+++ b/app/tables/creature.py
@@ -17,3 +17,7 @@ class Creatures:
def get_creature(self, creature_name):
"""Returns the dictionary of the specified creature."""
return self.creatures.get(creature_name)
+
+ def get_creature_list(self):
+ """Returns a list of all creatures."""
+ return sorted(list(self.creatures.keys()))
diff --git a/web/bestiary.html b/web/bestiary.html
new file mode 100644
index 0000000..1e7de93
--- /dev/null
+++ b/web/bestiary.html
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+ Gamma World Bestiary
+
+
+
+
+
+
Look Up A Creature
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/bestiary.js b/web/bestiary.js
new file mode 100644
index 0000000..32eaa8c
--- /dev/null
+++ b/web/bestiary.js
@@ -0,0 +1,226 @@
+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');
+ resultSection.innerHTML = '';
+ let imgElement = document.createElement('img');
+ imgElement.src = 'img/checking.gif';
+ imgElement.style.width = '500px';
+ imgElement.style.height = '500px';
+ imgElement.style.display = 'block';
+ imgElement.style.marginLeft = 'auto';
+ imgElement.style.marginRight = 'auto';
+ resultSection.appendChild(imgElement);
+
+ 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){
+ 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 = `
+