ui.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  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 json
  11. import cairo
  12. def load_palete(game):
  13. """Loads colors from assets/palete.json"""
  14. with open("assets/palete.json") as f:
  15. game.palete = json.load(f)
  16. def distance(list1, list2):
  17. """Calculates distnaces between two vectors"""
  18. d = []
  19. for n, i in enumerate(list1):
  20. try:
  21. d.append(max((i-list2[n]),(list2[n]-i)))
  22. except:
  23. d.append(0)
  24. return sum(d) / len(d)
  25. def color(game, layer, code, alpha=1):
  26. """Sets a cairo brush color from a palete by looking for the
  27. closest match."""
  28. # If it's a code in hex like #FF0000 so we need to convert it
  29. # to numbers first
  30. if type(code) == str and code.startswith("#") and (len(code) -1) %3 == 0:
  31. new_c = []
  32. for c in range(3):
  33. ind = int((len(code)-1)/3)
  34. new_c.append( int(code[ind*c+1:ind*c+ind+1], 16)/255 )
  35. code = new_c
  36. # If it's a code in RGB, we look for the nearest
  37. # color on the palete.
  38. if type(code) == list or type(code) == tuple:
  39. name = "black" # Place holder for a palete entry
  40. dist = 10000000000 # Imaginary large number
  41. # Chekcing for the lowest possible deviation of color
  42. for color in game.palete:
  43. new_dist = distance(game.palete[color], code)
  44. if dist > new_dist:
  45. name = color
  46. dist = new_dist
  47. r, g, b = game.palete[name]
  48. elif type(code) == str:
  49. try:
  50. r, g, b = game.palete[code]
  51. except:
  52. r, g, b = 0, 0, 0
  53. else:
  54. r, g, b = 0, 0, 0
  55. layer.set_source_rgba(r,g,b,alpha)
  56. def cache_sprite_sheet(game, sheet):
  57. """This function will cache sprite sheets into game.images"""
  58. if sheet not in game.images:
  59. # Loading the sheet image
  60. grid = cairo.ImageSurface.create_from_png(sheet)
  61. # Loading metadata
  62. try:
  63. with open(sheet.replace(".png", ".json")) as f:
  64. metadata = json.load(f)
  65. except:
  66. metadata = {}
  67. width = 1
  68. height = 1
  69. try:
  70. width = metadata["resolution"][0]
  71. height = metadata["resolution"][1]
  72. except:
  73. pass
  74. # Getting the resolution of the cell
  75. cellx = int(grid.get_width()/width)
  76. celly = int(grid.get_height()/height)
  77. data = {}
  78. for x in range(width):
  79. for y in range(height):
  80. # Creating individual cells
  81. cell = cairo.ImageSurface(cairo.FORMAT_ARGB32,
  82. cellx,
  83. celly)
  84. drawcell = cairo.Context(cell)
  85. drawcell.set_antialias(cairo.ANTIALIAS_NONE)
  86. drawcell.set_source_surface(grid,
  87. 1-(cellx*x+1),
  88. 1-(celly*y+1))
  89. drawcell.paint()
  90. # Putting the cell into the data as in "0:0" or "43:69".
  91. cellname = str(x)+":"+str(y)
  92. try:
  93. cellname = metadata[cellname]["title"]
  94. except:
  95. pass
  96. data[cellname] = cell
  97. game.images[sheet] = data
  98. def image(game, layer, x, y, name, code="0:0", color=False):
  99. x = int(x)
  100. y = int(y)
  101. """This function draws an image into a canvas. And load it if
  102. it's not loaded"""
  103. # Load the whole image as a sprite ( if it's not loaded yet )
  104. cache_sprite_sheet(game, name)
  105. # Drawing the image
  106. source = game.images[name].get(code,
  107. list(game.images[name].values())[-1])
  108. if color: #If we forcing color
  109. layer.set_antialias(cairo.ANTIALIAS_NONE)
  110. layer.mask_surface(source,x, y)
  111. layer.fill()
  112. else: #If we just using the image
  113. layer.set_source_surface(source, x, y)
  114. layer.paint()
  115. def text(game, layer, string, x, y, color=True, align="left"):
  116. """This function will draw text using an assets/font.png file."""
  117. for n, l in enumerate(string):
  118. ax = x+(8*n)
  119. if align == "center":
  120. ax = ax - int(len(string)*4)
  121. elif align == "right":
  122. ax = ax - int(len(string)*8)
  123. image(game, layer, ax, y, "assets/font.png", l, color=color)
  124. def button(game, layer, x, y, w, h,
  125. menu="", icon="", string="", func=False,
  126. scroll=""):
  127. """Draws a button"""
  128. # Let's add this button into a menu.
  129. menu_selected = False
  130. select_number = 0
  131. if menu:
  132. if menu not in game.menus :
  133. game.menus[menu] = {"selected":0,
  134. "buttons":[]}
  135. bmenudata = [x,y]
  136. if bmenudata not in game.menus[menu]["buttons"]:
  137. game.menus[menu]["buttons"].append(bmenudata)
  138. for n, i in enumerate(game.menus[menu]["buttons"]):
  139. if i == bmenudata:
  140. select_number = n
  141. if n == game.menus[menu]["selected"]:
  142. menu_selected = True
  143. if scroll and scroll in game.scroll:
  144. # Making sure that while I press either UP or DOWN it will
  145. # scroll to the correct spot.
  146. if (65362 in game.current["keys"]\
  147. or 65364 in game.current["keys"])\
  148. and menu_selected:
  149. game.scroll[scroll] = 0 - y + int(game.current["h"] / 2)
  150. y = y + int(game.scroll[scroll])
  151. x = int(round(x))
  152. y = int(round(y))
  153. w = int(round(w))
  154. h = int(round(h))
  155. color(game, layer, "#FF0000")
  156. layer.set_line_width(1)
  157. layer.rectangle(x,y,w,h)
  158. layer.stroke()
  159. mo = False
  160. if int(game.current["mx"]) in range(x, x+w) \
  161. and int(game.current["my"]) in range(y, y+h):
  162. mo = True
  163. if mo or menu_selected:
  164. if menu:
  165. game.menus[menu]["selected"] = select_number
  166. layer.rectangle( x+1,y+1, w-3, h-3 )
  167. layer.fill()
  168. # If you clicked the button
  169. if ( game.previous["LMB"] \
  170. and int(game.previous["LMB"][0]) in range(x, x+w) \
  171. and int(game.previous["LMB"][1]) in range(y, y+h) ) \
  172. or ( menu_selected and 65293 in game.previous["keys"]):
  173. color(game, layer, "yellow")
  174. layer.rectangle( x+1,y+1, w-3, h-3 )
  175. layer.fill()
  176. # If the is doing something
  177. if (( not game.current["LMB"] and game.previous["LMB"] and mo) \
  178. or (65293 not in game.current["keys"] \
  179. and 65293 in game.previous["keys"])) \
  180. and func:
  181. func()
  182. game.previous["LMB"] = False
  183. game.current["LMB"] = False
  184. game.current["keys"] = []
  185. game.previous["keys"] = []
  186. if icon:
  187. if mo or menu_selected:
  188. color(game, layer, "black")
  189. image(game, layer, x+2,y+1,"assets/menu_icons.png",
  190. icon, True)
  191. if string:
  192. if mo or menu_selected:
  193. color(game, layer, "black")
  194. text(game, layer, string, x+int(w/2), y-int(h/5)+3, align="center")
  195. def button_navigate(game, menu):
  196. """This function will run in each layer to provide keyboard navigation."""
  197. # We don't want to run it if non of the arrow keys are pressed.
  198. # Thus we check for either of the arrow keys is pressed.
  199. if 65361 in game.current["keys"]\
  200. or 65362 in game.current["keys"]\
  201. or 65363 in game.current["keys"]\
  202. or 65364 in game.current["keys"]:
  203. # We want to know things about the currently selected button
  204. select = game.menus[menu]["selected"] # It's number in the list
  205. cur = game.menus[menu]["buttons"][select] # It's coordinates x, y
  206. prevdistance = 100000000000 # Arbitrary huge number
  207. # For all buttons in the menu list, we are going to see if it's
  208. # in a correct direction from the currently selected button, based on
  209. # the arrow key:
  210. # 65361 - RIGHT
  211. # 65362 - UP
  212. # 65363 - LEFT
  213. # 65364 - DOWN
  214. # But since there could be more then one button towards the correct
  215. # side, we are also checking for the closest one ( by comparing the
  216. # lowest possible distance to the currently selected button ).
  217. for n, i in enumerate( game.menus[menu]["buttons"] ):
  218. curdistance = distance(i, cur)
  219. if ((65361 in game.current["keys"] and i[0] < cur[0] )\
  220. or (65362 in game.current["keys"] and i[1] < cur[1] )\
  221. or (65363 in game.current["keys"] and i[0] > cur[0] )\
  222. or (65364 in game.current["keys"] and i[1] > cur[1] ))\
  223. and (curdistance < prevdistance)\
  224. and i != cur:
  225. select = n
  226. prevdistance = curdistance
  227. # And we restart keys, so it will not do this on each frame
  228. game.current["keys"] = []
  229. # And we write the number of the selected key
  230. game.menus[menu]["selected"] = select
  231. def scroll_area(game, layer, menu, x, y, width, height, max_value,
  232. strenght=6):
  233. """This function makes an invisible area in the UI, where there
  234. could be scroolling."""
  235. if max_value == 0:
  236. max_value = 1
  237. x = int(x)
  238. y = int(y)
  239. width = int(width)
  240. height = int(height)
  241. max_value += 5
  242. amount = 0.0
  243. if int(game.current['mx']) in range(x, x+width) \
  244. and int(game.current['my']) in range(y, y+height):
  245. amount = game.current["scroll"][1] * strenght
  246. game.current["scroll"] = [0, 0]
  247. #if game.current["MMB"]:
  248. # amount = 0- ( game.current["mx"] - game.previous["mx"] )
  249. def logic():
  250. # Scroll logic
  251. game.scroll[menu] -= amount
  252. # If too low
  253. if game.scroll[menu] < (1-max_value+height):
  254. game.scroll[menu] = (1-max_value+height)
  255. # If too high
  256. if game.scroll[menu] > 0:
  257. game.scroll[menu] = 0
  258. logic()
  259. if game.current["testing"]:
  260. color(game, layer, "red")
  261. layer.rectangle(x,y+1,width-1,height-1)
  262. layer.stroke()