engine.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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. ####################################################################
  89. # #
  90. # #
  91. # GAME CURRENT STATE OBJECT INDEXING DYNAMICS #
  92. # #
  93. # #
  94. ####################################################################
  95. # [Object] [Potision] [Condition]
  96. # game.current["state"] = {"4211D79": {"xyz":[0,0,0],
  97. # "colision": False,
  98. # "orientation":"Up"}}
  99. game.current["state"] = {
  100. "objects":[
  101. # Objects that are not in the map, but load anyway
  102. {
  103. "asset":"characters/4211D79",
  104. "xyz":[0,0,0],
  105. "velocity":[0,0,0],
  106. "cell":[0,0],
  107. "last_before_colision":[0,0,0],
  108. "check_mark":[0,0,0],
  109. "orientation":"Up",
  110. "grounded":False,
  111. "health":1.0,
  112. "time-falling":0}],
  113. # List of objects that the caracter may control
  114. "controlling":{0:True},
  115. # List of objects that are on the map, but which
  116. # became dynamic, which means they need to be
  117. # excluded from the map statis view.
  118. "map_exclude":[]
  119. }
  120. # Making the copy of all current, so far into previous
  121. previous(game)
  122. # Setting up the FPS meter
  123. game.sFPS = datetime.datetime.now()
  124. # Setting the drawable
  125. gamedraw = Gtk.DrawingArea()
  126. gamedraw.set_size_request(800, 600)
  127. scroll.set_size_request(800, 600)
  128. game.add(scroll)
  129. scroll.add_with_viewport(gamedraw)
  130. gamedraw.connect("draw", gamedrawing, game)
  131. # Running
  132. game.show_all()
  133. Gtk.main()
  134. def gamedrawing(gamedrawing, main_layer, game):
  135. """You can think of this function as one that combines various
  136. layers into a finished image. And handles some logic related
  137. to it."""
  138. # FPS counter
  139. game.fFPS = datetime.datetime.now()
  140. game.tFPS = game.fFPS - game.sFPS
  141. game.FPS = int ( 1.0 / ( game.tFPS.microseconds /1000000))
  142. if game.current["frame"] % 30 == 0:
  143. game.AFPS = game.FPS
  144. game.sFPS = datetime.datetime.now()
  145. # Updating the frame
  146. game.current["frame"] += 1
  147. # Getting mouse data about the frame
  148. game.current['mx'] = game.get_pointer()[0] / game.settings["pixels"]
  149. game.current['my'] = game.get_pointer()[1] / game.settings["pixels"]
  150. game.current['w'] = int(round(game.get_size()[0] / game.settings["pixels"] ))
  151. game.current['h'] = int(round(game.get_size()[1] / game.settings["pixels"] ))
  152. if game.current["frame"] == 1:
  153. previous(game)
  154. # Switching the fullscreen mode on and off. Ctrl-F
  155. # Warning! The method used to fullscreen bypasses and disobeys some window manager
  156. # rules.
  157. # [CTRL] [F]
  158. if 65507 in game.current["keys"] and 102 in game.current["keys"]:
  159. game.settings["fullscreen"] = not game.settings["fullscreen"]
  160. game.current["keys"].remove(102)
  161. if game.settings["fullscreen"]:
  162. game.fullscreen()
  163. else:
  164. game.unfullscreen()
  165. layers = [] # Layers to draw
  166. if game.scene == "main_menu":
  167. layers.append(main_menu.layer(game))
  168. elif game.scene == "settings":
  169. layers.append(settings.layer(game))
  170. elif game.scene == "editor":
  171. layers.append(editor.layer(game))
  172. elif game.scene == "gameplay":
  173. layers.append(gameplay.layer(game))
  174. # Check if any of the keys are currently being pressed by comparing them
  175. # to the ones that are present in their respective arrays.
  176. # [KEYS] [ARRAY that has the common key IDs] [ARRAY that has ALL of the key IDs]
  177. game.current["ctrlDown"] = any(item in game.current["ctrl"] for item in game.current["keys"]) # Check if any CTRL key is pressed.
  178. game.current["plusDown"] = any(item in game.current["plus"] for item in game.current["keys"]) # Check if any + key is pressed.
  179. game.current["minusDown"] = any(item in game.current["minus"] for item in game.current["keys"]) # Check if any - key is pressed.
  180. # Switching the testing mode on and off. Ctrl-T
  181. # [CTRL] [T]
  182. if game.current["ctrlDown"] and 116 in game.current["keys"]:
  183. game.current["testing"] = not game.current["testing"]
  184. game.current["keys"].remove(116)
  185. if game.current["testing"]:
  186. layers.append(testing.layer(game))
  187. # Scaling the layer to the window size
  188. main_layer.scale(game.settings["pixels"],
  189. game.settings["pixels"])
  190. ui.color(game, main_layer, "black")
  191. main_layer.rectangle(0,0,
  192. game.current["w"],
  193. game.current["h"])
  194. main_layer.fill()
  195. for layer in layers:
  196. # Setting the layer as a kind of brush
  197. main_layer.set_source_surface(layer, 0 , 0)
  198. # This is making the pixel art look pixalated
  199. p = main_layer.get_source()
  200. p.set_filter(cairo.FILTER_NEAREST)
  201. # Painting the layer
  202. main_layer.paint()
  203. # Update things when window is stretched
  204. if game.current["h"] != game.previous["h"]\
  205. or game.current["w"] != game.previous["w"]:
  206. for i in game.menus:
  207. if "buttons" in game.menus[i]:
  208. game.menus[i]["buttons"] = []
  209. if 65307 in game.current["keys"]:
  210. game.scene = "main_menu"
  211. # [CTRL] [+]
  212. if game.current["ctrlDown"] and game.current["plusDown"]:
  213. game.settings["pixels"] += 1
  214. #save_settings(game)
  215. for key in game.current["plus"]:
  216. try:
  217. game.current["keys"].remove(key)
  218. except:
  219. pass
  220. # [CTRL] [-]
  221. if game.current["ctrlDown"] and game.current["minusDown"] and game.settings["pixels"] >1:
  222. game.settings["pixels"] -= 1
  223. #save_settings(game)
  224. for key in game.current["minus"]:
  225. try:
  226. game.current["keys"].remove(key)
  227. except:
  228. pass
  229. previous(game) # Go back to the previous screen
  230. # Refreshing the frame automatically
  231. gamedrawing.queue_draw()
  232. # Mouse
  233. def mouse_button_press(widget, event, game):
  234. # This function marks activation of the button. Not it's deactivation.
  235. for i, button in enumerate(["LMB", "MMB", "RMB"]):
  236. if i+1 == int(event.get_button()[1]):
  237. game.current[button] = [event.x / game.settings["pixels"],
  238. event.y / game.settings["pixels"]]
  239. def mouse_button_release(widget, event, game):
  240. # This function reverses the effects of the mouse_button_press() function.
  241. for i, button in enumerate(["LMB", "MMB", "RMB"]):
  242. if i+1 == int(event.get_button()[1]):
  243. game.current[button] = False
  244. def key_press(widget, event, game):
  245. if event.keyval not in game.current["keys"]:
  246. game.current["keys"].append(event.keyval)
  247. game.current["key_letter"] = event.string
  248. def key_release(widget, event, game):
  249. # Key values represent an ASCII code of the key's character
  250. # So 'A' is 65 and 'a' is 97. To mitigate sticky keys when
  251. # Shift is pressed, we need to remove all versions of the keys.
  252. upper = ord( chr( event.keyval ).upper() ) # Uppercase version
  253. lower = ord( chr( event.keyval ).lower() ) # Lowercase version
  254. for i in (upper, lower, event.keyval):
  255. if i in game.current["keys"]:
  256. game.current["keys"].remove(i)
  257. # I also want to clean the key letter. Because other wise in the
  258. # script writer I had weird key presses all the time.
  259. if not game.current["keys"]:
  260. game.current["key_letter"] = ""
  261. def scrolling(widget, event, game):
  262. e, x, y = event.get_scroll_deltas()
  263. game.current["scroll"] = [x,y]