Skip to content

Instantly share code, notes, and snippets.

@bootandy
Last active July 18, 2018 22:50
Show Gist options
  • Save bootandy/f9438a6253e5e58b5e5b7a89b85dfcd9 to your computer and use it in GitHub Desktop.
Save bootandy/f9438a6253e5e58b5e5b7a89b85dfcd9 to your computer and use it in GitHub Desktop.
A tiny text based game based on FTL
import random
import math
from os import system, name
ship_types = [
"Cobra", "Krait", "Viper", "Anaconda", "Sidewinder", "Vulture", "Asp",
]
ship_grading = [
"Rookie",
"Veteran",
"Competant",
"Dangerous",
"Deadly",
"Elite"
]
free_scrap_str = [
'Wow free scrap',
'There seems to have been a battle here recently',
'A disused mining robot can be junked for scrap',
'An empty ship shell can be converted into scrap'
]
free_weapon_str = [
'Boarding an abandoned ship allows you to salvage a weapon',
'A weapon just floating there in space!',
'Scanning an astroid reveals a small arms cache',
'An abandoned settlement has an abandoned weapon system',
]
shop_names = [
"Aperture Science Outlet", "Lacuna, Inc.", "Cyberdyne Systems", "Planet Express, Inc.", "Stark Industries", "Soylent Industry", "Weyland Corp"
]
movie_names = [
"2001 Space Odyssey", "Close Encounters of the Third Kind", "Star Wars", "Forbidden Planet", "Dune", "The Last Star Fighter", "Solaris", "Alien", "Serenity"
]
to_do_for_movie = [
"hum the theme tune from", "think of your favourite scene from", "contemplate who is the most attractive from", "wonder what you would do if this was a scene from"
]
weapons_start = {
0:'Tiny',
1:'Standard',
2:'Dangerous',
3:'Military',
4:'Epic',
}
weapons_mid = {
0:('Photon', 2, 7, 0.9),
1:('Tachyon', 1, 8, 0.95),
2:('Anti-Matter', 4, 5, 0.95),
3:('Death', 3, 10, 0.8),
}
weapons_end = {
0:('Canon', 1, 1),
1:('Blaster', 2, 1.3),
2:('Ray', 3, 1.6),
3:('Missile', 5, 2.0),
}
color_format = {
'RED': '\033[91m',
'GREEN': '\033[92m',
'END': '\033[0m',
}
class Weapon():
def __init__(self, name, low_dam, high_dam, freq, reliability, cost=None):
self.name = name
self.low_dam = (int)(low_dam)
self.high_dam = (int)(high_dam)
self.freq = (int)(freq)
self.reliability = reliability
self.cool_down = 0
if cost is None:
self.mycost = int((self.low_dam + self.high_dam)*3 / (math.sqrt(self.freq)))
else:
self.mycost = cost
def fire_weapon(self):
if self.cool_down == 0:
self.cool_down = max(self.cool_down, self.freq)
if random.random() > math.sqrt(self.reliability):
print(as_red('The {} missfires'.format(self.name)))
return 0
if random.random() > self.reliability:
print(as_red('The {} critically over heats!'.format(self.name)))
self.cool_down *= 3
return random.randint(self.low_dam, self.high_dam)
else:
return 0
def cool(self):
self.cool_down = max(self.cool_down - 1, 0)
def as_str(self):
return '{:30s}{:2d} {:2d} {:2d} {:.2f}'.format(self.name, self.low_dam, self.high_dam, self.freq, self.reliability)
def buy_cost(self):
return self.mycost
def sell_cost(self):
return int(self.buy_cost() * 0.75)
class Hostile():
def __init__(self, name, level):
self.name = name
self.level = level
self.weapons = {}
for i in range(1, level + 1):
self.weapons[str(i)] = weapon_builder(level - random.randint(0, 1))
self.health = random.randint(10, 20) + level * 8
def choose_weapon(self):
# Allow hostile captain 10 attempts to choose a weapon that is ready to fire
for _ in range(0, 10):
wp_index = random.choice(list(self.weapons.keys()))
if self.weapons[wp_index].cool_down <= 0:
return self.weapons[wp_index]
return self.weapons[wp_index]
def desc(self):
return "{} {}".format(ship_grading[min(self.level, len(ship_grading)) - 1], self.name)
class Me():
def __init__(self):
self.health = 100
self.weapons = {}
for i in range(0, 3):
self.weapons[str(i + 1)] = weapon_builder(1)
self.kills = 0
def choose_weapon(self):
print('Choose weapon to file:')
print(' Turn b4 Rdy, {:30s}Min Dam, Max Dam, Cooldown, Reliability'.format('Name,'))
for k in ['1', '2', '3', '4']:
if k in self.weapons:
v = self.weapons[k]
if v.cool_down > 0:
weapon_str = as_red(v.as_str())
else:
weapon_str = as_green(v.as_str())
print('{} : {:3d} {}'.format(k, v.cool_down, weapon_str))
else:
print('{} : {}'.format(k, as_red('None')))
wp = input()
return self.weapons.get(wp)
def get_rnd_scrap(self):
self.get_scrap(random.randint(1, 10))
def get_scrap(self, bonus):
print('You collect {} health from scrap'.format(as_green(str(bonus))))
self.health += bonus
def update_weapon(self, weapon, is_at_shop=False):
if not weapon:
return
print('Select a weapon to replace 1 -> 4 (or press q to abandon fitting)')
for k in ['1', '2', '3', '4']:
name = 'EMPTY'
if k in self.weapons:
name = self.weapons[k].name
print('{} : {}'.format(k, name))
wp = ''
while wp not in ['1', '2', '3', '4', 'q']:
wp = input()
if wp in ['1', '2', '3', '4']:
if is_at_shop:
self.sell(wp)
self.health -= weapon.buy_cost()
self.weapons[wp] = weapon
print('The new {} is fitted'.format(weapon.name))
def sell(self, choice):
if choice in self.weapons.keys():
self.health += self.weapons[choice].sell_cost()
print('You sell your {}. Your health is now: {}'.format(self.weapons[choice].name, self.health))
del self.weapons[choice]
class GameState():
sub_zone_limit = 7
def __init__(self):
self.zone = 10
self.me = Me()
# These flags added to stop you having the same chance encouner twice per zone.
self.has_seen_cargo = False
self.has_done_nothing = False
self.has_free_scrap = False
self.has_done_bargin = False
self.has_free_weapon = False
def move_zone(self):
self.zone += 1
if self.zone % 10 == GameState.sub_zone_limit:
self.zone = self.zone + (10 - GameState.sub_zone_limit)
self.has_seen_cargo = False
self.has_done_nothing = False
self.has_free_scrap = False
self.has_done_bargin = False
self.has_free_weapon = False
def level(self):
return int(self.zone / 10)
def generate_cargo(self):
# We can encounter cargo only once per zone.
if self.has_seen_cargo:
return False
if self.zone % 10 == [0, 1, 2]:
will_see = random.random() < 0.5
else:
will_see = True
if will_see:
self.has_seen_cargo = True
return True
return False
def as_green(s):
return "{}{}{}".format(color_format['GREEN'], str(s), color_format['END'])
def as_red(s):
return "{}{}{}".format(color_format['RED'], str(s), color_format['END'])
def weapon_builder(level):
level = max(0, min(level - 1, 4))
start = weapons_start[level]
mid = random.choice(weapons_mid)
end = random.choice(weapons_end)
name = start + " " + mid[0] + " " + end[0]
low_dam = (mid[1] + level * 2) * end[2]
high_dam = (mid[2] + level * 2) * end[2]
freq = end[1]
reliability = mid[3]
return Weapon(name, low_dam, high_dam, freq, reliability)
def valuable_cargo_builder(level):
return Weapon('Valuable Cargo', 0, 0, 0, 1.0, cost=50 + 20 * min(4, level))
def print_fight_stats(me, hostile):
print('\n')
sa = 'Your health: {:3d}'.format(me.health)
sb = ' vs '
sc = '{} health: {:3d}'.format(hostile.desc(), hostile.health)
len_all = len(sa) + len(sb) + len(sc)
print('-'*len_all)
print(as_green(sa) + sb + as_red(sc))
print('-'*len_all + '\n')
def fight(gs):
me = gs.me
hostile = Hostile(random.choice(ship_types), gs.level())
verbs = ['attacked', 'jumped', 'ambushed', 'challenged']
print('You are {} by an enemy {}'.format(random.choice(verbs), as_red(hostile.desc())))
print_fight_stats(me, hostile)
while me.health > 0 and hostile.health > 0:
wp = me.choose_weapon()
clear()
if wp:
if wp.cool_down == 0:
dmg = wp.fire_weapon()
hostile.health -= dmg
red_health = as_red('{}'.format(hostile.health))
damage = as_green('{}'.format(dmg))
print('Fire {}. You deal {} damage. Enemy health: {}'.format(wp.name, damage, red_health))
else:
print('That weapon fails to fire - wait for cooldown')
else:
print('You did not select a valid weapon. Nothing happens')
print('\n')
if hostile.health > 0:
wp = hostile.choose_weapon()
if wp and wp.cool_down == 0:
dmg = wp.fire_weapon()
me.health -= dmg
my_health = as_green('{}'.format(me.health))
damage = as_red('{}'.format(dmg))
print('Enemy {} fires {}. Does {} damage, your health: {}'.format(hostile.desc(), wp.name, damage, my_health))
else:
print('Enemy {} does not fire a weapon.'.format(hostile.desc()))
print_fight_stats(me, hostile)
for wp in me.weapons.values():
wp.cool()
for wp in hostile.weapons.values():
wp.cool()
print()
if hostile.health <= 0:
print('Enemy {} is destroyed'.format(hostile.name))
me.kills += 1
print('Choose your spoils')
print(' {:30s}Min Dam, Max Dam, Cooldown, Reliability'.format('Name,'))
wp = random.choice(list(hostile.weapons.values()))
print('w: {}'.format(wp.as_str()))
scrap = random.randint(3, 15) + gs.level() * 2
print('s: Choose {} scrap '.format(scrap))
gen_cargo = gs.generate_cargo()
if gen_cargo:
print('v: Choose valuable cargo')
choice = input()
while choice not in ['q', 'w', 's', 'v']:
print('Choose w, v, s or q to quit')
choice = input()
if choice == 's':
me.get_scrap(scrap)
if choice == 'v' and gen_cargo:
me.update_weapon(valuable_cargo_builder(hostile.level))
if choice == 'w':
me.update_weapon(wp)
for wp in me.weapons.values():
wp.cool_down = 0
def shop(gs):
me = gs.me
shop_name = random.choice(shop_names)
print('Welcome to the {} shop '.format(shop_name))
weapons = {}
for i in range(6, 10):
wp = weapon_builder(int(gs.zone / 10) - 1 + random.randint(0, 1))
weapons[str(i)] = wp # horrible str int casting in this code
choice = ''
while choice != 'q':
print('Your health (& money): {}'.format(as_green(me.health)))
print('To Sell: ')
print(' Cost, {:30s}Min Dam, Max Dam, Cooldown, Reliability'.format('Name,'))
for k in ['1', '2', '3', '4']:
if k in me.weapons:
wp = me.weapons[k]
print('{}: {:4d} {}'.format(k, wp.sell_cost(), wp.as_str()))
print('To Buy: ')
print(' Cost, {:30s}Min Dam, Max Dam, Cooldown, Reliability'.format('Name,'))
for k, wp in weapons.items():
print('{}: {:4d} {}'.format(k, wp.buy_cost(), wp.as_str()))
print('To buy/sell select the number on the left. To leave the shop press \'q\' (quit)')
choice = input()
clear()
if choice in weapons.keys():
me.update_weapon(weapons[choice], is_at_shop=True)
del weapons[choice]
if choice in me.weapons.keys():
me.sell(choice)
print('Thank you for shopping at {}'.format(shop_name))
def _enter_y_or_n():
choice = input()
while choice not in ['y', 'n']:
print('Enter y or n')
choice = input()
return choice
def bargin(me):
cost = random.randint(0, 12) + 4
dice = random.randint(0, 2)
if dice == 0:
print('Increase minimum & maximum damage of your weapons for cost of {} health? y / n'.format(cost))
if _enter_y_or_n() == 'y':
me.health -= cost
for w in me.weapons.values():
w.low_dam += 1
w.high_dam += 1
elif dice == 1:
print('Make all your weapons totally reliable for cost of {} health? y / n'.format(cost))
if _enter_y_or_n() == 'y':
me.health -= cost
for w in me.weapons.values():
w.reliability = 1.0
else:
print('Decrease all your weapon cooldowns by 1 for cost of {} health? y / n'.format(cost))
if _enter_y_or_n() == 'y':
me.health -= cost
for w in me.weapons.values():
w.freq = 0
def clear():
if name == 'nt':
_ = system('cls')
else:
_ = system('clear')
def encounter(gs):
clear()
print('You are in zone: {}. You have {} health'.format(as_green(str(gs.zone/10.0)), as_green(str(gs.me.health))))
if gs.zone % 10 == 0:
shop(gs)
else:
dice_roll = random.randint(0, 9)
if dice_roll == 0 and not gs.has_free_scrap:
gs.has_free_scrap = True
print(random.choice(free_scrap_str))
gs.me.get_rnd_scrap()
elif dice_roll == 1 and not gs.has_done_nothing:
gs.has_done_nothing = True
print('It is very quiet here.')
print('You {} the movie {}'.format(random.choice(to_do_for_movie), random.choice(movie_names)))
print('Nothing happens')
elif dice_roll == 2 and not gs.has_done_bargin:
gs.has_done_bargin = True
print('A Faustian bargin is proposed!')
bargin(gs.me)
elif dice_roll == 3 and not gs.has_free_weapon:
gs.has_free_weapon = True
weapon = weapon_builder(gs.level())
print(random.choice(free_weapon_str))
print()
print('{:30s}Min Dam, Max Dam, Cooldown, Reliability'.format('Name,'))
print('{}'.format(weapon.as_str()))
print()
gs.me.update_weapon(weapon)
else:
fight(gs)
def play():
gs = GameState()
clear()
print('Your aim is to travel across as many zones as you can. Each zone has {} phases'.format(gs.sub_zone_limit))
print('There is a shop at the end of each zone.')
print('-----------------------------------------------\n')
while gs.me.health > 0:
gs.move_zone()
print('\nPress any key to continue')
_ = input()
encounter(gs)
print('You are dead, you destroyed {} ships and got to zone {}'.format(gs.me.kills, gs.zone/10.0))
play()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment