当前位置:   article > 正文

开心消消乐:Python_checkforgem在python中的作用

checkforgem在python中的作用
  1. """
  2. 用到以下键:
  3. “x”和“y”——游戏在棋盘上的位置。0,0是左上角。
  4. 还有一个y可以设置的ROWABOVEBOARD行,表示它在棋盘上。
  5. 方向——四个常量变量之一,上、下、左、右,游戏前进的方向。
  6. 'imageNum' —— GEMIMAGES的整数索引,用于表示这个游戏用哪个图像。
  7. """
  8. import random, time, pygame, sys, copy
  9. from pygame.locals import *
  10. FPS = 30 # 每30秒更新屏幕
  11. WINDOWWIDTH = 600 # 窗口宽度
  12. WINDOWHEIGHT = 600 # 窗口高度
  13. BOARDWIDTH = 8 # 8列
  14. BOARDHEIGHT = 8 # 8行
  15. GEMIMAGESIZE = 64 # 每小格的宽度和高度
  16. NUMGEMIMAGES = 6 # 图片的类型,.png格式,从0开始命名 gem0.png
  17. assert NUMGEMIMAGES >= 5 # 最少5种图片
  18. NUMMATCHSOUNDS = 6 # 音乐的类型,.wav格式,从0开始命名 match0.wav
  19. MOVERATE = 25 #1到100,更大的num意味着更快的动作
  20. DEDUCTSPEED = 0.8 # 每减少1分扣减DEDUCTSPEED秒。
  21. # R G B
  22. PURPLE = (255, 0, 255)
  23. LIGHTBLUE = (255, 202, 255)
  24. BLUE = ( 77, 219, 255)
  25. RED = (255, 100, 100)
  26. BLACK = ( 0, 0, 0)
  27. BROWN = ( 85, 65, 0)
  28. HIGHLIGHTCOLOR = PURPLE # 游戏边界的颜色
  29. BGCOLOR = LIGHTBLUE # 屏幕上的背景色
  30. GRIDCOLOR = BLUE # 游戏板的颜色
  31. GAMEOVERCOLOR = RED #“游戏结束”文字的颜色
  32. GAMEOVERBGCOLOR = BLACK # “游戏结束”文字的颜色
  33. SCORECOLOR = BROWN # 得分文本的颜色
  34. # 从线到边的距离
  35. # 它被多次使用,因此在这里计算一次并存储在变量中
  36. XMARGIN = int((WINDOWWIDTH - GEMIMAGESIZE * BOARDWIDTH) / 2)
  37. YMARGIN = int((WINDOWHEIGHT - GEMIMAGESIZE * BOARDHEIGHT) / 2)
  38. # 方向常数
  39. UP = 'up'
  40. DOWN = 'down'
  41. LEFT = 'left'
  42. RIGHT = 'right'
  43. EMPTY_SPACE = -1 # 一个任意的非正值
  44. ROWABOVEBOARD = 'row above board' # 一个任意的非整数值
  45. def main():
  46. global FPSCLOCK, DISPLAYSURF, GEMIMAGES, GAMESOUNDS, BASICFONT, BOARDRECTS
  47. # 初始设置
  48. pygame.init()
  49. FPSCLOCK = pygame.time.Clock()
  50. DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
  51. pygame.display.set_caption('Gemgem')
  52. BASICFONT = pygame.font.Font('freesansbold.ttf', 36)
  53. # 加载图片
  54. GEMIMAGES = []
  55. for i in range(1, NUMGEMIMAGES+1):
  56. gemImage = pygame.image.load('gem%s.png' % i)
  57. if gemImage.get_size() != (GEMIMAGESIZE, GEMIMAGESIZE):
  58. gemImage = pygame.transform.smoothscale(gemImage, (GEMIMAGESIZE, GEMIMAGESIZE))
  59. GEMIMAGES.append(gemImage)
  60. # 加载声音
  61. GAMESOUNDS = {}
  62. GAMESOUNDS['bad swap'] = pygame.mixer.Sound('badswap.wav')
  63. GAMESOUNDS['match'] = []
  64. for i in range(NUMMATCHSOUNDS):
  65. GAMESOUNDS['match'].append(pygame.mixer.Sound('match%s.wav' % i))
  66. # 创建pygame,矩形对象,用于为每个板空间执行板坐标到像素坐标的转换
  67. BOARDRECTS = []
  68. for x in range(BOARDWIDTH):
  69. BOARDRECTS.append([])
  70. for y in range(BOARDHEIGHT):
  71. r = pygame.Rect((XMARGIN + (x * GEMIMAGESIZE),
  72. YMARGIN + (y * GEMIMAGESIZE),
  73. GEMIMAGESIZE,
  74. GEMIMAGESIZE))
  75. BOARDRECTS[x].append(r)
  76. while True:
  77. runGame()
  78. def runGame():
  79. # 运行游戏,当游戏结束时,这个函数返回
  80. # 初始化面板
  81. gameBoard = getBlankBoard()
  82. score = 0
  83. fillBoardAndAnimate(gameBoard, [], score) # Drop the initial gems.
  84. # 初始化变量
  85. firstSelectedGem = None
  86. lastMouseDownX = None
  87. lastMouseDownY = None
  88. gameIsOver = False
  89. lastScoreDeduction = time.time()
  90. clickContinueTextSurf = None
  91. while True: # 主要游戏循环
  92. clickedSpace = None
  93. for event in pygame.event.get(): # 事件处理循环
  94. if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
  95. pygame.quit()
  96. sys.exit()
  97. elif event.type == KEYUP and event.key == K_BACKSPACE:
  98. return # start a new game
  99. elif event.type == MOUSEBUTTONUP:
  100. if gameIsOver:
  101. return # 游戏结束后,点击开始一个新的游戏
  102. if event.pos == (lastMouseDownX, lastMouseDownY):
  103. # 此事件是鼠标单击,而不是鼠标拖动结束
  104. clickedSpace = checkForGemClick(event.pos)
  105. else:
  106. # 这是鼠标拖动结束
  107. firstSelectedGem = checkForGemClick((lastMouseDownX, lastMouseDownY))
  108. clickedSpace = checkForGemClick(event.pos)
  109. if not firstSelectedGem or not clickedSpace:
  110. # 如果不是有效拖动的一部分,取消选中这两个
  111. firstSelectedGem = None
  112. clickedSpace = None
  113. elif event.type == MOUSEBUTTONDOWN:
  114. # 这是鼠标点击或拖动的开始
  115. lastMouseDownX, lastMouseDownY = event.pos
  116. if clickedSpace and not firstSelectedGem:
  117. # 这是第一次点击宝石
  118. firstSelectedGem = clickedSpace
  119. elif clickedSpace and firstSelectedGem:
  120. #点击并选择了两个宝石,交换。
  121. firstSwappingGem, secondSwappingGem = getSwappingGems(gameBoard, firstSelectedGem, clickedSpace)
  122. if firstSwappingGem == None and secondSwappingGem == None:
  123. # 如果两者都不是,那么宝石就不是相邻的
  124. firstSelectedGem = None # 取消选择第一块宝石
  125. continue
  126. # 在屏幕上显示交换动画
  127. boardCopy = getBoardCopyMinusGems(gameBoard, (firstSwappingGem, secondSwappingGem))
  128. animateMovingGems(boardCopy, [firstSwappingGem, secondSwappingGem], [], score)
  129. # 交换在面板上的数据结构上的宝石
  130. gameBoard[firstSwappingGem['x']][firstSwappingGem['y']] = secondSwappingGem['imageNum']
  131. gameBoard[secondSwappingGem['x']][secondSwappingGem['y']] = firstSwappingGem['imageNum']
  132. # 看看这是不是一个匹配的动作
  133. matchedGems = findMatchingGems(gameBoard)
  134. if matchedGems == []:
  135. # 不是一个匹配的移动;交换宝石
  136. GAMESOUNDS['bad swap'].play()
  137. animateMovingGems(boardCopy, [firstSwappingGem, secondSwappingGem], [], score)
  138. gameBoard[firstSwappingGem['x']][firstSwappingGem['y']] = firstSwappingGem['imageNum']
  139. gameBoard[secondSwappingGem['x']][secondSwappingGem['y']] = secondSwappingGem['imageNum']
  140. else:
  141. # 是一个匹配的移动
  142. scoreAdd = 0
  143. while matchedGems != []:
  144. # 删除匹配的宝石,然后拉下板
  145. # points是一个dicts列表,它告诉fillBoardAndAnimate()在屏幕上显示文本以显示玩家获得多少点数。
  146. # 点数是一个列表,因为如果玩家得到多个匹配,那么就会出现多个点数文本。
  147. points = []
  148. for gemSet in matchedGems:
  149. scoreAdd += (10 + (len(gemSet) - 3) * 10)
  150. for gem in gemSet:
  151. gameBoard[gem[0]][gem[1]] = EMPTY_SPACE
  152. points.append({'points': scoreAdd,
  153. 'x': gem[0] * GEMIMAGESIZE + XMARGIN,
  154. 'y': gem[1] * GEMIMAGESIZE + YMARGIN})
  155. random.choice(GAMESOUNDS['match']).play()
  156. score += scoreAdd
  157. # 掉落新的宝石
  158. fillBoardAndAnimate(gameBoard, points, score)
  159. # 检查是否有新的匹配
  160. matchedGems = findMatchingGems(gameBoard)
  161. firstSelectedGem = None
  162. if not canMakeMove(gameBoard):
  163. gameIsOver = True
  164. # 画出面板
  165. DISPLAYSURF.fill(BGCOLOR)
  166. drawBoard(gameBoard)
  167. if firstSelectedGem != None:
  168. highlightSpace(firstSelectedGem['x'], firstSelectedGem['y'])
  169. if gameIsOver:
  170. if clickContinueTextSurf == None:
  171. #只呈现文本一次。在以后的迭代中,只需使用clickContinueTextSurf中已经存在的Surface对象
  172. clickContinueTextSurf = BASICFONT.render('Final Score: %s (Click to continue)' % (score), 1, GAMEOVERCOLOR, GAMEOVERBGCOLOR)
  173. clickContinueTextRect = clickContinueTextSurf.get_rect()
  174. clickContinueTextRect.center = int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2)
  175. DISPLAYSURF.blit(clickContinueTextSurf, clickContinueTextRect)
  176. elif score > 0 and time.time() - lastScoreDeduction > DEDUCTSPEED:
  177. # 分数随时间而下降
  178. score -= 1
  179. lastScoreDeduction = time.time()
  180. drawScore(score)
  181. pygame.display.update()
  182. FPSCLOCK.tick(FPS)
  183. def getSwappingGems(board, firstXY, secondXY):
  184. # 如果两个宝石在(X, Y)坐标处的宝石相邻,那么它们的“方向”键被设置为适当的方向值,以便彼此交换
  185. # 否则,返回(None, None)
  186. firstGem = {'imageNum': board[firstXY['x']][firstXY['y']],
  187. 'x': firstXY['x'],
  188. 'y': firstXY['y']}
  189. secondGem = {'imageNum': board[secondXY['x']][secondXY['y']],
  190. 'x': secondXY['x'],
  191. 'y': secondXY['y']}
  192. highlightedGem = None
  193. if firstGem['x'] == secondGem['x'] + 1 and firstGem['y'] == secondGem['y']:
  194. firstGem['direction'] = LEFT
  195. secondGem['direction'] = RIGHT
  196. elif firstGem['x'] == secondGem['x'] - 1 and firstGem['y'] == secondGem['y']:
  197. firstGem['direction'] = RIGHT
  198. secondGem['direction'] = LEFT
  199. elif firstGem['y'] == secondGem['y'] + 1 and firstGem['x'] == secondGem['x']:
  200. firstGem['direction'] = UP
  201. secondGem['direction'] = DOWN
  202. elif firstGem['y'] == secondGem['y'] - 1 and firstGem['x'] == secondGem['x']:
  203. firstGem['direction'] = DOWN
  204. secondGem['direction'] = UP
  205. else:
  206. # 这些宝石不相邻,不能交换
  207. return None, None
  208. return firstGem, secondGem
  209. def getBlankBoard():
  210. # 创建并返回空白板数据结构
  211. board = []
  212. for x in range(BOARDWIDTH):
  213. board.append([EMPTY_SPACE] * BOARDHEIGHT)
  214. return board
  215. def canMakeMove(board):
  216. # 如果棋盘处于可以对其进行匹配移动的状态,则返回True。否则返回假
  217. # oneOffPatterns中的模式表示gem,它们的配置方式是只需要移动一次就可以创建一个三元组
  218. oneOffPatterns = (((0,1), (1,0), (2,0)),
  219. ((0,1), (1,1), (2,0)),
  220. ((0,0), (1,1), (2,0)),
  221. ((0,1), (1,0), (2,1)),
  222. ((0,0), (1,0), (2,1)),
  223. ((0,0), (1,1), (2,1)),
  224. ((0,0), (0,2), (0,3)),
  225. ((0,0), (0,1), (0,3)))
  226. # x和y变量在黑板上的每个空间上迭代。如果我们使用+表示当前在黑板上迭代的空间,那么这个模式:(0,1),(1,0),(2,0))表示相同的
  227. # 宝石是这样设置的:
  228. #
  229. # +A
  230. # B
  231. # C
  232. #
  233. # 即,gem A与+的偏移量为(0,1),gem B与(1,0)的偏移量为(2,0)。在这种情况下,gem A可以被交换到左边以形成一个垂直的三排三连音
  234. #
  235. # 有八种可能的方式使宝石远离形成一个三元组,因此oneOffPattern有8种模式
  236. for x in range(BOARDWIDTH):
  237. for y in range(BOARDHEIGHT):
  238. for pat in oneOffPatterns:
  239. # 检查每个可能的模式“匹配在下一步”,看看是否可以做一个可能的行动
  240. if (getGemAt(board, x+pat[0][0], y+pat[0][1]) == \
  241. getGemAt(board, x+pat[1][0], y+pat[1][1]) == \
  242. getGemAt(board, x+pat[2][0], y+pat[2][1]) != None) or \
  243. (getGemAt(board, x+pat[0][1], y+pat[0][0]) == \
  244. getGemAt(board, x+pat[1][1], y+pat[1][0]) == \
  245. getGemAt(board, x+pat[2][1], y+pat[2][0]) != None):
  246. return True # return True the first time you find a pattern
  247. return False
  248. def drawMovingGem(gem, progress):
  249. # 画一个宝石沿着它的“方向”键指示的方向滑动。进度参数是从0(刚开始)到100(幻灯片完成)的数字
  250. movex = 0
  251. movey = 0
  252. progress *= 0.01
  253. if gem['direction'] == UP:
  254. movey = -int(progress * GEMIMAGESIZE)
  255. elif gem['direction'] == DOWN:
  256. movey = int(progress * GEMIMAGESIZE)
  257. elif gem['direction'] == RIGHT:
  258. movex = int(progress * GEMIMAGESIZE)
  259. elif gem['direction'] == LEFT:
  260. movex = -int(progress * GEMIMAGESIZE)
  261. basex = gem['x']
  262. basey = gem['y']
  263. if basey == ROWABOVEBOARD:
  264. basey = -1
  265. pixelx = XMARGIN + (basex * GEMIMAGESIZE)
  266. pixely = YMARGIN + (basey * GEMIMAGESIZE)
  267. r = pygame.Rect( (pixelx + movex, pixely + movey, GEMIMAGESIZE, GEMIMAGESIZE) )
  268. DISPLAYSURF.blit(GEMIMAGES[gem['imageNum']], r)
  269. def pullDownAllGems(board):
  270. # 将木板上的宝石拉到底部,以填补任何空缺
  271. for x in range(BOARDWIDTH):
  272. gemsInColumn = []
  273. for y in range(BOARDHEIGHT):
  274. if board[x][y] != EMPTY_SPACE:
  275. gemsInColumn.append(board[x][y])
  276. board[x] = ([EMPTY_SPACE] * (BOARDHEIGHT - len(gemsInColumn))) + gemsInColumn
  277. def getGemAt(board, x, y):
  278. if x < 0 or y < 0 or x >= BOARDWIDTH or y >= BOARDHEIGHT:
  279. return None
  280. else:
  281. return board[x][y]
  282. def getDropSlots(board):
  283. # 为每个列创建一个“drop slot”,并用该列缺少的一些gem填充该slot。这个函数假设宝石的重力已经下降
  284. boardCopy = copy.deepcopy(board)
  285. pullDownAllGems(boardCopy)
  286. dropSlots = []
  287. for i in range(BOARDWIDTH):
  288. dropSlots.append([])
  289. # 计算黑板上每一列的空格数
  290. for x in range(BOARDWIDTH):
  291. for y in range(BOARDHEIGHT-1, -1, -1): # 从底部开始,向上
  292. if boardCopy[x][y] == EMPTY_SPACE:
  293. possibleGems = list(range(len(GEMIMAGES)))
  294. for offsetX, offsetY in ((0, -1), (1, 0), (0, 1), (-1, 0)):
  295. # 缩小可能的宝石的范围,我们应该把它们放在空白的地方,这样当它们掉落的时候,我们就不会把两个相同的宝石放在一起
  296. neighborGem = getGemAt(boardCopy, x + offsetX, y + offsetY)
  297. if neighborGem != None and neighborGem in possibleGems:
  298. possibleGems.remove(neighborGem)
  299. newGem = random.choice(possibleGems)
  300. boardCopy[x][y] = newGem
  301. dropSlots[x].append(newGem)
  302. return dropSlots
  303. def findMatchingGems(board):
  304. gemsToRemove = [] # a list of lists of gems in matching triplets that should be removed
  305. boardCopy = copy.deepcopy(board)
  306. # 循环遍历每个空间,检查3个相邻的相同的宝石
  307. for x in range(BOARDWIDTH):
  308. for y in range(BOARDHEIGHT):
  309. # 寻找水平匹配
  310. if getGemAt(boardCopy, x, y) == getGemAt(boardCopy, x + 1, y) == getGemAt(boardCopy, x + 2, y) and getGemAt(boardCopy, x, y) != EMPTY_SPACE:
  311. targetGem = boardCopy[x][y]
  312. offset = 0
  313. removeSet = []
  314. while getGemAt(boardCopy, x + offset, y) == targetGem:
  315. #继续检查一行中是否有超过3个gem
  316. removeSet.append((x + offset, y))
  317. boardCopy[x + offset][y] = EMPTY_SPACE
  318. offset += 1
  319. gemsToRemove.append(removeSet)
  320. # 寻找垂直匹配
  321. if getGemAt(boardCopy, x, y) == getGemAt(boardCopy, x, y + 1) == getGemAt(boardCopy, x, y + 2) and getGemAt(boardCopy, x, y) != EMPTY_SPACE:
  322. targetGem = boardCopy[x][y]
  323. offset = 0
  324. removeSet = []
  325. while getGemAt(boardCopy, x, y + offset) == targetGem:
  326. # 继续检查,以防一行中有超过3个gem
  327. removeSet.append((x, y + offset))
  328. boardCopy[x][y + offset] = EMPTY_SPACE
  329. offset += 1
  330. gemsToRemove.append(removeSet)
  331. return gemsToRemove
  332. def highlightSpace(x, y):
  333. pygame.draw.rect(DISPLAYSURF, HIGHLIGHTCOLOR, BOARDRECTS[x][y], 4)
  334. def getDroppingGems(board):
  335. #找到所有在他们下面有一个空空间的宝石
  336. boardCopy = copy.deepcopy(board)
  337. droppingGems = []
  338. for x in range(BOARDWIDTH):
  339. for y in range(BOARDHEIGHT - 2, -1, -1):
  340. if boardCopy[x][y + 1] == EMPTY_SPACE and boardCopy[x][y] != EMPTY_SPACE:
  341. # 如果不是空的,这个空间会下降,但是下面的空间是空的
  342. droppingGems.append( {'imageNum': boardCopy[x][y], 'x': x, 'y': y, 'direction': DOWN} )
  343. boardCopy[x][y] = EMPTY_SPACE
  344. return droppingGems
  345. def animateMovingGems(board, gems, pointsText, score):
  346. # pointsText是一个带有'x'、'y'和'points'键的字典
  347. progress = 0 #进展在0代表开始,100代表完成
  348. while progress < 100: # 动画循环
  349. DISPLAYSURF.fill(BGCOLOR)
  350. drawBoard(board)
  351. for gem in gems: # 布置每一个宝石
  352. drawMovingGem(gem, progress)
  353. drawScore(score)
  354. for pointText in pointsText:
  355. pointsSurf = BASICFONT.render(str(pointText['points']), 1, SCORECOLOR)
  356. pointsRect = pointsSurf.get_rect()
  357. pointsRect.center = (pointText['x'], pointText['y'])
  358. DISPLAYSURF.blit(pointsSurf, pointsRect)
  359. pygame.display.update()
  360. FPSCLOCK.tick(FPS)
  361. progress += MOVERATE # 为下一帧的动画进行更多的进展
  362. def moveGems(board, movingGems):
  363. # 移动宝石是一个带有键x, y,方向,imageNum的字典列表
  364. for gem in movingGems:
  365. if gem['y'] != ROWABOVEBOARD:
  366. board[gem['x']][gem['y']] = EMPTY_SPACE
  367. movex = 0
  368. movey = 0
  369. if gem['direction'] == LEFT:
  370. movex = -1
  371. elif gem['direction'] == RIGHT:
  372. movex = 1
  373. elif gem['direction'] == DOWN:
  374. movey = 1
  375. elif gem['direction'] == UP:
  376. movey = -1
  377. board[gem['x'] + movex][gem['y'] + movey] = gem['imageNum']
  378. else:
  379. # 宝石位于板的上方(新的宝石来自板的上方)
  380. board[gem['x']][0] = gem['imageNum'] # move to top row
  381. def fillBoardAndAnimate(board, points, score):
  382. dropSlots = getDropSlots(board)
  383. while dropSlots != [[]] * BOARDWIDTH:
  384. # 只要有更多的宝石掉落,做掉落的动画
  385. movingGems = getDroppingGems(board)
  386. for x in range(len(dropSlots)):
  387. if len(dropSlots[x]) != 0:
  388. # 使每个槽中最低的宝石开始向下移动
  389. movingGems.append({'imageNum': dropSlots[x][0], 'x': x, 'y': ROWABOVEBOARD, 'direction': DOWN})
  390. boardCopy = getBoardCopyMinusGems(board, movingGems)
  391. animateMovingGems(boardCopy, movingGems, points, score)
  392. moveGems(board, movingGems)
  393. # 通过删除之前的最低的gem,从下拉槽中删除下一行gems是最低的
  394. for x in range(len(dropSlots)):
  395. if len(dropSlots[x]) == 0:
  396. continue
  397. board[x][0] = dropSlots[x][0]
  398. del dropSlots[x][0]
  399. def checkForGemClick(pos):
  400. # 查看鼠标点击是否在面板上
  401. for x in range(BOARDWIDTH):
  402. for y in range(BOARDHEIGHT):
  403. if BOARDRECTS[x][y].collidepoint(pos[0], pos[1]):
  404. return {'x': x, 'y': y}
  405. return None # Click不在面板上
  406. def drawBoard(board):
  407. for x in range(BOARDWIDTH):
  408. for y in range(BOARDHEIGHT):
  409. pygame.draw.rect(DISPLAYSURF, GRIDCOLOR, BOARDRECTS[x][y], 1)
  410. gemToDraw = board[x][y]
  411. if gemToDraw != EMPTY_SPACE:
  412. DISPLAYSURF.blit(GEMIMAGES[gemToDraw], BOARDRECTS[x][y])
  413. def getBoardCopyMinusGems(board, gems):
  414. # 创建并返回已传递的板数据结构的副本,并从其中删除“gems”列表中的gems
  415. # Gems是一个dicts列表,带有键x, y,方向,imageNum
  416. boardCopy = copy.deepcopy(board)
  417. # 从面板板数据结构拷贝中删除一些宝石
  418. for gem in gems:
  419. if gem['y'] != ROWABOVEBOARD:
  420. boardCopy[gem['x']][gem['y']] = EMPTY_SPACE
  421. return boardCopy
  422. def drawScore(score):
  423. scoreImg = BASICFONT.render(str(score), 1, SCORECOLOR)
  424. scoreRect = scoreImg.get_rect()
  425. scoreRect.bottomleft = (10, WINDOWHEIGHT - 6)
  426. DISPLAYSURF.blit(scoreImg, scoreRect)
  427. if __name__ == '__main__':
  428. main()

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/679525
推荐阅读
相关标签
  

闽ICP备14008679号