Merge branch 'garden_menu'
commit
1c5fbfa2e6
69
botany.py
69
botany.py
|
@ -11,20 +11,17 @@ import errno
|
||||||
import uuid
|
import uuid
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from menu_screen import *
|
from menu_screen import *
|
||||||
# ideas go here
|
|
||||||
# lifecycle of a plant
|
|
||||||
# seed -> seedling -> sprout -> young plant -> mature plant -> flower ->
|
|
||||||
# pollination -> fruit -> seeds
|
|
||||||
|
|
||||||
# neighboring plants can cross pollinate for different plants
|
|
||||||
# health based on checkups and watering
|
|
||||||
|
|
||||||
# development plan
|
# development plan
|
||||||
|
|
||||||
# build plant lifecycle just stepping through
|
# build plant lifecycle just stepping through
|
||||||
# - What else should it do during life? growth alone is not all that
|
# - What else should it do during life? growth alone is not all that
|
||||||
# interesting.
|
# interesting.
|
||||||
# - how long should each stage last ? thinking realistic lmao
|
# - how long should each stage last ? thinking realistic lmao
|
||||||
|
# seed -> seedling -> sprout -> young plant -> mature plant -> flower ->
|
||||||
|
# pollination -> fruit -> seeds
|
||||||
|
# - TODO: pollination and end of life
|
||||||
|
#
|
||||||
# interaction
|
# interaction
|
||||||
# - look at plant, how do you feel? (also gets rid of pests)
|
# - look at plant, how do you feel? (also gets rid of pests)
|
||||||
#
|
#
|
||||||
|
@ -48,23 +45,6 @@ from menu_screen import *
|
||||||
# build ascii trees
|
# build ascii trees
|
||||||
|
|
||||||
|
|
||||||
# def display_update:
|
|
||||||
# myscreen = curses.initscr()
|
|
||||||
#
|
|
||||||
# myscreen.border(0)
|
|
||||||
# myscreen.addstr(1, 2, "you've planted a seed")
|
|
||||||
# myscreen.refresh()
|
|
||||||
#
|
|
||||||
# for i in range(1,20):
|
|
||||||
# myscreen.addstr(i, 2, str(i))
|
|
||||||
# time.sleep(1)
|
|
||||||
# myscreen.refresh()
|
|
||||||
#
|
|
||||||
# myscreen.getch()
|
|
||||||
#
|
|
||||||
# curses.endwin()
|
|
||||||
# TODO: garden file json should be prettier.
|
|
||||||
|
|
||||||
class Plant(object):
|
class Plant(object):
|
||||||
# This is your plant!
|
# This is your plant!
|
||||||
stage_dict = {
|
stage_dict = {
|
||||||
|
@ -178,7 +158,7 @@ class Plant(object):
|
||||||
self.watered_24h = False
|
self.watered_24h = False
|
||||||
|
|
||||||
def new_seed(self,this_filename):
|
def new_seed(self,this_filename):
|
||||||
os.remove(this_filename)
|
# Creates life after death
|
||||||
self.__init__(this_filename)
|
self.__init__(this_filename)
|
||||||
|
|
||||||
def rarity_check(self):
|
def rarity_check(self):
|
||||||
|
@ -340,11 +320,26 @@ class DataManager(object):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def enable_autosave(self,this_plant):
|
def start_threads(self,this_plant):
|
||||||
# creates thread to save files every minute
|
# creates threads to save files every minute
|
||||||
thread = threading.Thread(target=self.autosave, args=(this_plant,))
|
death_check_thread = threading.Thread(target=self.death_check_update, args=(this_plant,))
|
||||||
thread.daemon = True
|
death_check_thread.daemon = True
|
||||||
thread.start()
|
death_check_thread.start()
|
||||||
|
|
||||||
|
autosave_thread = threading.Thread(target=self.autosave, args=(this_plant,))
|
||||||
|
autosave_thread.daemon = True
|
||||||
|
autosave_thread.start()
|
||||||
|
|
||||||
|
def death_check_update(self,this_plant):
|
||||||
|
# .1 second updates to minimize race condition
|
||||||
|
# TODO: improve how this is handled to eliminate race condition
|
||||||
|
while True:
|
||||||
|
is_dead = this_plant.dead_check()
|
||||||
|
if is_dead:
|
||||||
|
self.save_plant(this_plant)
|
||||||
|
self.data_write_json(this_plant)
|
||||||
|
self.garden_update(this_plant)
|
||||||
|
time.sleep(.1)
|
||||||
|
|
||||||
def autosave(self, this_plant):
|
def autosave(self, this_plant):
|
||||||
# running on thread
|
# running on thread
|
||||||
|
@ -354,6 +349,7 @@ class DataManager(object):
|
||||||
self.garden_update(this_plant)
|
self.garden_update(this_plant)
|
||||||
# TODO: change after debug
|
# TODO: change after debug
|
||||||
#time.sleep(60)
|
#time.sleep(60)
|
||||||
|
# TODO: if plant dies it should force save.
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
def load_plant(self):
|
def load_plant(self):
|
||||||
|
@ -387,9 +383,8 @@ class DataManager(object):
|
||||||
return age_formatted
|
return age_formatted
|
||||||
|
|
||||||
def garden_update(self, this_plant):
|
def garden_update(self, this_plant):
|
||||||
# garden is a list of 10 tuples sorted by plant score
|
# garden is a dict of dicts
|
||||||
# garden contains one entry for each plant id
|
# garden contains one entry for each plant id
|
||||||
# garden should be a dict of tuples not a list
|
|
||||||
|
|
||||||
age_formatted = self.plant_age_convert(this_plant)
|
age_formatted = self.plant_age_convert(this_plant)
|
||||||
this_plant_id = this_plant.plant_id
|
this_plant_id = this_plant.plant_id
|
||||||
|
@ -469,13 +464,9 @@ if __name__ == '__main__':
|
||||||
my_plant = Plant(my_data.savefile_path)
|
my_plant = Plant(my_data.savefile_path)
|
||||||
my_data.data_write_json(my_plant)
|
my_data.data_write_json(my_plant)
|
||||||
my_plant.start_life()
|
my_plant.start_life()
|
||||||
my_data.enable_autosave(my_plant)
|
my_data.start_threads(my_plant)
|
||||||
botany_menu = CursedMenu(my_plant)
|
botany_menu = CursedMenu(my_plant,my_data.garden_file_path)
|
||||||
botany_menu.show(["water","look","garden","instructions"], title=' botany ', subtitle='options')
|
botany_menu.show(["water","look","garden","instructions"], title=' botany ', subtitle='options')
|
||||||
# if not my_plant.dead:
|
|
||||||
# botany_menu.show(["water","look","garden","instructions"], title=' botany ', subtitle='options')
|
|
||||||
# else:
|
|
||||||
# botany_menu.show(["water","look","garden","instructions"], title=' botany ', subtitle='options')
|
|
||||||
my_data.save_plant(my_plant)
|
my_data.save_plant(my_plant)
|
||||||
my_data.data_write_json(my_plant)
|
my_data.data_write_json(my_plant)
|
||||||
my_data.garden_update(my_plant)
|
my_data.garden_update(my_plant)
|
||||||
|
|
135
menu_screen.py
135
menu_screen.py
|
@ -1,9 +1,9 @@
|
||||||
import curses, os, traceback, threading, time, datetime
|
import curses, os, traceback, threading, time, datetime, pickle
|
||||||
|
|
||||||
class CursedMenu(object):
|
class CursedMenu(object):
|
||||||
#TODO: create a side panel with log of events..?
|
#TODO: create a side panel with log of events..?
|
||||||
'''A class which abstracts the horrors of building a curses-based menu system'''
|
'''A class which abstracts the horrors of building a curses-based menu system'''
|
||||||
def __init__(self, this_plant):
|
def __init__(self, this_plant, this_garden_file_path):
|
||||||
'''Initialization'''
|
'''Initialization'''
|
||||||
self.initialized = False
|
self.initialized = False
|
||||||
self.screen = curses.initscr()
|
self.screen = curses.initscr()
|
||||||
|
@ -13,10 +13,14 @@ class CursedMenu(object):
|
||||||
curses.curs_set(0)
|
curses.curs_set(0)
|
||||||
self.screen.keypad(1)
|
self.screen.keypad(1)
|
||||||
self.plant = this_plant
|
self.plant = this_plant
|
||||||
|
self.garden_file_path = this_garden_file_path
|
||||||
self.plant_string = self.plant.parse_plant()
|
self.plant_string = self.plant.parse_plant()
|
||||||
self.plant_ticks = str(self.plant.ticks)
|
self.plant_ticks = str(self.plant.ticks)
|
||||||
self.exit = False
|
self.exit = False
|
||||||
self.instructiontoggle = False
|
self.instructiontoggle = False
|
||||||
|
#TODO: debugging
|
||||||
|
# self.gardenmenutoggle = True
|
||||||
|
self.gardenmenutoggle = False
|
||||||
self.maxy, self.maxx = self.screen.getmaxyx()
|
self.maxy, self.maxx = self.screen.getmaxyx()
|
||||||
# Highlighted and Normal line definitions
|
# Highlighted and Normal line definitions
|
||||||
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
|
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
|
||||||
|
@ -28,8 +32,8 @@ class CursedMenu(object):
|
||||||
# TODO: tweaking this to try to get rid of garble bug
|
# TODO: tweaking this to try to get rid of garble bug
|
||||||
self.screen.clear()
|
self.screen.clear()
|
||||||
|
|
||||||
def show(self, options, title="Title", subtitle="Subtitle"):
|
def show(self, options, title, subtitle):
|
||||||
'''Draws a menu with the given parameters'''
|
# Draws a menu with parameters
|
||||||
self.set_options(options)
|
self.set_options(options)
|
||||||
self.update_options()
|
self.update_options()
|
||||||
self.title = title
|
self.title = title
|
||||||
|
@ -39,27 +43,27 @@ class CursedMenu(object):
|
||||||
self.draw_menu()
|
self.draw_menu()
|
||||||
|
|
||||||
def update_options(self):
|
def update_options(self):
|
||||||
|
# Makes sure you can get a new plant if it dies
|
||||||
if self.plant.dead:
|
if self.plant.dead:
|
||||||
if "kill" in self.options:
|
if "kill" in self.options:
|
||||||
self.options.remove("kill")
|
self.options.remove("kill")
|
||||||
if "new" not in self.options:
|
if "new" not in self.options:
|
||||||
self.options.insert(-1,"new")
|
self.options.insert(-1,"new")
|
||||||
else:
|
else:
|
||||||
|
# TODO: remove after debug or bury in settings
|
||||||
if "new" in self.options:
|
if "new" in self.options:
|
||||||
self.options.remove("new")
|
self.options.remove("new")
|
||||||
if "kill" not in self.options:
|
if "kill" not in self.options:
|
||||||
self.options.insert(-1,"kill")
|
self.options.insert(-1,"kill")
|
||||||
#self.draw_menu()
|
|
||||||
#self.screen.clear()
|
|
||||||
|
|
||||||
def set_options(self, options):
|
def set_options(self, options):
|
||||||
'''Validates that the last option is "exit"'''
|
# Validates that the last option is "exit"
|
||||||
if options[-1] is not 'exit':
|
if options[-1] is not 'exit':
|
||||||
options.append('exit')
|
options.append('exit')
|
||||||
self.options = options
|
self.options = options
|
||||||
|
|
||||||
def draw_menu(self):
|
def draw_menu(self):
|
||||||
'''Actually draws the menu and handles branching'''
|
# Actually draws the menu and handles branching
|
||||||
request = ""
|
request = ""
|
||||||
try:
|
try:
|
||||||
while request is not "exit":
|
while request is not "exit":
|
||||||
|
@ -73,12 +77,9 @@ class CursedMenu(object):
|
||||||
self.__exit__()
|
self.__exit__()
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
def draw(self):
|
def draw_default(self):
|
||||||
'''Draw the menu and lines'''
|
# Draws default menu
|
||||||
# TODO: display refresh is hacky. Could be more precise
|
|
||||||
clear_bar = " " * (int(self.maxx*2/3))
|
clear_bar = " " * (int(self.maxx*2/3))
|
||||||
self.screen.refresh()
|
|
||||||
self.screen.border(0)
|
|
||||||
self.screen.addstr(2,2, self.title, curses.A_STANDOUT) # Title for this menu
|
self.screen.addstr(2,2, self.title, curses.A_STANDOUT) # Title for this menu
|
||||||
self.screen.addstr(4,2, self.subtitle, curses.A_BOLD) #Subtitle for this menu
|
self.screen.addstr(4,2, self.subtitle, curses.A_BOLD) #Subtitle for this menu
|
||||||
# Display all the menu items, showing the 'pos' item highlighted
|
# Display all the menu items, showing the 'pos' item highlighted
|
||||||
|
@ -103,6 +104,55 @@ class CursedMenu(object):
|
||||||
else:
|
else:
|
||||||
self.screen.addstr(5,13, clear_bar, curses.A_NORMAL)
|
self.screen.addstr(5,13, clear_bar, curses.A_NORMAL)
|
||||||
self.screen.addstr(5,13, " - you can't water a dead plant :(", curses.A_NORMAL)
|
self.screen.addstr(5,13, " - you can't water a dead plant :(", curses.A_NORMAL)
|
||||||
|
|
||||||
|
def format_garden_data(self,this_garden):
|
||||||
|
plant_table = ""
|
||||||
|
# TODO: include only live plants maybe
|
||||||
|
for plant_id in this_garden:
|
||||||
|
if this_garden[plant_id]:
|
||||||
|
if not this_garden[plant_id]["dead"]:
|
||||||
|
this_plant = this_garden[plant_id]
|
||||||
|
plant_table += this_plant["owner"] + " - "
|
||||||
|
plant_table += this_plant["age"] + " - "
|
||||||
|
plant_table += this_plant["description"] + " - "
|
||||||
|
plant_table += str(this_plant["score"]) + "\n"
|
||||||
|
return plant_table
|
||||||
|
|
||||||
|
def draw_garden(self):
|
||||||
|
# Draws neighborhood
|
||||||
|
clear_bar = " " * (self.maxx-2) + "\n"
|
||||||
|
control_keys = [curses.KEY_UP, curses.KEY_DOWN, curses.KEY_LEFT, curses.KEY_RIGHT]
|
||||||
|
# load data
|
||||||
|
with open(self.garden_file_path, 'rb') as f:
|
||||||
|
this_garden = pickle.load(f)
|
||||||
|
# format data
|
||||||
|
if not self.gardenmenutoggle:
|
||||||
|
plant_table_formatted = self.format_garden_data(this_garden)
|
||||||
|
self.gardenmenutoggle = not self.gardenmenutoggle
|
||||||
|
else:
|
||||||
|
plant_table_formatted = ""
|
||||||
|
for line in this_garden:
|
||||||
|
plant_table_formatted += clear_bar
|
||||||
|
self.gardenmenutoggle = not self.gardenmenutoggle
|
||||||
|
|
||||||
|
for y, line in enumerate(plant_table_formatted.splitlines(), 2):
|
||||||
|
self.screen.addstr(y+12, 2, line)
|
||||||
|
# TODO: this needs to be updated so that it only draws if the window
|
||||||
|
# is big enough.. or try catch it
|
||||||
|
self.screen.refresh()
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
# Draw the menu and lines
|
||||||
|
# TODO: this needs to either display the default menu screen or the
|
||||||
|
# garden/leaderboard thing based on self.gardenmenutoggle
|
||||||
|
# TODO: display refresh is hacky. Could be more precise
|
||||||
|
self.screen.refresh()
|
||||||
|
self.screen.border(0)
|
||||||
|
# if self.gardenmenutoggle:
|
||||||
|
# self.draw_garden()
|
||||||
|
# else:
|
||||||
|
# self.draw_default()
|
||||||
|
self.draw_default()
|
||||||
try:
|
try:
|
||||||
self.screen.refresh()
|
self.screen.refresh()
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
|
@ -123,10 +173,10 @@ class CursedMenu(object):
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
def get_user_input(self):
|
def get_user_input(self):
|
||||||
'''Gets the user's input and acts appropriately'''
|
# Gets the user's input and acts appropriately
|
||||||
user_in = self.screen.getch() # Gets user input
|
user_in = self.screen.getch() # Gets user input
|
||||||
|
|
||||||
'''Enter and exit Keys are special cases'''
|
# Enter and exit Keys are special cases
|
||||||
if user_in == 10:
|
if user_in == 10:
|
||||||
return self.options[self.selected]
|
return self.options[self.selected]
|
||||||
if user_in == 27:
|
if user_in == 27:
|
||||||
|
@ -145,49 +195,46 @@ class CursedMenu(object):
|
||||||
self.selected = self.selected % len(self.options)
|
self.selected = self.selected % len(self.options)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def draw_instructions(self):
|
||||||
def handle_request(self, request):
|
if not self.instructiontoggle:
|
||||||
'''This is where you do things with the request'''
|
instructions_txt = """welcome to botany. you've been given a seed
|
||||||
if request is None: return
|
|
||||||
if request is "kill":
|
|
||||||
self.plant.kill_plant()
|
|
||||||
if request is "new":
|
|
||||||
self.plant.new_seed(self.plant.file_name)
|
|
||||||
if request == "water":
|
|
||||||
self.plant.water()
|
|
||||||
if request == "instructions":
|
|
||||||
if not self.instructiontoggle:
|
|
||||||
instructions_txt = """welcome to botany. you've been given a seed
|
|
||||||
that will grow into a beautiful plant. check
|
that will grow into a beautiful plant. check
|
||||||
in and water your plant every 24h to keep it
|
in and water your plant every 24h to keep it
|
||||||
growing. 5 days without water = death. your
|
growing. 5 days without water = death. your
|
||||||
plant depends on you to live! more info is
|
plant depends on you to live! more info is
|
||||||
available in the readme :)
|
available in the readme :)
|
||||||
cheers,
|
cheers,
|
||||||
curio"""
|
curio"""
|
||||||
self.instructiontoggle = not self.instructiontoggle
|
self.instructiontoggle = not self.instructiontoggle
|
||||||
else:
|
else:
|
||||||
instructions_txt = """
|
instructions_txt = """
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.instructiontoggle = not self.instructiontoggle
|
self.instructiontoggle = not self.instructiontoggle
|
||||||
for y, line in enumerate(instructions_txt.splitlines(), 2):
|
for y, line in enumerate(instructions_txt.splitlines(), 2):
|
||||||
self.screen.addstr(self.maxy-12+y,self.maxx-47, line)
|
self.screen.addstr(self.maxy-12+y,self.maxx-47, line)
|
||||||
self.screen.refresh()
|
self.screen.refresh()
|
||||||
|
def handle_request(self, request):
|
||||||
|
'''This is where you do things with the request'''
|
||||||
|
if request == None: return
|
||||||
|
if request == "kill":
|
||||||
|
self.plant.kill_plant()
|
||||||
|
if request == "new":
|
||||||
|
self.plant.new_seed(self.plant.file_name)
|
||||||
|
if request == "water":
|
||||||
|
self.plant.water()
|
||||||
|
if request == "instructions":
|
||||||
|
self.draw_instructions()
|
||||||
|
if request == "garden":
|
||||||
|
self.draw_garden()
|
||||||
def __exit__(self):
|
def __exit__(self):
|
||||||
self.exit = True
|
self.exit = True
|
||||||
curses.curs_set(2)
|
curses.curs_set(2)
|
||||||
curses.endwin()
|
curses.endwin()
|
||||||
os.system('clear')
|
os.system('clear')
|
||||||
|
|
||||||
|
|
||||||
'''demo'''
|
|
||||||
# cm = CursedMenu()
|
|
||||||
# cm.show([1,"water",3], title=' botany ', subtitle='Options')
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue