init
This commit is contained in:
		
						commit
						53728db742
					
				
					 9 changed files with 755 additions and 0 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | __pycache__/ | ||||||
|  | save.pickle | ||||||
|  | venv/ | ||||||
							
								
								
									
										117
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,117 @@ | ||||||
|  | # Scoundrel | ||||||
|  | 
 | ||||||
|  | This is a terminal port of the rogue-lite card game Scoundrel. I got really into Balatro for a week | ||||||
|  | and immediately started wanting to code a game. I stumbled upon a youtube video for a rogue-lite | ||||||
|  | card game you can play with just a deck of cards and decided to port it to the terminal as a preliminary exercise before I begin to write a new game (which may never happen, I have 5 other pressing projects that take priority over game development). | ||||||
|  | 
 | ||||||
|  | # Installation | ||||||
|  | 
 | ||||||
|  | You must have python installed on your machine... | ||||||
|  | 
 | ||||||
|  | 1. clone the repository | ||||||
|  | 2. cd into the repository and create a virtual environment `python3 -m venv venv` | ||||||
|  | 3. activate the virtual environment `source venv/bin/activate` | ||||||
|  | 4. install requirements `pip install -r requirements.txt` | ||||||
|  | 5. run the game `python3 main.py` | ||||||
|  | 
 | ||||||
|  | # Rules  | ||||||
|  | ### 1. INTRODUCTION: | ||||||
|  | 
 | ||||||
|  | Welcome to Scoundrel! The solo rogue-lite dungeon crawler card game! | ||||||
|  | This is the unneccessary terminal port of the game by Dominic DiTaranto. | ||||||
|  | If you enjoy this port, check out my website for other cool stuff | ||||||
|  | https://www.domdit.com | ||||||
|  | 
 | ||||||
|  | Table Of Contents: | ||||||
|  | 1. INTRODUCTION | ||||||
|  | 3. CONTROLS | ||||||
|  | 2. RULES | ||||||
|  | 
 | ||||||
|  | ### 2. CONTROLS | ||||||
|  | 
 | ||||||
|  | Arrow Keys    -  Select a Card | ||||||
|  | 
 | ||||||
|  | Enter Button  -  Engage with a Card | ||||||
|  | 
 | ||||||
|  | s             -  Skip a Room | ||||||
|  | 
 | ||||||
|  | b             -  Fight Barehanded | ||||||
|  | 
 | ||||||
|  | p             -  Pause/Unpause | ||||||
|  | 
 | ||||||
|  | h             -  See High Scores (from start menu) | ||||||
|  | 
 | ||||||
|  | u             -  Change User Name (from start menu) | ||||||
|  | 
 | ||||||
|  | Esc           -  Exit Game | ||||||
|  | 
 | ||||||
|  | ?             -  Takes you to this help page. | ||||||
|  | 
 | ||||||
|  | ### 3.a RULES - Setup and Rules | ||||||
|  | 
 | ||||||
|  | You are a dungeon explorer making your way through a series of rooms. | ||||||
|  | A room is made up of 4 cards. | ||||||
|  | 
 | ||||||
|  | In each room you may encounter: | ||||||
|  | - Equippable Weapons (Diamond Suit Cards) | ||||||
|  | - Health Potions (Heart Suit Cards) | ||||||
|  | - Enemies (Spade and Club Suit Cards) | ||||||
|  | - Merchants (Joker Cards) [Not Yet Implemented] | ||||||
|  | 
 | ||||||
|  | Your goal is to traverse the rooms until the deck runs out of cards. | ||||||
|  | 
 | ||||||
|  | ### 3.b RULES - Selecting a Card | ||||||
|  | 
 | ||||||
|  | When you enter a room, select a card by navigating to it with the arrow keys | ||||||
|  | and pressing Enter. | ||||||
|  | 
 | ||||||
|  | If the card is a weapon, you equip that weapon. Each weapon does as much damage | ||||||
|  | as its value. Picking up a weapon will automatically discard your currently equipped weapon. | ||||||
|  | 
 | ||||||
|  | If the card is a health potion, you will gain life equal to the value of the card. | ||||||
|  | Your life may not exceed 20.  | ||||||
|  | 
 | ||||||
|  | You can skip a room by pressing s, but you cannot skip two rooms in a row. the room  | ||||||
|  | that you skip gets moved to the bottom of the deck to be traversed through later. | ||||||
|  | 
 | ||||||
|  | ### 3.c RULES - Combat | ||||||
|  | 
 | ||||||
|  | If you select an enemy, you enter the combat phase. | ||||||
|  | 
 | ||||||
|  | If you do not have a weapon equipped you will take damage to your life points | ||||||
|  | equal to the value of the enemy card. This is called barehanded combat. | ||||||
|  | 
 | ||||||
|  | If you have an equipped weapon and the value of the weapon is greater than the enemy value | ||||||
|  | You do not take any damage to your health. Your weapon becomes damaged and you can only  | ||||||
|  | attack enemies with a value lower than the previous enemy's value. This is the weapons damage rating. | ||||||
|  | 
 | ||||||
|  | If you attack an enemy with a weapon that has a value lower than the enemy's value, you | ||||||
|  | take damage equal to the enemy value - the weapon value. Example: if your weapon is a 5  | ||||||
|  | and the enemy is an 8, you will take 3 damage. | ||||||
|  | 
 | ||||||
|  | ### 3.d RULES - Combat Continued | ||||||
|  | 
 | ||||||
|  | If you attack an enemy with a value higher than the weapon's damage rating, | ||||||
|  | you take all of that damage as if it were barehanded. You will lose life equal  | ||||||
|  | to the value of the enemy. | ||||||
|  | 
 | ||||||
|  | You should only attack enemies with a weapon that has a damage rating above or equal | ||||||
|  | to the enemy's value. There will be situations where you have no choice but to take | ||||||
|  | the barehanded damage combat. This is part of the strategy element. | ||||||
|  | 
 | ||||||
|  | Your Weapon's damage rating is shown in the brackets next to the weapon. | ||||||
|  | 
 | ||||||
|  | When a room only has 1 card left, you move on to the next room. That card follows you | ||||||
|  | into the next room | ||||||
|  | 
 | ||||||
|  | ### 3.e RULES - Scoring | ||||||
|  | 
 | ||||||
|  | If you die, the remaining monster's values are subtracted from your score.  | ||||||
|  | The negative value is your score. | ||||||
|  | 
 | ||||||
|  | If you beat the whole dungeon, your score is your positive life points. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Future Updates | ||||||
|  | - Endless mode: reshuffle the deck and keep playing | ||||||
|  | - Jokers as Merchants: Add jokers back to the deck and have them either buff you or hurt you based on RNG. | ||||||
							
								
								
									
										41
									
								
								assets/ascii_art.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								assets/ascii_art.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | ||||||
|  | START_SCREEN = ''' | ||||||
|  |    ▄████████  ▄████████  ▄██████▄  ███    █▄  ███▄▄▄▄   ████████▄     ▄████████    ▄████████  ▄█        | ||||||
|  |   ███    ███ ███    ███ ███    ███ ███    ███ ███▀▀▀██▄ ███   ▀███   ███    ███   ███    ███ ███        | ||||||
|  |   ███    █▀  ███    █▀  ███    ███ ███    ███ ███   ███ ███    ███   ███    ███   ███    █▀  ███        | ||||||
|  |   ███        ███        ███    ███ ███    ███ ███   ███ ███    ███  ▄███▄▄▄▄██▀  ▄███▄▄▄     ███        | ||||||
|  | ▀███████████ ███        ███    ███ ███    ███ ███   ███ ███    ███ ▀▀███▀▀▀▀▀   ▀▀███▀▀▀     ███        | ||||||
|  |          ███ ███    █▄  ███    ███ ███    ███ ███   ███ ███    ███ ▀███████████   ███    █▄  ███        | ||||||
|  |    ▄█    ███ ███    ███ ███    ███ ███    ███ ███   ███ ███   ▄███   ███    ███   ███    ███ ███▌    ▄  | ||||||
|  |  ▄████████▀  ████████▀   ▀██████▀  ████████▀   ▀█   █▀  ████████▀    ███    ███   ██████████ █████▄▄██  | ||||||
|  |                                                                      ███    ███              ▀          | ||||||
|  |     Port by Dominic Ditaranto (https://www.domdit.com)                                          v1.0.0 | ||||||
|  |     Press ? for Rules and Controls | Press Enter to Start | ||||||
|  |     Press u to Set Your Username! (Currently: {username}) | ||||||
|  |     Press h to See High Score List! | ||||||
|  | 
 | ||||||
|  |                                          | ||||||
|  |      | ||||||
|  | ''' | ||||||
|  | 
 | ||||||
|  | GAME_OVER = ''' | ||||||
|  |                 SCORE: {score} | ||||||
|  |    ▄██████▄     ▄████████   ▄▄▄▄███▄▄▄▄      ▄████████  | ||||||
|  |   ███    ███   ███    ███ ▄██▀▀▀███▀▀▀██▄   ███    ███  | ||||||
|  |   ███    █▀    ███    ███ ███   ███   ███   ███    █▀   | ||||||
|  |  ▄███          ███    ███ ███   ███   ███  ▄███▄▄▄      | ||||||
|  | ▀▀███ ████▄  ▀███████████ ███   ███   ███ ▀▀███▀▀▀      | ||||||
|  |   ███    ███   ███    ███ ███   ███   ███   ███    █▄   | ||||||
|  |   ███    ███   ███    ███ ███   ███   ███   ███    ███  | ||||||
|  |   ████████▀    ███    █▀   ▀█   ███   █▀    ██████████  | ||||||
|  |                                                         | ||||||
|  |  ▄██████▄   ▄█    █▄     ▄████████    ▄████████         | ||||||
|  | ███    ███ ███    ███   ███    ███   ███    ███         | ||||||
|  | ███    ███ ███    ███   ███    █▀    ███    ███         | ||||||
|  | ███    ███ ███    ███  ▄███▄▄▄      ▄███▄▄▄▄██▀         | ||||||
|  | ███    ███ ███    ███ ▀▀███▀▀▀     ▀▀███▀▀▀▀▀           | ||||||
|  | ███    ███ ███    ███   ███    █▄  ▀███████████         | ||||||
|  | ███    ███ ███    ███   ███    ███   ███    ███         | ||||||
|  |  ▀██████▀   ▀██████▀    ██████████   ███    ███         | ||||||
|  |                                      ███    ███         | ||||||
|  | Press Enter To Return To Start Page | Press Esc to Quit | ||||||
|  | ''' | ||||||
							
								
								
									
										116
									
								
								assets/help.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								assets/help.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,116 @@ | ||||||
|  | PAGE_ONE = ''' | ||||||
|  | 1. INTRODUCTION: | ||||||
|  | 
 | ||||||
|  | Welcome to Scoundrel! The solo rogue-lite dungeon crawler card game! | ||||||
|  | This is the unneccessary terminal port of the game by Dominic DiTaranto. | ||||||
|  | If you enjoy this port, check out my website for other cool stuff | ||||||
|  | https://www.domdit.com | ||||||
|  | 
 | ||||||
|  | Table Of Contents: | ||||||
|  | 1. INTRODUCTION | ||||||
|  | 3. CONTROLS | ||||||
|  | 2. RULES | ||||||
|  | 
 | ||||||
|  | Use the up and down arrow keys to scroll through the help pages.. | ||||||
|  | 
 | ||||||
|  |                                 1 | ||||||
|  | ''' | ||||||
|  | 
 | ||||||
|  | PAGE_TWO = ''' | ||||||
|  | 2. CONTROLS | ||||||
|  | 
 | ||||||
|  | Arrow Keys      Select a Card | ||||||
|  | Enter Button    Engage with a Card | ||||||
|  | s               Skip a Room | ||||||
|  | b               Fight Barehanded | ||||||
|  | p               Pause/Unpause | ||||||
|  | h               See High Scores (from start menu) | ||||||
|  | u               Change User Name (from start menu) | ||||||
|  | Esc             Exit Game | ||||||
|  | ?               Takes you to this help page. | ||||||
|  |                                 2 | ||||||
|  | ''' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | PAGE_THREE = ''' | ||||||
|  | 3.a RULES - Setup and Rules | ||||||
|  | 
 | ||||||
|  | You are a dungeon explorer making your way through a series of rooms. | ||||||
|  | A room is made up of 4 cards. | ||||||
|  | 
 | ||||||
|  | In each room you may encounter: | ||||||
|  | - Equippable Weapons (Diamond Suit Cards) | ||||||
|  | - Health Potions (Heart Suit Cards) | ||||||
|  | - Enemies (Spade and Club Suit Cards) | ||||||
|  | - Merchants (Joker Cards) [Not Yet Implemented] | ||||||
|  | 
 | ||||||
|  | Your goal is to traverse the rooms until the deck runs out of cards. | ||||||
|  |                                 3 | ||||||
|  | ''' | ||||||
|  | 
 | ||||||
|  | PAGE_FOUR = ''' | ||||||
|  | 3.b RULES - Selecting a Card | ||||||
|  | 
 | ||||||
|  | When you enter a room, select a card by navigating to it with the arrow keys | ||||||
|  | and pressing Enter. | ||||||
|  | 
 | ||||||
|  | If the card is a weapon, you equip that weapon. Each weapon does as much damage | ||||||
|  | as its value. Picking up a weapon will automatically discard your currently equipped weapon. | ||||||
|  | 
 | ||||||
|  | If the card is a health potion, you will gain life equal to the value of the card. | ||||||
|  | Your life may not exceed 20.  | ||||||
|  | 
 | ||||||
|  | You can skip a room by pressing s, but you cannot skip two rooms in a row. the room  | ||||||
|  | that you skip gets moved to the bottom of the deck to be traversed through later. | ||||||
|  |                                 4 | ||||||
|  | ''' | ||||||
|  | 
 | ||||||
|  | PAGE_FIVE = ''' | ||||||
|  | 3.c RULES - Combat | ||||||
|  | 
 | ||||||
|  | If you select an enemy, you enter the combat phase. | ||||||
|  | 
 | ||||||
|  | If you do not have a weapon equipped you will take damage to your life points | ||||||
|  | equal to the value of the enemy card. This is called barehanded combat. | ||||||
|  | 
 | ||||||
|  | If you have an equipped weapon and the value of the weapon is greater than the enemy value | ||||||
|  | You do not take any damage to your health. Your weapon becomes damaged and you can only  | ||||||
|  | attack enemies with a value lower than the previous enemy's value. This is the weapons damage rating. | ||||||
|  | 
 | ||||||
|  | If you attack an enemy with a weapon that has a value lower than the enemy's value, you | ||||||
|  | take damage equal to the enemy value - the weapon value. Example: if your weapon is a 5  | ||||||
|  | and the enemy is an 8, you will take 3 damage. | ||||||
|  |                                 5 | ||||||
|  | ''' | ||||||
|  | 
 | ||||||
