engine.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. # THIS IS A SOURCE CODE FILE FROM I'M NOT EVEN HUMAN THE GAME.
  2. # IT COULD BE USED IN A DIFFERENT PIECE OF SOFTWARE ( LIKE A
  3. # DIFFERENT GAME ), BUT IT WAS ORIGINALLY WRITTEN FOR I'M NOT
  4. # EVEN HUMAN THE GAME.
  5. # THE DEVELOPERS OF THE GAME ARE : (C) J.Y.AMIHUD, AYYZEE AND
  6. # OTHER CONTRIBUTORS. THIS AND OTHER FILES IN THIS GAME,
  7. # UNLESS SPECIFICALLY NOTED, COULD BE USED UNDER THE TERMS OF
  8. # GNU GENERAL PUBLIC LICENSE VERSION 3 OR ANY LATER VERSION.
  9. import os
  10. import datetime # For the FPS meter mainly
  11. # GTK module ( Graphical interface
  12. import gi
  13. gi.require_version('Gtk', '3.0')
  14. from gi.repository import Gtk
  15. from gi.repository import Gdk
  16. import cairo
  17. import threading
  18. # Engine submodules ( Layers )
  19. from modules import ui
  20. # Game scenes ( layers )
  21. from modules import testing
  22. from modules import main_menu
  23. from modules import settings
  24. from modules import editor
  25. from modules import world
  26. from modules import gameplay
  27. def previous(game):
  28. """This function is making an exact copy of game.current
  29. to preserve the values from the previous frame."""
  30. game.previous = {}
  31. for i in game.current:
  32. if type(game.current[i]) == list or type(game.current[i]) is dict:
  33. game.previous[i] = game.current[i].copy()
  34. else:
  35. game.previous[i] = game.current[i]
  36. def run():
  37. """This function will launch the game's engine and make a
  38. window and everything."""
  39. # This is a hack. game will be a Gtk.Window() object. But I
  40. # am going to add into it a lot of other things and pass it
  41. # to all the sub-functions as a kind of container for semi-
  42. # global variables. Anything important goes into the game
  43. # variable.
  44. game = Gtk.Window()
  45. game.set_default_size(1200,720)
  46. game.set_position(Gtk.WindowPosition.CENTER)
  47. game.connect("destroy", Gtk.main_quit)
  48. game.set_default_icon_from_file("icon.png")
  49. game.set_title("I'm Not Even Human - The Game")
  50. # Catching events from keys and mouse clicks
  51. game.connect("button-press-event", mouse_button_press, game)
  52. game.connect("button-release-event", mouse_button_release, game)
  53. game.connect("key-press-event", key_press, game)
  54. game.connect("key-release-event", key_release, game)
  55. # To catch the scroll events from the mouse wheel in GTK I
  56. # need to have a scrolled windows widget. There is no use
  57. # for it. I'm making it only to add this functionality
  58. # which is just an another hack.
  59. scroll = Gtk.ScrolledWindow()
  60. scroll.connect("scroll-event", scrolling, game)
  61. game.scene = "main_menu" # Current menu of the game
  62. game.map = {} # Chunks of the map
  63. game.current = {} # Values for the current frame
  64. game.previous = {} # Values for the previous frame
  65. game.images = {} # All images loaded to memory
  66. game.menus = {} # Metada to navigate buttons using a keyboard
  67. game.FPS = 0 # Frames per second of the game.
  68. game.AFPS = 0 # Average frames per second
  69. game.scroll = {} # Scrolls
  70. settings.update_worlds(game) # Worlds metadata | game.worlds
  71. settings.load_settings(game) # Loading settings | game.settings
  72. world.update_elements(game) # Loading assets | game.elements
  73. ui.load_palete(game) # game.palete ( colors from assets/palete.json )
  74. game.current["frame"] = 0 # The number of frames from launch
  75. game.current["camera"] = [0,0,0] # Game camera
  76. game.current["testing"] = False # Whether the devmode is on / off
  77. game.current["LMB"] = False # Left Mouse Button
  78. game.current["MMB"] = False # Middle Mouse Button
  79. game.current["RMB"] = False # Right Mouse Button
  80. game.current["keys"] = [] # List of pressed keys ( IDs )
  81. game.current["scroll"] = [0,0]
  82. # The same keys can have multiple IDs depending on what type of
  83. # keyboard you have or where on the keyboard they are located.
  84. # These arrays store the IDs of common keys.
  85. game.current["ctrl"] = [65507, 65508] # [CTRL]
  86. game.current["plus"] = [61, 43, 65451] # [+]
  87. game.current["minus"] = [45, 95, 65453] # [-]
  88. # States of various dynamic stuff in the game
  89. # [Object] [Potision] [Condition]
  90. game.current["state"] = {"4211D79": {"xyz":[0,0,0], "colision": False}}
  91. # Making the copy of all current, so far into previous
  92. previous(game)
  93. # Setting up the FPS meter
  94. game.sFPS = datetime.datetime.now()
  95. # Setting the drawable
  96. gamedraw = Gtk.DrawingArea()
  97. gamedraw.set_size_request(800, 600)
  98. scroll.set_size_request(800, 600)
  99. game.add(scroll)
  100. scroll.add_with_viewport(gamedraw)
  101. gamedraw.connect("draw", gamedrawing, game)
  102. # Running
  103. game.show_all()
  104. Gtk.main()
  105. def gamedrawing(gamedrawing, main_layer, game):
  106. """You can think of this function as one that combines various
  107. layers into a finished image. And handles some logic related
  108. to it."""
  109. # FPS counter
  110. game.fFPS = datetime.datetime.now()
  111. game.tFPS = game.fFPS - game.sFPS
  112. game.FPS = int ( 1.0 / ( game.tFPS.microseconds /1000000))
  113. if game.current["frame"] % 30 == 0:
  114. game.AFPS = game.FPS
  115. game.sFPS = datetime.datetime.now()
  116. # Updating the frame
  117. game.current["frame"] += 1
  118. # Getting mouse data about the frame
  119. game.current['mx'] = game.get_pointer()[0] / game.settings["pixels"]
  120. game.current['my'] = game.get_pointer()[1] / game.settings["pixels"]
  121. game.current['w'] = int(round(game.get_size()[0] / game.settings["pixels"] ))
  122. game.current['h'] = int(round(game.get_size()[1] / game.settings["pixels"] ))
  123. if game.current["frame"] == 1:
  124. previous(game)
  125. # Switching the fullscreen mode on and off. Ctrl-F
  126. # Warning! The method used to fullscreen bypasses and disobeys some window manager
  127. # rules.
  128. # [CTRL] [F]
  129. if 65507 in game.current["keys"] and 102 in game.current["keys"]:
  130. game.settings["fullscreen"] = not game.settings["fullscreen"]
  131. game.current["keys"].remove(102)
  132. if game.settings["fullscreen"]:
  133. game.fullscreen()
  134. else:
  135. game.unfullscreen()
  136. layers = [] # Layers to draw
  137. if game.scene == "main_menu":
  138. layers.append(main_menu.layer(game))
  139. elif game.scene == "settings":
  140. layers.append(settings.layer(game))
  141. elif game.scene == "editor":
  142. layers.append(editor.layer(game))
  143. elif game.scene == "gameplay":
  144. layers.append(gameplay.layer(game))
  145. # Check if any of the keys are currently being pressed by comparing them
  146. # to the ones that are present in their respective arrays.
  147. # [KEYS] [ARRAY that has the common key IDs] [ARRAY that has ALL of the key IDs]
  148. ctrlDown = any(item in game.current["ctrl"] for item in game.current["keys"]) # Check if any CTRL key is pressed.
  149. plusDown = any(item in game.current["plus"] for item in game.current["keys"]) # Check if any + key is pressed.
  150. minusDown = any(item in game.current["minus"] for item in game.current["keys"]) # Check if any - key is pressed.
  151. # Switching the testing mode on and off. Ctrl-T
  152. # [CTRL] [T]
  153. if ctrlDown and 116 in game.current["keys"]:
  154. game.current["testing"] = not game.current["testing"]
  155. game.current["keys"].remove(116)
  156. if game.current["testing"]:
  157. layers.append(testing.layer(game))
  158. # Scaling the layer to the window size
  159. main_layer.scale(game.settings["pixels"],
  160. game.settings["pixels"])
  161. ui.color(game, main_layer, "black")
  162. main_layer.rectangle(0,0,
  163. game.current["w"],
  164. game.current["h"])
  165. main_layer.fill()
  166. for layer in layers:
  167. # Setting the layer as a kind of brush
  168. main_layer.set_source_surface(layer, 0 , 0)
  169. # This is making the pixel art look pixalated
  170. p = main_layer.get_source()
  171. p.set_filter(cairo.FILTER_NEAREST)
  172. # Painting the layer
  173. main_layer.paint()
  174. # Update things when window is stretched
  175. if game.current["h"] != game.previous["h"]\
  176. or game.current["w"] != game.previous["w"]:
  177. for i in game.menus:
  178. if "buttons" in game.menus[i]:
  179. game.menus[i]["buttons"] = []
  180. if 65307 in game.current["keys"]:
  181. game.scene = "main_menu"
  182. # [CTRL] [+]
  183. if ctrlDown and plusDown:
  184. game.settings["pixels"] += 1
  185. #save_settings(game)
  186. for key in game.current["plus"]:
  187. try:
  188. game.current["keys"].remove(key)
  189. except:
  190. pass
  191. # [CTRL] [-]
  192. if ctrlDown and minusDown and game.settings["pixels"] >1:
  193. game.settings["pixels"] -= 1
  194. #save_settings(game)
  195. for key in game.current["minus"]:
  196. try:
  197. game.current["keys"].remove(key)
  198. except:
  199. pass
  200. previous(game) # Go back to the previous screen
  201. # Refreshing the frame automatically
  202. gamedrawing.queue_draw()
  203. # Mouse
  204. def mouse_button_press(widget, event, game):
  205. # This function marks activation of the button. Not it's deactivation.
  206. for i, button in enumerate(["LMB", "MMB", "RMB"]):
  207. if i+1 == int(event.get_button()[1]):
  208. game.current[button] = [event.x / game.settings["pixels"],
  209. event.y / game.settings["pixels"]]
  210. def mouse_button_release(widget, event, game):
  211. # This function reverses the effects of the mouse_button_press() function.
  212. for i, button in enumerate(["LMB", "MMB", "RMB"]):
  213. if i+1 == int(event.get_button()[1]):
  214. game.current[button] = False
  215. def key_press(widget, event, game):
  216. if event.keyval not in game.current["keys"]:
  217. game.current["keys"].append(event.keyval)
  218. game.current["key_letter"] = event.string
  219. def key_release(widget, event, game):
  220. # Key values represent an ASCII code of the key's character
  221. # So 'A' is 65 and 'a' is 97. To mitigate sticky keys when
  222. # Shift is pressed, we need to remove all versions of the keys.
  223. upper = ord( chr( event.keyval ).upper() ) # Uppercase version
  224. lower = ord( chr( event.keyval ).lower() ) # Lowercase version
  225. for i in (upper, lower, event.keyval):
  226. if i in game.current["keys"]:
  227. game.current["keys"].remove(i)
  228. # I also want to clean the key letter. Because other wise in the
  229. # script writer I had weird key presses all the time.
  230. if not game.current["keys"]:
  231. game.current["key_letter"] = ""
  232. def scrolling(widget, event, game):
  233. e, x, y = event.get_scroll_deltas()
  234. game.current["scroll"] = [x,y]