Example #1
0
	def purge() -> None:

		# Debug
		Debug.print("Purging stored images", DebugChannel.GRAPHICS)

		# Clear Images
		ImageLoader.data = {}
Example #2
0
	def state_update(self, state: str, store: bool = False, data: Dict = None) -> None:

		# Debug
		Debug.print("Updating to %s state" % state, DebugChannel.STATE)

		# Purge Images
		ImageLoader.purge()

		# Existing State
		if self.state_active is not None:

			# Store Existing
			if store is True:
				self.state_active.on_store()
				self.state_stored = self.state_active

			# Terminate Existing
			else:
				self.state_active.on_terminate()

		# Initialise State
		self.state_active = self.state_loaded[state](self)
		# NOTE: put the above into a method to gracefully handle this
		self.state_active.on_start(data)

		# Bind Events
		self.state_bind()
Example #3
0
		def state_load(directory: str) -> Dict[str, State]:

			# Debug
			Debug.print("Loading states from directory %s" % directory, DebugChannel.STATE)

			# Directory Path
			directory_path = os.path.join(os.getcwd(), directory)

			# List Files
			file_list = ArrayList(os.listdir(directory_path)).reject(lambda it: it.startswith("_")).map(lambda it: it.split(".")[0])
			# NOTE: current reject is not going to ignore directories

			# Module Logic
			# def load_module(module: module) -> List[module]:
			# type is <class 'module'> but hasn't been imported
			def load_module(module):

				# List Attributes
				result = ArrayList(list(module.__dict__.keys())).reject(lambda it: it == "State")

				# Map Classes
				result = result.map(lambda it: (it, getattr(module, it)))

				# Return States
				return result.filter(lambda _, v: inspect.isclass(v) and issubclass(v, State))

			# Return States
			result = {}
			for module in file_list.map(lambda it: load_module(importlib.import_module("%s.%s" % (directory.split("/")[-1], it)))):
				for name, state in module:
					result[name] = state
					Debug.print(" - %s" % name, DebugChannel.STATE)
			return result
Example #4
0
    def play(sound: str) -> None:

        # Debug
        Debug.print("Playing sound %s" % sound, DebugChannel.AUDIO)

        # Thread Logic
        def execute(sound: str, _) -> None:
            playsound("resources/sounds/%s.mp3" % sound)

        # Spawn Thread
        Thread(target=execute, args=(sound, None), daemon=False).start()
Example #5
0
	def add_event(self, time_ms: int, logic: Callable) -> None:

		# Create Event
		event_new = {
			"logic": logic,
			"timer": (time.time() * 1000) + time_ms
		}

		# Debug
		Debug.print("Creating new event %d to fire in %d ms" % (id(event_new), time_ms), DebugChannel.STATE)

		# Append Event
		self.event = self.event.add(event_new)
Example #6
0
	def tick_event(self) -> None:

		# Check Events
		time_ms: int = time.time() * 1000
		for event in self.event.filter(lambda it: time_ms >= it["timer"]):

			# Debug
			Debug.print("Invoking event %d" % id(event), DebugChannel.STATE)

			# Invoke Logic
			event["logic"]()

			# Remove Event
			self.event = self.event.remove(event)
Example #7
0
	def _store(image_file: str, image_name: str, size: Dimensions, point: Point) -> None:

		# Load Image
		image_data = Image.open("resources/images/%s.png" % image_file)

		# Crop Image
		if size is not None and point is not None:
			posX: int = point.x * size.width
			posY: int = point.y * size.height
			image_data = image_data.crop((posX, posY, posX + size.width, posY + size.height))

		# Store Image
		ImageLoader.data[image_name] = ImageTk.PhotoImage(image_data)

		# Debug
		Debug.print("Stored image %s (%d)" % (image_name, len(ImageLoader.data)), DebugChannel.GRAPHICS)
Example #8
0
	def terminate(self) -> None:

		# Already Terminating
		if self.running is False:
			return

		# Debug
		Debug.print("Terminating application")

		# Application Status
		self.running = False

		# Terminate Controller
		self.controller.terminate()

		# System Exit
		sys.exit()
Example #9
0
	def state_revert(self, data: Dict = None) -> None:

		# Debug
		Debug.print("Reverting to stored state", DebugChannel.STATE)

		# Purge Images
		ImageLoader.purge()

		# Nothing Stored
		if self.state_stored is None:
			raise Exception("No stored state to revert to!")

		# Terminate Existing
		self.state_active.on_terminate()

		# Revert State
		self.state_active = self.state_stored
		self.state_active.on_revert(data)
		self.state_stored = None

		# Bind Events
		self.state_bind()
Example #10
0
	def on_key_pressed(self, event: Any) -> None:
		# NOTE: event should be specifically typed here
		if event.keycode in Keyboard.action:
			Debug.print(event, DebugChannel.INPUT)
			self.action(Keyboard.action[event.keycode])
Example #11
0
	def action(self, action: Action) -> None:
		Debug.print(action, DebugChannel.STATE)
		self.state_active.on_action(action)
Example #12
0
	def _debug_parse(value: str, title: str, size: Dimensions, tick_ms: int) -> None:

		# Invalid Value
		if not isinstance(value, str) or re.match(r"^[\+\-][A-Z]*$", value) is False:
			raise Exception("Invalid debug string!")

		# Disable Channels
		if value[0] == "-":

			# Disable All
			if len(value) == 1:
				return

			# Disable Specific
			if "R" not in value: Debug.debug_channels[DebugChannel.RIEM] = True
			if "S" not in value: Debug.debug_channels[DebugChannel.STATE] = True
			if "A" not in value: Debug.debug_channels[DebugChannel.AUDIO] = True
			if "G" not in value: Debug.debug_channels[DebugChannel.GRAPHICS] = True
			if "I" not in value: Debug.debug_channels[DebugChannel.INPUT] = True

		# Enable Channels
		if value[0] == "+":

			# Enable All
			if len(value) == 1:
				Debug.debug_channels = {
					DebugChannel.RIEM: True,
					DebugChannel.STATE: True,
					DebugChannel.AUDIO: True,
					DebugChannel.GRAPHICS: True,
					DebugChannel.INPUT: True
				}

			# Enable Specific
			if "R" in value: Debug.debug_channels[DebugChannel.RIEM] = True
			if "S" in value: Debug.debug_channels[DebugChannel.STATE] = True
			if "A" in value: Debug.debug_channels[DebugChannel.AUDIO] = True
			if "G" in value: Debug.debug_channels[DebugChannel.GRAPHICS] = True
			if "I" in value: Debug.debug_channels[DebugChannel.INPUT] = True

		# Print Info
		print("")
		Debug.print("Application Debug Mode")
		Debug.print("======================")
		Debug.print("Version: %s" % __version__)
		Debug.print("Project: %s" % title)
		Debug.print("Window:  %d x %d" % (size.width, size.height))
		Debug.print("Tick:    %d ms" % tick_ms)
		Debug.print()
Example #13
0
	def __init__(self, title: str, state_initial: str, state_directory: str, default_text: Dict[str, str] = None, icon: str = None, size: Dimensions = Dimensions(960, 720), tick_ms: int = 250, **kwargs) -> None:

		# Default Properties
		maximise: bool = False

		# Parse Kwargs
		for k, v in kwargs.items():

			# Option: Debug
			if k == "debug": Application._debug_parse(v, title, size, tick_ms)

			# Option: Maximise
			if k == "fullscreen" and v == True:
				maximise = True

		# Public Properties
		self.size: Dimensions = size

		# State Logic
		def state_load(directory: str) -> Dict[str, State]:

			# Debug
			Debug.print("Loading states from directory %s" % directory, DebugChannel.STATE)

			# Directory Path
			directory_path = os.path.join(os.getcwd(), directory)

			# List Files
			file_list = ArrayList(os.listdir(directory_path)).reject(lambda it: it.startswith("_")).map(lambda it: it.split(".")[0])
			# NOTE: current reject is not going to ignore directories

			# Module Logic
			# def load_module(module: module) -> List[module]:
			# type is <class 'module'> but hasn't been imported
			def load_module(module):

				# List Attributes
				result = ArrayList(list(module.__dict__.keys())).reject(lambda it: it == "State")

				# Map Classes
				result = result.map(lambda it: (it, getattr(module, it)))

				# Return States
				return result.filter(lambda _, v: inspect.isclass(v) and issubclass(v, State))

			# Return States
			result = {}
			for module in file_list.map(lambda it: load_module(importlib.import_module("%s.%s" % (directory.split("/")[-1], it)))):
				for name, state in module:
					result[name] = state
					Debug.print(" - %s" % name, DebugChannel.STATE)
			return result

		# State Management
		self.state_active = None
		self.state_stored = None
		self.state_loaded = state_load(state_directory)
		self.state_bind = lambda: self.app.bind("<Key>", self.on_key_pressed)
		# NOTE: these shouldn't be public

		# Create Application
		Debug.print("Creating application", DebugChannel.RIEM)
		self.app = Tk()
		self.app.title(title)
		self.app.geometry("%dx%d" % (self.size.width, self.size.height))
		self.app.resizable(False, False)
		if maximise is True:
			self.app.attributes("-fullscreen", True)
		if icon is not None:
			Debug.print(" - locating custom icon %s" % icon, DebugChannel.RIEM)
			self.app.iconbitmap(r'%s' % os.path.join(os.getcwd(), "resources", "icons", "%s.ico" % icon))
		# NOTE: self.app shouldn't be public

		# Create Canvas
		canvas = Canvas(self.app, bg = "black", width = self.size.width, height = self.size.height, highlightthickness = 0)
		canvas.pack()

		# Create Graphics
		gfx: Graphics = Graphics(canvas, default_text)

		# Intro State
		self.state_active = StateIntro(self, state_initial)

		# Initialise Controller
		Debug.print("Initialising controller", DebugChannel.INPUT)
		self.controller = Controller(self)

		# Application Status
		self.running = True

		# Create Loop
		def loop() -> None:

			# Not Running
			if self.running is not True:
				return

			# Timer Start
			loop_time: int = (time.time() * 1000)

			# Controller Actions
			self.controller.get_actions().each(lambda it: self.action(it))

			# Application Tick
			self.state_active.tick()
			self.state_active.tick_event()

			# Application Render
			gfx.draw_rect(Point(0, 0), self.size, "black", True)
			self.state_active.render(gfx)

			# Schedule Loop
			loop_time = (time.time() * 1000) - loop_time
			loop_wait: int = 0
			if loop_time < tick_ms:
				loop_wait = tick_ms - loop_time
			self.app.after(int(loop_wait), loop)

		# Invoke Loop
		loop()

		# Start Application
		Debug.print("Initialising application loop", DebugChannel.RIEM)
		self.app.mainloop()