|  | PAGE_SIX = ''' | ||||||
|  | 3.d RULES - Combat Continued | ||||||
|  | 
 | ||||||
|  | If you attack an enemy with a value higher than the weapon's damage rating, | ||||||
|  | you take all of that damage as if it were barehanded. You will lose life equal  | ||||||
|  | to the value of the enemy. | ||||||
|  | 
 | ||||||
|  | You should only attack enemies with a weapon that has a damage rating above or equal | ||||||
|  | to the enemy's value. There will be situations where you have no choice but to take | ||||||
|  | the barehanded damage combat. This is part of the strategy element. | ||||||
|  | 
 | ||||||
|  | Your Weapon's damage rating is shown in the brackets next to the weapon. | ||||||
|  | 
 | ||||||
|  | When a room only has 1 card left, you move on to the next room. That card follows you | ||||||
|  | into the next room | ||||||
|  |                                 6 | ||||||
|  | ''' | ||||||
|  | 
 | ||||||
|  | PAGE_SEVEN = ''' | ||||||
|  | 3.e RULES - Scoring | ||||||
|  | 
 | ||||||
|  | If you die, the remaining monster's values are subtracted from your score.  | ||||||
|  | The negative value is your score. | ||||||
|  | 
 | ||||||
|  | If you beat the whole dungeon, your score is your positive life points. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |                                 7 | ||||||
|  | ''' | ||||||
							
								
								
									
										38
									
								
								card.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								card.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | ||||||
|  | from colorama import Fore, Back, Style | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Card: | ||||||
|  |     def __init__(self, value, suit): | ||||||
|  |         self.value = value | ||||||
|  |         self.suit = suit | ||||||
|  |         self.selected = False | ||||||
|  | 
 | ||||||
