diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..76c605a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +*.css linguist-detectable=true +*.css linguist-language=css +*.js linguist-detectable=true +*.js linguist-language=js +*.sql linguist-detectable=true +*.sql linguist-language=sql \ No newline at end of file diff --git a/__pycache__/creature_game.cpython-312.pyc b/__pycache__/creature_game.cpython-312.pyc new file mode 100644 index 0000000..387386b Binary files /dev/null and b/__pycache__/creature_game.cpython-312.pyc differ diff --git a/__pycache__/creature_game.cpython-39.pyc b/__pycache__/creature_game.cpython-39.pyc new file mode 100644 index 0000000..0b45f84 Binary files /dev/null and b/__pycache__/creature_game.cpython-39.pyc differ diff --git a/creature_game.py b/creature_game.py index d147dad..91d4ea2 100644 --- a/creature_game.py +++ b/creature_game.py @@ -22,7 +22,7 @@ class Database: """Database connection handler""" - def __init__(self, host='localhost', user='root', password='', database='creature_catcher'): + def __init__(self, host='localhost', user='root', password='123456789', database='creature_catcher'): self.connection = pymysql.connect( host=host, user=user, @@ -659,7 +659,7 @@ def main_menu(self): db = Database( host='localhost', user='root', - password='Cde3xsw2', + password='123456789', database='creature_catcher' ) print("✅ Connected!\n") diff --git a/exitpage/congrat.html b/exitpage/congrat.html new file mode 100644 index 0000000..516aa6f --- /dev/null +++ b/exitpage/congrat.html @@ -0,0 +1,21 @@ + + + + + + Creature Catcher + + + + +
+

Congratulations!

+

You have caught all the creatures!

+ +
+ + + diff --git a/exitpage/exit.css b/exitpage/exit.css new file mode 100644 index 0000000..311f948 --- /dev/null +++ b/exitpage/exit.css @@ -0,0 +1,26 @@ +body { + background: url("exitbackground.jpg"); + background-size: cover; + background-position: center; + background-repeat: no repeat; + font-family: pixelify sans, sans-serif; + color: #8b4513; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + margin: 0; +} +.exit-wrapper { + text-align: center; +} + +.btn { + padding: 10px; + background: #7ed957; + border-radius: 6px; + border: none; + font-size: 16px; + font-weight: bold; + cursor: pointer; +} diff --git a/exitpage/exit.html b/exitpage/exit.html new file mode 100644 index 0000000..be1389e --- /dev/null +++ b/exitpage/exit.html @@ -0,0 +1,23 @@ + + + + + + + Creature Catcher + + + + +
+

You have successfully logged out!

+

Thank you for playing creature catch!

+

Click play to play again!

+ +
+ + + diff --git a/exitpage/exit.js b/exitpage/exit.js new file mode 100644 index 0000000..4a6acec --- /dev/null +++ b/exitpage/exit.js @@ -0,0 +1,4 @@ +const playBtn = document.getElementById("playBtn"); +playBtn.addEventListener("click", () => { + window.location.href = "/"; +}); diff --git a/exitpage/exitbackground.jpg b/exitpage/exitbackground.jpg new file mode 100644 index 0000000..2ab48db Binary files /dev/null and b/exitpage/exitbackground.jpg differ diff --git a/game.py b/game.py new file mode 100644 index 0000000..f0654d8 --- /dev/null +++ b/game.py @@ -0,0 +1,590 @@ +from flask import Flask, render_template, request, jsonify, send_from_directory +from flask_cors import CORS +import random + +from creature_game import Database, Game, Player, Creature, PlayerCreature, Habitat + + +class Base: + def get_db(self): + return Database( + host='127.0.0.1', + user='root', + password='123456789', + database='creature_catcher' + ) + + +class Front(Base): + def login_page(self): + return send_from_directory('loginpage', 'login.html') + + def game_page(self): + return render_template('index.html') + + def login_assets(self, filename): + return send_from_directory('loginpage', filename) + + def journal_page_files(self, filename): + return send_from_directory('journal', filename) + + def image_files(self, filename): + return send_from_directory('images', filename) + + def manage_page(self): + return send_from_directory('manage_page/templates', 'manage.html') + + def manage_assets(self, filename): + return send_from_directory('manage_page/static', filename) + + def exit_page(self): + return send_from_directory('exitpage', 'exit.html') + + def congrat_page(self): + return send_from_directory('exitpage', 'congrat.html') + + def exit_assets(self, filename): + return send_from_directory('exitpage', filename) + + +class Authentication(Base): + def login(self): + data = request.json + username = data.get('username', '').strip() + + if not username: + return jsonify({'error': 'no username'}), 400 + + messages = [] + + try: + db = self.get_db() + game = Game(db) + + # Check if new player + existing_player = Player.get_by_username(db, username) + + if not existing_player: + messages.append(f"🌟 Creating new sanctuary for {username}...") + game.login_or_create(username) + starter = Creature.from_db(db, 1) + messages.append( + f"✨ You received your first creature: {starter.name} ({starter.type.name} type)!") + messages.append(f"📖 {starter.description}") + messages.append("") + messages.append( + f"🏠 {starter.name} has been placed in Habitat 1") + messages.append("") + else: + game.login_or_create(username) + messages.append( + f"👋 Welcome back to your sanctuary, {username}!") + messages.append("") + + return jsonify({ + 'success': True, + 'messages': messages, + 'player_id': game.player.id, + 'username': game.player.username + }) + + except Exception as e: + messages.append(f"❌ Error: {str(e)}") + return jsonify({'success': False, 'messages': messages}), 500 + + +class Explore(Base): + + def start(self): + player_id = request.args.get('player_id') + + if not player_id: + return jsonify({'error': 'Player ID required'}), 400 + + try: + db = self.get_db() + player = Player.from_db(db, int(player_id)) + unplaced = player.get_unplaced_creatures(db) + + messages = [] + + # unplaced creatures + if unplaced: + messages.append("") + messages.append( + "⚠️ You have unplaced creatures! Place all creatures in habitats before exploring.") + messages.append( + f"📦 {len(unplaced)} creature(s) need placement.") + messages.append("") + return jsonify({'success': False, 'messages': messages, 'error': 'unplaced'}) + + if not player.are_all_happy(db): + messages.append("") + messages.append( + "⚠️ Some creatures are unhappy! You must fix happiness before exploring.") + messages.append( + "Rearrange creatures in habitats or release unhappy ones.") + messages.append("") + return jsonify({'success': False, 'messages': messages, 'error': 'unhappy'}) + + # companions + habitats = player.get_habitats(db) + all_creatures = [] + for habitat in habitats: + all_creatures.extend(habitat.creatures) + + if not all_creatures: + messages.append("") + messages.append( + "❌ You have no creatures to take as companions!") + messages.append("") + return jsonify({'success': False, 'messages': messages, 'error': 'no_creatures'}) + + messages.append("") + messages.append("🎒 Choose a companion for exploration:") + for i, pc in enumerate(all_creatures, 1): + messages.append( + f" [{i}] {pc.nickname} ({pc.creature.type.name}) - Happiness: {pc.happiness}%") + messages.append("") + messages.append("Select companion (number):") + + companions_data = [{'id': pc.id, 'nickname': pc.nickname, + 'type': pc.creature.type.name} for pc in all_creatures] + + return jsonify({ + 'success': True, + 'messages': messages, + 'companions': companions_data + }) + + except Exception as e: + return jsonify({'error': str(e)}), 500 + + def encounter(self): + data = request.json + player_id = data.get('player_id') + companion_id = data.get('companion_id') + + if not player_id or not companion_id: + return jsonify({'error': 'Player ID and companion ID required'}), 400 + + try: + db = self.get_db() + player = Player.from_db(db, int(player_id)) + companion = PlayerCreature.from_db(db, int(companion_id)) + + messages = [] + messages.append("") + messages.append( + f"🌿 {companion.nickname} joins you for exploration!") + messages.append("") + + # Check if all discovered + all_species = Creature.get_all(db) + discovered = player.get_discovered_species(db) + + if len(discovered) >= 16: + messages.append( + "🎊 ═══════════════════════════════════════════════════ 🎊") + messages.append( + " 🌟 CONGRATULATIONS! YOU'VE CAUGHT THEM ALL! 🌟") + messages.append( + "🎊 ═══════════════════════════════════════════════════ 🎊") + messages.append("") + messages.append( + " You've discovered all 16 creature species!") + messages.append(" Your sanctuary is complete!") + messages.append("") + return jsonify({'success': True, 'messages': messages, 'complete': True}) + + # undiscovered creatures + undiscovered = [c for c in all_species if c.id not in discovered] + num_to_show = min(3, len(undiscovered)) + wild_choices = random.sample(undiscovered, num_to_show) + + if num_to_show == 1: + messages.append("🔍 You encounter a wild creature:") + else: + messages.append( + f"🔍 You encounter {num_to_show} wild creatures:") + + wild_data = [] + for i, wild in enumerate(wild_choices, 1): + effectiveness = companion.creature.type.get_effectiveness( + db, wild.type.id) + status = "✅ Strong" if effectiveness >= 2.0 else "🟡 Normal" if effectiveness >= 1.0 else "❌ Weak" + messages.append( + f" [{i}] {wild.name} ({wild.type.name}) - {status} attraction - ✨ NEW!") + messages.append(f" {wild.description}") + + wild_data.append({ + 'id': wild.id, + 'name': wild.name, + 'type': wild.type.name, + 'description': wild.description, + 'effectiveness': effectiveness, + 'status': status + }) + + messages.append("") + choice_range = f"1-{num_to_show}" if num_to_show > 1 else "1" + messages.append( + f"Which creature to approach? ({choice_range} or 0 to leave):") + + return jsonify({ + 'success': True, + 'messages': messages, + 'wild_creatures': wild_data, + 'companion_id': companion.id + }) + + except Exception as e: + return jsonify({'error': str(e)}), 500 + + def check_completion(self): + player_id = request.args.get('player_id') + + if not player_id: + return jsonify({'error': 'Player ID required'}), 400 + + try: + db = self.get_db() + player = Player.from_db(db, int(player_id)) + discovered = player.get_discovered_species(db) + + # Check if all discovered + is_complete = len(discovered) >= 16 + + return jsonify({ + 'success': True, + 'completed': is_complete + }) + + except Exception as e: + return jsonify({'error': str(e)}), 500 + + def catch(self): + """Attempt to catch a creature""" + data = request.json + player_id = data.get('player_id') + wild_creature_id = data.get('wild_creature_id') + effectiveness = data.get('effectiveness', 1.0) + + if not player_id or not wild_creature_id: + return jsonify({'error': 'error'}), 400 + + try: + db = self.get_db() + player = Player.from_db(db, int(player_id)) + target = Creature.from_db(db, int(wild_creature_id)) + base_chance = 0.3 + modified_chance = min(0.95, base_chance * effectiveness) + + messages = [] + messages.append("") + messages.append(f"🎯 Attempting to attract {target.name}...") + messages.append(f" Base chance: 30%") + messages.append(f" Type multiplier: {effectiveness}x") + messages.append(f" Final chance: {int(modified_chance * 100)}%") + messages.append("") + + success = random.random() < modified_chance + + if success: + messages.append( + f"🎉 Success! {target.name} trusts you and joins your sanctuary!") + messages.append("") + player.catch_creature(db, target) + messages.append( + f"✨ {target.name} has been added to your unplaced creatures.") + messages.append("") + else: + messages.append( + f"😞 {target.name} was not convinced and fled. Better luck next time!") + messages.append("") + + return jsonify({ + 'success': True, + 'messages': messages, + 'caught': success, + 'creature_id': target.id + }) + + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +class Place(Base): + + def view_habitats(self): + player_id = request.args.get('player_id') + + if not player_id: + return jsonify({'error': 'Player ID required'}), 400 + + try: + db = self.get_db() + player = Player.from_db(db, int(player_id)) + habitats = player.get_habitats(db) + unplaced = player.get_unplaced_creatures(db) + + messages = [] + messages.append("") + messages.append("🏠 === SANCTUARY HABITATS ===") + messages.append("") + + habitat_data = [] + for habitat in habitats: + messages.append( + f"Habitat {habitat.number} ({len(habitat.creatures)}/{Habitat.MAX_SLOTS} creatures):") + creatures_list = [] + if habitat.creatures: + for i, cre in enumerate(habitat.creatures, 1): + emoji = "😊" if cre.happiness >= 70 else "😐" if cre.happiness >= 30 else "😢" + messages.append( + f" [{i}] {emoji} {cre.nickname} ({cre.creature.type.name}) - {cre.happiness}% happy") + creatures_list.append({ + 'id': cre.id, + 'nickname': cre.nickname, + 'type': cre.creature.type.name, + 'happiness': cre.happiness + }) + else: + messages.append(" (empty)") + messages.append("") + + habitat_data.append({ + 'id': habitat.id, + 'number': habitat.number, + 'creatures': creatures_list, + 'is_full': habitat.is_full() + }) + + messages.append(f"📦 Unplaced Creatures ({len(unplaced)}):") + unplaced_data = [] + if unplaced: + for i, pc in enumerate(unplaced, 1): + messages.append( + f" [{i}] {pc.nickname} ({pc.creature.type.name})") + unplaced_data.append({ + 'id': pc.id, + 'nickname': pc.nickname, + 'type': pc.creature.type.name + }) + else: + messages.append(" (none)") + messages.append("") + + return jsonify({ + 'success': True, + 'messages': messages, + 'habitats': habitat_data, + 'unplaced': unplaced_data + }) + + except Exception as e: + return jsonify({'error': 'error'}), 500 + + def move_creature(self): + data = request.json + player_id = data.get('player_id') + creature_id = data.get('creature_id') + target_habitat_id = data.get('target_habitat_id') + + if not player_id or not creature_id: + return jsonify({'error': 'error'}), 400 + + try: + db = self.get_db() + player = Player.from_db(db, int(player_id)) + creature = PlayerCreature.from_db(db, creature_id) + + if not creature or creature.player_id != player.id: + return jsonify({'error': 'Invalid creature'}), 400 + + messages = [] + + if target_habitat_id is None: + # Move to unplaced + db.execute_commit( + "UPDATE player_creatures SET habitat_id = NULL, habitat_slot = NULL WHERE id = %s", + (creature_id,) + ) + messages.append("") + messages.append( + f"✅ Moved {creature.nickname} to unplaced creatures") + messages.append("") + return jsonify({'success': True, 'messages': messages}) + else: + # Move to habitat + habitats = player.get_habitats(db) + target_habitat = None + for h in habitats: + if h.id == target_habitat_id: + target_habitat = h + break + + if not target_habitat: + return jsonify({'error': 'Invalid habitat'}), 400 + + if target_habitat.is_full(): + messages.append("") + messages.append( + f"❌ Habitat {target_habitat.number} is full!") + messages.append("") + return jsonify({'success': False, 'messages': messages}) + + new_slot = len(target_habitat.creatures) + 1 + db.execute_commit( + "UPDATE player_creatures SET habitat_id = %s, habitat_slot = %s WHERE id = %s", + (target_habitat_id, new_slot, creature_id) + ) + + target_habitat.creatures.append(creature) + target_habitat.update_happiness(db) + + messages.append("") + messages.append( + f"✅ Moved {creature.nickname} to Habitat {target_habitat.number}") + messages.append( + f"Updated happiness for Habitat {target_habitat.number}") + messages.append("") + + return jsonify({'success': True, 'messages': messages}) + + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +class Journal(Base): + + def view_journal(self): + player_id = request.args.get('player_id') + + if not player_id: + return jsonify({'error': 'Player ID required'}), 400 + + try: + db = self.get_db() + player = Player.from_db(db, int(player_id)) + discovered = player.get_discovered_species(db) + all_species = Creature.get_all(db) + + creatures_data = [] + for creature in all_species: + is_discovered = creature.id in discovered + creatures_data.append({ + 'id': creature.id, + 'name': creature.name if is_discovered else "???", + 'type': creature.type.name if is_discovered else "???", + 'description': creature.description if is_discovered else "Not yet discovered", + 'image': creature.image_path if is_discovered else None, + 'discovered': is_discovered + }) + + return jsonify({ + 'success': True, + 'creatures': creatures_data, + 'discovered_count': len(discovered), + 'total_species': len(all_species) + }) + + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +class CreatureCatcherApp: + def __init__(self): + self.app = Flask(__name__) + CORS(self.app) + + self.front = Front() + self.authentication = Authentication() + self.explore = Explore() + self.place = Place() + self.journal = Journal() + + self.setup_routes() + + def setup_routes(self): + # file routes + @self.app.route('/') + def login_page(): + return self.front.login_page() + + @self.app.route('/game') + def game_page(): + return self.front.game_page() + + @self.app.route('/manage') + def manage_page(): + return self.front.manage_page() + + @self.app.route('/loginpage/') + def login_assets(filename): + return self.front.login_assets(filename) + + @self.app.route('/journal_page/') + def journal_page_files(filename): + return self.front.journal_page_files(filename) + + @self.app.route('/images/') + def image_files(filename): + return self.front.image_files(filename) + + @self.app.route('/manage_page/static/') + def manage_assets(filename): + return self.front.manage_assets(filename) + + @self.app.route('/exit') + def exit_page(): + return self.front.exit_page() + + @self.app.route('/exitpage/') + def exit_assets(filename): + return self.front.exit_assets(filename) + # API routes + + @self.app.route('/login', methods=['POST']) + def login(): + return self.authentication.login() + + @self.app.route('/explore/start', methods=['GET']) + def explore_start(): + return self.explore.start() + + @self.app.route('/explore/encounter', methods=['POST']) + def explore_encounter(): + return self.explore.encounter() + + @self.app.route('/explore/catch', methods=['POST']) + def explore_catch(): + return self.explore.catch() + + @self.app.route('/habitats', methods=['GET']) + def view_habitats(): + return self.place.view_habitats() + + @self.app.route('/move', methods=['POST']) + def move_creature(): + return self.place.move_creature() + + @self.app.route('/journal', methods=['GET']) + def view_journal(): + return self.journal.view_journal() + + @self.app.route('/congrat') + def congrat_page(): + return self.front.congrat_page() + + @self.app.route('/check_completion', methods=['GET']) + def check_completion(): + return self.explore.check_completion() + + def run(self): + self.app.run(debug=True, host='127.0.0.1', port=8080) + + +if __name__ == '__main__': + creature_app = CreatureCatcherApp() + creature_app.run() diff --git a/h b/h new file mode 100644 index 0000000..c3849d4 --- /dev/null +++ b/h @@ -0,0 +1,48 @@ +1a437f7 (HEAD -> main_page, origin/main_page) Merge remote-tracking branch 'origin/main_page' into main_page +2ec38a8 updated menu page (main page) +0c5b4d3 Merge branch 'main' into main_page +c7e7c3e main page +96e4b9b Merge pull request #3 from Sorathor/exit +8caaa78 add exit.html +38c40c2 exit +e1c362c Merge pull request #2 from Sorathor/login +419abe5 (origin/login) Merge branch 'main' into login +90a8d28 change pwd +8bf7a6c login.c +b95a462 login.c +4269f30 login.html +b39b216 update .gitattributes to include SQL language detection +a43b85b remove .gitattributes file for CSS and JS language detection +57f38af change dir +e7afd5b Merge pull request #1 from Sorathor/explore +a41e01c (origin/explore) restore .gitattributes for CSS and JS file detection +72a2d65 update .gitattributes to enhance language detection for CSS and JS files +21f345e add .gitattributes for CSS and JS file detection +d4de2a1 add compiled bytecode +707971b core game logic and endpoint +398da4d add frontend +2ed39bf change the password +0276d7d first commit +5e2377f (main) Update README.md +5f3589c Update README.md +c40bd2b Update README.md +a16321e Add files via upload +b13bd9c Create borealynx.png +732236a Delete assets directory +4cc3764 Create setup-database.py +76927a6 Create creature_schema.sql +740f20f Create creature_game.py +3fa680c Delete game.py +36840be Delete story.py +f9b04da Update README.md +705f632 Update README.md +af57ab1 Update README.md +7a76fda Update README.md +c770379 Add files via upload +36b225f Create text.png +a6b6a2f Create example.pyc +fec64a0 Create story.py +e1d8957 Create game.py +25d5ba7 Rename gitignore.txt to .gitignore +e6442be Add files via upload +e14510e Initial commit diff --git a/images/borealynx.png b/images/borealynx.png index 26190bc..cadcaca 100644 Binary files a/images/borealynx.png and b/images/borealynx.png differ diff --git a/images/capychara.png b/images/capychara.png index 61fb43c..383022a 100644 Binary files a/images/capychara.png and b/images/capychara.png differ diff --git a/images/chromutt.png b/images/chromutt.png index d16184d..b83ddfa 100644 Binary files a/images/chromutt.png and b/images/chromutt.png differ diff --git a/images/cirroptera.png b/images/cirroptera.png index ea3b1c9..15714df 100644 Binary files a/images/cirroptera.png and b/images/cirroptera.png differ diff --git a/images/cosmeow.png b/images/cosmeow.png index 8d918d8..d985b12 100644 Binary files a/images/cosmeow.png and b/images/cosmeow.png differ diff --git a/images/geckathyst.png b/images/geckathyst.png index 1950d80..12a1de2 100644 Binary files a/images/geckathyst.png and b/images/geckathyst.png differ diff --git a/images/gemwraith.png b/images/gemwraith.png index 17424f1..142e99b 100644 Binary files a/images/gemwraith.png and b/images/gemwraith.png differ diff --git a/images/granidam.png b/images/granidam.png index bb6abc2..34762e8 100644 Binary files a/images/granidam.png and b/images/granidam.png differ diff --git a/images/marinlet.png b/images/marinlet.png index 782ccb8..59c75c8 100644 Binary files a/images/marinlet.png and b/images/marinlet.png differ diff --git a/images/obscurine.png b/images/obscurine.png index 881fe2c..3cf4b6c 100644 Binary files a/images/obscurine.png and b/images/obscurine.png differ diff --git a/images/pandascend.png b/images/pandascend.png index 6889fe7..a42cb9f 100644 Binary files a/images/pandascend.png and b/images/pandascend.png differ diff --git a/images/reverwing.png b/images/reverwing.png index 39af02a..30347f8 100644 Binary files a/images/reverwing.png and b/images/reverwing.png differ diff --git a/images/shaduran.png b/images/shaduran.png index 98b970f..7c8b0ba 100644 Binary files a/images/shaduran.png and b/images/shaduran.png differ diff --git a/images/spriggle.png b/images/spriggle.png index b689e14..d95b078 100644 Binary files a/images/spriggle.png and b/images/spriggle.png differ diff --git a/journal/bg.png b/journal/bg.png new file mode 100644 index 0000000..e31ccdd Binary files /dev/null and b/journal/bg.png differ diff --git a/journal/server.py b/journal/server.py new file mode 100644 index 0000000..5630348 --- /dev/null +++ b/journal/server.py @@ -0,0 +1,26 @@ +import mysql.connector +from flask import Flask, request, jsonify +from flask_cors import CORS + +app = Flask(__name__) +CORS(app) + + +@app.route('/journal', methods=['GET']) +def get_journal(): + db = mysql.connector.connect( + host='127.0.0.1', + user='root', + password='123456789', + database='creature_catcher' + ) + cursor = db.cursor() + cursor.execute( + """SELECT a.id AS SN, a.name AS name, b.name AS type FROM creatures a LEFT JOIN types b ON a.id = b.id""") + result = cursor.fetchall() + print(result) + return jsonify(result) + + +if __name__ == '__main__': + app.run(debug=True) diff --git a/setup-database.py b/journal/setup-database.py similarity index 85% rename from setup-database.py rename to journal/setup-database.py index 8e5aec0..c1e5026 100644 --- a/setup-database.py +++ b/journal/setup-database.py @@ -12,7 +12,7 @@ def setup_database(): # Connection details - UPDATE THESE DB_HOST = 'localhost' DB_USER = 'root' - DB_PASSWORD = 'Cde3xsw2' # <-- UPDATE WITH YOUR PASSWORD + DB_PASSWORD = '123456789' # <-- UPDATE WITH YOUR PASSWORD DB_NAME = 'creature_catcher' try: @@ -22,7 +22,7 @@ def setup_database(): host=DB_HOST, user=DB_USER, password=DB_PASSWORD, - charset='utf8mb4' + database=DB_NAME ) with connection.cursor() as cursor: @@ -35,19 +35,22 @@ def setup_database(): # You can paste the SQL here or read from file # For now, let's read from the schema file try: - with open('creature_schema.sql', 'r') as f: + with open('Software2-Project\creature_schema.sql', 'r') as f: sql_content = f.read() # Split by semicolons and execute each statement - statements = [s.strip() for s in sql_content.split(';') if s.strip()] + statements = [s.strip() + for s in sql_content.split(';') if s.strip()] for i, statement in enumerate(statements, 1): try: cursor.execute(statement) if i % 10 == 0: - print(f" Executed {i}/{len(statements)} statements...") + print( + f" Executed {i}/{len(statements)} statements...") except Exception as e: - print(f" Warning: Statement {i} failed: {str(e)[:50]}") + print( + f" Warning: Statement {i} failed: {str(e)[:50]}") connection.commit() print(f"✅ Database setup complete!") diff --git a/journal/viewjournal.css b/journal/viewjournal.css new file mode 100644 index 0000000..65bbfac --- /dev/null +++ b/journal/viewjournal.css @@ -0,0 +1,109 @@ +@import url('https://fonts.googleapis.com/css2?family=Pixelify+Sans:wght@400..700&display=swap'); +body { + margin: 0; + padding: 20px 0; + font-family: "Pixelify Sans"; + background-image: url("bg.png"); + background-size: cover; + background-repeat: no-repeat; + background-attachment: fixed; + background-position: center; + display: flex; + flex-direction: column; + align-items: center; + gap: 20px; + min-height: 100vh; +} + +#journal { + width: 60vw; + height: 60vw; + max-width: 800px; + max-height: 800px; + border-radius: 15px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + padding: 15px; +} + +.journal-summary { + font-size: 18px; + font-weight: bold; + color: #000; + margin-bottom: 10px; + text-align: center; +} + +#journalList { + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-auto-rows: 1fr; + gap: 70px; + width: 100%; + height: 100%; +} + +.journal-item { + background: rgba(255, 255, 255, 0.3); + border-radius: 10px; + color: #000; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + aspect-ratio: 1 / 1; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + transition: transform 0.2s, box-shadow 0.2s; +} + +.journal-item:hover { + transform: scale(1.05); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5); +} + +.journal-item img { + width: 75%; + height: 80%; + object-fit: contain; +} + +.journal-item .placeholder { + width: 70%; + height: 70%; + background: rgba(255, 255, 255, 0.5); + display: flex; + align-items: center; + justify-content: center; + font-size: 32px; + font-weight: bold; + color: #000; + text-shadow: none; +} + +.journal-item p { + margin: 5px 0 0 0; + font-size: 12px; + text-align: center; + color: #000; +} + +#backBtn { + background: rgba(255, 255, 255, 0.4); + color: #000; + border: none; + border-radius: 8px; + padding: 8px 16px; + font-size: 14px; + font-weight: bold; + cursor: pointer; + transition: background 0.2s, transform 0.2s, box-shadow 0.2s; + margin-top: 20px; +} + +#backBtn:hover { + background: rgba(255, 255, 255, 0.6); + transform: translateY(-2px); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); +} diff --git a/journal/viewjournal.html b/journal/viewjournal.html new file mode 100644 index 0000000..3244696 --- /dev/null +++ b/journal/viewjournal.html @@ -0,0 +1,19 @@ + + + + + + Creature Catcher + + + + +

View Journal

+ +
+
+
+ + + + diff --git a/journal/viewjournal.js b/journal/viewjournal.js new file mode 100644 index 0000000..7d4cd48 --- /dev/null +++ b/journal/viewjournal.js @@ -0,0 +1,75 @@ +const params = new URLSearchParams(window.location.search); +const playerId = params.get("player_id"); + +function goBack() { + window.location.href = `/game?player_id=${playerId}`; +} + +async function loadJournalPage(playerId) { + const journal = document.getElementById("journal"); + + const existingGrid = document.getElementById("journalList"); + if (existingGrid) existingGrid.remove(); + + try { + const response = await fetch(`/journal?player_id=${playerId}`); + const data = await response.json(); + + if (!data.success) { + throw new Error("Failed to fetch journal data"); + } + + const existingSummary = document.querySelector(".journal-summary"); + if (existingSummary) existingSummary.remove(); + + const summary = document.createElement("div"); + summary.className = "journal-summary"; + summary.textContent = `Discovered: ${data.discovered_count || 0} / ${ + data.total_species || 16 + }`; + journal.appendChild(summary); + + const grid = document.createElement("div"); + grid.id = "journalList"; + journal.appendChild(grid); + + data.creatures.forEach((creature) => { + const item = document.createElement("div"); + item.className = "journal-item"; + + if (creature.discovered) { + const img = document.createElement("img"); + img.src = `../${creature.image}`; + img.alt = creature.name; + + img.onerror = () => { + img.src = "https://placehold.co/600x400"; + }; + + item.appendChild(img); + + const info = document.createElement("p"); + info.innerHTML = `${creature.name}
${creature.type}`; + item.appendChild(info); + } else { + const placeholder = document.createElement("div"); + placeholder.className = "placeholder"; + placeholder.textContent = "???"; + item.appendChild(placeholder); + + const info = document.createElement("p"); + info.textContent = "???"; + item.appendChild(info); + } + + grid.appendChild(item); + }); + } catch (error) { + console.error("Error loading journal:", error); + journal.textContent = "Failed to load journal."; + } +} + +document.addEventListener("DOMContentLoaded", () => { + loadJournalPage(playerId); +}); diff --git a/loginpage/backgroundimg.jpg b/loginpage/backgroundimg.jpg new file mode 100644 index 0000000..49a767d Binary files /dev/null and b/loginpage/backgroundimg.jpg differ diff --git a/loginpage/login.css b/loginpage/login.css new file mode 100644 index 0000000..ef3438f --- /dev/null +++ b/loginpage/login.css @@ -0,0 +1,50 @@ +body { + background: url("backgroundimg.jpg"); + background-size: cover; + background-position: center; + background-repeat: no repeat; + font-family: Pixelify sans, sans-serif; + color: #f1d1aa; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + margin: 0; +} +.wrapper { + text-align: centre; +} + +.login-box { + margin-top: 20px; + display: flex; + flex-direction: column; + font-size: 22px; + gap: 20px; + width: 260px; + margin-left: auto; + margin-right: auto; + text-align: center; +} +h1 { + font-size: 58.8px; + font-weight: bold; + text-align: center; +} + +.input { + padding: 10px; + border-radius: 6px; + border: 4px; + outline: none; + font-size: 16px; +} +.btn { + padding: 10px; + background: #7ed957; + border-radius: 6px; + border: none; + font-size: 16px; + font-weight: bold; + cursor: pointer; +} diff --git a/loginpage/login.html b/loginpage/login.html new file mode 100644 index 0000000..1bf93ce --- /dev/null +++ b/loginpage/login.html @@ -0,0 +1,25 @@ + + + + + + + Creature Catcher + + + + +
+

Welcome to Creature Catcher

+ +
+ + + diff --git a/loginpage/login.js b/loginpage/login.js new file mode 100644 index 0000000..f6d549c --- /dev/null +++ b/loginpage/login.js @@ -0,0 +1,43 @@ +const btn = document.getElementById("enterBtn"); +const username = document.getElementById("username"); + +username.addEventListener("keydown", (event) => { + if (event.key === "Enter") { + btn.click(); + } +}); + +btn.addEventListener("click", async () => { + const user = username.value.trim(); + if (user === "") { + alert("Please enter a username to continue."); + return; + } + + try { + const response = await fetch("/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ username: user }), + }); + + const data = await response.json(); + + if (data.success) { + const params = new URLSearchParams({ + player_id: data.player_id, + username: data.username, + }); + location.href = `/game?${params.toString()}`; + } else { + alert( + "Login failed: " + + (data.messages ? data.messages.join("\n") : "Unknown error") + ); + } + } catch (error) { + alert("Error: " + error); + } +}); diff --git a/mainpage/Main-Paige.html b/mainpage/Main-Paige.html new file mode 100644 index 0000000..a7d1fbd --- /dev/null +++ b/mainpage/Main-Paige.html @@ -0,0 +1,21 @@ + + + + + Main Page + + + + + + +

Main Page

+ +
    +
  • +
  • +
  • +
  • +
+ + diff --git a/mainpage/background.jpg b/mainpage/background.jpg new file mode 100644 index 0000000..f03ebd0 Binary files /dev/null and b/mainpage/background.jpg differ diff --git a/mainpage/style.css b/mainpage/style.css new file mode 100644 index 0000000..381424a --- /dev/null +++ b/mainpage/style.css @@ -0,0 +1,39 @@ +@import url('https://fonts.googleapis.com/css2?family=Pixelify+Sans:wght@400..700&display=swap'); + +ul { + list-style: none; + padding: 0; + margin: 0; +} + +body { + font-family: "pixelify sans", sans-serif ; + text-align: center; + color: #8b4513; + background-image: url("background.jpg"); + font-size: 40px; + text-shadow: 0px 0px 10px rgba(0,0,0,0.3); /* adding shadow behind the text, to make it more visible*/ + +} + +button { + padding: 40px 300px; + margin: 4px; + width: 200px; + height: 60px; + background-color: #7ed957; + color: #8b4513; + border: none; + border-radius: 2px; + cursor: pointer; + font-family: "Pixelify Sans", sans-serif; + font-size: 22px; + text-align: center; + white-space: nowrap; + line-height: 10px; + text-indent: -40px; /* moving the text more to the center */ +} + +button:hover { + background-color: darkolivegreen; +} diff --git a/manage_page/game.py b/manage_page/game.py new file mode 100644 index 0000000..a7846ea --- /dev/null +++ b/manage_page/game.py @@ -0,0 +1,21 @@ +import mysql.connector + +connection = mysql.connector.connect( + host='127.0.0.1', + port= 5000, + database='creature_catcher', + user='root', + password='123456789', + autocommit=True + ) + +cursor = connection.cursor() + +player_id = 1 + +cursor.execute("Insert ignore into players (id, username) values (1,'player1)") + +for i in range (1,5): + cursor.execute( + "Insert ignore into habitats (payer_id" + ) \ No newline at end of file diff --git a/manage_page/static/PixelifySans-Regular.ttf b/manage_page/static/PixelifySans-Regular.ttf new file mode 100644 index 0000000..783cefa Binary files /dev/null and b/manage_page/static/PixelifySans-Regular.ttf differ diff --git a/manage_page/static/manage.css b/manage_page/static/manage.css new file mode 100644 index 0000000..849ce55 --- /dev/null +++ b/manage_page/static/manage.css @@ -0,0 +1,104 @@ +@font-face { + font-family: "pixelify"; + src: + url('./PixelifySans-Regular.ttf') format('truetype') +} + +body{ + background-image: url('./managepage.png'); + background-repeat: no-repeat; + background-position: center; + background-size:cover; + background-attachment: fixed; + font-family: pixelify, sans-serif + +} + +.managepagemain{ + max-width: 100%; + /* margin: auto; */ + padding: 0px; + border-radius: 10px; + +} + +.habitat_table{ + display: flex; + justify-content: center; + align-items: center; + + + max-width: 100%; + + +} + +#managetable{ + justify-content:space-evenly; + align-items: center; + width: 90%; + border: 1px solid black; + /* border-radius: 20px; */ + background-color: rgba(187, 173, 124, 0.5); + /* background-color: #7ed957; */ + color: white; + border-collapse: collapse; + /* box-shadow: rgba(187, 173, 124, 1) */ + +} + +#managetable thead th{ + background-color: #8b4513; + + padding: 10px; + font-size: 30px; + + text-align: center; + + +} + +#managetable tbody{ + color: black; + font-size: 15px; + +} +#managetable tbody td{ + padding: 10px; + font-size: 20px; +} + +#managetable tbody tr td:hover{ + background-color: #bbad7c +} + +#managetable tbody td img{ + width: 155px; + height: 145px; +} +#manage_btn{ + display: flex; + justify-content:center; + align-items: center; + +} +#manage_btn button{ + background: #7ed957; + font-family: pixelify; + padding: 10px; + margin: 30px; + +} + +#pagetitle{ + display: flex; + justify-content: center; + +} +#pagetitle h1{ + color: white ; + font-size: 58.8px; +} +/* #habitat_row{ + background-color: white; +} */ \ No newline at end of file diff --git a/manage_page/static/manage.js b/manage_page/static/manage.js new file mode 100644 index 0000000..7c8f175 --- /dev/null +++ b/manage_page/static/manage.js @@ -0,0 +1,261 @@ +const params = new URLSearchParams(location.search); +const player_id = params.get("player_id"); + +if (!player_id) { + location.href = "/"; +} +let value1 = ""; +let value2 = ""; + +const table = document.querySelector(".habitat_table"); +const cdiv = document.createElement("div"); + +let h4 = document.createElement("h4"); +h4.innerText = ""; + +const edit = document.getElementById("Editbtn"); +edit.addEventListener("click", async () => { + console.log("You Clicked the Edit Button"); + + value1, (value2 = await edit_btn()); + + let value3 = await move_creature(value1, value2); + console.log(value3, "this is value 3"); +}); + +async function edit_btn() { + let div = document.querySelector("#manage_btn"); + + h4.innerText = "Click the creature image that you want to move"; + + div.appendChild(h4); + + value1 = await after_click(); + + h4.innerText = `You Selected ${value1.toUpperCase()}\n Click the habitat name where you want it to locate`; + + value2 = await after_click(); + ////add the logic to move only after checking the slot if either full or not + h4.innerText = `You moved ${value1} to the ${value2}`; + // console.log(value1,value2) + return value1, value2; +} + +function after_click() { + return new Promise((resolve) => { + function handler(event) { + const target = event.target; + + //if an image is selected + if (target.tagName === "IMG") { + const src = target.src; + const fileName = src.substring(src.lastIndexOf("/") + 1); + const nameWithoutExt = fileName.split(".")[0]; + + console.log("User clicked image:", nameWithoutExt); + + table.removeEventListener("click", handler); + resolve(nameWithoutExt); + } + + //if habitat row is selectd + else if (target.tagName === "TD" && target.cellIndex === 0) { + const habitatName = target.innerText.trim(); + console.log("User clicked habitat:", habitatName); + + table.removeEventListener("click", handler); + resolve(habitatName); + } + } + + table.addEventListener("click", handler); + }); +} + +const data = document.querySelector(".habitat_table"); + +async function get_habitat(params) { + const url = `http://127.0.0.1:8080//habitats?player_id=${player_id}`; + try { + const response = await fetch(url); + const result = await response.json(); + return result; + } catch (error) { + return JSON.stringify({ status: "fail", message: String(error) }); + } +} + +async function move_habitat_API(creatureID, targetID) { + let url = `http://127.0.0.1:8080/move`; + try { + const response = await fetch(url, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + player_id: player_id, + creature_id: creatureID, + target_habitat_id: targetID, + }), + }); + const result = await response.json(); + return result; + } catch (error) { + return JSON.stringify({ status: "fail", message: String(error) }); + } +} +async function put_habitat(params) { + //create a variable habitatbox that will be the main table + const habitatbox = document.querySelector(".habitat_table"); + if (!habitatbox) return; + + habitatbox.innerHTML = ""; + const table = document.createElement("table"); + + table.id = "managetable"; + const thead = document.createElement("thead"); + thead.innerHTML = "HabitatCreature"; + + let r = await get_habitat(); + const tbody = document.createElement("tbody"); + table.appendChild(thead); + table.appendChild(tbody); + habitatbox.appendChild(table); + + //loop through result to fetch the habitat number + if (r["habitats"]) { + for (let i = 0; i < r["habitats"].length; i++) { + let tr = document.createElement("tr"); + let num = r["habitats"][i]; + let td = document.createElement("td"); + let habitat_num = `Habitat ${num.number}`; + td.innerText = `${habitat_num}`; + tr.appendChild(td); + + //loop to get the specific creature in specific habitat to get the name for the image + //append each image in the specific tr of the table + + for (let j = 0; j < r["habitats"][i]["creatures"].length; j++) { + let td2 = document.createElement("td"); + let crtr_name = r["habitats"][i]["creatures"][j]["nickname"]; + let img_name = crtr_name.toLowerCase(); + let a = `/images/${img_name}.png`; + td2.innerHTML = ``; + tr.appendChild(td2); + } + //append the habitat name in the table + tbody.appendChild(tr); + } + } + + // Display unplaced creatures + if (r["unplaced"] && r["unplaced"].length > 0) { + let tr = document.createElement("tr"); + let td = document.createElement("td"); + td.innerText = "Unplaced"; + tr.appendChild(td); + + for (let j = 0; j < r["unplaced"].length; j++) { + let td2 = document.createElement("td"); + let crtr_name = r["unplaced"][j]["nickname"]; + let img_name = crtr_name.toLowerCase(); + let a = `/images/${img_name}.png`; + td2.innerHTML = ``; + tr.appendChild(td2); + } + tbody.appendChild(tr); + } +} + +async function move_creature(value1, value2) { + let result = await get_habitat(); + console.log(result, "this is the result of move"); + let current_habitat = []; + + // habitat creatures + for (let i = 0; i < result["habitats"].length; i++) { + let serial_num = i + 1; + let target_habitat_id = result["habitats"][i]["id"]; + for (let j = 0; j < result["habitats"][i]["creatures"].length; j++) { + let crtr_name = result["habitats"][i]["creatures"][j]["nickname"]; + let crtr_id = result["habitats"][i]["creatures"][j]["id"]; + + response = { + id: `${serial_num}`, + creaturename: `${crtr_name}`, + creatureid: `${crtr_id}`, + habitat_id: `${target_habitat_id}`, + }; + current_habitat.push(response); + } + } + + // unplaced creatures + if (result["unplaced"]) { + for (let j = 0; j < result["unplaced"].length; j++) { + let crtr_name = result["unplaced"][j]["nickname"]; + let crtr_id = result["unplaced"][j]["id"]; + + response = { + id: "unplaced", + creaturename: `${crtr_name}`, + creatureid: `${crtr_id}`, + habitat_id: null, + }; + current_habitat.push(response); + } + } + console.log(value1, value2, "crt name and habittat id in move fn"); + // {id: '3', creaturename: 'Obscurine', creatureid: '83', habitat_id: '7'} 'this is inside the 6 loop of current_habitat in move_crtr fn' + // return (current_habitat) + + let final_habitat_id; + let final_creature_id; + if (value2.startsWith("Habitat")) { + let hab_num = +value2.split(" ")[1]; + for (let h of result.habitats) { + if (h.number === hab_num) { + final_habitat_id = +h.id; + } + } + } else if (value2 === "Unplaced") { + final_habitat_id = null; + } + + for (let i = 0; i < current_habitat.length; i++) { + console.log( + current_habitat[i], + `this is inside the ${i} loop of current_habitat in move_crtr fn` + ); + let crt = current_habitat[i].creaturename; + let hbt = current_habitat[i]; + + if (crt.toLowerCase() === value1.toLowerCase()) { + final_creature_id = +hbt.creatureid; + } + } + + const move_result = await move_habitat_API( + final_creature_id, + final_habitat_id + ); + console.log(move_result, "response api"); + if (move_result["messages"] && move_result["messages"][1]) { + console.log(move_result["messages"][1]); + } + + if (move_result.success) { + await put_habitat(); + } + return move_result; +} + +get_habitat(); + +put_habitat(); + +const backBtn = document.getElementById("Backbtn"); +if (backBtn) { + backBtn.addEventListener("click", () => { + location.href = "/game" + location.search; + }); +} diff --git a/manage_page/static/managepage.png b/manage_page/static/managepage.png new file mode 100644 index 0000000..834af03 Binary files /dev/null and b/manage_page/static/managepage.png differ diff --git a/manage_page/static/spriggle.png b/manage_page/static/spriggle.png new file mode 100644 index 0000000..b689e14 Binary files /dev/null and b/manage_page/static/spriggle.png differ diff --git a/manage_page/templates/manage.html b/manage_page/templates/manage.html new file mode 100644 index 0000000..d1260e2 --- /dev/null +++ b/manage_page/templates/manage.html @@ -0,0 +1,22 @@ + + + + + + Creature Catcher + + + + +

Manage Habitat

+ +
+
+ +
+ + +
+
+ + diff --git a/static/backgroundimg.jpg b/static/backgroundimg.jpg new file mode 100644 index 0000000..49a767d Binary files /dev/null and b/static/backgroundimg.jpg differ diff --git a/static/game.css b/static/game.css new file mode 100644 index 0000000..15f684a --- /dev/null +++ b/static/game.css @@ -0,0 +1,221 @@ +body { + padding: 20px; + line-height: 1.5; +} +.container { + max-width: 800px; + margin: 0 auto; +} +.page, +.hidden, +.popup.hidden { + display: none; +} +.page.active { + display: block; +} +.popup { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 200; +} +.btn, +input { + width: 100%; + padding: 10px; + border-radius: 6px; + border: 4px; + outline: none; + font-size: 16px; + margin-bottom: 10px; + display: block; + font-size: 16px; + text-decoration: none; + text-align: center; + box-sizing: border-box; +} + +.popup-content { + background-color: #795c34; + padding: 40px; + border-radius: 20px; + border: 3px solid #5a4222; + text-align: center; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3); + max-width: 400px; + width: 90%; +} + +#popupMessage { + color: #fcd34d; + font-size: 1.5rem; + margin-bottom: 20px; + font-family: "Pixelify Sans", sans-serif; + text-shadow: 1px 1px 0px #5a4222; +} + +#popupOkBtn { + background-color: white; + color: #4ade80; + font-weight: bold; + font-size: 1.2rem; + padding: 10px 30px; + border-radius: 50px; + border: none; + cursor: pointer; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + width: auto; + display: inline-block; +} + +.creature-item { + padding: 5px 0; + cursor: pointer; +} +.journal-item { + text-align: center; + padding: 10px; +} +.journal-item img { + width: 100%; + height: auto; + margin-bottom: 5px; +} +h1, +h3 { + margin-bottom: 15px; + font-weight: normal; +} +#explorePage.active { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 50; + display: flex; + flex-direction: column; + align-items: center; + overflow-y: auto; + padding: 20px 0; + box-sizing: border-box; + background-color: #e3d5b0; + background-image: url("backgroundimg.jpg"); + background-size: cover; + background-position: center; +} + +.explore-modal { + background-color: #795c34; + padding: 40px 60px; + border-radius: 20px; + border: 3px solid #5a4222; + text-align: center; + max-width: 600px; + width: 90%; + margin: auto; +} + +.explore-modal h1 { + color: #fcd34d; + font-size: 3.5rem; + margin-bottom: 10px; + font-family: "Pixelify Sans", sans-serif; +} + +.explore-modal p { + color: #d1b06b; + font-size: 1.2rem; + margin-bottom: 30px; +} + +#companionList { + display: flex; + flex-direction: column; + gap: 15px; + align-items: center; +} + +#companionList .btn, +#wildList .btn { + background-color: white; + color: #4ade80; + font-weight: bold; + font-size: 1.2rem; + padding: 15px 40px; + border-radius: 50px; + border: none; + cursor: pointer; + width: auto; +} + +#wildBackBtn, +#exploreBackBtn { + background-color: #ef4444; + color: white; + font-weight: bold; + padding: 10px 30px; + border-radius: 25px; + border: none; + cursor: pointer; + margin-top: 20px; +} + +#mainPage.active { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 40; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + font-family: "Pixelify Sans", sans-serif; + text-align: center; + color: #8b4513; + background-image: url("main_background.jpg"); + background-size: cover; + background-position: center; + font-size: 40px; + text-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3); +} + +#mainPage ul { + list-style: none; + padding: 0; + margin: 0; +} + +#mainPage button { + padding: 40px 300px; + margin: 4px; + width: 200px; + height: 60px; + background-color: #7ed957; + color: #8b4513; + border: none; + border-radius: 2px; + cursor: pointer; + font-family: "Pixelify Sans", sans-serif; + font-size: 22px; + text-align: center; + white-space: nowrap; + line-height: 10px; + display: flex; + justify-content: center; + align-items: center; +} + +#mainPage button:hover { + background-color: darkolivegreen; + color: white +} diff --git a/static/game.js b/static/game.js new file mode 100644 index 0000000..327d51d --- /dev/null +++ b/static/game.js @@ -0,0 +1,201 @@ +let playerId = null; +let playerUsername = null; +let currentCompanions = []; +let currentWildCreatures = []; +let currentCompanionId = null; +let currentHabitatsData = null; + +document.addEventListener("DOMContentLoaded", () => { + const params = new URLSearchParams(location.search); + playerId = params.get("player_id"); + playerUsername = params.get("username"); + + if (!playerId) { + location.href = "/"; + } else { + showPage("mainPage"); + } +}); + +function showPage(pageId) { + document.querySelectorAll(".page").forEach((page) => { + page.classList.remove("active"); + }); + document.getElementById(pageId).classList.add("active"); +} + +function showPopup(message) { + const popup = document.getElementById("popup"); + const popupMessage = document.getElementById("popupMessage"); + popupMessage.textContent = message; + popup.classList.remove("hidden"); +} + +function hidePopup() { + document.getElementById("popup").classList.add("hidden"); +} + +async function loadExplorePage() { + showPage("explorePage"); + + try { + const response = await fetch(`/explore/start?player_id=${playerId}`); + const data = await response.json(); + + if (data.success && data.companions) { + currentCompanions = data.companions; + const companionList = document.getElementById("companionList"); + companionList.innerHTML = ""; + + data.companions.forEach((comp) => { + const btn = document.createElement("button"); + btn.className = "btn"; + btn.textContent = `${comp.nickname} (${comp.type})`; + btn.onclick = () => selectCompanion(comp.id); + companionList.appendChild(btn); + }); + + document.getElementById("companionSelection").classList.remove("hidden"); + document.getElementById("wildSelection").classList.add("hidden"); + } else { + showPopup( + data.messages ? data.messages.join("\n") : "Cannot explore", + false + ); + showPage("mainPage"); + } + } catch (error) { + showPopup("Error: " + error, false); + showPage("mainPage"); + } finally { + } +} + +async function selectCompanion(companionId) { + currentCompanionId = companionId; + + try { + const response = await fetch("/explore/encounter", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + player_id: playerId, + companion_id: companionId, + }), + }); + + const data = await response.json(); + + if (data.complete) { + showPopup("Congratulations! You've caught them all!", true); + showPage("mainPage"); + return; + } + + if (data.success && data.wild_creatures) { + currentWildCreatures = data.wild_creatures; + const wildList = document.getElementById("wildList"); + wildList.innerHTML = ""; + + data.wild_creatures.forEach((wild) => { + const btn = document.createElement("button"); + btn.className = "btn"; + btn.textContent = `${wild.name} (${wild.type}) - ${wild.status}`; + btn.onclick = () => catchCreature(wild.id, wild.effectiveness); + wildList.appendChild(btn); + }); + + document.getElementById("companionSelection").classList.add("hidden"); + document.getElementById("wildSelection").classList.remove("hidden"); + } + } catch (error) { + showPopup("Error: " + error, false); + } finally { + } +} + +async function catchCreature(wildCreatureId, effectiveness) { + try { + const response = await fetch("/explore/catch", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + player_id: playerId, + wild_creature_id: wildCreatureId, + companion_id: currentCompanionId, + effectiveness: effectiveness, + }), + }); + + const data = await response.json(); + + if (data.success) { + const message = data.caught + ? "Success! Creature caught!" + : "Failed! Creature escaped."; + showPopup(message, data.caught); + document.getElementById("wildBackBtn").addEventListener("click", () => { + hidePopup(); + showPage("mainPage"); + }); + } + } catch (error) { + showPopup("Error: " + error, false); + } finally { + } +} +async function completeCheck() { + if (!playerId) return false; + try { + const response = await fetch(`/check_completion?player_id=${playerId}`); + const data = await response.json(); + if (data.success && data.completed) { + location.href = "/congrat"; + return true; + } + } catch (error) { + console.error("Error checking completion:", error); + } + return false; +} + +// action +document.getElementById("exploreBtn").addEventListener("click", async () => { + const Done = await completeCheck(); + if (!Done) { + loadExplorePage(); + } +}); + +document.getElementById("manageBtn").addEventListener("click", () => { + location.href = "/manage" + location.search; +}); +document.getElementById("journalBtn").addEventListener("click", () => { + location.href = `/journal_page/viewjournal.html?player_id=${playerId}`; +}); +document.getElementById("exitBtn").addEventListener("click", async () => { + const Done = await completeCheck(); + if (!Done) { + if (confirm("Are you sure you want to exit?")) { + location.href = "/exit"; + } + } +}); +document.getElementById("wildBackBtn").addEventListener("click", () => { + if (!document.getElementById("wildSelection").classList.contains("hidden")) { + loadExplorePage(); + } else { + showPage("mainPage"); + } +}); +document.getElementById("exploreBackBtn").addEventListener("click", () => { + showPage("mainPage"); +}); +document.getElementById("popupOkBtn").addEventListener("click", () => { + hidePopup(); + showPage("mainPage"); +}); diff --git a/static/main_background.jpg b/static/main_background.jpg new file mode 100644 index 0000000..f03ebd0 Binary files /dev/null and b/static/main_background.jpg differ diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..27f670e --- /dev/null +++ b/templates/index.html @@ -0,0 +1,58 @@ + + + + + + Creature Catcher + + + + +
+ +
+

Main Page

+
    +
  • +
  • +
  • +
  • +
+
+ + +
+
+

Explore

+
+ +
+

Choose companions:

+
+ +
+ + +
+
+
+ + + +
+ + + +