Last active
March 5, 2021 20:54
-
-
Save hyperdriveguy/142b5d2fa60a83c43c71d5289d04a039 to your computer and use it in GitHub Desktop.
A text adventure with rpg elements I wrote for class in less than 2 weeks.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
Copyright (C) 2021 Hyperdriveguy | |
This program is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program. If not, see <https://www.gnu.org/licenses/>. | |
Full plain text license https://www.gnu.org/licenses/gpl-3.0.txt | |
""" | |
import random | |
import sys | |
# Raw ANSI codes for color coding | |
# Unsure about Windows support | |
class Color: | |
bold = '\033[01m' | |
yellow = '\033[93m' | |
green = '\033[32m' | |
red = '\033[31m' | |
# If no color outputs in the terminal, uncomment the code below | |
#red = '*' | |
end = '\033[0m' | |
#end = '*' | |
# Used with random.choice() in a few different functions | |
stat_list = ['Health', 'Strength', 'Skill', 'Stamina', 'Speed'] | |
def clear(prompt=False): | |
if prompt == True: | |
input('Press Enter to continue...') | |
print('\033c') | |
def coinflip(): | |
rand = random.randint(0, 1) | |
if rand == 0: | |
return True | |
else: | |
return False | |
def new_game(): | |
# Name | |
name_confirm = '' | |
while name_confirm != 'y': | |
clear() | |
your_name = input('What is your name? ').title() | |
name_confirm = input(f'So, {your_name} is your name? (y/N) ').lower() | |
# Gender | |
gender = '' | |
while gender != 'boy' and gender != 'girl': | |
clear() | |
gender = input(f'Are you a {Color.bold}b{Color.end}oy or a {Color.bold}g{Color.end}irl? ').lower() | |
if 'b' in gender: | |
gender = 'boy' | |
elif 'g' in gender: | |
gender = 'girl' | |
else: | |
continue | |
gender_confirm = input(f'So, you\'re a {gender}? (y/N) ').lower() | |
if gender_confirm != 'y': | |
gender = '' | |
# Character point distribution | |
clear() | |
print('Now you need to distribute your Character Points.') | |
print('Character Points are associtated with different skills that will benefit you in a variety of ways.') | |
clear(True) | |
stat_confirm = '' | |
while stat_confirm != 'y': | |
stats = distribute_char_points() | |
clear() | |
print_char_point_table(stats) | |
stat_confirm = input('\nIs this okay? (y/N) ').lower() | |
# How to play | |
clear() | |
how_to_prompt = input('Would you like instructions on how to play? (y/N) ').lower() | |
if how_to_prompt == 'y': | |
print('This is a text based adventure game where you will need to type out certain actions.') | |
print(f'Actions will be formatted with {Color.red}Red text{Color.end}, like so.') | |
print(f'For example, if you\'re given an option to either {Color.red}Run{Color.end} away or {Color.red}Hide{Color.end} in the bushes...') | |
print(f'You would type {Color.red}"run"{Color.end} or {Color.red}"hide"{Color.end}, ' | |
f'or just {Color.red}"r"{Color.end} or {Color.red}"h"{Color.end} for short, as that is what\'s capitalized.') | |
print(f'Unlisted actions that are always available are {Color.red}Quit{Color.end}, {Color.red}Stats{Color.end}, and {Color.red}Inventory.{Color.end}') | |
print(f'These options have the reserved shortcuts {Color.red}qt{Color.end}, {Color.red}ss{Color.end}, and {Color.red}in{Color.end}.') | |
clear(True) | |
return({'name': your_name, 'gender': gender, 'stats': stats}) | |
def distribute_char_points(): | |
avail_points = 25 | |
char_points = {'Health': 1, 'Strength': 1, 'Skill': 1, 'Stamina': 1, 'Speed': 1} | |
while avail_points > 0: | |
clear() | |
print_char_point_table(char_points) | |
print(f'\nYou have {avail_points} points left to distribute.\n') | |
char_dist_target = input('What attribute will you add points to? ').title() | |
try: | |
char_points[char_dist_target] | |
except KeyError: | |
if char_dist_target == 'Auto': | |
char_points = {'Health': 10, 'Strength': 5, 'Skill': 5, 'Stamina': 5, 'Speed': 5} | |
return char_points | |
print('That attribute doesn\'t exist. Please try again.') | |
clear(True) | |
continue | |
try: | |
# Type changes from string to integer here | |
char_dist_amount = input(f'How many points would you like to add to {char_dist_target}? ').lower() | |
if char_dist_amount == 'all': | |
char_dist_amount = avail_points | |
elif char_dist_amount == 'max': | |
char_dist_amount = 10 - char_points[char_dist_target] | |
else: | |
char_dist_amount = int(char_dist_amount) | |
except ValueError: | |
print('Not a valid integer. No parital points may be allotted.') | |
clear(True) | |
continue | |
# Conditions for adding points | |
if char_dist_amount > avail_points: | |
print('Not enough Character Points.') | |
clear(True) | |
continue | |
elif char_dist_amount + char_points[char_dist_target] > 10: | |
char_point_overflow = char_dist_amount + char_points[char_dist_target] - 10 | |
print(f'Max character points per attribute is 10. You attempted to allot {char_point_overflow} in excess.') | |
if char_points[char_dist_target] < 10: | |
max_out_attribute_points = 10 - char_points[char_dist_target] | |
change_allotment = input(f'Would you like to max {char_dist_target} by allotting {max_out_attribute_points} instead? (y/N) ').lower() | |
if change_allotment == 'y': | |
char_dist_amount = max_out_attribute_points | |
else: | |
continue | |
else: | |
continue | |
# Conditions for subtracting points | |
elif char_dist_amount + char_points[char_dist_target] < 0: | |
print('Minimum character points per attribute is 0 (minimum recommended is 1).') | |
change_allotment = input(f'Would you like to take back all points currently allotted to {char_dist_target}? (y/N)').lower() | |
if change_allotment == 'y': | |
char_dist_amount = char_points[char_dist_target] * -1 | |
else: | |
continue | |
char_points[char_dist_target] = char_points[char_dist_target] + char_dist_amount | |
avail_points = avail_points - char_dist_amount | |
return(char_points) | |
def print_char_point_table(char_points): | |
# The funky tricks used for alignment and bold require double quotes | |
print(f"{Color.bold}{'Attribute' : <15}{'Description' : ^45}{'Level' : ^15}{Color.end}") | |
print('-' * (15 + 45 + 15)) | |
print(f"{'Health' : <15}{'How much damage you can take before you die' : ^45}{round(char_points['Health'], 2) : ^15}") | |
print(f"{'Strength' : <15}{'Your brute power without weapons' : ^45}{round(char_points['Strength'], 2) : ^15}") | |
print(f"{'Skill' : <15}{'How practiced you are in your talents' : ^45}{round(char_points['Skill'], 2) : ^15}") | |
print(f"{'Stamina' : <15}{'How long it takes before you get exhausted' : ^45}{round(char_points['Stamina'], 2) : ^15}") | |
print(f"{'Speed' : <15}{'How fast your movement and reactions are' : ^45}{round(char_points['Speed'], 2) : ^15}") | |
def action_prompt(story, *actions): | |
valid_action = [] | |
# Prompt specific actions | |
for action in actions: | |
# The only time there should be a list here is if this is called from the battle function | |
if type(action) is list: | |
for sub_action in action: | |
valid_action.append(sub_action.lower()) | |
# generate action shortcuts | |
shortcut = '' | |
for letter in sub_action: | |
if letter.isupper(): | |
shortcut = shortcut + letter | |
# In case no uppercase was found | |
if len(shortcut) > 0: | |
valid_action.append(shortcut.lower()) | |
else: | |
valid_action.append(action.lower()) | |
# generate action shortcuts | |
shortcut = '' | |
for letter in action: | |
if letter.isupper(): | |
shortcut = shortcut + letter | |
# In case no uppercase was found | |
if len(shortcut) > 0: | |
valid_action.append(shortcut.lower()) | |
statuses = apply_status() | |
# Decision loop | |
choice = '' | |
while choice not in valid_action: | |
if is_dead(): | |
clear(True) | |
sys.exit(0) | |
clear() | |
print(story) | |
print() | |
for status in statuses: | |
print(status) | |
print() | |
choice = input(f'{Color.green}>>> {Color.end}').lower() | |
# Check for reserved commands | |
# Stats screen | |
if choice == 'stats' or choice == 'ss': | |
clear() | |
print(character['name'].title()) | |
print(character['gender'].title()) | |
print(f"\nAbility: {character['ability'].title()}") | |
print('\nStatus:') | |
if len(character['status']) == 0: | |
print('\tNone') | |
else: | |
for status in character['status']: | |
print(f'\t{Color.green}{status}{Color.end}') | |
print() | |
print_char_point_table(character['stats']) | |
clear(True) | |
# Inventory screen | |
elif choice == 'inventory' or choice == 'in': | |
clear() | |
for item, num_item in character['inventory'].items(): | |
if type(num_item) is dict: | |
print(f'{Color.yellow}{item.title()}{Color.end} contains:') | |
for contained_item, num_contained_item in num_item.items(): | |
print(f'\t{Color.yellow}{contained_item.title()}{Color.end}: {num_contained_item}') | |
else: | |
if num_item > 0: | |
print(f'{Color.yellow}{item.title()}{Color.end}: {num_item}') | |
if len(character['inventory']) == 0: | |
print('Nothing in inventory.') | |
clear(True) | |
elif choice == 'help' or choice == 'hl': | |
clear() | |
print('This is a text based adventure game where you will need to type out certain actions.') | |
print(f'Actions are formatted with {Color.red}Red text{Color.end}, like so.') | |
print(f'For example, if you\'re given an option to either {Color.red}Run{Color.end} away or {Color.red}Hide{Color.end} in the bushes...') | |
print(f'You would type {Color.red}"run"{Color.end} or {Color.red}"hide"{Color.end}, ' | |
f'or just {Color.red}"r"{Color.end} or {Color.red}"h"{Color.end} for short, as that is what\'s capitalized.') | |
print(f'Unlisted actions that are always available are {Color.red}Help{Color.end}, {Color.red}Quit{Color.end}, {Color.red}Stats{Color.end}, and {Color.red}Inventory.{Color.end}') | |
print(f'These options have the reserved shortcuts {Color.red}hl{Color.end}, {Color.red}qt{Color.end}, {Color.red}ss{Color.end}, and {Color.red}in{Color.end}.') | |
clear(True) | |
elif choice == 'quit' or choice == 'qt': | |
clear() | |
ask_quit = input('Are you sure you want to quit? (y/N) ').lower() | |
if ask_quit == 'y': | |
sys.exit(0) | |
return choice | |
def is_dead(): | |
if character['stats']['Health'] <= 0: | |
clear() | |
print(f'{Color.bold}{Color.red}You died unexpectedly. Try again next time.{Color.end}') | |
return True | |
else: | |
return False | |
def apply_status(): | |
global character | |
global danger_health_flag | |
status_messages = [] | |
# Specific case for exhaustion status | |
if character['stats']['Stamina'] < 0: | |
# Random stat debuff | |
debuff_stat = random.choice(stat_list) | |
character['stats'][debuff_stat] = character['stats'][debuff_stat] - character['stats']['Stamina'] / random.randint(2, 5) | |
character['stats']['Health'] = character['stats']['Health'] + character['stats']['Stamina'] / random.randint(1, 5) | |
character['stats']['Stamina'] = 0 | |
status_messages.append(f'{Color.bold}{Color.red}Warning! You have started to take damage from exhaustion.{Color.end}') | |
elif character['stats']['Stamina'] > 10: | |
character['stats']['Health'] = character['stats']['Health'] + character['stats']['Stamina'] - 10 | |
character['stats']['Stamina'] = 10 | |
# This is just to make the exhaustion status show and hide in the menu | |
if character['stats']['Stamina'] < 1: | |
character['status'].append('Exhaustion') | |
else: | |
try: | |
character['status'].remove('Exhaustion') | |
except ValueError: | |
pass | |
if 'Poisoned' in character['status']: | |
character['stats']['Health'] = character['stats']['Health'] - 0.25 | |
status_messages.append(f'{Color.bold}{Color.red}Warning! You have started to take damage from poison.{Color.end}') | |
# If HP is below a certain threshold, buff stats | |
if character['stats']['Health'] < 3 and not danger_health_flag: | |
danger_health_flag = True | |
character['stats']['Strength'] = character['stats']['Strength'] * 1.5 | |
character['stats']['Skill'] = character['stats']['Skill'] * 1.5 | |
character['stats']['Stamina'] = character['stats']['Stamina'] * 1.5 | |
character['stats']['Speed'] = character['stats']['Speed'] * 1.5 | |
character['status'].append('Adrenaline') | |
status_messages.append('You feel your adrenaline start pumping.') | |
elif character['stats']['Health'] >= 3 and danger_health_flag: | |
danger_health_flag = False | |
# Got stat increases while the buff applied? Sucks for you! | |
character['stats']['Strength'] = character['stats']['Strength'] / 1.5 | |
character['stats']['Skill'] = character['stats']['Skill'] / 1.5 | |
character['stats']['Stamina'] = character['stats']['Stamina'] / 1.5 | |
character['stats']['Speed'] = character['stats']['Speed'] / 1.5 | |
character['status'].remove('Adrenaline') | |
status_messages.append('Your adrenaline levels drop and you feel more calm.') | |
return status_messages | |
def give_damage(damage): | |
global character | |
if damage < 0: | |
damage = 0 | |
character['stats']['Health'] = character['stats']['Health'] - damage | |
return damage | |
def shooting_plot(): | |
global character | |
print('You arrive in the desert and find a nice hill to shoot against.\n' | |
f'You set up your {Color.yellow}target{Color.end} and reload your ' | |
f'{Color.yellow}rifle{Color.end} using {Color.yellow}ammo{Color.end} from ' | |
f'the {Color.yellow}ammo box{Color.end}.') | |
clear(True) | |
print('You hold your breath and take a shot.') | |
num_field_tannerite = 0 | |
result = 'shoot' | |
rand_buff = [0.1, 0.1, 0.1, 0.1, 0.5, 1, -0.1, -0.5] | |
target_destroyed = False | |
# Target shooting | |
while not target_destroyed: | |
if result == 'shoot' or result == 's': | |
hit_or_miss = target_shot_calc(character['stats']['Skill'], character['stats']['Strength'], num_field_tannerite) | |
character['inventory']['rifle']['ammo'] = character['inventory']['rifle']['ammo'] - 1 | |
if character['stats']['Skill'] >= 10: | |
character['stats']['Skill'] = character['stats']['Skill'] - random.choice(rand_buff) | |
elif character['stats']['Skill'] < 0: | |
character['stats']['Skill'] = 0 | |
else: | |
character['stats']['Skill'] = character['stats']['Skill'] + random.choice(rand_buff) | |
if hit_or_miss < 20: | |
print(f'You missed the {Color.yellow}target{Color.end} by a mile. You may need more practice shots.') | |
character['stats']['Stamina'] = character['stats']['Stamina'] - 0.1 | |
elif hit_or_miss >= 20 and hit_or_miss <= 80: | |
if num_field_tannerite <= 0: | |
print(f'You hit the {Color.yellow}target{Color.end}, but it wasn\'t a particularly impressive shot.') | |
else: | |
print(f'You hit the {Color.yellow}target{Color.end}, but you still managed to miss the {Color.yellow}tannerite{Color.end}.') | |
# > 80 percent | |
else: | |
if num_field_tannerite <= 0: | |
print(f'You hit the target dead center! You consider setting out your {Color.yellow}tannerite{Color.end}.') | |
elif num_field_tannerite == 1: | |
print(f'The {Color.yellow}tannerite{Color.end} made a satisfying explosion near the {Color.yellow}target{Color.end}. ' | |
f'The {Color.yellow}target{Color.end} still stands.') | |
num_field_tannerite = 0 | |
else: | |
target_destroyed = True | |
continue | |
clear(True) | |
total_ammo = character['inventory']['rifle']['ammo'] + character['inventory']['ammo box']['ammo'] | |
if total_ammo == 0: | |
print(f'You ran out of {Color.yellow}ammo{Color.end} entirely. Disappointed, you returned home.') | |
print('Game over.') | |
clear(True) | |
sys.exit(0) | |
if character['inventory']['tannerite'] > 0: | |
if character['inventory']['rifle']['ammo'] < 15 and character['inventory']['rifle']['ammo'] > 0 and character['inventory']['ammo box']['ammo'] > 0: | |
action_text = (f'Will you continue to {Color.red}Shoot{Color.end} the {Color.yellow}target{Color.end}, ' | |
f'{Color.red}Reload{Color.end} your {Color.yellow}rifle{Color.end}, ' | |
f'or set out {Color.red}Tannerite{Color.end}?') | |
result = action_prompt(action_text, 'Shoot', 'Reload', 'Tannerite') | |
elif character['inventory']['rifle']['ammo'] > 0 or character['inventory']['ammo box']['ammo'] == 0: | |
action_text = (f'Will you continue to {Color.red}Shoot{Color.end} the {Color.yellow}target{Color.end}, ' | |
f'or set out {Color.red}Tannerite{Color.end}?') | |
result = action_prompt(action_text, 'Shoot', 'Tannerite') | |
# No ammo in rifle, ammo in ammo box | |
else: | |
action_text = (f'You ran out of {Color.yellow}ammo{Color.end}. Will you {Color.red}Reload{Color.end} your {Color.yellow}rifle{Color.end} ' | |
f'or set out {Color.red}Tannerite{Color.end}?') | |
result = action_prompt(action_text, 'Reload', 'Tannerite') | |
else: | |
if character['inventory']['rifle']['ammo'] < 15 and character['inventory']['rifle']['ammo'] > 0 and character['inventory']['ammo box']['ammo'] > 0: | |
action_text = (f'Will you continue to {Color.red}Shoot{Color.end} the {Color.yellow}target{Color.end} or ' | |
f'{Color.red}Reload{Color.end} your {Color.yellow}rifle{Color.end}?') | |
result = action_prompt(action_text, 'Shoot', 'Reload') | |
elif character['inventory']['rifle']['ammo'] > 0 or character['inventory']['ammo box']['ammo'] == 0: | |
action_text = (f'Will you continue to {Color.red}Shoot{Color.end} the {Color.yellow}target{Color.end}?') | |
result = action_prompt(action_text, 'Shoot') | |
# No ammo in rifle, ammo in ammo box | |
else: | |
action_text = (f'You ran out of {Color.yellow}ammo{Color.end}. Will you {Color.red}Reload{Color.end} your {Color.yellow}rifle{Color.end}?') | |
result = action_prompt(action_text, 'Reload') | |
if result == 'reload' or result == 'r': | |
potential_reload = 15 - character['inventory']['rifle']['ammo'] | |
if character['inventory']['ammo box']['ammo'] < potential_reload: | |
character['inventory']['rifle']['ammo'] = character['inventory']['rifle']['ammo'] + character['inventory']['ammo box']['ammo'] | |
character['inventory']['ammo box']['ammo'] = 0 | |
else: | |
character['inventory']['ammo box']['ammo'] = character['inventory']['ammo box']['ammo'] - potential_reload | |
character['inventory']['rifle']['ammo'] = 15 | |
print(f'Your {Color.yellow}rifle{Color.end} was reloaded.') | |
elif result == 'tannerite' or result == 't': | |
print(f"You have {character['inventory']['tannerite']} {Color.yellow}tannerite{Color.end} in your inventory.") | |
queued_tannerite = input('How much will you set out? ') | |
try: | |
if queued_tannerite == 'all' or queued_tannerite == 'max': | |
queued_tannerite = character['inventory']['tannerite'] | |
queued_tannerite = int(queued_tannerite) | |
except ValueError: | |
continue | |
if queued_tannerite > character['inventory']['tannerite']: | |
print('You do not have that much.') | |
clear(True) | |
continue | |
character['inventory']['tannerite'] = character['inventory']['tannerite'] - queued_tannerite | |
num_field_tannerite = queued_tannerite | |
print(f'The {Color.yellow}tannerite{Color.end} ignited and destroyed the {Color.yellow}target{Color.end}, ' | |
'sending shrapnels of metal everywhere.') | |
# Metal flying back at you | |
if num_field_tannerite == 3: | |
num_field_tannerite = 0 | |
print('...\nIncluding at you!') | |
clear(True) | |
avoid_metal = (f'Will you attempt to {Color.red}Brace{Color.end} for impact, {Color.red}Dodge{Color.end}, ' | |
f'or {Color.red}Run{Color.end} away from the metal?') | |
result = action_prompt(avoid_metal, 'Brace', 'Dodge', 'Run') | |
clear() | |
if result == 'brace' or result == 'b': | |
damage_taken = character['stats']['Health'] - (7 - character['stats']['Stamina']) | |
give_damage(damage_taken) | |
print(f'You braced for the impact and took {Color.green}{damage_taken} damage{Color.end}.') | |
clear(True) | |
elif result == 'dodge' or result == 'd': | |
dodge_chance = (character['stats']['Speed'] + character['stats']['Skill']) * 0.05 * random.randint(0, 100) | |
if dodge_chance > 80: | |
print('You were able to evade the metal shards entirely!') | |
elif dodge_chance > 50 and dodge_chance <= 80: | |
damage_taken = 10 - round(dodge_chance / 10) | |
give_damage(damage_taken) | |
print(f'You evaded most of the metal shards, but still took {Color.green}{damage_taken} damage{Color.end}.') | |
else: | |
give_damage(7) | |
print(f'You failed to evade the shards and took {Color.green}7 damage{Color.end}.') | |
clear(True) | |
# Run | |
elif result == 'run' or choice == 'r': | |
run_chance = (character['stats']['Speed'] + character['stats']['Stamina']) * 0.03 * random.randint(0, 100) + random.randint(0, 40) | |
if run_chance > 80: | |
print('You were able to run from the metal shards!') | |
elif run_chance > 30 and run_chance <= 80: | |
damage_taken = 10 - round(run_chance / 10) | |
give_damage(damage_taken) | |
print(f'You ran from most of the metal shards, but still took {Color.green}{damage_taken} damage{Color.end}.') | |
else: | |
give_damage(9) | |
print(f'You tripped and took {Color.green}9 damage{Color.end} from the shards and your fall.') | |
clear(True) | |
else: | |
clear(True) | |
print('The flying metal agitated some agressive wildlife nearby. A hog-like creature is rapidly approaching!') | |
clear(True) | |
cur_kill_count = 0 | |
while True: | |
if cur_kill_count > 0: | |
print(f'Your current kill streak is {cur_kill_count}.') | |
clear(True) | |
battle('Agressive Javelina', 15, 8, 3, 6, {'Ram': 4, 'Mega Ram': 7}, {'Bellow': 1}) | |
cur_kill_count = cur_kill_count + 1 | |
victory_scream_prompt = ('You\'re very satisfied with how fended off that animal.\nYou instinctively want to' | |
f' give a victory {Color.red}Yell{Color.end}, but something tells you it might be ' | |
f'{Color.red}Quiet{Color.end} instead.') | |
victory_scream = action_prompt(victory_scream_prompt, 'Yell', 'Quiet') | |
if victory_scream == 'yell' or victory_scream == 'y': | |
print('You agitated another wild animal!') | |
clear(True) | |
elif victory_scream == 'quiet' or victory_scream == 'q': | |
if cur_kill_count > 1: | |
print(f'Your final kill streak was {cur_kill_count}.') | |
print('You quietly celebrated your victory. Feeling exhausted, you pack up and go home.') | |
print('The end.') | |
clear(True) | |
sys.exit(0) | |
def target_shot_calc(skill, strength, tannerite): | |
chance = skill * 0.1 + skill * strength * 0.02 * (tannerite + 1) | |
return chance * random.randint(0, 100) | |
def explore_plot(): | |
global character | |
explore_prompt_1 = ('You arrive in the desert and stumble upon a small ruined building.\n' | |
'You see a small glimmer in the rubble as well as something in the distance.\n' | |
f'Do you want to use your {Color.red}Binoculars{Color.end} to investigate the ' | |
f'object in the distance, {Color.red}Sift{Color.end} through the rubble, or ' | |
f'or ignore these and {Color.red}Continue{Color.end} on the path?') | |
choice = action_prompt(explore_prompt_1, 'Binoculars', 'Sift', 'Continue') | |
if choice == 'sift' or choice == 's': | |
print(f'You sifted through the rubble and found an {Color.yellow}odd gem{Color.end}. You added it to your inventory.') | |
character['inventory']['odd gem'] = 1 | |
clear(True) | |
post_sift_prompt = (f'Do you want to use your {Color.red}Binoculars{Color.end} to investigate the ' | |
f'object in the distance or or ignore it and {Color.red}Continue{Color.end} on the path?') | |
choice = action_prompt(post_sift_prompt, 'Binoculars', 'Continue') | |
# Not elif due to the choice also being modified if the player chooses to take the odd gem | |
if choice == 'binoculars' or choice == 'b': | |
binocular_prompt = ('You see a larger building as you put on your binoculars. Maybe this is part of a larger ghost town?\n' | |
f'Will you go {Color.red}West{Color.end} towards the ruins, or {Color.red}Continue{Color.end} on the path?') | |
choice = action_prompt(binocular_prompt, 'West', 'Continue') | |
if choice == 'west' or choice == 'w': | |
if 'odd gem' in character['inventory']: | |
haunted_ruins_prompt = ('The sky turns an ominous red as you approach the large ruinous building.\n' | |
'The building\'s original use is not easily identified due to its decay.\n' | |
f'Nearby the building, you see what appears to be an old {Color.red}Mineshaft{Color.end}\n' | |
f'and a barely recognizable {Color.red}Cemetery{Color.end}, faded from its age.') | |
choice = action_prompt(haunted_ruins_prompt, 'Mineshaft', 'Cemetery') | |
if choice == 'mineshaft' or choice == 'm': | |
clear() | |
print('The mineshaft is deceptively short. You see what appears to be a miner blankly staring at the end of the mine.') | |
print('You try to greet him, but he doesn\'t respond for a moment. About to leave, you hear a gunshot and feel a bullet barely miss your cheek.') | |
print('You quickly turn back to inside the shaft and see the crazed face of the miner, revolver in hand.') | |
print('The miner picks up a pickaxe nearby and runs toward you. He isn\'t going to let you leave without a fight.') | |
clear(True) | |
battle('Crazed Miner', 12, 4, 3, 4, {'Pickaxe Swing': 7, 'Feeble Punch': 2}, {'Revolver Shot': 7, 'Pebble Throw': 1}) | |
print('You exit the mine as fast as your legs will carry you and call for a helicopter to come pick you up.') | |
print('You\'re safe now, but wonder about the old town you stepped upon...') | |
print('The end.') | |
clear(True) | |
sys.exit(0) | |
elif choice == 'cemetery' or choice == 'c': | |
clear() | |
print('The cemetery is very obviously over a couple centuries old. You can\'t make out anything on the headstones.') | |
print('Suddenly, one of the graves breaks open with an undead being set on destroying you.') | |
clear(True) | |
battle('Zombie', 10, 4, 3, 20, {'Chomp': 4, 'Feeble Punch': 2}, {'Piercing Screech': 5, 'Pebble Throw': 1}) | |
print('You run away from the cemetery as fast as your legs will carry you and call for a helicopter to come pick you up.') | |
print('You\'re safe now, but wonder about the old town you stepped upon...') | |
print('The end.') | |
clear(True) | |
sys.exit(0) | |
else: | |
abandoned_ruins_prompt = ('You approach the large ruinous building.\n' | |
'The building\'s original use is not easily identified due to its decay.\n' | |
f'Nearby the building, you see what appears to be an old {Color.red}Mineshaft{Color.end}\n' | |
f'and a barely recognizable {Color.red}Cemetery{Color.end}, faded from its age.') | |
choice = action_prompt(abandoned_ruins_prompt, 'Mineshaft', 'Cemetery') | |
if choice == 'mineshaft' or choice == 'm': | |
mineshaft_prompt = ('The mineshaft is deceptively short. You see a pickaxe and lamp lying around.\n' | |
f'Will you {Color.red}Examine{Color.end} the pickaxe and lamp or {Color.red}eXit{Color.end}' | |
f' the mine and call for a ride home?') | |
choice = action_prompt(mineshaft_prompt, 'Examine', 'eXit') | |
if choice == 'exit' or choice == 'x': | |
print('You exited the mine and called for a helicopter to give you a lift home.') | |
print('You can\'t help but wonder if there was more to the old town you stepped upon...') | |
print('The end.') | |
clear(True) | |
sys.exit(0) | |
elif choice == 'examine' or 'e': | |
print('You tried to touch the pickaxe and lamp to examine them, but they started moving on their own!') | |
print('You need to defend yourself from these inanimate fiends!') | |
clear(True) | |
battle('Pickaxe and Lamp', 10, 2, 7, 4, {'Pickaxe Swing': 5, 'Lamp Bash': 4}, {'Blinding Lights': 2, 'Pickaxe Rapid Spin': 4}) | |
print('Relieved, scared and confused, you exit the mine and call for a ride home.') | |
print('You\'re safe now, but wonder about the old town you stepped upon...') | |
print('The end.') | |
clear(True) | |
sys.exit(0) | |
elif choice == 'cemetery' or choice == 'c': | |
clear() | |
print('The cemetery is very obviously over a couple centuries old. You can\'t make out anything on the headstones.') | |
print('There isn\'t much here, so you decide to make your way back to the path you were on.') | |
clear(True) | |
choice = 'continue' | |
# Fall through to continue | |
if choice == 'continue' or choice == 'c': | |
continue_path_prompt = ('A rattlesnake blocks the path at a very narrow point. You aren\'t likely to get by without provoking ' | |
f'it. Will you try to {Color.red}sNeak{Color.end} around it or {Color.red}Shoot{Color.end} with ' | |
f'your {Color.yellow}Pistol{Color.end}?') | |
choice = action_prompt(continue_path_prompt, 'sNeak', 'Shoot') | |
if choice == 'sneak' or choice == 'n': | |
print('You attempted to sneak around the snake.') | |
sneak_chance = (character['stats']['Speed'] + character['stats']['Skill']) * 0.05 * random.randint(0, 100) | |
if sneak_chance > 50: | |
print('You were able to sneak past the snake with no issue.') | |
else: | |
print('The snake noticed you and bit you before you could react. You may have been poisoned.') | |
if coinflip(): | |
character['status'].append('Poisoned') | |
elif choice == 'shoot' or choice == 's': | |
shoot_snake_chance = character['stats']['Skill'] * 0.09 + (character['stats']['Skill'] + character['stats']['Strength']) * 0.04 * random.randint(0, 100) | |
character['inventory']['pistol']['ammo'] = character['inventory']['pistol']['ammo'] - 1 | |
if shoot_snake_chance >= 65: | |
print(f'You shot snake and killed it. You took a {Color.yellow}snake fang{Color.end} from the snake as a souvenir.') | |
character['inventory']['snake fang'] = 1 | |
if coinflip(): | |
print(f'The snake was hefty enough that the {Color.yellow}snake meat{Color.end} ' | |
'looks like it could sustain you, so you skinned the snake and took it\'s meat.') | |
character['inventory']['snake meat'] = 5 | |
else: | |
print('You missed your shot. Agitated, the snake bit you before you could react. You may have been poisoned.') | |
print(f'You ripped the snake off your leg. You pulled out the {Color.yellow}snake fang{Color.end} out of your leg and added it to your inventory.') | |
character['inventory']['snake fang'] = 1 | |
if coinflip(): | |
character['status'].append('Poisoned') | |
clear(True) | |
wall_prompt_general = ('After following the path a little while longer, you stumble upon a steep mountain.\n' | |
'The mountain appears impossible to scale; however, upon further inspection you notice\n' | |
'a couple odd shaped divets. One divet appears to be vaguely fang shaped and the other\n' | |
'oval shaped.\n') | |
wall_prompt_fang_only = (f'Do you want to attempt to {Color.red}Place{Color.end} the {Color.yellow}snake fang{Color.end} or ' | |
f'{Color.red}Call{Color.end} a helicopter to take you home?') | |
wall_prompt_odd_gem = (f'Do you want to attempt to {Color.red}Place{Color.end} the {Color.yellow}snake fang{Color.end} ' | |
f'and {Color.yellow}odd gem{Color.end} or {Color.red}Call{Color.end} a helicopter to take you home?') | |
if 'odd gem' in character['inventory']: | |
choice = action_prompt(wall_prompt_general + wall_prompt_odd_gem, 'Place', 'Call') | |
if choice == 'place' or choice == 'p': | |
clear() | |
print('The ground shakes and the mountainside opens up. Gold, silver, and precious metals lay in ' | |
'in front of you where the mountainside was. You call a helicopter to help transport it home.\n' | |
'Arriving home with your new riches, you can\'t wait to go exploring again!') | |
clear(True) | |
sys.exit(0) | |
else: | |
choice = action_prompt(wall_prompt_general + wall_prompt_fang_only, 'Place', 'Call') | |
if choice == 'place' or choice == 'p': | |
print('The ground shakes and the mountainside opens up. A large monster pops outside of the mountainside' | |
', looking to pick a fight.') | |
clear(True) | |
battle('Guardian', 20, 7, 5, 4, {'Brute Punch': 5, 'Mega Yeet': 7}, {'Ominous Beam': 9, 'Screech': 3}) | |
print('Gold, silver, and precious metals lay in in front of you where the Guardian was!\n' | |
'You call a helicopter to help transport it home.\n' | |
'Arriving home with your new riches, you can\'t wait to go exploring again!') | |
if choice == 'call' or choice == 'c': | |
clear() | |
print(f'You called a helicopter to take you home on your {Color.yellow}satellite phone{Color.end}. ' | |
'You found your trip interesting and are glad to be back home.') | |
print('The end.') | |
clear(True) | |
sys.exit(0) | |
def battle(name, health, strength, skill, stamina, melee_attacks, special_attacks): | |
global character | |
full_health = health | |
melee_attack_list = [] | |
special_attack_list = [] | |
for m_attack in melee_attacks: | |
melee_attack_list.append(m_attack) | |
for s_attack in special_attack_list: | |
special_attack_list.append(s_attack) | |
enemy_attack_scope = [] | |
if len(melee_attack_list) > 0: | |
enemy_attack_scope.append('melee') | |
if len(special_attack_list) > 0: | |
enemy_attack_scope.append('special') | |
while health > 0: | |
# Make battle HUD | |
build_hud_1 = (f'{name}\n' | |
f'{Color.bold}HP:{Color.end} ') | |
health_bar = '' | |
if health < full_health: | |
existing_health_bar = f'{Color.green}█{Color.end}' * int(round(health)) | |
damage_done_bar = f'{Color.red}🮐{Color.end}' * int(round(full_health - health)) | |
health_bar = existing_health_bar + damage_done_bar | |
else: | |
health_bar = f'{Color.green}█{Color.end}' * int(round(health)) | |
actions_hud = (f'\n\n{Color.red}') | |
avail_actions = ['Rest', 'Punch'] | |
if 'rifle' in character['inventory']: | |
if character['inventory']['ammo box']['ammo'] > 0 and character['inventory']['rifle']['ammo'] < 15: | |
avail_actions.append('reLoad') | |
if character['inventory']['rifle']['ammo'] > 0: | |
avail_actions.append('Shoot') | |
else: | |
try: | |
if character['inventory']['pistol']['ammo'] > 0: | |
avail_actions.append('Shoot') | |
except KeyError: | |
pass | |
if 'snack' in character['inventory']: | |
if character['inventory']['snack'] > 0: | |
avail_actions.append('Eat') | |
if 'snake meat' in character['inventory']: | |
if character['inventory']['snack'] > 0: | |
avail_actions.append('Eat') | |
for action in avail_actions: | |
actions_hud = actions_hud + action + f' {Color.end}| {Color.red}' | |
actions_hud = actions_hud + Color.end | |
your_health = f"\n\n\n{character['name'].title()}\n{Color.bold}HP: {round(character['stats']['Health'])}{Color.end}" | |
full_hud = build_hud_1 + health_bar + your_health + actions_hud | |
battle_turn_act = action_prompt(full_hud, avail_actions) | |
# Restore a random stat | |
if battle_turn_act == 'rest' or battle_turn_act == 'r': | |
restore_stat = random.choice(stat_list) | |
restore_amount = random.randint(1, 5) / 2 | |
character['stats'][restore_stat] = character['stats'][restore_stat] + restore_amount | |
print(f'You rested for a little bit and restored {Color.green}{restore_amount} {restore_stat.lower()}{Color.end}!') | |
clear(True) | |
# Brute force punch that consumes stamina | |
elif battle_turn_act == 'punch' or battle_turn_act == 'p': | |
punch_damage = character['stats']['Strength'] - stamina / random.randint(1, round(int(character['stats']['Strength']))) | |
health = health - punch_damage | |
character['stats']['Stamina'] = character['stats']['Stamina'] - 1 | |
print(f'You punched the {name} and dealt {Color.green}{round(punch_damage)} damage{Color.end}!') | |
clear(True) | |
# Attack that takes skill and consumes ammo | |
elif battle_turn_act == 'shoot' or battle_turn_act == 's': | |
print(f'You took a shot at the {name}.') | |
if character['ability'] == 'marksman': | |
character['inventory']['rifle']['ammo'] = character['inventory']['rifle']['ammo'] - 1 | |
gun_damage_modifier = 2 | |
else: | |
character['inventory']['pistol']['ammo'] = character['inventory']['pistol']['ammo'] - 1 | |
gun_damage_modifier = 1 | |
gun_damage = character['stats']['Skill'] * gun_damage_modifier - skill / random.randint(1, round(int(character['stats']['Skill']))) | |
health = health - gun_damage | |
print(f'You dealt {Color.green}{round(gun_damage)} damage{Color.end} with your shot!') | |
clear(True) | |
# Determine foode restoration/damage | |
elif battle_turn_act == 'eat' or battle_turn_act == 'e': | |
if 'snack' in character['inventory']: | |
if coinflip(): | |
restore_health = random.randint(0, 3) | |
else: | |
restore_health = random.randint(0, 8) | |
else: | |
if coinflip(): | |
restore_health = random.randint(-1, 4) | |
if coinflip(): | |
if 'Poisoned' in character['status']: | |
character['status'].remove('Poisoned') | |
print(f'The {Color.yellow}snake meat{Color.end} cured your {Color.green}poison{Color.end}!') | |
else: | |
restore_health = random.randint(2, 8) | |
if coinflip(): | |
if 'Poisoned' not in character['status']: | |
character['status'].append('Poisoned') | |
print(f'The {Color.yellow}snake meat{Color.end} {Color.green}poisoned{Color.end} you!') | |
if restore_health > 0: | |
print(f'The food restored {Color.green}{restore_health} health{Color.end}!') | |
clear(True) | |
else: | |
print(f'The food caused {Color.green}{restore_health * -1} damage{Color.end} to you!') | |
clear(True) | |
character['stats']['Health'] = character['stats']['Health'] + restore_health | |
# Reload rifle (The pistol cannot be reloaded) | |
elif battle_turn_act == 'reload' or battle_turn_act == 'l': | |
potential_reload = 15 - character['inventory']['rifle']['ammo'] | |
if character['inventory']['ammo box']['ammo'] < potential_reload: | |
character['inventory']['rifle']['ammo'] = character['inventory']['rifle']['ammo'] + character['inventory']['ammo box']['ammo'] | |
character['inventory']['ammo box']['ammo'] = 0 | |
else: | |
character['inventory']['ammo box']['ammo'] = character['inventory']['ammo box']['ammo'] - potential_reload | |
character['inventory']['rifle']['ammo'] = 15 | |
print(f'Your {Color.yellow}rifle{Color.end} was reloaded.') | |
clear(True) | |
if stamina < 0: | |
health = stamina + health | |
print(f'The {name} started to take damage from {Color.green}exhaustion{Color.end}.') | |
clear(True) | |
if health <= 0: | |
break | |
# The enemy now has a chance to counterattack | |
next_attack_set = random.choice(enemy_attack_scope) | |
if next_attack_set == 'melee': | |
next_attack = random.choice(melee_attack_list) | |
deal_damage_player = melee_attacks[next_attack] - character['stats']['Stamina'] / random.randint(1, strength) | |
stamina = stamina - 1 | |
else: | |
next_attack = random.choice(special_attack_list) | |
deal_damage_player = special_attacks[next_attack] - character['stats']['Stamina'] / random.randint(1, skill) | |
rand_enemy_debuff = random.choice([health, strength, skill, stamina]) | |
rand_enemy_debuff_amt = random.randint(0, 2) | |
deal_damage_player = give_damage(deal_damage_player) | |
print(f'The {name} used their {next_attack} attack and dealt {Color.green}{round(deal_damage_player)} damage{Color.end} to you!') | |
clear(True) | |
clear() | |
print(f'You defeated the {name}!') | |
clear(True) | |
return True | |
def main(): | |
global danger_health_flag | |
danger_health_flag = False | |
# Make character | |
global character | |
character = new_game() | |
# character['name'] | |
# character['gender'] | |
# character['stats'] | |
# Make empty inventory | |
character['inventory'] = {} | |
character['ability'] = '' | |
character['status'] = [] | |
# Initial prompt goes here because it subtly determines the skill | |
# This prompt also branches the story significantly | |
shoot_or_explore = ('You have been planning a trip out to the desert for a while;\n' | |
f'however, you\'re not sure whether you want to go {Color.red}Shooting{Color.end}\n' | |
f'or simply {Color.red}Explore{Color.end} the area. Which do you prefer?') | |
result = action_prompt(shoot_or_explore, 'Shooting', 'Explore') | |
# Pass character attributes over to main game | |
if result == 'shooting' or result == 's': | |
character['ability'] = 'marksman' | |
# Populate inventory | |
character['inventory']['rifle'] = {'ammo': 15} | |
# Never actually needed in your inventory | |
# character['inventory']['target'] = 1 | |
character['inventory']['ammo box'] = {'ammo': 185} | |
character['inventory']['tannerite'] = 3 | |
character['inventory']['snack'] = 3 | |
print(f'You add your {Color.yellow}rifle{Color.end}, {Color.yellow}ammo box{Color.end}, ' | |
f'a {Color.yellow}target{Color.end}, three {Color.yellow}tannerite{Color.end}, and ' | |
f'some {Color.yellow}snacks{Color.end} to your inventory and head out to the desert.') | |
clear(True) | |
shooting_plot() | |
elif result == 'explore' or result == 'e': | |
character['ability'] = 'scoutsman' | |
# Populate inventory | |
character['inventory']['pistol'] = {'ammo': 7} | |
character['inventory']['compass'] = 1 | |
character['inventory']['binoculars'] = 1 | |
character['inventory']['satellite phone'] = 1 | |
print(f'You add your {Color.yellow}pistol{Color.end}, {Color.yellow}compass{Color.end}, ' | |
f'{Color.yellow}binoculars{Color.end}, and {Color.yellow}satellite phone{Color.end} ' | |
'to your inventory and head out to the desert.') | |
clear(True) | |
explore_plot() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment