您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

275 行
8.4KB

  1. #!/usr/bin/python3
  2. import collections as cl
  3. import networkx as nx
  4. import curses as c
  5. import random
  6. from grid import Grid, bresenhams
  7. from fov import fieldOfView
  8. class Tile():
  9. def __init__(self, glyph, name, description, solid=False, transparent=True, color=c.COLOR_WHITE):
  10. self.appearance = glyph
  11. self.name = name
  12. self.description = description
  13. self.solid = solid
  14. self.transparent = transparent
  15. self.color = color
  16. def draw(self):
  17. return (self.glyph, self.color)
  18. def describe(self):
  19. return self.description
  20. class World(Grid):
  21. def __init__(self, window, width, height, initial_tile):
  22. super().__init__(width, height, initial_tile)
  23. self.run = True
  24. self.window = window
  25. self.width = width
  26. self.height = height
  27. self.bodies = []
  28. self.behavior = []
  29. class Behaviour():
  30. def __init__(self, name):
  31. self.name = name
  32. self.body = None
  33. self.world = None
  34. def behave(self):
  35. pass
  36. def incarnate(self, body):
  37. if not self.body is None:
  38. self.body.behavior = None
  39. if not body.behavior is None:
  40. body.behavior.body = None
  41. self.body = body
  42. body.behavior = self
  43. def message(self, msg):
  44. pass
  45. class Player(Behaviour):
  46. def __init__(self, name):
  47. super().__init__(name)
  48. self.msgbuffer = cl.deque([], 200)
  49. self.messaged = False
  50. self.messaged_last = False
  51. def message(self, msg):
  52. if len(self.msgbuffer) > 0 and msg == self.msgbuffer[0][1] and self.messaged_last:
  53. self.msgbuffer.appendleft((self.msgbuffer.popleft()[0] + 1, msg))
  54. else:
  55. self.msgbuffer.appendleft((1, msg))
  56. self.messaged = True
  57. def behave(self):
  58. # calculate visibility
  59. visibility = Grid(self.world.width, self.world.height, False)
  60. fieldOfView(self.body.x, self.body.y, self.world.width, self.world.height, 100, lambda x, y: visibility.set(x, y, True), lambda x, y: self.world.get(x, y).solid)
  61. self.world.window.erase()
  62. # draw interface you
  63. for x in range(0, self.world.width):
  64. for y in range(0, self.world.height):
  65. if visibility.get(x, y):
  66. self.world.window.addch(y, x, self.world.get(x, y).appearance)
  67. for body in self.world.bodies:
  68. if visibility.get(body.x, body.y):
  69. body.draw()
  70. if self.messaged:
  71. if self.msgbuffer[0][0] == 1:
  72. self.world.window.addstr(self.world.height + 1, 0, self.msgbuffer[0][1])
  73. else:
  74. self.world.window.addstr(self.world.height + 1, 0, self.msgbuffer[0][1] + " (x" + str(self.msgbuffer[0][0]) + ")")
  75. self.messaged = False
  76. self.messaged_last = True
  77. else:
  78. self.messaged_last = False
  79. self.world.window.refresh()
  80. # this is the one gameplay input point.
  81. key = self.world.window.getkey()
  82. match key:
  83. case 'w':
  84. if not self.body.move((0, -1)):
  85. self.message("You cannot go there.")
  86. case 's':
  87. if not self.body.move((0, +1)):
  88. self.message("You cannot go there.")
  89. case 'a':
  90. if not self.body.move((-1, 0)):
  91. self.message("You cannot go there.")
  92. case 'd':
  93. if not self.body.move((+1, 0)):
  94. self.message("You cannot go there.")
  95. case 'c':
  96. coords = self.world.neighbors(self.body.x, self.body.y)
  97. for coord in coords:
  98. x, y = coord
  99. t = self.world.get(x, y)
  100. if t.name == "open door":
  101. self.body.close_door(x, y)
  102. break
  103. else:
  104. self.message("You see nothing here to close.")
  105. case 'q':
  106. self.world.run = False
  107. # case _: self.world.message(key)
  108. class Drunkard(Behaviour):
  109. def behave(self):
  110. while True:
  111. dx, dy = random.randint(-1,1), random.randint(-1,1)
  112. tx, ty = dx + self.body.x, dy + self.body.y
  113. if self.world.in_bounds(tx, ty) and not self.world.get(tx, ty).solid:
  114. break
  115. bodies = [body for body in self.world.bodies if body.x == tx and body.y == ty]
  116. if bodies:
  117. for tb in bodies:
  118. tb.behavior.message("A " + self.body.name + " bumps into you.")
  119. else:
  120. self.body.move((dx, dy))
  121. class Body():
  122. def __init__(self, name, appearence, description, color):
  123. self.name = name
  124. self.appearence = appearence
  125. self.description = description
  126. self.color = color
  127. self.behavior = None
  128. self.world = None
  129. self.x = None
  130. self.y = None
  131. def move(self, direction):
  132. x, y = direction
  133. tx, ty = self.x + x, self.y + y
  134. if not self.world.in_bounds(tx, ty):
  135. return False
  136. elif self.world.get(tx, ty).name == "closed door":
  137. self.open_door(tx, ty)
  138. return True
  139. elif self.world.get(tx, ty).solid:
  140. return False
  141. else:
  142. self.x = tx
  143. self.y = ty
  144. return True
  145. def open_door(self, x, y):
  146. if not self.world.in_bounds(x, y):
  147. self.behavior.message("You cannot open anything there.")
  148. return False
  149. elif self.world.get(x, y).name == "closed door":
  150. self.world.set(x, y, door_open)
  151. self.behavior.message("You open the door.")
  152. return True
  153. else:
  154. self.behavior.message("You cannot open anything there.")
  155. return False
  156. def close_door(self, x, y):
  157. if not self.world.in_bounds(x, y):
  158. self.behavior.message("You cannot close anything there.")
  159. return False
  160. elif self.world.get(x, y).name == "open door":
  161. self.world.set(x, y, door_closed)
  162. self.behavior.message("You close the door.")
  163. return True
  164. else:
  165. self.behavior.message("You cannot close anything there.")
  166. return False
  167. def describe(self):
  168. return self.description
  169. def draw(self):
  170. self.world.window.addch(self.y, self.x, self.appearence)
  171. class Conversation(list):
  172. def __init__(self, title, description):
  173. self.title = title
  174. self.description = description
  175. def run(self):
  176. # rendering code goes here
  177. pass
  178. class Response():
  179. def __init__(self, name, text, target=None, prerequisite=None, function=None):
  180. self.name = name
  181. self.text = text
  182. self.target = target
  183. self.function = function
  184. def respond(self, title, description):
  185. # rendering code goes here
  186. if function:
  187. function()
  188. if target:
  189. target.run()
  190. bum_conversation = Conversation("Conversing With a Drunkard", "A foul-smelling drunkard waves to and fro, trying to form words. His tattered clothes are brown with filth.")
  191. bum_conversation.append(Response("Hello? Is everything all right?", "Hargl... hurgl...", target=bum_conversation))
  192. bum_conversation.append(Response("I will take my leave, good sir.", "..."))
  193. p = Player("you")
  194. pb = Body("yourself", '@', "an unremarkable person", c.COLOR_WHITE)
  195. p.incarnate(pb)
  196. floor = Tile('.', "floor", "an unremarkable piece of floor")
  197. wall = Tile('#', "wall", "an unremarkable wall", solid=True, transparent=False)
  198. door_closed = Tile('+', "closed door", "an unremarkable door", solid=True, transparent=False)
  199. door_open = Tile(',', "open door", "an unremarkable door", solid=False, transparent=True)
  200. w = c.initscr()
  201. c.curs_set(0)
  202. c.raw()
  203. wld = World(w, 80, 23, floor)
  204. drunk = Drunkard("drunkard")
  205. db = Body("drunkard", 'H', "A smelly drunkard, stumbling about.", c.COLOR_WHITE)
  206. drunk.incarnate(db)
  207. db.world = wld
  208. drunk.world = wld
  209. db.x = 20
  210. db.y = 16
  211. wld.behaviors = [p, drunk]
  212. wld.bodies = [pb, db]
  213. pb.world = wld
  214. p.world = wld
  215. pb.x = 3
  216. pb.y = 6
  217. wld.set(12, 12, wall)
  218. wld.set(13, 12, wall)
  219. wld.set(14, 12, wall)
  220. wld.set(15, 12, wall)
  221. wld.set(16, 12, wall)
  222. wld.set(16, 13, wall)
  223. wld.set(16, 14, wall)
  224. wld.set(18, 12, wall)
  225. wld.set(18, 13, wall)
  226. wld.set(18, 14, wall)
  227. wld.set(12, 13, door_closed)
  228. while wld.run:
  229. for behavior in wld.behaviors:
  230. behavior.behave()
  231. c.endwin()