|  |         self.suit_map = { | ||||||
|  |             0: '♠', | ||||||
|  |             1: '❤', | ||||||
|  |             2: '♣', | ||||||
|  |             3: '♦' | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         self.value_map = { | ||||||
|  |             1: 'A', | ||||||
|  |             11: 'J', | ||||||
|  |             12: 'Q', | ||||||
|  |             13: 'K' | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     def display_card(self): | ||||||
|  |         return f"{Back.WHITE}{Fore.RED if self.suit in [1,3] else Fore.BLUE}{self.value_map.get(self.value, self.value)}{self.suit_map[self.suit]} {Style.RESET_ALL}" | ||||||
|  | 
 | ||||||
|  |     def __repr__(self): | ||||||
|  |         s = Back.WHITE if not self.selected else Back.GREEN | ||||||
|  |         return f"{s}{Fore.RED if self.suit in [1,3] else Fore.BLUE}{self.value_map.get(self.value, self.value)}{self.suit_map[self.suit]} {Style.RESET_ALL}" | ||||||
|  | 
 | ||||||
|  |     def help_text(self, weapon=None): | ||||||
|  |         if self.suit in [0, 2]: | ||||||
|  |             return f'Attack {self.display_card()} {"barehanded?" if not weapon else "with {}? [b]".format(weapon)}' | ||||||
|  |         elif self.suit == 1: | ||||||
|  |             return f"Use {self.display_card()} as a health potion?" | ||||||
|  |         elif self.suit == 3: | ||||||
|  |             return f"Equip {self.display_card()} as a weapon?" | ||||||
|  |      | ||||||
							
								
								
									
										29
									
								
								deck.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								deck.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | ||||||
|  | import random | ||||||
|  | from collections import deque | ||||||
|  | 
 | ||||||
|  | from card import Card | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Deck: | ||||||
|  |     def __init__(self): | ||||||
|  |         self.deck = deque() | ||||||
|  | 
 | ||||||
|  |     def generate_deck(self): | ||||||
|  |         for suit in range(4): | ||||||
|  |             if suit in [0, 2]: | ||||||
|  |                 for i in range(1, 14): | ||||||
|  |                     c = Card(i, suit) | ||||||
|  |                     self.deck.append(c) | ||||||
|  |             elif suit in [1, 3]: | ||||||
|  |                 for i in range(2, 11): | ||||||
|  |                     c = Card(i, suit) | ||||||
|  |                     self.deck.append(c) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def shuffle(self): | ||||||
|  |         random.shuffle(self.deck) | ||||||
|  | 
 | ||||||
|  |     def draw(self): | ||||||
|  |         if len(self.deck) > 0: | ||||||
|  |             return self.deck.popleft() | ||||||
|  | 
 | ||||||
							
								
								
									
										240
									
								
								game.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								game.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,240 @@ | ||||||
|  | import os | ||||||
|  | import pickle | ||||||
|  | import sys | ||||||
|  | import time | ||||||
|  | 
 | ||||||
|  | from pynput.keyboard import Key, Listener, KeyCode | ||||||
|  | 
 | ||||||
|  | from assets.help import PAGE_ONE, PAGE_TWO, PAGE_THREE, PAGE_FOUR, PAGE_FIVE, PAGE_SIX, PAGE_SEVEN | ||||||
|  | from deck import Deck | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Game: | ||||||
|  |     def __init__(self, username='Wayne Skyler'): | ||||||
|  |         self.username = username | ||||||
|  | 
 | ||||||
|  |         self.deck = Deck() | ||||||
|  |         self.current_room_cards = [] | ||||||
|  |         self.selected_card = 0 | ||||||
|  |         self.graveyard = [] | ||||||
|  | 
 | ||||||
|  |         self.paused = False | ||||||
|  | 
 | ||||||
|  |         self.room = 1 | ||||||
|  |         self.block_skip = False | ||||||
|  | 
 | ||||||
|  |         self.weapon = None | ||||||
|  |         self.defeated = [] | ||||||
|  | 
 | ||||||
|  |         self.max_health = 20 | ||||||
|  |         self.health = 20 | ||||||
|  | 
 | ||||||
|  |         self.history = [] | ||||||
|  | 
 | ||||||
|  |         self.help_screens = [PAGE_ONE, PAGE_TWO, PAGE_THREE, PAGE_FOUR, PAGE_FIVE, PAGE_SIX, PAGE_SEVEN] | ||||||
|  |         self.current_help_screen = 0 | ||||||
|  |         self.on_help_screen = False | ||||||
|  |         self.from_screen = None | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         self.score = len(self.deck.deck) * -1 | ||||||
|  |         self.save_file = 'save.pickle' | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def current_card(self): | ||||||
|  |         return self.current_room_cards[self.selected_card] | ||||||
|  | 
 | ||||||
|  |     def initialize(self): | ||||||
|  |         self.deck = Deck() | ||||||
|  |         self.deck.generate_deck() | ||||||
|  |         self.deck.shuffle() | ||||||
|  |          | ||||||
|  |         while len(self.current_room_cards) < 4 and len(self.deck.deck) > 0: | ||||||
|  |             self.current_room_cards.append(self.deck.draw()) | ||||||
|  | 
 | ||||||
|  |         self.current_room_cards[0].selected = True | ||||||
|  | 
 | ||||||
|  |     def display(self): | ||||||
|  |         os.system('clear') | ||||||
|  |         print(f"Current Room: {self.room} | Health: {self.health}/{self.max_health} | ? for Rules & Controls") | ||||||
|  |         print('\t'.join([str(x) for x in self.current_room_cards])) | ||||||
|  |         print('\n') | ||||||
|  |         print('\n') | ||||||
|  |         print(f"Weapon: {self.weapon if self.weapon else 'None'} {'[{}]'.format(' '.join([str(x) for x in self.defeated]) if self.graveyard else None)}") | ||||||
|  |         print(f"{self.current_card.help_text(self.weapon)} [Enter]") | ||||||
|  | 
 | ||||||
|  |     def flash_message(self, msg): | ||||||
|  |         for i in reversed(range(3)): | ||||||
|  |             os.system('clear') | ||||||
|  |             print(msg) | ||||||
|  |             print(f'\n\tWait {str(i+1)} Seconds...') | ||||||
|  |             time.sleep(1) | ||||||
|  | 
 | ||||||
|  |     def toggle_pause(self): | ||||||
|  |         self.paused = True if not self.paused else False | ||||||
|  | 
 | ||||||
|  |     def input(self): | ||||||
|  | 
 | ||||||
|  |         def on_press(key): | ||||||
|  |             self.history.append(key) | ||||||
|  | 
 | ||||||
|  |             if self.paused and key != KeyCode.from_char('p'): | ||||||
|  |                 return False | ||||||
|  | 
 | ||||||
|  |             if key == Key.right: | ||||||
|  |                 self.change_selected_card() | ||||||
|  |             elif key == Key.left: | ||||||
|  |                 self.change_selected_card(False) | ||||||
|  |             elif key == Key.enter: | ||||||
|  |                 if self.current_card.suit in [0, 2]: # Interact with selected card | ||||||
|  |                     self.attack() | ||||||
|  |                 elif self.current_card.suit == 1: | ||||||
|  |                     self.health_potion() | ||||||
|  |                 elif self.current_card.suit == 3: | ||||||
|  |                     self.equip_weapon() | ||||||
|  |             elif key == KeyCode.from_char('s'): # Skip Room | ||||||
|  |                 self.skip_room() | ||||||
|  |             elif key == KeyCode.from_char('b'): # Attack Barehanded | ||||||
|  |                 weapon_copy = self.weapon | ||||||
|  |                 self.weapon = None | ||||||
|  |                 self.attack() | ||||||
|  |                 self.weapon = weapon_copy | ||||||
|  |             elif key == KeyCode.from_char('p'): | ||||||
|  |                 self.toggle_pause() | ||||||
|  |             elif key == Key.esc: # Quit The Game | ||||||
|  |                 self.store_high_score() | ||||||
|  |                 sys.exit() | ||||||
|  |             if key == KeyCode.from_char('?'): | ||||||
|  |                 self.on_help_screen = True if not self.on_help_screen else False | ||||||
|  | 
 | ||||||
|  |             if key == Key.up: | ||||||
|  |                 if self.current_help_screen != 0: | ||||||
|  |                     self.current_help_screen -= 1 | ||||||
|  |                     self.help_screen() | ||||||
|  |             if key == Key.down: | ||||||
|  |                 if self.current_help_screen < len(self.help_screens) - 1: | ||||||
|  |                     self.current_help_screen += 1 | ||||||
|  |                     self.help_screen() | ||||||
|  | 
 | ||||||
|  |             return False | ||||||
|  | 
 | ||||||
|  |         with Listener(on_press=on_press) as listener: | ||||||
|  |             listener.join() | ||||||
|  | 
 | ||||||
|  |     def help_screen(self): | ||||||
|  |         os.system('clear') | ||||||
|  |         print(self.help_screens[self.current_help_screen]) | ||||||
|  | 
 | ||||||
|  |     def change_selected_card(self, right=True): | ||||||
|  |         self.current_card.selected = False | ||||||
|  |         if right: | ||||||
|  |             if self.selected_card != len(self.current_room_cards) - 1: | ||||||
|  |                 self.selected_card += 1 | ||||||
|  |         else: | ||||||
|  |             if self.selected_card > 0: | ||||||
|  |                 self.selected_card -= 1 | ||||||
|  |         self.current_card.selected = True | ||||||
|  | 
 | ||||||
|  |     def attack(self): | ||||||
|  |         if self.current_card.suit not in [0, 2]: | ||||||
|  |             self.flash_message('\n\n\n\tSelected card is not an enemy!') | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         if not self.weapon: | ||||||
|  |             self.health -= self.current_card.value | ||||||
|  |              | ||||||
|  |             if self.health > 0: | ||||||
|  |                 self.send_selected_to_grave() | ||||||
|  |         else: | ||||||
|  |             if self.defeated and self.defeated[-1].value < self.current_card.value: | ||||||
|  |                 self.health -= self.current_card.value | ||||||
|  |                 self.flash_message('\n\n\n\tYou should not have done that! \n\tYour weapon was not strong enough; causing you to take all of the damage!' ) | ||||||
|  |                 self.send_selected_to_grave() | ||||||
|  |                 return | ||||||
|  |             if self.weapon.value < self.current_card.value: | ||||||
|  |                 damage = self.current_card.value - self.weapon.value | ||||||
|  |                 self.health -= damage | ||||||
|  |                 if self.health <= 0: | ||||||
|  |                     pass | ||||||
|  |             self.defeated.append(self.current_card) | ||||||
|  |             self.send_selected_to_grave() | ||||||
|  |      | ||||||
|  |     def send_selected_to_grave(self): | ||||||
|  |         self.graveyard.append(self.current_room_cards.pop(self.selected_card)) | ||||||
|  |         self.check_win() | ||||||
|  |         self.selected_card = 0 | ||||||
|  |         if len(self.current_room_cards) == 1: | ||||||
|  |             self.advance_room() | ||||||
|  |         if len(self.current_room_cards) > 0: | ||||||
|  |             self.current_card.selected = True | ||||||
|  |         self.deselect_defeated() | ||||||
|  | 
 | ||||||
|  |     def health_potion(self): | ||||||
|  |         self.health += self.current_card.value | ||||||
|  |         if self.health > 20: | ||||||
|  |             self.health = 20 | ||||||
|  |         self.send_selected_to_grave() | ||||||
|  | 
 | ||||||
|  |     def equip_weapon(self): | ||||||
|  |         self.defeated = [] | ||||||
|  |         self.weapon = self.current_card | ||||||
|  |         self.current_card.selected = False | ||||||
|  |         self.send_selected_to_grave() | ||||||
|  | 
 | ||||||
|  |     def deselect_defeated(self): | ||||||
|  |         for i in self.defeated: | ||||||
|  |             i.selected = False | ||||||
|  | 
 | ||||||
|  |     def advance_room(self): | ||||||
|  |         self.block_skip = False | ||||||
|  |         self.room += 1 | ||||||
|  |          | ||||||
|  |         # TODO: This can be simplified with initialize room func | ||||||
|  |         while len(self.current_room_cards) < 4: | ||||||
|  |             self.current_room_cards.append(self.deck.draw()) | ||||||
|  |         self.current_room_cards[0].selected = True | ||||||
|  | 
 | ||||||
|  |     def skip_room(self): | ||||||
|  |         if self.block_skip: | ||||||
|  |             self.flash_message('\n\n\n\tYou cannot run away!') | ||||||
|  |         else: | ||||||
|  |             for card in self.current_room_cards: | ||||||
|  |                 card.selected = False | ||||||
|  |                 self.deck.deck.append(card) | ||||||
|  |             self.current_room_cards = [] | ||||||
|  |             self.advance_room() | ||||||
|  |             self.block_skip = True | ||||||
|  |             self.flash_message('\n\n\n\tRun Away, Coward!! 🤡') | ||||||
|  | 
 | ||||||
|  |     def cleanup(self): | ||||||
|  |         self.current_room_cards = [x for x in self.current_room_cards if x is not None] | ||||||
|  | 
 | ||||||
|  |     def store_high_score(self): | ||||||
|  |         self.calculate_score() | ||||||
|  |         with open(self.save_file, 'rb') as f: | ||||||
|  |             save_data = pickle.load(f) | ||||||
|  |             save_data['high_scores'].append((self.username, self.score)) | ||||||
|  |             sorted_high_scores = sorted(save_data['high_scores'], key=lambda x: x[1], reverse=True)[:9] | ||||||
|  |             save_data['high_scores'] = sorted_high_scores | ||||||
|  | 
 | ||||||
|  |         with open(self.save_file, 'wb') as f: | ||||||
|  |             pickle.dump(save_data, f) | ||||||
|  | 
 | ||||||
|  |     def calculate_score(self): | ||||||
|  |         self.score = 0 | ||||||
|  |         for card in self.current_room_cards + list(self.deck.deck): | ||||||
|  |             if card.suit in [0, 2]: | ||||||
|  |                 self.score -= card.value | ||||||
|  |          | ||||||
|  |         if self.score == 0: | ||||||
|  |             self.score += self.health | ||||||
|  |             for card in self.current_room_cards + list(self.deck.deck): | ||||||
|  |                 if card.suit == 1: | ||||||
|  |                     self.score += card.value | ||||||
|  | 
 | ||||||
|  |     def check_win(self): | ||||||
|  |         if len(self.deck.deck) <= 0 and len(self.current_room_cards) <= 0: | ||||||
|  |             self.calculate_score() | ||||||
|  |             self.store_high_score() | ||||||
|  |             return True | ||||||
|  | 
 | ||||||
							
								
								
									
										163
									
								
								main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								main.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,163 @@ | ||||||
|  | import os | ||||||
|  | import pickle | ||||||
|  | import sys | ||||||
|  | import termios | ||||||
|  | 
 | ||||||
|  | from game import Game | ||||||
|  | from assets.ascii_art import START_SCREEN, GAME_OVER | ||||||
|  | from assets.help import PAGE_ONE, PAGE_TWO, PAGE_THREE, PAGE_FOUR, PAGE_FIVE, PAGE_SIX, PAGE_SEVEN | ||||||
|  | 
 | ||||||
|  | from pynput.keyboard import Key, Listener, KeyCode | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Main: | ||||||
|  |     def __init__(self): | ||||||
|  |         self.screen_mode = True | ||||||
|  |         self.game = None | ||||||
|  |         self.username = 'Wayne Skyler' | ||||||
|  | 
 | ||||||
|  |         self.save_file = 'save.pickle' | ||||||
|  |         self.save_data = None | ||||||
|  | 
 | ||||||
|  |         self.help_screens = [PAGE_ONE, PAGE_TWO, PAGE_THREE, PAGE_FOUR, PAGE_FIVE, PAGE_SIX, PAGE_SEVEN] | ||||||
|  |         self.current_help_screen = 0 | ||||||
|  |         self.on_help_screen = False | ||||||
|  |         self.from_screen = None | ||||||
|  | 
 | ||||||
|  |         if not os.path.isfile(self.save_file): | ||||||
|  | 
 | ||||||
|  |             with open(self.save_file, 'wb') as f: | ||||||
|  |                 save_data = { | ||||||
|  |                     'username': self.username, | ||||||
|  |                     'high_scores': [] | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 pickle.dump(save_data, f) | ||||||
|  | 
 | ||||||
|  |         else: | ||||||
|  |             with open(self.save_file, 'rb') as f: | ||||||
|  |                 self.save_data = pickle.load(f) | ||||||
|  |                 self.username = self.save_data['username'] | ||||||
|  | 
 | ||||||
|  |     def main(self): | ||||||
|  |         self.screens() | ||||||
|  | 
 | ||||||
|  |     def screens(self, mode=None): | ||||||
|  |         while self.screen_mode: | ||||||
|  |             if self.game and mode != 'win': | ||||||
|  |                 if self.game.health <= 0: | ||||||
|  |                     os.system('clear') | ||||||
|  |                     self.game.store_high_score() | ||||||
|  |                     print(GAME_OVER.format(score=self.game.score)) | ||||||
|  |                     self.input(mode='game_over') | ||||||
|  |             else: | ||||||
|  |                 os.system('clear') | ||||||
|  |                 if mode == 'win': | ||||||
|  |                     self.game.health = 0 | ||||||
|  |                     self.game.flash_message(f'\n\n\n\twin. Congratulashon. {self.game.score}') | ||||||
|  |                     self.game = None | ||||||
|  |                     self.screens(mode=None) | ||||||
|  |                 else:  | ||||||
|  |                     print(START_SCREEN.format(username=self.username)) | ||||||
|  |                     self.input(mode='start') | ||||||
|  | 
 | ||||||
|  |     def gameplay(self): | ||||||
|  |         self.game = Game(username=self.username) | ||||||
|  |         self.game.initialize() | ||||||
|  |         while self.game.health > 0: | ||||||
|  |             if not self.game.paused and not self.game.on_help_screen: | ||||||
|  |                 self.game.cleanup() | ||||||
|  |                 if self.game.check_win(): | ||||||
|  |                     self.screen_mode = True | ||||||
|  |                     self.screens(mode='win') | ||||||
|  |                 if None in self.game.current_room_cards: | ||||||
|  |                     breakpoint() | ||||||
|  |                     self.game = None | ||||||
|  |                 self.game.display() | ||||||
|  |                 self.game.input() | ||||||
|  |             else: | ||||||
|  |                 if self.game.paused: | ||||||
|  |                     self.pause_game() | ||||||
|  |                     self.game.input() | ||||||
|  |                 elif self.game.on_help_screen: | ||||||
|  |                     self.game.help_screen() | ||||||
|  |                     self.game.input() | ||||||
|  | 
 | ||||||
|  |         self.screen_mode = True | ||||||
|  |         self.screens() | ||||||
|  | 
 | ||||||
|  |     def pause_game(self): | ||||||
|  |         os.system('clear') | ||||||
|  |         print('\n\n\t PAUSED') | ||||||
|  |         print('\t Press p to unpause...') | ||||||
|  | 
 | ||||||
|  |     def high_scores(self): | ||||||
|  |         with open(self.save_file, 'rb') as f: | ||||||
|  |             save_data = pickle.load(f) | ||||||
|  | 
 | ||||||
|  |         os.system('clear') | ||||||
|  |         print('\t\tHIGH SCORES\n') | ||||||
|  |         for score in save_data['high_scores']: | ||||||
|  |             print(f"\t{score[0]}\t{score[1]}") | ||||||
|  | 
 | ||||||
|  |         print('\n\tPress Enter to Return to Start Screen \n') | ||||||
|  | 
 | ||||||
|  |         self.input('game_over') | ||||||
|  | 
 | ||||||
|  |     def help_screen(self): | ||||||
|  |         os.system('clear') | ||||||
|  |         print(self.help_screens[self.current_help_screen]) | ||||||
|  | 
 | ||||||
|  |     def input(self, mode='start'): | ||||||
|  |         def on_press(key): | ||||||
|  |             termios.tcflush(sys.stdin, termios.TCIOFLUSH) | ||||||
|  |             if key == Key.enter: | ||||||
|  |                 if mode == 'start': | ||||||
|  |                     self.screen_mode = False | ||||||
|  |                     self.gameplay() | ||||||
|  |                 if mode == 'game_over': | ||||||
|  |                     self.game = None | ||||||
|  |                     self.screen_mode = True | ||||||
|  |                     self.screens() | ||||||
|  |             if key == Key.esc: | ||||||
|  |                 sys.exit() | ||||||
|  |             if key == KeyCode.from_char('h'): | ||||||
|  |                 self.high_scores() | ||||||
|  |             if key == KeyCode.from_char('u'): | ||||||
|  |                 self.username = input('Input Your Username: ') | ||||||
|  |                 with open(self.save_file, 'rb') as f: | ||||||
|  |                     self.save_data = pickle.load(f) | ||||||
|  | 
 | ||||||
|  |                 self.save_data['username'] = self.username | ||||||
|  | 
 | ||||||
|  |                 with open(self.save_file, 'wb') as f: | ||||||
|  |                     pickle.dump(self.save_data, f) | ||||||
|  | 
 | ||||||
|  |                 self.screens() | ||||||
|  |             if key == KeyCode.from_char('?'): | ||||||
|  |                 if mode != 'game_help': | ||||||
|  |                     self.on_help_screen = True if not self.on_help_screen else False | ||||||
|  |                     if self.on_help_screen: | ||||||
|  |                         self.help_screen() | ||||||
|  |                     else: | ||||||
|  |                         self.screens() | ||||||
|  |                 elif mode == 'game_help': | ||||||
|  |                     self.game.on_help_screen = True if not self.game.on_help_screen else False | ||||||
|  | 
 | ||||||
|  |             if key == Key.up: | ||||||
|  |                 if self.current_help_screen != 0: | ||||||
|  |                     self.current_help_screen -= 1 | ||||||
|  |                     self.help_screen() | ||||||
|  |             if key == Key.down: | ||||||
|  |                 if self.current_help_screen < len(self.help_screens) - 1: | ||||||
|  |                     self.current_help_screen += 1 | ||||||
|  |                     self.help_screen() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         with Listener(on_press=on_press) as listener: | ||||||
|  |             listener.join() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     main = Main() | ||||||
|  |     main.main() | ||||||
							
								
								
									
										8
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | colorama==0.4.6 | ||||||
|  | evdev==1.9.1 | ||||||
|  | keyboard==0.13.5 | ||||||
|  | pynput==1.8.0 | ||||||
|  | PyRect==0.2.0 | ||||||
|  | python-xlib==0.33 | ||||||
|  | six==1.17.0 | ||||||
|  | wheel==0.45.1 | ||||||
		Loading…
	
		Reference in a new issue