登录

1.根据激活状态和未激活状态分别显示树桩

2.用户使用植物道具进行果树种植

3.解锁树桩

4.化肥/修剪/浇水/宠物粮小图标显示

种植栏的功能实现

1. 客户端需要的植物相关参数: 总树桩数量, 当前用户激活树桩数量, 当前种植的树桩数量, 树桩列表状态

2. 客户端根据激活状态和未激活状态分别显示树桩

3. 服务端在用户进入种植园时提供上面的数据

4. 用户如果第一次进入种植园需要初始化参数

5. 数据库中必须预设树桩的相关参数

6. 用户可以使用道具对树桩进行响应的操作

7. 用户可以在背包里面进行果树的种植

1.根据激活状态和未激活状态分别显示树桩

1.my_orchard.html-先确定前端需要哪些数据

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>用户中心</title>
  5. <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  6. <meta charset="utf-8">
  7. <link rel="stylesheet" href="../static/css/main.css">
  8. <script src="../static/js/vue.js"></script>
  9. <script src="../static/js/axios.js"></script>
  10. <script src="../static/js/main.js"></script>
  11. <script src="../static/js/uuid.js"></script>
  12. <script src="../static/js/settings.js"></script>
  13. <script src="../static/js/socket.io.js"></script>
  14. </head>
  15. <body>
  16. <div class="app orchard orchard-frame" id="app">
  17. <div class="background">
  18. <img class="grassland2" src="../static/images/grassland2.png" alt="">
  19. <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
  20. <img class="stake1" src="../static/images/stake1.png" alt="">
  21. <img class="stake2" src="../static/images/stake2.png" alt="">
  22. </div>
  23. <div class="pet-box">
  24. <div class="pet">
  25. <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
  26. </div>
  27. <div class="pet" v-if="pet_number > 1">
  28. <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt="">
  29. </div>
  30. <div class="pet turned_off" v-if="pet_number==1">
  31. <img class="turned_image" src="../static/images/turned_off.png" alt="">
  32. <p>请购买宠物</p>
  33. </div>
  34. </div>
  35. <div class="tree-list">
  36. <div class="tree-box">
  37. <!-- 已激活但是未种植的树桩列表 -->
  38. <div class="tree" v-for="i in active_tree">
  39. <img src="../static/images/tree1.png" alt="">
  40. </div>
  41. <!-- 未激活树桩列表 -->
  42. <div class="tree" v-for="i in lock_tree">
  43. <img src="../static/images/tree0.png" alt="">
  44. </div>
  45. </div>
  46.  
  47. </div>
  48. <div class="prop-list">
  49. <div class="prop">
  50. <img src="../static/images/prop1.png" alt="">
  51. <span>1</span>
  52. <p>化肥</p>
  53. </div>
  54. <div class="prop">
  55. <img src="../static/images/prop2.png" alt="">
  56. <span>0</span>
  57. <p>修剪</p>
  58. </div>
  59. <div class="prop">
  60. <img src="../static/images/prop3.png" alt="">
  61. <span>1</span>
  62. <p>浇水</p>
  63. </div>
  64. <div class="prop">
  65. <img src="../static/images/prop4.png" alt="">
  66. <span>1</span>
  67. <p>宠物粮</p>
  68. </div>
  69. </div>
  70. <div class="pet-hp-list">
  71. <div class="pet-hp" v-for="pet in pet_list">
  72. <p>宠物1 饱食度</p>
  73. <div class="hp">
  74. <div :style="{width: pet.hp+'%'}" class="process">{{pet.hp}}%</div>
  75. </div>
  76. </div>
  77. </div>
  78. </div>
  79. <script>
  80. apiready = function(){
  81. init();
  82. new Vue({
  83. el:"#app",
  84. data(){
  85. return {
  86. namespace: '/mofang',
  87. token:"",
  88. socket: null,
  89. pet_list:[],
  90. user_tree_data:{
  91. "total_tree":9, // 总树桩数量
  92. "user_tree_number": 5, // 当前用户激活树桩数量
  93. "user_tree_list":[ // 当前种植的树桩列表状态
  94.  
  95. ],
  96. },
  97. pet_number:[],
  98. timeout: 0,
  99. prev:{name:"",url:"",params:{}},
  100. current:{name:"orchard",url:"orchard.html",params:{}},
  101. }
  102. },
  103. computed:{
  104. // 已激活但是未使用的树桩
  105. active_tree(){
  106. return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
  107. },
  108. // 未激活的剩余树桩
  109. lock_tree(){
  110. return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
  111. },
  112. },
  113. created(){
  114. this.show_pet_list();
  115. },
  116. methods:{
  117. show_pet_list(){
  118. api.addEventListener({
  119. name: 'pet_show_success'
  120. }, (ret, err)=>{
  121. if( ret ){
  122. // 用户购买道具
  123. this.pet_list = this.game.get("pet_list");
  124. this.pet_number = parseInt(this.game.get("pet_number"));
  125. }
  126. });
  127. }
  128. }
  129. });
  130. }
  131. </script>
  132. </body>
  133. </html>

my_orchard.html-先确定前端需要哪些数据

2.my_orchard.html-确定当前种植的树桩状态列表的数据结构

  1. user_tree_data:{
  2. "total_tree":9, // 总树桩数量
  3. "user_tree_number": 5, // 当前用户激活树桩数量
  4. "user_tree_list":[ // 当前种植的树桩列表状态
  5. { // 树桩状态
  6. "time":1609808084, // 种植时间
  7. "status":4, // 状态
  8. "has_time": 300, // 状态时间
  9. },
  10. ],
  11. },
  12. pet_number:[],
  13. tree_status:{
  14. "tree0": "../static/images/tree0.png", // 树桩
  15. "tree1": "../static/images/tree1.png", // 空桩
  16. "tree2": "../static/images/tree2.png", // 幼苗
  17. "tree3": "../static/images/tree3.png", // 成长
  18. "tree4": "../static/images/tree4.png", // 成熟
  19. },

客户端显示种植植物相关的参数,my_orchard.html代码

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>用户中心</title>
  5. <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  6. <meta charset="utf-8">
  7. <link rel="stylesheet" href="../static/css/main.css">
  8. <script src="../static/js/vue.js"></script>
  9. <script src="../static/js/axios.js"></script>
  10. <script src="../static/js/main.js"></script>
  11. <script src="../static/js/uuid.js"></script>
  12. <script src="../static/js/settings.js"></script>
  13. <script src="../static/js/socket.io.js"></script>
  14. </head>
  15. <body>
  16. <div class="app orchard orchard-frame" id="app">
  17. <div class="background">
  18. <img class="grassland2" src="../static/images/grassland2.png" alt="">
  19. <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
  20. <img class="stake1" src="../static/images/stake1.png" alt="">
  21. <img class="stake2" src="../static/images/stake2.png" alt="">
  22. </div>
  23. <div class="pet-box">
  24. <div class="pet">
  25. <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
  26. </div>
  27. <div class="pet" v-if="pet_number > 1">
  28. <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt="">
  29. </div>
  30. <div class="pet turned_off" v-if="pet_number==1">
  31. <img class="turned_image" src="../static/images/turned_off.png" alt="">
  32. <p>请购买宠物</p>
  33. </div>
  34. </div>
  35. <div class="tree-list">
  36. <div class="tree-box">
  37. <div class="tree" v-for="tree in user_tree_data.user_tree_list">
  38. <img v-if="tree.status==2" src="../static/images/tree2.png" alt="">
  39. <img v-if="tree.status==3" src="../static/images/tree3.png" alt="">
  40. <img v-if="tree.status==4" src="../static/images/tree4.png" alt="">
  41. </div>
  42. <!-- 已激活但是未种植的树桩列表 -->
  43. <div class="tree" v-for="i in active_tree">
  44. <img src="../static/images/tree1.png" alt="">
  45. </div>
  46. <!-- 未激活树桩列表 -->
  47. <div class="tree" v-for="i in lock_tree">
  48. <img src="../static/images/tree0.png" alt="">
  49. </div>
  50. </div>
  51.  
  52. </div>
  53. <div class="prop-list">
  54. <div class="prop">
  55. <img src="../static/images/prop1.png" alt="">
  56. <span>1</span>
  57. <p>化肥</p>
  58. </div>
  59. <div class="prop">
  60. <img src="../static/images/prop2.png" alt="">
  61. <span>0</span>
  62. <p>修剪</p>
  63. </div>
  64. <div class="prop">
  65. <img src="../static/images/prop3.png" alt="">
  66. <span>1</span>
  67. <p>浇水</p>
  68. </div>
  69. <div class="prop">
  70. <img src="../static/images/prop4.png" alt="">
  71. <span>1</span>
  72. <p>宠物粮</p>
  73. </div>
  74. </div>
  75. <div class="pet-hp-list">
  76. <div class="pet-hp" v-for="pet in pet_list">
  77. <p>宠物1 饱食度</p>
  78. <div class="hp">
  79. <div :style="{width: pet.hp+'%'}" class="process">{{pet.hp}}%</div>
  80. </div>
  81. </div>
  82. </div>
  83. </div>
  84. <script>
  85. apiready = function(){
  86. init();
  87. new Vue({
  88. el:"#app",
  89. data(){
  90. return {
  91. namespace: '/mofang',
  92. token:"",
  93. socket: null,
  94. pet_list:[],
  95. user_tree_data:{
  96. "total_tree":9, // 总树桩数量
  97. "user_tree_number": 5, // 当前用户激活树桩数量
  98. "user_tree_list":[ // 当前种植的树桩列表状态
  99. { // 树桩状态
  100. "time":1609808084, // 种植时间
  101. "status":4, // 状态
  102. "has_time": 300, // 状态时间
  103. },
  104. ],
  105. },
  106. pet_number:[],
  107. tree_status:{
  108. "tree0": "../static/images/tree0.png", // 树桩
  109. "tree1": "../static/images/tree1.png", // 空桩
  110. "tree2": "../static/images/tree2.png", // 幼苗
  111. "tree3": "../static/images/tree3.png", // 成长
  112. "tree4": "../static/images/tree4.png", // 成熟
  113. },
  114. timeout: 0,
  115. prev:{name:"",url:"",params:{}},
  116. current:{name:"orchard",url:"orchard.html",params:{}},
  117. }
  118. },
  119. computed:{
  120. // 已激活但是未使用的树桩
  121. active_tree(){
  122. return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
  123. },
  124. // 未激活的剩余树桩
  125. lock_tree(){
  126. return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
  127. },
  128. },
  129. created(){
  130. this.show_pet_list();
  131. },
  132. methods:{
  133. show_pet_list(){
  134. api.addEventListener({
  135. name: 'pet_show_success'
  136. }, (ret, err)=>{
  137. if( ret ){
  138. // 用户购买道具
  139. this.pet_list = this.game.get("pet_list");
  140. this.pet_number = parseInt(this.game.get("pet_number"));
  141. }
  142. });
  143. }
  144. }
  145. });
  146. }
  147. </script>
  148. </body>
  149. </html>

my_orchard.html-确定当前种植的树桩状态列表的数据结构

3.服务端提供种植植物的相关数据

1.在setting表中添加种植植物的参数数据

添加参数数据,SQL代码:

  1. INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (19, 'tree_status_0', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-未解锁', 'tree0.png');
  2. INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (20, 'tree_status_1', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-未种植', 'tree1.png');
  3. INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (21, 'tree_status_2', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-幼苗', 'tree2.png');
  4. INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (22, 'tree_status_3', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-成长', 'tree3.png');
  5. INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (23, 'tree_status_4', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-成熟', 'tree4.png');

2.后端返回当前用户种植的植物信息

服务端返回当前用户种植的植物信息,socket.py, 代码:

  1. from application import socketio
  2. from flask import request
  3. from application.apps.users.models import User
  4. from flask_socketio import join_room, leave_room
  5. from application import mongo
  6. from .models import Goods,Setting,db
  7. from status import APIStatus as status
  8. from message import ErrorMessage as errmsg
  9.  
  10. # 断开socket通信
  11. @socketio.on("disconnect", namespace="/mofang")
  12. def user_disconnect():
  13. print("用户%s退出了种植园" % request.sid )
  14.  
  15. @socketio.on("login", namespace="/mofang")
  16. def user_login(data):
  17. # 分配房间
  18. room = data["uid"]
  19. join_room(room)
  20. # 保存当前用户和sid的绑定关系
  21. # 判断当前用户是否在mongo中有记录
  22. query = {
  23. "_id": data["uid"]
  24. }
  25. ret = mongo.db.user_info_list.find_one(query)
  26. if ret:
  27. mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}})
  28. else:
  29. mongo.db.user_info_list.insert_one({
  30. "_id": data["uid"],
  31. "sid": request.sid,
  32. })
  33.  
  34. # 返回种植园的相关配置参数
  35. orchard_settings = {}
  36. setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all()
  37. """
  38. 现在的格式:
  39. [<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>]
  40. 需要返回的格式:
  41. {
  42. package_number_base:4,
  43. package_number_max: 32,
  44. ...
  45. }
  46. """
  47. for item in setting_list:
  48. orchard_settings[item.name] = item.value
  49.  
  50. # 返回当前用户相关的配置参数
  51. user_settings = {}
  52. # 从mongo中查找用户信息,判断用户是否激活了背包格子
  53. user_dict = mongo.db.user_info_list.find_one({"sid":request.sid})
  54. # 背包格子
  55. if user_dict.get("package_number") is None:
  56. user_settings["package_number"] = orchard_settings.get("package_number_base",4)
  57. mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}})
  58. else:
  59. user_settings["package_number"] = user_dict.get("package_number")
  60.  
  61. """种植园植物信息"""
  62. # 总树桩数量
  63. setting = Setting.query.filter(Setting.name == "user_total_tree").first()
  64. if setting is None:
  65. tree_total = 9
  66. else:
  67. tree_total = int(setting.value)
  68.  
  69. # 用户已经激活的树桩
  70. setting = Setting.query.filter(Setting.name == "user_active_tree").first()
  71. if setting is None:
  72. user_tree_number = 3
  73. else:
  74. user_tree_number = int(setting.value)
  75. user_tree_number = user_dict.get("user_tree_number",user_tree_number)
  76.  
  77. # 种植的植物列表
  78. user_tree_list = user_dict.get("user_tree_list", [])
  79.  
  80. socketio.emit("login_response", {
  81. "errno":status.CODE_OK,
  82. "errmsg":errmsg.ok,
  83. "orchard_settings":orchard_settings,
  84. "user_settings":user_settings,
  85. "tree_total":tree_total,
  86. "user_tree_number":user_tree_number,
  87. "user_tree_list":user_tree_list,
  88. }, namespace="/mofang", room=room)
  89.  
  90. @socketio.on("user_buy_prop", namespace="/mofang")
  91. def user_buy_prop(data):
  92. """用户购买道具"""
  93. room = request.sid
  94. # 从mongo中获取当前用户信息
  95. user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
  96. user = User.query.get(user_info.get("_id"))
  97. if user is None:
  98. socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
  99. return
  100.  
  101. # 判断背包物品存储是否达到上限
  102. use_package_number = int(user_info.get("use_package_number",0)) # 当前诗经使用的格子数量
  103. package_number = int(user_info.get("package_number",0)) # 当前用户已经解锁的格子数量
  104. # 本次购买道具需要使用的格子数量
  105. setting = Setting.query.filter(Setting.name == "td_prop_max").first()
  106. if setting is None:
  107. td_prop_max = 10
  108. else:
  109. td_prop_max = int(setting.value)
  110.  
  111. # 计算购买道具以后需要额外占用的格子数量
  112. if ("prop_%s" % data["pid"]) in user_info.get("prop_list",{}):
  113. """曾经购买过当前道具"""
  114. prop_num = int( user_info.get("prop_list")["prop_%s" % data["pid"]]) # 购买前的道具数量
  115. new_prop_num = prop_num+int(data["num"]) # 如果成功购买道具以后的数量
  116. old_td_num = prop_num // td_prop_max
  117. if prop_num % td_prop_max > 0:
  118. old_td_num+=1
  119. new_td_num = new_prop_num // td_prop_max
  120. if new_prop_num % td_prop_max > 0:
  121. new_td_num+=1
  122. td_num = new_td_num - old_td_num
  123. else:
  124. """新增购买的道具"""
  125. # 计算本次购买道具需要占用的格子数量
  126.  
  127. if int(data["num"]) > td_prop_max:
  128. """需要多个格子"""
  129. td_num = int(data["num"]) // td_prop_max
  130. if int(data["num"]) % td_prop_max > 0:
  131. td_num+=1
  132. else:
  133. """需要一个格子"""
  134. td_num = 1
  135.  
  136. if use_package_number+td_num > package_number:
  137. """超出存储上限"""
  138. socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_PACKAGE, "errmsg": errmsg.no_package},
  139. namespace="/mofang", room=room)
  140. return
  141.  
  142. # 从mysql中获取商品价格
  143. prop = Goods.query.get(data["pid"])
  144. if user.money > 0: # 当前商品需要通过RMB购买
  145. if float(user.money) < float(prop.price) * int(data["num"]):
  146. socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room)
  147. return
  148. else:
  149. """当前通过果子进行购买"""
  150. if int(user.credit) < int(prop.credit) * int(data["num"]):
  151. socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
  152. namespace="/mofang", room=room)
  153. return
  154.  
  155. # 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额
  156. query = {"sid": request.sid}
  157. if user_info.get("prop_list") is None:
  158. """此前没有购买任何道具"""
  159. message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}}
  160. mongo.db.user_info_list.update_one(query,message)
  161. else:
  162. """此前有购买了道具"""
  163. prop_list = user_info.get("prop_list") # 道具列表
  164. if ("prop_%s" % prop.id) in prop_list:
  165. """如果再次同一款道具"""
  166. prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"])
  167. else:
  168. """此前没有购买过这种道具"""
  169. prop_list[("prop_%s" % prop.id)] = int(data["num"])
  170.  
  171. mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}})
  172.  
  173. # 扣除余额或果子
  174. if prop.price > 0:
  175. user.money = float(user.money) - float(prop.price) * int(data["num"])
  176. else:
  177. user.credit = int(user.credit) - int(prop.credit) * int(data["num"])
  178.  
  179. db.session.commit()
  180.  
  181. # 返回购买成功的信息
  182. socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room)
  183. # 返回最新的用户道具列表
  184. user_prop()
  185.  
  186. @socketio.on("user_prop", namespace="/mofang")
  187. def user_prop():
  188. """用户道具"""
  189. userinfo = mongo.db.user_info_list.find_one({"sid":request.sid})
  190. prop_list = userinfo.get("prop_list",{})
  191. prop_id_list = []
  192. for prop_str,num in prop_list.items():
  193. pid = int(prop_str[5:])
  194. prop_id_list.append(pid)
  195.  
  196. data = []
  197. prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all()
  198. setting = Setting.query.filter(Setting.name == "td_prop_max").first()
  199. if setting is None:
  200. td_prop_max = 10
  201. else:
  202. td_prop_max = int(setting.value)
  203.  
  204. for prop_data in prop_list_data:
  205. num = int( prop_list[("prop_%s" % prop_data.id)])
  206. if td_prop_max > num:
  207. data.append({
  208. "num": num,
  209. "image": prop_data.image,
  210. "pid": prop_data.id
  211. })
  212. else:
  213. padding_time = num // td_prop_max
  214. padding_last = num % td_prop_max
  215. arr = [{
  216. "num": td_prop_max,
  217. "image": prop_data.image,
  218. "pid": prop_data.id
  219. }] * padding_time
  220. if padding_last != 0:
  221. arr.append({
  222. "num": padding_last,
  223. "image": prop_data.image,
  224. "pid": prop_data.id
  225. })
  226. data = data + arr
  227. mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"use_package_number":len(data)}})
  228. room = request.sid
  229. socketio.emit("user_prop_response", {
  230. "errno": status.CODE_OK,
  231. "errmsg": errmsg.ok,
  232. "data":data,
  233. }, namespace="/mofang",
  234. room=room)
  235.  
  236. @socketio.on("unlock_package", namespace="/mofang")
  237. def unlock_package():
  238. """解锁背包"""
  239. # 从mongo获取当前用户解锁的格子数量
  240. user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
  241. user = User.query.get(user_info.get("_id"))
  242. if user is None:
  243. socketio.emit("unlock_package_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
  244. return
  245.  
  246. package_number = int(user_info.get("package_number"))
  247. num = 7 - (32 - package_number) // 4 # 没有解锁的格子
  248.  
  249. # 从数据库中获取解锁背包的价格
  250. setting = Setting.query.filter(Setting.name == "package_unlock_price_%s" % num).first()
  251. if setting is None:
  252. unlock_price = 0
  253. else:
  254. unlock_price = int(setting.value)
  255.  
  256. # 判断是否有足够的积分或者价格
  257. room = request.sid
  258. if user.money < unlock_price:
  259. socketio.emit("unlock_package_response", {"errno": status.CODE_NO_MONEY, "errmsg": errmsg.money_no_enough},
  260. namespace="/mofang", room=room)
  261. return
  262.  
  263. # 解锁成功
  264. user.money = float(user.money) - float(unlock_price)
  265. db.session.commit()
  266.  
  267. # mongo中调整数量
  268. mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": package_number+1}})
  269. # 返回解锁的结果
  270. socketio.emit("unlock_package_response", {
  271. "errno": status.CODE_OK,
  272. "errmsg": errmsg.ok},
  273. namespace="/mofang", room=room)
  274. import math
  275. from application import redis
  276. @socketio.on("pet_show", namespace="/mofang")
  277. def pet_show():
  278. """显示宠物"""
  279. room = request.sid
  280. user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
  281. user = User.query.get(user_info.get("_id"))
  282. if user is None:
  283. socketio.emit("pet_show_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
  284. return
  285.  
  286. # 获取宠物列表
  287. pet_list = user_info.get("pet_list", [])
  288.  
  289. """
  290. pet_list: [
  291. {
  292. "pid":11,
  293. "image":"pet.png",
  294. "hp":100%,
  295. "created_time":xxxx-xx-xx xx:xx:xx,
  296. "skill":"70%",
  297. "has_time":30天
  298. },
  299. ]
  300. """
  301. # 从redis中提取当前宠物的饱食度和有效期
  302. for key,pet in enumerate(pet_list):
  303. pet["hp"] = math.ceil( redis.ttl("pet_%s_%s_hp" % (user.id,key+1)) / 86400 * 100 )
  304. pet["has_time"] = redis.ttl("pet_%s_%s_expire" % (user.id,key+1))
  305.  
  306. pet_number = user_info.get("pet_number", 1)
  307. socketio.emit(
  308. "pet_show_response",
  309. {
  310. "errno": status.CODE_OK,
  311. "errmsg": errmsg.ok,
  312. "pet_list": pet_list,
  313. "pet_number": pet_number,
  314. },
  315. namespace="/mofang",
  316. room=room
  317. )
  318.  
  319. from datetime import datetime
  320. @socketio.on("use_prop", namespace="/mofang")
  321. def use_prop(pid):
  322. """使用宠物"""
  323. # 1. 判断当前的宠物数量
  324. room = request.sid
  325. user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
  326. user = User.query.get(user_info.get("_id"))
  327. if user is None:
  328. socketio.emit("pet_use_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
  329. return
  330.  
  331. # 获取宠物列表
  332. pet_list = user_info.get("pet_list", [])
  333. if len(pet_list) > 1:
  334. socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
  335. return
  336.  
  337. # 2. 是否有空余的宠物栏位
  338. pet_number = user_info.get("pet_number",1)
  339. if pet_number <= len(pet_list):
  340. socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
  341. return
  342.  
  343. # 3. 初始化当前宠物信息
  344. pet = Goods.query.get(pid)
  345. if pet is None:
  346. socketio.emit("pet_use_response", {"errno":status.CODE_NO_SUCH_PET,"errmsg":errmsg.not_such_pet}, namespace="/mofang", room=room)
  347. return
  348.  
  349. # 获取有效期和防御值
  350. exp_data = Setting.query.filter(Setting.name=="pet_expire_%s" % pid).first()
  351. ski_data = Setting.query.filter(Setting.name=="pet_skill_%s" % pid).first()
  352.  
  353. if exp_data is None:
  354. # 默认7天有效期
  355. expire = 7
  356. else:
  357. expire = exp_data.value
  358.  
  359. if ski_data is None:
  360. skill = 10
  361. else:
  362. skill = ski_data.value
  363.  
  364. # 在redis中设置当前宠物的饱食度
  365. pipe = redis.pipeline()
  366. pipe.multi()
  367. pipe.setex("pet_%s_%s_hp" % (user.id, len(pet_list)+1), 24*60*60, "_")
  368. pipe.setex("pet_%s_%s_expire" % (user.id, len(pet_list)+1), int(expire)*24*60*60, "_")
  369. pipe.execute()
  370.  
  371. # 基本保存到mongo
  372. mongo.db.user_info_list.update_one({"sid":request.sid},{"$push":{"pet_list":{
  373. "pid": pid,
  374. "image": pet.image,
  375. "created_time": int( datetime.now().timestamp() ),
  376. "skill": skill,
  377. }}})
  378.  
  379. """
  380. db.user_info_list.updateOne({"_id":"52"},{"$push":{"pet_list":{
  381. "pid": 2,
  382. "image": "pet1.png",
  383. "created_time": 1609727155,
  384. "skill": 30,
  385. }}})
  386. """
  387.  
  388. # 扣除背包中的道具数量
  389. prop_list = user_info.get("prop_list",{})
  390. for key,value in prop_list.items():
  391. if key == ("prop_%s" % pid):
  392. if int(value) > 1:
  393. prop_list[key] = int(value) - 1
  394. else:
  395. prop_list.pop(key)
  396. break
  397.  
  398. mongo.db.user_info_list.update_one({"sid":room},{"$set":{"prop_list":prop_list}})
  399. user_prop()
  400. pet_show()
  401.  
  402. socketio.emit("pet_use_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
  403. namespace="/mofang", room=room)

用户登录种植园成功后,后端返回当前用户种植的植物信息

4.orchard.html-登陆种植园成功后将植物相关信息获取并保存在前端,并发送事件

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>用户中心</title>
  5. <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  6. <meta charset="utf-8">
  7. <link rel="stylesheet" href="../static/css/main.css">
  8. <script src="../static/js/vue.js"></script>
  9. <script src="../static/js/axios.js"></script>
  10. <script src="../static/js/main.js"></script>
  11. <script src="../static/js/uuid.js"></script>
  12. <script src="../static/js/settings.js"></script>
  13. <script src="../static/js/socket.io.js"></script>
  14. </head>
  15. <body>
  16. <div class="app orchard" id="app">
  17. <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
  18. <div class="orchard-bg">
  19. <img src="../static/images/bg2.png">
  20. <img class="board_bg2" src="../static/images/board_bg2.png">
  21. </div>
  22. <img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
  23. <div class="header">
  24. <div class="info" @click="go_home">
  25. <div class="avatar">
  26. <img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
  27. <img class="user_avatar" src="../static/images/avatar.png" alt="">
  28. <img class="avatar_border" src="../static/images/avatar_border.png" alt="">
  29. </div>
  30. <p class="user_name">好听的昵称</p>
  31. </div>
  32. <div class="wallet">
  33. <div class="balance" @click="user_recharge">
  34. <p class="title"><img src="../static/images/money.png" alt="">钱包</p>
  35. <p class="num">{{money}}</p>
  36. </div>
  37. <div class="balance">
  38. <p class="title"><img src="../static/images/integral.png" alt="">果子</p>
  39. <p class="num">99,999.00</p>
  40. </div>
  41. </div>
  42. <div class="menu-list">
  43. <div class="menu">
  44. <img src="../static/images/menu1.png" alt="">
  45. 排行榜
  46. </div>
  47. <div class="menu">
  48. <img src="../static/images/menu2.png" alt="">
  49. 签到有礼
  50. </div>
  51. <div class="menu" @click="go_orchard_shop">
  52. <img src="../static/images/menu3.png" alt="">
  53. 道具商城
  54. </div>
  55. <div class="menu">
  56. <img src="../static/images/menu4.png" alt="">
  57. 邮件中心
  58. </div>
  59. </div>
  60. </div>
  61. <div class="footer" >
  62. <ul class="menu-list">
  63. <li class="menu">新手</li>
  64. <li class="menu" @click="go_my_package">背包</li>
  65. <li class="menu-center" @click="go_orchard_shop">商店</li>
  66. <li class="menu">消息</li>
  67. <li class="menu">好友</li>
  68. </ul>
  69. </div>
  70. </div>
  71. <script>
  72. apiready = function(){
  73. init();
  74. new Vue({
  75. el:"#app",
  76. data(){
  77. return {
  78. music_play:true,
  79. namespace: '/mofang',
  80. token:"",
  81. money:"",
  82. settings_info:{
  83. orchard: {}, // 种植园公共参数
  84. user:{}, // 用户私有相关参数
  85. },
  86. socket: null,
  87. recharge_list: ['10','20','50','100','200','500','1000'],
  88. timeout: 0,
  89. prev:{name:"",url:"",params:{}},
  90. current:{name:"orchard",url:"orchard.html",params:{}},
  91. }
  92. },
  93. beforeCreate(){
  94. this.game.goFrame("orchard","my_orchard.html", this.current,{
  95. x: 0,
  96. y: 180,
  97. w: 'auto',
  98. h: 410,
  99. },null);
  100. },
  101. created(){
  102. this.checkout();
  103. this.money = this.game.fget("money");
  104. },
  105. methods:{
  106. user_recharge(){
  107. // 发起充值请求
  108. api.actionSheet({
  109. title: '余额充值',
  110. cancelTitle: '取消',
  111. buttons: this.recharge_list
  112. }, (ret, err)=>{
  113. if( ret ){
  114. if(ret.buttonIndex <= this.recharge_list.length){
  115. // 充值金额
  116. money = this.recharge_list[ret.buttonIndex-1];
  117. // 调用支付宝充值
  118. this.create_recharge(money);
  119. }
  120. }else{
  121.  
  122. }
  123. });
  124.  
  125. },
  126. create_recharge(money){
  127. // 获取历史信息记录
  128. var token = this.game.get("access_token") || this.game.fget("access_token");
  129. this.game.checkout(this, token, (new_access_token)=>{
  130. this.axios.post("",{
  131. "jsonrpc": "2.0",
  132. "id": this.uuid(),
  133. "method": "Recharge.create",
  134. "params": {
  135. "money": money,
  136. }
  137. },{
  138. headers:{
  139. Authorization: "jwt " + token,
  140. }
  141. }).then(response=>{
  142. if(parseInt(response.data.result.errno)==1000){
  143. // 前往支付宝
  144. var aliPayPlus = api.require('aliPayPlus');
  145. aliPayPlus.payOrder({
  146. orderInfo: response.data.result.order_string,
  147. sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
  148. }, (ret, err)=>{
  149. pay_result = {
  150. 9000:"支付成功",
  151. 8000:"正在处理中",
  152. 4000:"订单支付失败",
  153. 5000:"重复请求",
  154. 6001:"取消支付",
  155. 6002:"网络连接出错",
  156. 6004:"支付结果未知",
  157. }
  158. api.alert({
  159. title: '支付结果',
  160. msg: pay_result[ret.code],
  161. buttons: ['确定']
  162. });
  163. // 通知服务端, 修改充值结果
  164. this.return_recharge(response.data.result.order_number,token);
  165. });
  166. }else{
  167. this.game.print(response.data);
  168. }
  169. }).catch(error=>{
  170. // 网络等异常
  171. this.game.print(error);
  172. });
  173. })
  174. },
  175. return_recharge(out_trade_number,token){
  176. this.axios.post("",{
  177. "jsonrpc": "2.0",
  178. "id": this.uuid(),
  179. "method": "Recharge.return",
  180. "params": {
  181. "out_trade_number": out_trade_number,
  182. }
  183. },{
  184. headers:{
  185. Authorization: "jwt " + token,
  186. }
  187. }).then(response=>{
  188. if(parseInt(response.data.result.errno)==1000){
  189. this.money = response.data.result.money.toFixed(2);
  190. }
  191. })
  192. },
  193. checkout(){
  194. var token = this.game.get("access_token") || this.game.fget("access_token");
  195. this.game.checkout(this,token,(new_access_token)=>{
  196. this.connect();
  197. this.login();
  198. this.user_package();
  199. this.buy_prop();
  200. this.unlock_package_number();
  201. this.show_pet();
  202. this.use_prop();
  203. });
  204. },
  205. connect(){
  206. // socket连接
  207. this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
  208. this.socket.on('connect', ()=>{
  209. this.game.print("开始连接服务端");
  210. var id = this.game.fget("id");
  211. this.socket.emit("login",{"uid":id});
  212. this.socket.emit("user_prop");
  213. });
  214. },
  215. login(){
  216. this.socket.on("login_response",(message)=>{
  217. this.settings_info.orchard = message.orchard_settings;
  218. this.settings_info.user=message.user_settings;
  219. this.game.fsave({
  220. "orchard_settings":message.orchard_settings,
  221. "user_settings":message.user_settings,
  222. });
  223. this.game.save({
  224. "tree_total":message.tree_total,
  225. "user_tree_number":message.user_tree_number,
  226. "user_tree_list":message.user_tree_list,
  227. "tree_status":message.tree_status,
  228. });
  229. setTimeout(()=>{
  230. // 通知种植园页面获取到了当前用户的种植信息
  231. api.sendEvent({
  232. name: 'user_tree_data',
  233. extra: {}
  234. });
  235. },500);
  236. });
  237. },
  238. user_package(){
  239. // 用户背包道具列表
  240. this.socket.on("user_prop_response",(message)=>{
  241. this.game.fsave({
  242. "user_package":message.data,
  243. })
  244. })
  245. },
  246. go_index(){
  247. this.game.goWin("root");
  248. },
  249. go_friends(){
  250. this.game.goFrame("friends","friends.html",this.current);
  251. this.game.goFrame("friend_list","friend_list.html",this.current,{
  252. x: 0,
  253. y: 190,
  254. w: 'auto',
  255. h: 'auto',
  256. },null,true);
  257. },
  258. go_home(){
  259. this.game.goWin("user","user.html", this.current);
  260. },
  261. go_orchard_shop(){
  262. // 种植园商店
  263. this.game.goFrame("orchard_shop","shop.html", this.current,null,{
  264. type:"push",
  265. subType:"from_top",
  266. duration:300
  267. });
  268. },
  269. go_my_package(){
  270. // 我的背包
  271. this.game.goFrame("package","package.html", this.current,null,{
  272. type:"push",
  273. subType:"from_top",
  274. duration:300
  275. });
  276. },
  277. buy_prop(){
  278. api.addEventListener({
  279. name: 'buy_prop'
  280. }, (ret, err)=>{
  281. if( ret ){
  282. // 用户购买道具
  283. this.socket.emit("user_buy_prop",ret.value);
  284. }
  285. });
  286. this.socket.on("user_buy_prop_response",(message)=>{
  287. alert(message.errmsg);
  288. })
  289. },
  290. use_prop(){
  291. // 使用道具
  292. var pid = 0;
  293. api.addEventListener({
  294. name: 'use_prop'
  295. }, (ret, err)=>{
  296. if( ret ){
  297. // 用户使用道具
  298. pid = ret.value.pid;
  299. this.socket.emit("use_prop",ret.value.pid);
  300. }
  301. });
  302.  
  303. this.socket.on("pet_use_response",(message)=>{
  304. if(parseInt(message.errno) === 1000){
  305. api.sendEvent({
  306. name: 'pet_use_success',
  307. extra: {
  308. pid: pid
  309. }
  310. });
  311. }else{
  312. api.alert({
  313. title: '提示',
  314. msg: message.errmsg,
  315. });
  316.  
  317. }
  318. });
  319. },
  320. unlock_package_number(){
  321. api.addEventListener({
  322. name: 'unlock_package_number'
  323. }, (ret, err)=>{
  324. if( ret ){
  325. // 用户购买道具
  326. this.socket.emit("unlock_package");
  327. }
  328. });
  329.  
  330. this.socket.on("unlock_package_response",(message)=>{
  331. if(parseInt(message.errno) === 1000){
  332. api.sendEvent({
  333. name: 'unlock_package_success',
  334. extra: {
  335. }
  336. });
  337. }else{
  338. api.alert({
  339. title: '提示',
  340. msg: message.errmsg,
  341. });
  342.  
  343. }
  344.  
  345. })
  346. },
  347. show_pet(){
  348. // 显示宠物
  349. this.socket.emit("pet_show");
  350. this.socket.on("pet_show_response",(message)=>{
  351. if(parseInt(message.errno) === 1000){
  352. // 把宠物信息保存到本地
  353. this.game.save({"pet_list":message.pet_list})
  354. this.game.save({"pet_number":message.pet_number})
  355. setTimeout(()=>{
  356. api.sendEvent({
  357. name: 'pet_show_success',
  358. extra: {}
  359. });
  360. },500);
  361. }else{
  362. api.alert({
  363. title: '提示',
  364. msg: message.errmsg,
  365. });
  366. }
  367.  
  368. })
  369. }
  370. }
  371. });
  372. }
  373. </script>
  374. </body>
  375. </html>

orchard.html-登陆种植园成功后将植物相关信息获取并保存在前端,并发送事件

5.my_orchard.html-监听事件,并获取前端已经保存好的植物数据

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>用户中心</title>
  5. <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  6. <meta charset="utf-8">
  7. <link rel="stylesheet" href="../static/css/main.css">
  8. <script src="../static/js/vue.js"></script>
  9. <script src="../static/js/axios.js"></script>
  10. <script src="../static/js/main.js"></script>
  11. <script src="../static/js/uuid.js"></script>
  12. <script src="../static/js/settings.js"></script>
  13. <script src="../static/js/socket.io.js"></script>
  14. </head>
  15. <body>
  16. <div class="app orchard orchard-frame" id="app">
  17. <div class="background">
  18. <img class="grassland2" src="../static/images/grassland2.png" alt="">
  19. <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
  20. <img class="stake1" src="../static/images/stake1.png" alt="">
  21. <img class="stake2" src="../static/images/stake2.png" alt="">
  22. </div>
  23. <div class="pet-box">
  24. <div class="pet">
  25. <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
  26. </div>
  27. <div class="pet" v-if="pet_number > 1">
  28. <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt="">
  29. </div>
  30. <div class="pet turned_off" v-if="pet_number==1">
  31. <img class="turned_image" src="../static/images/turned_off.png" alt="">
  32. <p>请购买宠物</p>
  33. </div>
  34. </div>
  35. <div class="tree-list">
  36. <div class="tree-box">
  37. <div class="tree" v-for="tree in user_tree_data.user_tree_list">
  38. <img :src="tree_img(tree.status)" alt="">
  39. </div>
  40. <!-- 已激活但是未种植的树桩列表 -->
  41. <div class="tree" v-for="i in active_tree">
  42. <img src="../static/images/tree1.png" alt="">
  43. </div>
  44. <!-- 未激活树桩列表 -->
  45. <div class="tree" v-for="i in lock_tree">
  46. <img src="../static/images/tree0.png" alt="">
  47. </div>
  48. </div>
  49.  
  50. </div>
  51. <div class="prop-list">
  52. <div class="prop">
  53. <img src="../static/images/prop1.png" alt="">
  54. <span>1</span>
  55. <p>化肥</p>
  56. </div>
  57. <div class="prop">
  58. <img src="../static/images/prop2.png" alt="">
  59. <span>0</span>
  60. <p>修剪</p>
  61. </div>
  62. <div class="prop">
  63. <img src="../static/images/prop3.png" alt="">
  64. <span>1</span>
  65. <p>浇水</p>
  66. </div>
  67. <div class="prop">
  68. <img src="../static/images/prop4.png" alt="">
  69. <span>1</span>
  70. <p>宠物粮</p>
  71. </div>
  72. </div>
  73. <div class="pet-hp-list">
  74. <div class="pet-hp" v-for="pet in pet_list">
  75. <p>宠物1 饱食度</p>
  76. <div class="hp">
  77. <div :style="{width: pet.hp+'%'}" class="process">{{pet.hp}}%</div>
  78. </div>
  79. </div>
  80. </div>
  81. </div>
  82. <script>
  83. apiready = function(){
  84. init();
  85. new Vue({
  86. el:"#app",
  87. data(){
  88. return {
  89. namespace: '/mofang',
  90. token:"",
  91. socket: null,
  92. pet_list:[],
  93. user_tree_data:{
  94. "total_tree": 9, // 总树桩数量
  95. "user_tree_number": 0, // 当前用户激活树桩数量
  96. "user_tree_list":[ // 当前种植的树桩列表状态
  97.  
  98. ],
  99. },
  100. tree_status:{
  101.  
  102. },
  103. // user_tree_data:{
  104. // "total_tree":9, // 总树桩数量
  105. // "user_tree_number": 5, // 当前用户激活树桩数量
  106. // "user_tree_list":[ // 当前种植的树桩列表状态
  107. // { // 树桩状态
  108. // "time":1609808084, // 种植时间
  109. // "status":4, // 植物状态
  110. // "has_time": 300, // 状态时间
  111. // },
  112. // ],
  113. // },
  114. // tree_status:{
  115. // "tree_status_0": "tree0.png", // 树桩
  116. // "tree_status_1": "tree1.png", // 空桩
  117. // "tree_status_2": "tree2.png", // 幼苗
  118. // "tree_status_3": "tree3.png", // 成长
  119. // "tree_status_4": "tree4.png", // 成熟
  120. // },
  121. pet_number:[],
  122. timeout: 0,
  123. prev:{name:"",url:"",params:{}},
  124. current:{name:"orchard",url:"orchard.html",params:{}},
  125. }
  126. },
  127. computed:{
  128. // 已激活但是未使用的树桩
  129. active_tree(){
  130. return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
  131. },
  132. // 未激活的剩余树桩
  133. lock_tree(){
  134. return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
  135. },
  136. },
  137. created(){
  138. this.show_pet_list();
  139. this.show_tree_list();
  140. },
  141. methods:{
  142. tree_img(status){
  143. return '../static/images/'+this.tree_status[status];
  144. },
  145. show_pet_list(){
  146. api.addEventListener({
  147. name: 'pet_show_success'
  148. }, (ret, err)=>{
  149. if( ret ){
  150. // 用户购买道具
  151. this.pet_list = this.game.get("pet_list");
  152. this.pet_number = parseInt(this.game.get("pet_number"));
  153. }
  154. });
  155. },
  156. show_tree_list(){
  157. api.addEventListener({
  158. name: 'user_tree_data'
  159. }, (ret, err)=>{
  160. if( ret ){
  161. // 用户种植植物信息
  162. this.user_tree_data.tree_total = parseInt(this.game.get("tree_total"));
  163. this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number"));
  164. this.user_tree_data.user_tree_list = this.game.get("user_tree_list");
  165. this.tree_status = this.game.get("tree_status");
  166. }
  167. });
  168. }
  169. }
  170. });
  171. }
  172. </script>
  173. </body>
  174. </html>

my_orchard.html-监听事件,并获取前端已经保存好的植物数据

2.用户使用植物道具进行果树种植

1.package.html-用户点击背包中的道具,前端发起使用道具的请求

用户点击背包道具,客户端发起使用道具的请求,

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>我的背包</title>
  5. <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  6. <meta charset="utf-8">
  7. <link rel="stylesheet" href="../static/css/main.css">
  8. <script src="../static/js/vue.js"></script>
  9. <script src="../static/js/axios.js"></script>
  10. <script src="../static/js/main.js"></script>
  11. <script src="../static/js/uuid.js"></script>
  12. <script src="../static/js/settings.js"></script>
  13. </head>
  14. <body>
  15. <div class="app frame avatar add_friend package" id="app">
  16. <div class="box">
  17. <p class="title">我的背包</p>
  18. <img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt="">
  19. <div class="prop_list">
  20. <div class="item" v-for="prop in user_package" @click="use_prop(prop.pid)">
  21. <img :src="settings.static_url+prop.image" alt="">
  22. <span>{{prop.num}}</span>
  23. </div>
  24. <div class="item" v-for="number in unlock_td_number"></div>
  25. <div class="item lock" @click="unlock_package()" v-for="number in lock_td_number"></div>
  26. </div>
  27. </div>
  28. </div>
  29. <script>
  30. apiready = function(){
  31. init();
  32. new Vue({
  33. el:"#app",
  34. data(){
  35. return {
  36. td: 36, // 背包格子总数量
  37. user_id: "", // 当前登陆用户Id
  38. orchard_settings:{}, // 种植园相关公共参数
  39. user_settings:{}, // 用户相关私有参数
  40. user_package:[], // 用户背包信息
  41. prev:{name:"",url:"",params:{}},
  42. current:{name:"package",url:"package.html",params:{}},
  43. }
  44. },
  45. computed:{// 计算属性
  46. lock_td_number(){
  47. // 未解锁的格子
  48. return parseInt(this.orchard_settings.package_number_max-this.user_settings.package_number);
  49. },
  50. unlock_td_number(){
  51. // 解锁的格子
  52. return parseInt( this.user_settings.package_number - this.user_package.length);
  53. }
  54. },
  55. created(){
  56. this.user_id = this.game.get("id") || this.game.fget("id");
  57. this.orchard_settings = JSON.parse(this.game.fget("orchard_settings"));
  58. this.user_settings = JSON.parse(this.game.fget("user_settings"));
  59. this.user_package = JSON.parse(this.game.fget("user_package"));
  60. },
  61. methods:{
  62. use_prop(pid){
  63. // 发起使用道具的通知
  64. api.confirm({
  65. title: '提示',
  66. msg: '确认使用当前道具吗?',
  67. buttons: ['确定', '取消']
  68. }, (ret, err)=>{
  69. if( ret.buttonIndex == 1 ){
  70. api.sendEvent({
  71. name: "use_prop",
  72. extra: {
  73. pid: pid,
  74. }
  75. });
  76.  
  77. }
  78. });
  79.  
  80. api.addEventListener({
  81. name: 'prop_use_success'
  82. }, (ret, err)=>{
  83. if( ret ){
  84. // 扣除指定道具
  85. var pid = ret.value.pid;
  86. for(var i in this.user_package){
  87. if(this.user_package[i].pid == pid){
  88. this.user_package[i].num-=1;
  89. if(this.user_package[i].num == 0){
  90. this.user_package.splice(i,1);
  91. }
  92. }
  93. }
  94. this.game.fsave({"user_package":this.user_package});
  95. }else{
  96. alert( JSON.stringify( err ) );
  97. }
  98. });
  99.  
  100. },
  101. unlock_package(){
  102. // 解锁格子上限
  103. api.confirm({
  104. title: '提示',
  105. msg: '解锁背包上限',
  106. buttons: ['确定', '取消']
  107. }, (ret, err)=>{
  108. if( ret.buttonIndex == 1 ){
  109.  
  110. api.sendEvent({
  111. name: 'unlock_package_number',
  112. extra: {}
  113. });
  114.  
  115. api.addEventListener({
  116. name: 'unlock_package_success'
  117. }, (ret, err)=>{
  118. this.user_settings.package_number=parseInt(this.user_settings.package_number)+1;
  119. this.game.fsave({"user_settings":this.user_settings});
  120. });
  121. }
  122. });
  123.  
  124. },
  125. close_frame(){
  126. this.game.outFrame("package");
  127. },
  128. }
  129. });
  130. }
  131. </script>
  132. </body>
  133. </html>

package.html-用户点击背包中的道具,前端发起使用道具的请求

2.orchard.html-监听使用道具事件

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>用户中心</title>
  5. <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  6. <meta charset="utf-8">
  7. <link rel="stylesheet" href="../static/css/main.css">
  8. <script src="../static/js/vue.js"></script>
  9. <script src="../static/js/axios.js"></script>
  10. <script src="../static/js/main.js"></script>
  11. <script src="../static/js/uuid.js"></script>
  12. <script src="../static/js/settings.js"></script>
  13. <script src="../static/js/socket.io.js"></script>
  14. </head>
  15. <body>
  16. <div class="app orchard" id="app">
  17. <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
  18. <div class="orchard-bg">
  19. <img src="../static/images/bg2.png">
  20. <img class="board_bg2" src="../static/images/board_bg2.png">
  21. </div>
  22. <img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
  23. <div class="header">
  24. <div class="info" @click="go_home">
  25. <div class="avatar">
  26. <img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
  27. <img class="user_avatar" src="../static/images/avatar.png" alt="">
  28. <img class="avatar_border" src="../static/images/avatar_border.png" alt="">
  29. </div>
  30. <p class="user_name">好听的昵称</p>
  31. </div>
  32. <div class="wallet">
  33. <div class="balance" @click="user_recharge">
  34. <p class="title"><img src="../static/images/money.png" alt="">钱包</p>
  35. <p class="num">{{money}}</p>
  36. </div>
  37. <div class="balance">
  38. <p class="title"><img src="../static/images/integral.png" alt="">果子</p>
  39. <p class="num">99,999.00</p>
  40. </div>
  41. </div>
  42. <div class="menu-list">
  43. <div class="menu">
  44. <img src="../static/images/menu1.png" alt="">
  45. 排行榜
  46. </div>
  47. <div class="menu">
  48. <img src="../static/images/menu2.png" alt="">
  49. 签到有礼
  50. </div>
  51. <div class="menu" @click="go_orchard_shop">
  52. <img src="../static/images/menu3.png" alt="">
  53. 道具商城
  54. </div>
  55. <div class="menu">
  56. <img src="../static/images/menu4.png" alt="">
  57. 邮件中心
  58. </div>
  59. </div>
  60. </div>
  61. <div class="footer" >
  62. <ul class="menu-list">
  63. <li class="menu">新手</li>
  64. <li class="menu" @click="go_my_package">背包</li>
  65. <li class="menu-center" @click="go_orchard_shop">商店</li>
  66. <li class="menu">消息</li>
  67. <li class="menu">好友</li>
  68. </ul>
  69. </div>
  70. </div>
  71. <script>
  72. apiready = function(){
  73. init();
  74. new Vue({
  75. el:"#app",
  76. data(){
  77. return {
  78. music_play:true,
  79. namespace: '/mofang',
  80. token:"",
  81. money:"",
  82. settings_info:{
  83. orchard: {}, // 种植园公共参数
  84. user:{}, // 用户私有相关参数
  85. },
  86. socket: null,
  87. recharge_list: ['10','20','50','100','200','500','1000'],
  88. timeout: 0,
  89. prev:{name:"",url:"",params:{}},
  90. current:{name:"orchard",url:"orchard.html",params:{}},
  91. }
  92. },
  93. beforeCreate(){
  94. this.game.goFrame("orchard","my_orchard.html", this.current,{
  95. x: 0,
  96. y: 180,
  97. w: 'auto',
  98. h: 410,
  99. },null);
  100. },
  101. created(){
  102. this.checkout();
  103. this.money = this.game.fget("money");
  104. },
  105. methods:{
  106. user_recharge(){
  107. // 发起充值请求
  108. api.actionSheet({
  109. title: '余额充值',
  110. cancelTitle: '取消',
  111. buttons: this.recharge_list
  112. }, (ret, err)=>{
  113. if( ret ){
  114. if(ret.buttonIndex <= this.recharge_list.length){
  115. // 充值金额
  116. money = this.recharge_list[ret.buttonIndex-1];
  117. // 调用支付宝充值
  118. this.create_recharge(money);
  119. }
  120. }else{
  121.  
  122. }
  123. });
  124.  
  125. },
  126. create_recharge(money){
  127. // 获取历史信息记录
  128. var token = this.game.get("access_token") || this.game.fget("access_token");
  129. this.game.checkout(this, token, (new_access_token)=>{
  130. this.axios.post("",{
  131. "jsonrpc": "2.0",
  132. "id": this.uuid(),
  133. "method": "Recharge.create",
  134. "params": {
  135. "money": money,
  136. }
  137. },{
  138. headers:{
  139. Authorization: "jwt " + token,
  140. }
  141. }).then(response=>{
  142. if(parseInt(response.data.result.errno)==1000){
  143. // 前往支付宝
  144. var aliPayPlus = api.require('aliPayPlus');
  145. aliPayPlus.payOrder({
  146. orderInfo: response.data.result.order_string,
  147. sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
  148. }, (ret, err)=>{
  149. pay_result = {
  150. 9000:"支付成功",
  151. 8000:"正在处理中",
  152. 4000:"订单支付失败",
  153. 5000:"重复请求",
  154. 6001:"取消支付",
  155. 6002:"网络连接出错",
  156. 6004:"支付结果未知",
  157. }
  158. api.alert({
  159. title: '支付结果',
  160. msg: pay_result[ret.code],
  161. buttons: ['确定']
  162. });
  163. // 通知服务端, 修改充值结果
  164. this.return_recharge(response.data.result.order_number,token);
  165. });
  166. }else{
  167. this.game.print(response.data);
  168. }
  169. }).catch(error=>{
  170. // 网络等异常
  171. this.game.print(error);
  172. });
  173. })
  174. },
  175. return_recharge(out_trade_number,token){
  176. this.axios.post("",{
  177. "jsonrpc": "2.0",
  178. "id": this.uuid(),
  179. "method": "Recharge.return",
  180. "params": {
  181. "out_trade_number": out_trade_number,
  182. }
  183. },{
  184. headers:{
  185. Authorization: "jwt " + token,
  186. }
  187. }).then(response=>{
  188. if(parseInt(response.data.result.errno)==1000){
  189. this.money = response.data.result.money.toFixed(2);
  190. }
  191. })
  192. },
  193. checkout(){
  194. var token = this.game.get("access_token") || this.game.fget("access_token");
  195. this.game.checkout(this,token,(new_access_token)=>{
  196. this.connect();
  197. this.login();
  198. this.user_package();
  199. this.buy_prop();
  200. this.unlock_package_number();
  201. this.show_pet();
  202. this.use_prop();
  203. });
  204. },
  205. connect(){
  206. // socket连接
  207. this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
  208. this.socket.on('connect', ()=>{
  209. this.game.print("开始连接服务端");
  210. var id = this.game.fget("id");
  211. this.socket.emit("login",{"uid":id});
  212. this.socket.emit("user_prop");
  213. });
  214. },
  215. login(){
  216. this.socket.on("login_response",(message)=>{
  217. this.settings_info.orchard = message.orchard_settings;
  218. this.settings_info.user=message.user_settings;
  219. this.game.fsave({
  220. "orchard_settings":message.orchard_settings,
  221. "user_settings":message.user_settings,
  222. });
  223. this.game.save({
  224. "tree_total":message.tree_total,
  225. "user_tree_number":message.user_tree_number,
  226. "user_tree_list":message.user_tree_list,
  227. "tree_status":message.tree_status,
  228. });
  229. setTimeout(()=>{
  230. // 通知种植园页面获取到了当前用户的种植信息
  231. api.sendEvent({
  232. name: 'user_tree_data',
  233. extra: {}
  234. });
  235. },500);
  236. });
  237. },
  238. user_package(){
  239. // 用户背包道具列表
  240. this.socket.on("user_prop_response",(message)=>{
  241. this.game.fsave({
  242. "user_package":message.data,
  243. })
  244. })
  245. },
  246. go_index(){
  247. this.game.goWin("root");
  248. },
  249. go_friends(){
  250. this.game.goFrame("friends","friends.html",this.current);
  251. this.game.goFrame("friend_list","friend_list.html",this.current,{
  252. x: 0,
  253. y: 190,
  254. w: 'auto',
  255. h: 'auto',
  256. },null,true);
  257. },
  258. go_home(){
  259. this.game.goWin("user","user.html", this.current);
  260. },
  261. go_orchard_shop(){
  262. // 种植园商店
  263. this.game.goFrame("orchard_shop","shop.html", this.current,null,{
  264. type:"push",
  265. subType:"from_top",
  266. duration:300
  267. });
  268. },
  269. go_my_package(){
  270. // 我的背包
  271. this.game.goFrame("package","package.html", this.current,null,{
  272. type:"push",
  273. subType:"from_top",
  274. duration:300
  275. });
  276. },
  277. buy_prop(){
  278. api.addEventListener({
  279. name: 'buy_prop'
  280. }, (ret, err)=>{
  281. if( ret ){
  282. // 用户购买道具
  283. this.socket.emit("user_buy_prop",ret.value);
  284. }
  285. });
  286. this.socket.on("user_buy_prop_response",(message)=>{
  287. alert(message.errmsg);
  288. })
  289. },
  290. use_prop(){
  291. // 使用道具
  292. var pid = 0;
  293. api.addEventListener({
  294. name: 'use_prop'
  295. }, (ret, err)=>{
  296. if( ret ){
  297. // 用户使用道具
  298. pid = ret.value.pid;
  299. this.socket.emit("use_prop",ret.value.pid);
  300. }
  301. });
  302.  
  303. this.socket.on("prop_use_response",(message)=>{
  304. if(parseInt(message.errno) === 1000){
  305. api.sendEvent({
  306. name: 'prop_use_success',
  307. extra: {
  308. pid: pid
  309. }
  310. });
  311. }else{
  312. api.alert({
  313. title: '提示',
  314. msg: message.errmsg,
  315. });
  316.  
  317. }
  318. });
  319. },
  320. unlock_package_number(){
  321. api.addEventListener({
  322. name: 'unlock_package_number'
  323. }, (ret, err)=>{
  324. if( ret ){
  325. // 用户购买道具
  326. this.socket.emit("unlock_package");
  327. }
  328. });
  329.  
  330. this.socket.on("unlock_package_response",(message)=>{
  331. if(parseInt(message.errno) === 1000){
  332. api.sendEvent({
  333. name: 'unlock_package_success',
  334. extra: {
  335. }
  336. });
  337. }else{
  338. api.alert({
  339. title: '提示',
  340. msg: message.errmsg,
  341. });
  342.  
  343. }
  344.  
  345. })
  346. },
  347. show_pet(){
  348. // 显示宠物
  349. this.socket.emit("pet_show");
  350. this.socket.on("pet_show_response",(message)=>{
  351. if(parseInt(message.errno) === 1000){
  352. // 把宠物信息保存到本地
  353. this.game.save({"pet_list":message.pet_list})
  354. this.game.save({"pet_number":message.pet_number})
  355. setTimeout(()=>{
  356. api.sendEvent({
  357. name: 'pet_show_success',
  358. extra: {}
  359. });
  360. },500);
  361. }else{
  362. api.alert({
  363. title: '提示',
  364. msg: message.errmsg,
  365. });
  366. }
  367.  
  368. })
  369. }
  370. }
  371. });
  372. }
  373. </script>
  374. </body>
  375. </html>

orchard.html-监听使用道具事件

3.my_orchard.html-增加tree_img方法,前端根据后端传过来的status显示植物的不同形态

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>用户中心</title>
  5. <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  6. <meta charset="utf-8">
  7. <link rel="stylesheet" href="../static/css/main.css">
  8. <script src="../static/js/vue.js"></script>
  9. <script src="../static/js/axios.js"></script>
  10. <script src="../static/js/main.js"></script>
  11. <script src="../static/js/uuid.js"></script>
  12. <script src="../static/js/settings.js"></script>
  13. <script src="../static/js/socket.io.js"></script>
  14. </head>
  15. <body>
  16. <div class="app orchard orchard-frame" id="app">
  17. <div class="background">
  18. <img class="grassland2" src="../static/images/grassland2.png" alt="">
  19. <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
  20. <img class="stake1" src="../static/images/stake1.png" alt="">
  21. <img class="stake2" src="../static/images/stake2.png" alt="">
  22. </div>
  23. <div class="pet-box">
  24. <div class="pet">
  25. <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
  26. </div>
  27. <div class="pet" v-if="pet_number > 1">
  28. <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt="">
  29. </div>
  30. <div class="pet turned_off" v-if="pet_number==1">
  31. <img class="turned_image" src="../static/images/turned_off.png" alt="">
  32. <p>请购买宠物</p>
  33. </div>
  34. </div>
  35. <div class="tree-list">
  36. <div class="tree-box">
  37. <div class="tree" v-for="tree in user_tree_data.user_tree_list">
  38. <img :src="tree_img(tree.status)" alt="">
  39. </div>
  40. <!-- 已激活但是未种植的树桩列表 -->
  41. <div class="tree" v-for="i in active_tree">
  42. <img src="../static/images/tree1.png" alt="">
  43. </div>
  44. <!-- 未激活树桩列表 -->
  45. <div class="tree" v-for="i in lock_tree">
  46. <img src="../static/images/tree0.png" alt="">
  47. </div>
  48. </div>
  49.  
  50. </div>
  51. <div class="prop-list">
  52. <div class="prop">
  53. <img src="../static/images/prop1.png" alt="">
  54. <span>1</span>
  55. <p>化肥</p>
  56. </div>
  57. <div class="prop">
  58. <img src="../static/images/prop2.png" alt="">
  59. <span>0</span>
  60. <p>修剪</p>
  61. </div>
  62. <div class="prop">
  63. <img src="../static/images/prop3.png" alt="">
  64. <span>1</span>
  65. <p>浇水</p>
  66. </div>
  67. <div class="prop">
  68. <img src="../static/images/prop4.png" alt="">
  69. <span>1</span>
  70. <p>宠物粮</p>
  71. </div>
  72. </div>
  73. <div class="pet-hp-list">
  74. <div class="pet-hp" v-for="pet in pet_list">
  75. <p>宠物1 饱食度</p>
  76. <div class="hp">
  77. <div :style="{width: pet.hp+'%'}" class="process">{{pet.hp}}%</div>
  78. </div>
  79. </div>
  80. </div>
  81. </div>
  82. <script>
  83. apiready = function(){
  84. init();
  85. new Vue({
  86. el:"#app",
  87. data(){
  88. return {
  89. namespace: '/mofang',
  90. token:"",
  91. socket: null,
  92. pet_list:[],
  93. user_tree_data:{
  94. "total_tree": 9, // 总树桩数量
  95. "user_tree_number": 0, // 当前用户激活树桩数量
  96. "user_tree_list":[ // 当前种植的树桩列表状态
  97.  
  98. ],
  99. },
  100. tree_status:{
  101.  
  102. },
  103. // user_tree_data:{
  104. // "total_tree":9, // 总树桩数量
  105. // "user_tree_number": 5, // 当前用户激活树桩数量
  106. // "user_tree_list":[ // 当前种植的树桩列表状态
  107. // { // 树桩状态
  108. // "time":1609808084, // 种植时间
  109. // "status":4, // 植物状态
  110. // "has_time": 300, // 状态时间
  111. // },
  112. // ],
  113. // },
  114. // tree_status:{
  115. // "tree_status_0": "tree0.png", // 树桩
  116. // "tree_status_1": "tree1.png", // 空桩
  117. // "tree_status_2": "tree2.png", // 幼苗
  118. // "tree_status_3": "tree3.png", // 成长
  119. // "tree_status_4": "tree4.png", // 成熟
  120. // },
  121. pet_number:[],
  122. timeout: 0,
  123. prev:{name:"",url:"",params:{}},
  124. current:{name:"orchard",url:"orchard.html",params:{}},
  125. }
  126. },
  127. computed:{
  128. // 已激活但是未使用的树桩
  129. active_tree(){
  130. return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
  131. },
  132. // 未激活的剩余树桩
  133. lock_tree(){
  134. return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
  135. },
  136. },
  137. created(){
  138. this.show_pet_list();
  139. this.show_tree_list();
  140. },
  141. methods:{
  142. tree_img(status){
  143. return '../static/images/'+this.tree_status[status];
  144. },
  145. show_pet_list(){
  146. api.addEventListener({
  147. name: 'pet_show_success'
  148. }, (ret, err)=>{
  149. if( ret ){
  150. // 用户购买道具
  151. this.pet_list = this.game.get("pet_list");
  152. this.pet_number = parseInt(this.game.get("pet_number"));
  153. }
  154. });
  155. },
  156. show_tree_list(){
  157. api.addEventListener({
  158. name: 'user_tree_data'
  159. }, (ret, err)=>{
  160. if( ret ){
  161. // 用户种植植物信息
  162. this.user_tree_data.tree_total = parseInt(this.game.get("tree_total"));
  163. this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number"));
  164. this.user_tree_data.user_tree_list = this.game.get("user_tree_list");
  165. this.tree_status = this.game.get("tree_status");
  166. }
  167. });
  168. }
  169. }
  170. });
  171. }
  172. </script>
  173. </body>
  174. </html>

my_orchard.html-增加tree_img方法,前端根据后端传过来的status显示植物的不同形态

4.服务端调整使用道具的后端接口和进入种植园的登录接口

  1. from application import socketio
  2. from flask import request
  3. from application.apps.users.models import User
  4. from flask_socketio import join_room, leave_room
  5. from application import mongo
  6. from .models import Goods,Setting,db
  7. from status import APIStatus as status
  8. from message import ErrorMessage as errmsg
  9. # 建立socket通信
  10. # @socketio.on("connect", namespace="/mofang")
  11. # def user_connect():
  12. # """用户连接"""
  13. # print("用户%s连接过来了!" % request.sid)
  14. # # 主动响应数据给客户端
  15. # socketio.emit("server_response","hello",namespace="/mofang")
  16.  
  17. # 断开socket通信
  18. @socketio.on("disconnect", namespace="/mofang")
  19. def user_disconnect():
  20. print("用户%s退出了种植园" % request.sid )
  21.  
  22. @socketio.on("login", namespace="/mofang")
  23. def user_login(data):
  24. # 分配房间
  25. room = data["uid"]
  26. join_room(room)
  27. # 保存当前用户和sid的绑定关系
  28. # 判断当前用户是否在mongo中有记录
  29. query = {
  30. "_id": data["uid"]
  31. }
  32. ret = mongo.db.user_info_list.find_one(query)
  33. if ret:
  34. mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}})
  35. else:
  36. mongo.db.user_info_list.insert_one({
  37. "_id": data["uid"],
  38. "sid": request.sid,
  39. })
  40.  
  41. # 返回种植园的相关配置参数
  42. orchard_settings = {}
  43. setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all()
  44. """
  45. 现在的格式:
  46. [<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>]
  47. 需要返回的格式:
  48. {
  49. package_number_base:4,
  50. package_number_max: 32,
  51. ...
  52. }
  53. """
  54. for item in setting_list:
  55. orchard_settings[item.name] = item.value
  56.  
  57. # 返回当前用户相关的配置参数
  58. user_settings = {}
  59. # 从mongo中查找用户信息,判断用户是否激活了背包格子
  60. user_dict = mongo.db.user_info_list.find_one({"sid":request.sid})
  61. # 背包格子
  62. if user_dict.get("package_number") is None:
  63. user_settings["package_number"] = orchard_settings.get("package_number_base",4)
  64. mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}})
  65. else:
  66. user_settings["package_number"] = user_dict.get("package_number")
  67.  
  68. """种植园植物信息"""
  69. # 总树桩数量
  70. setting = Setting.query.filter(Setting.name == "user_total_tree").first()
  71. if setting is None:
  72. tree_total = 9
  73. else:
  74. tree_total = int(setting.value)
  75.  
  76. # 用户已经激活的树桩
  77. setting = Setting.query.filter(Setting.name == "user_active_tree").first()
  78. if setting is None:
  79. user_tree_number = 3
  80. else:
  81. user_tree_number = int(setting.value)
  82. user_tree_number = user_dict.get("user_tree_number",user_tree_number)
  83.  
  84. # 种植的植物列表
  85. user_tree_list = user_dict.get("user_tree_list", [])
  86. key = 0
  87. for tree_item in user_tree_list:
  88. tree_item["status"] = "tree_status_%s" % tree_item["status"] # 植物状态
  89. tree_item["water_time"] = redis.ttl("user_tree_water_%s_%s" % (data["uid"],key))
  90. tree_item["growup_time"] = redis.ttl("user_tree_growup_%s_%s" % (data["uid"],key))
  91. key+=1
  92. # 植物状态信息
  93. status_list = [
  94. "tree_status_0",
  95. "tree_status_1",
  96. "tree_status_2",
  97. "tree_status_3",
  98. "tree_status_4",
  99. ]
  100. setting_list = Setting.query.filter(Setting.name.in_(status_list)).all()
  101. tree_status = {}
  102. for item in setting_list:
  103. tree_status[item.name] = item.value
  104. message = {
  105. "errno":status.CODE_OK,
  106. "errmsg":errmsg.ok,
  107. "orchard_settings":orchard_settings,
  108. "user_settings":user_settings,
  109. "tree_total":tree_total,
  110. "user_tree_number":user_tree_number,
  111. "user_tree_list":user_tree_list,
  112. "tree_status":tree_status,
  113. }
  114. socketio.emit("login_response", message, namespace="/mofang", room=room)
  115.  
  116. @socketio.on("user_buy_prop", namespace="/mofang")
  117. def user_buy_prop(data):
  118. """用户购买道具"""
  119. room = request.sid
  120. # 从mongo中获取当前用户信息
  121. user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
  122. user = User.query.get(user_info.get("_id"))
  123. if user is None:
  124. socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
  125. return
  126.  
  127. # 判断背包物品存储是否达到上限
  128. use_package_number = int(user_info.get("use_package_number",0)) # 当前诗经使用的格子数量
  129. package_number = int(user_info.get("package_number",0)) # 当前用户已经解锁的格子数量
  130. # 本次购买道具需要使用的格子数量
  131. setting = Setting.query.filter(Setting.name == "td_prop_max").first()
  132. if setting is None:
  133. td_prop_max = 10
  134. else:
  135. td_prop_max = int(setting.value)
  136.  
  137. # 计算购买道具以后需要额外占用的格子数量
  138. if ("prop_%s" % data["pid"]) in user_info.get("prop_list",{}):
  139. """曾经购买过当前道具"""
  140. prop_num = int( user_info.get("prop_list")["prop_%s" % data["pid"]]) # 购买前的道具数量
  141. new_prop_num = prop_num+int(data["num"]) # 如果成功购买道具以后的数量
  142. old_td_num = prop_num // td_prop_max
  143. if prop_num % td_prop_max > 0:
  144. old_td_num+=1
  145. new_td_num = new_prop_num // td_prop_max
  146. if new_prop_num % td_prop_max > 0:
  147. new_td_num+=1
  148. td_num = new_td_num - old_td_num
  149. else:
  150. """新增购买的道具"""
  151. # 计算本次购买道具需要占用的格子数量
  152.  
  153. if int(data["num"]) > td_prop_max:
  154. """需要多个格子"""
  155. td_num = int(data["num"]) // td_prop_max
  156. if int(data["num"]) % td_prop_max > 0:
  157. td_num+=1
  158. else:
  159. """需要一个格子"""
  160. td_num = 1
  161.  
  162. if use_package_number+td_num > package_number:
  163. """超出存储上限"""
  164. socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_PACKAGE, "errmsg": errmsg.no_package},
  165. namespace="/mofang", room=room)
  166. return
  167.  
  168. # 从mysql中获取商品价格
  169. prop = Goods.query.get(data["pid"])
  170. if user.money > 0: # 当前商品需要通过RMB购买
  171. if float(user.money) < float(prop.price) * int(data["num"]):
  172. socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room)
  173. return
  174. else:
  175. """当前通过果子进行购买"""
  176. if int(user.credit) < int(prop.credit) * int(data["num"]):
  177. socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
  178. namespace="/mofang", room=room)
  179. return
  180.  
  181. # 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额
  182. query = {"sid": request.sid}
  183. if user_info.get("prop_list") is None:
  184. """此前没有购买任何道具"""
  185. message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}}
  186. mongo.db.user_info_list.update_one(query,message)
  187. else:
  188. """此前有购买了道具"""
  189. prop_list = user_info.get("prop_list") # 道具列表
  190. if ("prop_%s" % prop.id) in prop_list:
  191. """如果再次同一款道具"""
  192. prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"])
  193. else:
  194. """此前没有购买过这种道具"""
  195. prop_list[("prop_%s" % prop.id)] = int(data["num"])
  196.  
  197. mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}})
  198.  
  199. # 扣除余额或果子
  200. if prop.price > 0:
  201. user.money = float(user.money) - float(prop.price) * int(data["num"])
  202. else:
  203. user.credit = int(user.credit) - int(prop.credit) * int(data["num"])
  204.  
  205. db.session.commit()
  206.  
  207. # 返回购买成功的信息
  208. socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room)
  209. # 返回最新的用户道具列表
  210. user_prop()
  211.  
  212. @socketio.on("user_prop", namespace="/mofang")
  213. def user_prop():
  214. """用户道具"""
  215. userinfo = mongo.db.user_info_list.find_one({"sid":request.sid})
  216. prop_list = userinfo.get("prop_list",{})
  217. prop_id_list = []
  218. for prop_str,num in prop_list.items():
  219. pid = int(prop_str[5:])
  220. prop_id_list.append(pid)
  221.  
  222. data = []
  223. prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all()
  224. setting = Setting.query.filter(Setting.name == "td_prop_max").first()
  225. if setting is None:
  226. td_prop_max = 10
  227. else:
  228. td_prop_max = int(setting.value)
  229.  
  230. for prop_data in prop_list_data:
  231. num = int( prop_list[("prop_%s" % prop_data.id)])
  232. if td_prop_max > num:
  233. data.append({
  234. "num": num,
  235. "image": prop_data.image,
  236. "pid": prop_data.id,
  237. "pty": prop_data.prop_type,
  238. })
  239. else:
  240. padding_time = num // td_prop_max
  241. padding_last = num % td_prop_max
  242. arr = [{
  243. "num": td_prop_max,
  244. "image": prop_data.image,
  245. "pid": prop_data.id,
  246. "pty": prop_data.prop_type,
  247. }] * padding_time
  248. if padding_last != 0:
  249. arr.append({
  250. "num": padding_last,
  251. "image": prop_data.image,
  252. "pid": prop_data.id,
  253. "pty": prop_data.prop_type,
  254. })
  255. data = data + arr
  256. mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"use_package_number":len(data)}})
  257. room = request.sid
  258. socketio.emit("user_prop_response", {
  259. "errno": status.CODE_OK,
  260. "errmsg": errmsg.ok,
  261. "data":data,
  262. }, namespace="/mofang",
  263. room=room)
  264.  
  265. @socketio.on("unlock_package", namespace="/mofang")
  266. def unlock_package():
  267. """解锁背包"""
  268. # 从mongo获取当前用户解锁的格子数量
  269. user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
  270. user = User.query.get(user_info.get("_id"))
  271. if user is None:
  272. socketio.emit("unlock_package_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
  273. return
  274.  
  275. package_number = int(user_info.get("package_number"))
  276. num = 7 - (32 - package_number) // 4 # 没有解锁的格子
  277.  
  278. # 从数据库中获取解锁背包的价格
  279. setting = Setting.query.filter(Setting.name == "package_unlock_price_%s" % num).first()
  280. if setting is None:
  281. unlock_price = 0
  282. else:
  283. unlock_price = int(setting.value)
  284.  
  285. # 判断是否有足够的积分或者价格
  286. room = request.sid
  287. if user.money < unlock_price:
  288. socketio.emit("unlock_package_response", {"errno": status.CODE_NO_MONEY, "errmsg": errmsg.money_no_enough},
  289. namespace="/mofang", room=room)
  290. return
  291.  
  292. # 解锁成功
  293. user.money = float(user.money) - float(unlock_price)
  294. db.session.commit()
  295.  
  296. # mongo中调整数量
  297. mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": package_number+1}})
  298. # 返回解锁的结果
  299. socketio.emit("unlock_package_response", {
  300. "errno": status.CODE_OK,
  301. "errmsg": errmsg.ok},
  302. namespace="/mofang", room=room)
  303. import math
  304. from application import redis
  305. @socketio.on("pet_show", namespace="/mofang")
  306. def pet_show():
  307. """显示宠物"""
  308. room = request.sid
  309. user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
  310. user = User.query.get(user_info.get("_id"))
  311. if user is None:
  312. socketio.emit("pet_show_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
  313. return
  314.  
  315. # 获取宠物列表
  316. pet_list = user_info.get("pet_list", [])
  317.  
  318. """
  319. pet_list: [
  320. {
  321. "pid":11,
  322. "image":"pet.png",
  323. "hp":100%,
  324. "created_time":xxxx-xx-xx xx:xx:xx,
  325. "skill":"70%",
  326. "has_time":30天
  327. },
  328. ]
  329. """
  330. # 从redis中提取当前宠物的饱食度和有效期
  331. for key,pet in enumerate(pet_list):
  332. pet["hp"] = math.ceil( redis.ttl("pet_%s_%s_hp" % (user.id,key+1)) / 86400 * 100 )
  333. pet["has_time"] = redis.ttl("pet_%s_%s_expire" % (user.id,key+1))
  334.  
  335. pet_number = user_info.get("pet_number", 1)
  336. socketio.emit(
  337. "pet_show_response",
  338. {
  339. "errno": status.CODE_OK,
  340. "errmsg": errmsg.ok,
  341. "pet_list": pet_list,
  342. "pet_number": pet_number,
  343. },
  344. namespace="/mofang",
  345. room=room
  346. )
  347.  
  348. from datetime import datetime
  349. @socketio.on("use_prop", namespace="/mofang")
  350. def use_prop(pid):
  351. """使用道具"""
  352. room = request.sid
  353. # 获取mongo中的用户信息
  354. user_info = mongo.db.user_info_list.find_one({"sid": request.sid})
  355. # 获取mysql中的用户信息
  356. user = User.query.get(user_info.get("_id"))
  357. if user is None:
  358. socketio.emit("pet_use_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists},
  359. namespace="/mofang", room=room)
  360. return
  361.  
  362. # 获取道具
  363. prop_data = Goods.query.get(pid)
  364. if prop_data is None:
  365. socketio.emit("pet_use_response", {"errno":status.CODE_NO_SUCH_PROP,"errmsg":errmsg.not_such_prop}, namespace="/mofang", room=room)
  366. return
  367.  
  368. if int(prop_data.prop_type) == 0:
  369. """使用植物道具"""
  370. # 1. 判断当前的植物数量是否有空余
  371. tree_list = user_info.get("user_tree_list", [])
  372.  
  373. # 当前用户最多可种植的数量
  374. setting = Setting.query.filter(Setting.name == "user_active_tree").first()
  375. if setting is None:
  376. user_tree_number = 3
  377. else:
  378. user_tree_number = int(setting.value)
  379. user_tree_number = user_info.get("user_tree_number", user_tree_number)
  380.  
  381. if len(tree_list) >= user_tree_number:
  382. socketio.emit("prop_use_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty},
  383. namespace="/mofang", room=room)
  384. return
  385.  
  386. # 使用道具
  387. mongo.db.user_info_list.update_one({"sid":room},{"$push":{"user_tree_list":
  388. { # 植物状态
  389. "time": int(datetime.now().timestamp()), # 种植时间
  390. "status": 2, # 植物状态,2表示幼苗状态
  391. "water_time": 0, # 浇水次数
  392. }
  393. }})
  394.  
  395. # 从种下去到浇水的时间
  396. pipe = redis.pipeline()
  397. pipe.multi()
  398. setting = Setting.query.filter(Setting.name == "tree_water_time").first()
  399. if setting is None:
  400. tree_water_time = 3600
  401. else:
  402. tree_water_time = int(setting.value)
  403. # 必须等时间到了才可以浇水
  404. pipe.setex("user_tree_water_%s_%s" % (user.id,len(tree_list)),int(tree_water_time),"_")
  405. # 必须等时间到了才可以到成长期
  406. setting = Setting.query.filter(Setting.name == "tree_growup_time").first()
  407. if setting is None:
  408. tree_growup_time = 3600
  409. else:
  410. tree_growup_time = int(setting.value)
  411. pipe.setex("user_tree_growup_%s_%s" % (user.id,len(tree_list)),tree_growup_time, "_")
  412. pipe.execute()
  413.  
  414. user_login({"uid": user.id})
  415.  
  416. if int(prop_data.prop_type) == 1:
  417. """使用宠物道具"""
  418. # 1. 判断当前的宠物数量
  419. # 获取宠物列表
  420. pet_list = user_info.get("pet_list", [])
  421. if len(pet_list) > 1:
  422. socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
  423. return
  424.  
  425. # 2. 是否有空余的宠物栏位
  426. pet_number = user_info.get("pet_number",1)
  427. if pet_number <= len(pet_list):
  428. socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
  429. return
  430.  
  431. # 3. 初始化当前宠物信息
  432. # 获取有效期和防御值
  433. exp_data = Setting.query.filter(Setting.name=="pet_expire_%s" % pid).first()
  434. ski_data = Setting.query.filter(Setting.name=="pet_skill_%s" % pid).first()
  435.  
  436. if exp_data is None:
  437. # 默认7天有效期
  438. expire = 7
  439. else:
  440. expire = exp_data.value
  441.  
  442. if ski_data is None:
  443. skill = 10
  444. else:
  445. skill = ski_data.value
  446.  
  447. # 在redis中设置当前宠物的饱食度
  448. pipe = redis.pipeline()
  449. pipe.multi()
  450. pipe.setex("pet_%s_%s_hp" % (user.id, len(pet_list)+1), 24*60*60, "_")
  451. pipe.setex("pet_%s_%s_expire" % (user.id, len(pet_list)+1), int(expire)*24*60*60, "_")
  452. pipe.execute()
  453.  
  454. # 基本保存到mongo
  455. mongo.db.user_info_list.update_one({"sid":request.sid},{"$push":{"pet_list":{
  456. "pid": pid,
  457. "image": prop_data.image,
  458. "created_time": int( datetime.now().timestamp() ),
  459. "skill": skill,
  460. }}})
  461.  
  462. """
  463. db.user_info_list.updateOne({"_id":"52"},{"$push":{"pet_list":{
  464. "pid": 2,
  465. "image": "pet1.png",
  466. "created_time": 1609727155,
  467. "skill": 30,
  468. }}})
  469. """
  470.  
  471. pet_show()
  472.  
  473. # 扣除背包中的道具数量
  474. prop_list = user_info.get("prop_list",{})
  475. for key,value in prop_list.items():
  476. if key == ("prop_%s" % pid):
  477. if int(value) > 1:
  478. prop_list[key] = int(value) - 1
  479. else:
  480. prop_list.pop(key)
  481. break
  482.  
  483. mongo.db.user_info_list.update_one({"sid":room},{"$set":{"prop_list":prop_list}})
  484. user_prop()
  485.  
  486. socketio.emit("prop_use_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
  487. namespace="/mofang", room=room)

服务端调整使用道具后端接口&种植园登录接口

3.解锁树桩

1.在Setting表中添加解锁树桩的费用

  1. INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (28, 'active_tree_price', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '激活树桩的递增果子费用', '1000');

2.后端提供解锁树桩的接口

  1. @socketio.on("active_tree", namespace="/mofang")
  2. def active_tree():
  3. """激活树桩"""
  4. room = request.sid
  5. # 获取mongo中的用户信息
  6. user_info = mongo.db.user_info_list.find_one({"sid": request.sid})
  7. # 获取mysql中的用户信息
  8. user = User.query.get(user_info.get("_id"))
  9. if user is None:
  10. socketio.emit("active_tree_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists},
  11. namespace="/mofang", room=room)
  12. return
  13.  
  14. # 判断树桩是否达到上限
  15. tree_number_data = Setting.query.filter(Setting.name == "user_active_tree").first()
  16. total_tree_data = Setting.query.filter(Setting.name == "user_total_tree").first()
  17. if tree_number_data is None:
  18. tree_number = 1
  19. else:
  20. tree_number = tree_number_data.value
  21.  
  22. if total_tree_data is None:
  23. total_tree = 9
  24. else:
  25. total_tree = int(total_tree_data.value)
  26.  
  27. user_tree_number = int(user_info.get("user_tree_number",tree_number))
  28. if user_tree_number >= total_tree:
  29. socketio.emit("active_tree_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty},
  30. namespace="/mofang", room=room)
  31. return
  32.  
  33. # 扣除激活的果子数量
  34. ret = Setting.query.filter(Setting.name == "active_tree_price").first()
  35. if ret is None:
  36. active_tree_price = 100 * user_tree_number
  37. else:
  38. active_tree_price = int(ret.value) * user_tree_number
  39.  
  40. if active_tree_price > int(user.credit):
  41. socketio.emit("active_tree_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
  42. namespace="/mofang", room=room)
  43. return
  44.  
  45. user.credit = int(user.credit) - active_tree_price
  46. db.session.commit()
  47.  
  48. mongo.db.user_info_list.update_one({"sid":room},{"$set":{"user_tree_number":user_tree_number+1}})
  49.  
  50. socketio.emit("active_tree_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
  51. namespace="/mofang", room=room)
  52. return

3.前端发起请求解锁树桩

1.my_orchard.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>用户中心</title>
  5. <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  6. <meta charset="utf-8">
  7. <link rel="stylesheet" href="../static/css/main.css">
  8. <script src="../static/js/vue.js"></script>
  9. <script src="../static/js/axios.js"></script>
  10. <script src="../static/js/main.js"></script>
  11. <script src="../static/js/uuid.js"></script>
  12. <script src="../static/js/settings.js"></script>
  13. <script src="../static/js/socket.io.js"></script>
  14. </head>
  15. <body>
  16. <div class="app orchard orchard-frame" id="app">
  17. <div class="background">
  18. <img class="grassland2" src="../static/images/grassland2.png" alt="">
  19. <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
  20. <img class="stake1" src="../static/images/stake1.png" alt="">
  21. <img class="stake2" src="../static/images/stake2.png" alt="">
  22. </div>
  23. <div class="pet-box">
  24. <div class="pet">
  25. <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
  26. </div>
  27. <div class="pet" v-if="pet_number > 1">
  28. <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt="">
  29. </div>
  30. <div class="pet turned_off" v-if="pet_number==1">
  31. <img class="turned_image" src="../static/images/turned_off.png" alt="">
  32. <p>请购买宠物</p>
  33. </div>
  34. </div>
  35. <div class="tree-list">
  36. <div class="tree-box">
  37. <div class="tree" v-for="tree in user_tree_data.user_tree_list">
  38. <img :src="tree_img(tree.status)" alt="">
  39. </div>
  40. <!-- 已激活但是未种植的树桩列表 -->
  41. <div class="tree" v-for="i in active_tree">
  42. <img src="../static/images/tree1.png" alt="">
  43. </div>
  44. <!-- 未激活树桩列表 -->
  45. <div @click="unlock_tree" class="tree" v-for="i in lock_tree">
  46. <img src="../static/images/tree0.png" alt="">
  47. </div>
  48. </div>
  49.  
  50. </div>
  51. <div class="prop-list">
  52. <div class="prop">
  53. <img src="../static/images/prop1.png" alt="">
  54. <span>1</span>
  55. <p>化肥</p>
  56. </div>
  57. <div class="prop">
  58. <img src="../static/images/prop2.png" alt="">
  59. <span>0</span>
  60. <p>修剪</p>
  61. </div>
  62. <div class="prop">
  63. <img src="../static/images/prop3.png" alt="">
  64. <span>1</span>
  65. <p>浇水</p>
  66. </div>
  67. <div class="prop">
  68. <img src="../static/images/prop4.png" alt="">
  69. <span>1</span>
  70. <p>宠物粮</p>
  71. </div>
  72. </div>
  73. <div class="pet-hp-list">
  74. <div class="pet-hp" v-for="pet in pet_list">
  75. <p>宠物1 饱食度</p>
  76. <div class="hp">
  77. <div :style="{width: pet.hp+'%'}" class="process">{{pet.hp}}%</div>
  78. </div>
  79. </div>
  80. </div>
  81. </div>
  82. <script>
  83. apiready = function(){
  84. init();
  85. new Vue({
  86. el:"#app",
  87. data(){
  88. return {
  89. namespace: '/mofang',
  90. token:"",
  91. socket: null,
  92. pet_list:[],
  93. user_tree_data:{
  94. "total_tree": 9, // 总树桩数量
  95. "user_tree_number": 0, // 当前用户激活树桩数量
  96. "user_tree_list":[ // 当前种植的树桩列表状态
  97.  
  98. ],
  99. },
  100. tree_status:{
  101.  
  102. },
  103. // user_tree_data:{
  104. // "total_tree":9, // 总树桩数量
  105. // "user_tree_number": 5, // 当前用户激活树桩数量
  106. // "user_tree_list":[ // 当前种植的树桩列表状态
  107. // { // 树桩状态
  108. // "time":1609808084, // 种植时间
  109. // "status":4, // 植物状态
  110. // "has_time": 300, // 状态时间
  111. // },
  112. // ],
  113. // },
  114. // tree_status:{
  115. // "tree_status_0": "tree0.png", // 树桩
  116. // "tree_status_1": "tree1.png", // 空桩
  117. // "tree_status_2": "tree2.png", // 幼苗
  118. // "tree_status_3": "tree3.png", // 成长
  119. // "tree_status_4": "tree4.png", // 成熟
  120. // },
  121. pet_number:[],
  122. timeout: 0,
  123. prev:{name:"",url:"",params:{}},
  124. current:{name:"orchard",url:"orchard.html",params:{}},
  125. }
  126. },
  127. computed:{
  128. // 已激活但是未使用的树桩
  129. active_tree(){
  130. return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
  131. },
  132. // 未激活的剩余树桩
  133. lock_tree(){
  134. return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
  135. },
  136. },
  137. created(){
  138. this.show_pet_list();
  139. this.show_tree_list();
  140. },
  141. methods:{
  142. tree_img(status){
  143. return '../static/images/'+this.tree_status[status];
  144. },
  145. show_pet_list(){
  146. api.addEventListener({
  147. name: 'pet_show_success'
  148. }, (ret, err)=>{
  149. if( ret ){
  150. // 用户购买道具
  151. this.pet_list = this.game.get("pet_list");
  152. this.pet_number = parseInt(this.game.get("pet_number"));
  153. }
  154. });
  155. },
  156. show_tree_list(){
  157. api.addEventListener({
  158. name: 'user_tree_data'
  159. }, (ret, err)=>{
  160. if( ret ){
  161. // 用户种植植物信息
  162. this.user_tree_data.tree_total = parseInt(this.game.get("tree_total"));
  163. this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number"));
  164. this.user_tree_data.user_tree_list = this.game.get("user_tree_list");
  165. this.tree_status = this.game.get("tree_status");
  166. }
  167. });
  168. },
  169. unlock_tree(){
  170. // 激活树桩通知
  171. api.confirm({
  172. title: '提示',
  173. msg: '是否激活树桩?',
  174. buttons: ['确定', '取消']
  175. }, (ret, err)=>{
  176. if( ret.buttonIndex == 1 ){
  177. api.sendEvent({
  178. name: 'active_tree',
  179. extra: {
  180.  
  181. }
  182. });
  183. }
  184. });
  185.  
  186. // 激活成功!
  187. api.addEventListener({
  188. name: 'active_tree_success'
  189. }, (ret, err)=>{
  190. if( ret ){
  191. // 新增树桩的数量
  192. this.user_tree_data.user_tree_number+=1;
  193. this.game.save({"user_tree_number": this.user_tree_data.user_tree_number});
  194. }
  195. });
  196.  
  197. }
  198. }
  199. });
  200. }
  201. </script>
  202. </body>
  203. </html>

解锁树桩:my_orchard.html

2.orchard.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>用户中心</title>
  5. <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  6. <meta charset="utf-8">
  7. <link rel="stylesheet" href="../static/css/main.css">
  8. <script src="../static/js/vue.js"></script>
  9. <script src="../static/js/axios.js"></script>
  10. <script src="../static/js/main.js"></script>
  11. <script src="../static/js/uuid.js"></script>
  12. <script src="../static/js/settings.js"></script>
  13. <script src="../static/js/socket.io.js"></script>
  14. </head>
  15. <body>
  16. <div class="app orchard" id="app">
  17. <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
  18. <div class="orchard-bg">
  19. <img src="../static/images/bg2.png">
  20. <img class="board_bg2" src="../static/images/board_bg2.png">
  21. </div>
  22. <img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
  23. <div class="header">
  24. <div class="info" @click="go_home">
  25. <div class="avatar">
  26. <img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
  27. <img class="user_avatar" src="../static/images/avatar.png" alt="">
  28. <img class="avatar_border" src="../static/images/avatar_border.png" alt="">
  29. </div>
  30. <p class="user_name">好听的昵称</p>
  31. </div>
  32. <div class="wallet">
  33. <div class="balance" @click="user_recharge">
  34. <p class="title"><img src="../static/images/money.png" alt="">钱包</p>
  35. <p class="num">{{money}}</p>
  36. </div>
  37. <div class="balance">
  38. <p class="title"><img src="../static/images/integral.png" alt="">果子</p>
  39. <p class="num">99,999.00</p>
  40. </div>
  41. </div>
  42. <div class="menu-list">
  43. <div class="menu">
  44. <img src="../static/images/menu1.png" alt="">
  45. 排行榜
  46. </div>
  47. <div class="menu">
  48. <img src="../static/images/menu2.png" alt="">
  49. 签到有礼
  50. </div>
  51. <div class="menu" @click="go_orchard_shop">
  52. <img src="../static/images/menu3.png" alt="">
  53. 道具商城
  54. </div>
  55. <div class="menu">
  56. <img src="../static/images/menu4.png" alt="">
  57. 邮件中心
  58. </div>
  59. </div>
  60. </div>
  61. <div class="footer" >
  62. <ul class="menu-list">
  63. <li class="menu">新手</li>
  64. <li class="menu" @click="go_my_package">背包</li>
  65. <li class="menu-center" @click="go_orchard_shop">商店</li>
  66. <li class="menu">消息</li>
  67. <li class="menu">好友</li>
  68. </ul>
  69. </div>
  70. </div>
  71. <script>
  72. apiready = function(){
  73. init();
  74. new Vue({
  75. el:"#app",
  76. data(){
  77. return {
  78. music_play:true,
  79. namespace: '/mofang',
  80. token:"",
  81. money:"",
  82. settings_info:{
  83. orchard: {}, // 种植园公共参数
  84. user:{}, // 用户私有相关参数
  85. },
  86. socket: null,
  87. recharge_list: ['10','20','50','100','200','500','1000'],
  88. timeout: 0,
  89. prev:{name:"",url:"",params:{}},
  90. current:{name:"orchard",url:"orchard.html",params:{}},
  91. }
  92. },
  93. beforeCreate(){
  94. this.game.goFrame("orchard","my_orchard.html", this.current,{
  95. x: 0,
  96. y: 180,
  97. w: 'auto',
  98. h: 410,
  99. },null);
  100. },
  101. created(){
  102. this.checkout();
  103. this.money = this.game.fget("money");
  104. },
  105. methods:{
  106. user_recharge(){
  107. // 发起充值请求
  108. api.actionSheet({
  109. title: '余额充值',
  110. cancelTitle: '取消',
  111. buttons: this.recharge_list
  112. }, (ret, err)=>{
  113. if( ret ){
  114. if(ret.buttonIndex <= this.recharge_list.length){
  115. // 充值金额
  116. money = this.recharge_list[ret.buttonIndex-1];
  117. // 调用支付宝充值
  118. this.create_recharge(money);
  119. }
  120. }else{
  121.  
  122. }
  123. });
  124.  
  125. },
  126. create_recharge(money){
  127. // 获取历史信息记录
  128. var token = this.game.get("access_token") || this.game.fget("access_token");
  129. this.game.checkout(this, token, (new_access_token)=>{
  130. this.axios.post("",{
  131. "jsonrpc": "2.0",
  132. "id": this.uuid(),
  133. "method": "Recharge.create",
  134. "params": {
  135. "money": money,
  136. }
  137. },{
  138. headers:{
  139. Authorization: "jwt " + token,
  140. }
  141. }).then(response=>{
  142. if(parseInt(response.data.result.errno)==1000){
  143. // 前往支付宝
  144. var aliPayPlus = api.require('aliPayPlus');
  145. aliPayPlus.payOrder({
  146. orderInfo: response.data.result.order_string,
  147. sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
  148. }, (ret, err)=>{
  149. pay_result = {
  150. 9000:"支付成功",
  151. 8000:"正在处理中",
  152. 4000:"订单支付失败",
  153. 5000:"重复请求",
  154. 6001:"取消支付",
  155. 6002:"网络连接出错",
  156. 6004:"支付结果未知",
  157. }
  158. api.alert({
  159. title: '支付结果',
  160. msg: pay_result[ret.code],
  161. buttons: ['确定']
  162. });
  163. // 通知服务端, 修改充值结果
  164. this.return_recharge(response.data.result.order_number,token);
  165. });
  166. }else{
  167. this.game.print(response.data);
  168. }
  169. }).catch(error=>{
  170. // 网络等异常
  171. this.game.print(error);
  172. });
  173. })
  174. },
  175. return_recharge(out_trade_number,token){
  176. this.axios.post("",{
  177. "jsonrpc": "2.0",
  178. "id": this.uuid(),
  179. "method": "Recharge.return",
  180. "params": {
  181. "out_trade_number": out_trade_number,
  182. }
  183. },{
  184. headers:{
  185. Authorization: "jwt " + token,
  186. }
  187. }).then(response=>{
  188. if(parseInt(response.data.result.errno)==1000){
  189. this.money = response.data.result.money.toFixed(2);
  190. }
  191. })
  192. },
  193. checkout(){
  194. var token = this.game.get("access_token") || this.game.fget("access_token");
  195. this.game.checkout(this,token,(new_access_token)=>{
  196. this.connect();
  197. this.login();
  198. this.user_package();
  199. this.buy_prop();
  200. this.unlock_package_number();
  201. this.show_pet();
  202. this.use_prop();
  203. this.active_tree();
  204. });
  205. },
  206. connect(){
  207. // socket连接
  208. this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
  209. this.socket.on('connect', ()=>{
  210. this.game.print("开始连接服务端");
  211. var id = this.game.fget("id");
  212. this.socket.emit("login",{"uid":id});
  213. this.socket.emit("user_prop");
  214. });
  215. },
  216. login(){
  217. this.socket.on("login_response",(message)=>{
  218. this.settings_info.orchard = message.orchard_settings;
  219. this.settings_info.user=message.user_settings;
  220. this.game.fsave({
  221. "orchard_settings":message.orchard_settings,
  222. "user_settings":message.user_settings,
  223. });
  224. this.game.save({
  225. "tree_total":message.tree_total,
  226. "user_tree_number":message.user_tree_number,
  227. "user_tree_list":message.user_tree_list,
  228. "tree_status":message.tree_status,
  229. });
  230. setTimeout(()=>{
  231. // 通知种植园页面获取到了当前用户的种植信息
  232. api.sendEvent({
  233. name: 'user_tree_data',
  234. extra: {}
  235. });
  236. },500);
  237. });
  238. },
  239. user_package(){
  240. // 用户背包道具列表
  241. this.socket.on("user_prop_response",(message)=>{
  242. this.game.fsave({
  243. "user_package":message.data,
  244. })
  245. })
  246. },
  247. go_index(){
  248. this.game.goWin("root");
  249. },
  250. go_friends(){
  251. this.game.goFrame("friends","friends.html",this.current);
  252. this.game.goFrame("friend_list","friend_list.html",this.current,{
  253. x: 0,
  254. y: 190,
  255. w: 'auto',
  256. h: 'auto',
  257. },null,true);
  258. },
  259. go_home(){
  260. this.game.goWin("user","user.html", this.current);
  261. },
  262. go_orchard_shop(){
  263. // 种植园商店
  264. this.game.goFrame("orchard_shop","shop.html", this.current,null,{
  265. type:"push",
  266. subType:"from_top",
  267. duration:300
  268. });
  269. },
  270. go_my_package(){
  271. // 我的背包
  272. this.game.goFrame("package","package.html", this.current,null,{
  273. type:"push",
  274. subType:"from_top",
  275. duration:300
  276. });
  277. },
  278. buy_prop(){
  279. api.addEventListener({
  280. name: 'buy_prop'
  281. }, (ret, err)=>{
  282. if( ret ){
  283. // 用户购买道具
  284. this.socket.emit("user_buy_prop",ret.value);
  285. }
  286. });
  287. this.socket.on("user_buy_prop_response",(message)=>{
  288. alert(message.errmsg);
  289. })
  290. },
  291. use_prop(){
  292. // 使用道具
  293. var pid = 0;
  294. api.addEventListener({
  295. name: 'use_prop'
  296. }, (ret, err)=>{
  297. if( ret ){
  298. // 用户使用道具
  299. pid = ret.value.pid;
  300. this.socket.emit("use_prop",ret.value.pid);
  301. }
  302. });
  303.  
  304. this.socket.on("prop_use_response",(message)=>{
  305. if(parseInt(message.errno) === 1000){
  306. api.sendEvent({
  307. name: 'prop_use_success',
  308. extra: {
  309. pid: pid
  310. }
  311. });
  312. }else{
  313. api.alert({
  314. title: '提示',
  315. msg: message.errmsg,
  316. });
  317.  
  318. }
  319. });
  320. },
  321. unlock_package_number(){
  322. api.addEventListener({
  323. name: 'unlock_package_number'
  324. }, (ret, err)=>{
  325. if( ret ){
  326. // 用户购买道具
  327. this.socket.emit("unlock_package");
  328. }
  329. });
  330.  
  331. this.socket.on("unlock_package_response",(message)=>{
  332. if(parseInt(message.errno) === 1000){
  333. api.sendEvent({
  334. name: 'unlock_package_success',
  335. extra: {
  336. }
  337. });
  338. }else{
  339. api.alert({
  340. title: '提示',
  341. msg: message.errmsg,
  342. });
  343.  
  344. }
  345.  
  346. })
  347. },
  348. show_pet(){
  349. // 显示宠物
  350. this.socket.emit("pet_show");
  351. this.socket.on("pet_show_response",(message)=>{
  352. if(parseInt(message.errno) === 1000){
  353. // 把宠物信息保存到本地
  354. this.game.save({"pet_list":message.pet_list})
  355. this.game.save({"pet_number":message.pet_number})
  356. setTimeout(()=>{
  357. api.sendEvent({
  358. name: 'pet_show_success',
  359. extra: {}
  360. });
  361. },500);
  362. }else{
  363. api.alert({
  364. title: '提示',
  365. msg: message.errmsg,
  366. });
  367. }
  368.  
  369. })
  370. },
  371. active_tree(){
  372. // 激活树桩
  373. api.addEventListener({
  374. name: 'active_tree'
  375. }, (ret, err)=>{
  376. if( ret ){
  377. this.socket.emit("active_tree");
  378. }
  379. });
  380.  
  381. this.socket.on("active_tree_response",(message)=>{
  382. if(parseInt(message.errno) === 1000){
  383. // 更新数据到本地
  384. api.sendEvent({
  385. name: 'active_tree_success',
  386. extra: {}
  387. });
  388. }else{
  389. api.alert({
  390. title: '提示',
  391. msg: message.errmsg,
  392. });
  393. }
  394.  
  395. })
  396. }
  397. }
  398. });
  399. }
  400. </script>
  401. </body>
  402. </html>

解锁树桩:orchard.html

4.化肥/修剪/浇水/宠物粮小图标显示

1.显示化肥和宠物粮的道具数量

1.后端返回道具数量

服务端提供化肥和宠物粮的道具数量

  1. from application import socketio
  2. from flask import request
  3. from application.apps.users.models import User
  4. from flask_socketio import join_room, leave_room
  5. from application import mongo
  6. from .models import Goods,Setting,db
  7. from status import APIStatus as status
  8. from message import ErrorMessage as errmsg
  9. # 建立socket通信
  10. # @socketio.on("connect", namespace="/mofang")
  11. # def user_connect():
  12. # """用户连接"""
  13. # print("用户%s连接过来了!" % request.sid)
  14. # # 主动响应数据给客户端
  15. # socketio.emit("server_response","hello",namespace="/mofang")
  16.  
  17. # 断开socket通信
  18. @socketio.on("disconnect", namespace="/mofang")
  19. def user_disconnect():
  20. print("用户%s退出了种植园" % request.sid )
  21.  
  22. @socketio.on("login", namespace="/mofang")
  23. def user_login(data):
  24. # 分配房间
  25. room = data["uid"]
  26. join_room(room)
  27. # 保存当前用户和sid的绑定关系
  28. # 判断当前用户是否在mongo中有记录
  29. query = {
  30. "_id": data["uid"]
  31. }
  32. ret = mongo.db.user_info_list.find_one(query)
  33. if ret:
  34. mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}})
  35. else:
  36. mongo.db.user_info_list.insert_one({
  37. "_id": data["uid"],
  38. "sid": request.sid,
  39. })
  40.  
  41. # 返回种植园的相关配置参数
  42. orchard_settings = {}
  43. setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all()
  44. """
  45. 现在的格式:
  46. [<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>]
  47. 需要返回的格式:
  48. {
  49. package_number_base:4,
  50. package_number_max: 32,
  51. ...
  52. }
  53. """
  54. for item in setting_list:
  55. orchard_settings[item.name] = item.value
  56.  
  57. # 返回当前用户相关的配置参数
  58. user_settings = {}
  59. # 从mongo中查找用户信息,判断用户是否激活了背包格子
  60. user_dict = mongo.db.user_info_list.find_one({"sid":request.sid})
  61. # 背包格子
  62. if user_dict.get("package_number") is None:
  63. user_settings["package_number"] = orchard_settings.get("package_number_base",4)
  64. mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}})
  65. else:
  66. user_settings["package_number"] = user_dict.get("package_number")
  67.  
  68. """种植园植物信息"""
  69. # 总树桩数量
  70. setting = Setting.query.filter(Setting.name == "user_total_tree").first()
  71. if setting is None:
  72. tree_total = 9
  73. else:
  74. tree_total = int(setting.value)
  75.  
  76. # 用户已经激活的树桩
  77. setting = Setting.query.filter(Setting.name == "user_active_tree").first()
  78. if setting is None:
  79. user_tree_number = 3
  80. else:
  81. user_tree_number = int(setting.value)
  82. user_tree_number = user_dict.get("user_tree_number",user_tree_number)
  83.  
  84. # 种植的植物列表
  85. user_tree_list = user_dict.get("user_tree_list", [])
  86. key = 0
  87. for tree_item in user_tree_list:
  88. tree_item["status"] = "tree_status_%s" % tree_item["status"] # 植物状态
  89. tree_item["water_time"] = redis.ttl("user_tree_water_%s_%s" % (data["uid"],key))
  90. tree_item["growup_time"] = redis.ttl("user_tree_growup_%s_%s" % (data["uid"],key))
  91. key+=1
  92. # 植物状态信息
  93. status_list = [
  94. "tree_status_0",
  95. "tree_status_1",
  96. "tree_status_2",
  97. "tree_status_3",
  98. "tree_status_4",
  99. ]
  100. setting_list = Setting.query.filter(Setting.name.in_(status_list)).all()
  101. tree_status = {}
  102. for item in setting_list:
  103. tree_status[item.name] = item.value
  104.  
  105. """显示植物相关道具"""
  106. fertilizer_num,pet_food_num = get_orchard_prop_list(user_dict)
  107.  
  108. message = {
  109. "errno":status.CODE_OK,
  110. "errmsg":errmsg.ok,
  111. "orchard_settings":orchard_settings,
  112. "user_settings":user_settings,
  113. "tree_total":tree_total,
  114. "user_tree_number":user_tree_number,
  115. "user_tree_list":user_tree_list,
  116. "fertilizer_num":fertilizer_num,
  117. "pet_food_num":pet_food_num,
  118. "tree_status":tree_status,
  119. }
  120. socketio.emit("login_response", message, namespace="/mofang", room=room)
  121.  
  122. def get_orchard_prop_list(user_dict):
  123. fertilizer_num = 0
  124. pet_food_num = 0
  125. prop_list = user_dict.get("prop_list", {})
  126. for prop_item, num in prop_list.items():
  127. pid = prop_item.split("_")[-1]
  128. num = int(num)
  129. prop_obj = Goods.query.get(pid)
  130. if prop_obj.prop_type == 2:
  131. # 提取化肥
  132. fertilizer_num += num
  133. elif prop_obj.prop_type == 3:
  134. # 提取宠物粮
  135. pet_food_num += num
  136.  
  137. return fertilizer_num,pet_food_num
  138.  
  139. @socketio.on("user_buy_prop", namespace="/mofang")
  140. def user_buy_prop(data):
  141. """用户购买道具"""
  142. room = request.sid
  143. # 从mongo中获取当前用户信息
  144. user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
  145. user = User.query.get(user_info.get("_id"))
  146. if user is None:
  147. socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
  148. return
  149.  
  150. # 判断背包物品存储是否达到上限
  151. use_package_number = int(user_info.get("use_package_number",0)) # 当前诗经使用的格子数量
  152. package_number = int(user_info.get("package_number",0)) # 当前用户已经解锁的格子数量
  153. # 本次购买道具需要使用的格子数量
  154. setting = Setting.query.filter(Setting.name == "td_prop_max").first()
  155. if setting is None:
  156. td_prop_max = 10
  157. else:
  158. td_prop_max = int(setting.value)
  159.  
  160. # 计算购买道具以后需要额外占用的格子数量
  161. if ("prop_%s" % data["pid"]) in user_info.get("prop_list",{}):
  162. """曾经购买过当前道具"""
  163. prop_num = int( user_info.get("prop_list")["prop_%s" % data["pid"]]) # 购买前的道具数量
  164. new_prop_num = prop_num+int(data["num"]) # 如果成功购买道具以后的数量
  165. old_td_num = prop_num // td_prop_max
  166. if prop_num % td_prop_max > 0:
  167. old_td_num+=1
  168. new_td_num = new_prop_num // td_prop_max
  169. if new_prop_num % td_prop_max > 0:
  170. new_td_num+=1
  171. td_num = new_td_num - old_td_num
  172. else:
  173. """新增购买的道具"""
  174. # 计算本次购买道具需要占用的格子数量
  175.  
  176. if int(data["num"]) > td_prop_max:
  177. """需要多个格子"""
  178. td_num = int(data["num"]) // td_prop_max
  179. if int(data["num"]) % td_prop_max > 0:
  180. td_num+=1
  181. else:
  182. """需要一个格子"""
  183. td_num = 1
  184.  
  185. if use_package_number+td_num > package_number:
  186. """超出存储上限"""
  187. socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_PACKAGE, "errmsg": errmsg.no_package},
  188. namespace="/mofang", room=room)
  189. return
  190.  
  191. # 从mysql中获取商品价格
  192. prop = Goods.query.get(data["pid"])
  193. if user.money > 0: # 当前商品需要通过RMB购买
  194. if float(user.money) < float(prop.price) * int(data["num"]):
  195. socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room)
  196. return
  197. else:
  198. """当前通过果子进行购买"""
  199. if int(user.credit) < int(prop.credit) * int(data["num"]):
  200. socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
  201. namespace="/mofang", room=room)
  202. return
  203.  
  204. # 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额
  205. query = {"sid": request.sid}
  206. if user_info.get("prop_list") is None:
  207. """此前没有购买任何道具"""
  208. message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}}
  209. mongo.db.user_info_list.update_one(query,message)
  210. else:
  211. """此前有购买了道具"""
  212. prop_list = user_info.get("prop_list") # 道具列表
  213. if ("prop_%s" % prop.id) in prop_list:
  214. """如果再次同一款道具"""
  215. prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"])
  216. else:
  217. """此前没有购买过这种道具"""
  218. prop_list[("prop_%s" % prop.id)] = int(data["num"])
  219.  
  220. mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}})
  221.  
  222. # 扣除余额或果子
  223. if prop.price > 0:
  224. user.money = float(user.money) - float(prop.price) * int(data["num"])
  225. else:
  226. user.credit = int(user.credit) - int(prop.credit) * int(data["num"])
  227.  
  228. db.session.commit()
  229.  
  230. # 返回购买成功的信息
  231. socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room)
  232. # 返回最新的用户道具列表
  233. user_prop()
  234.  
  235. @socketio.on("user_prop", namespace="/mofang")
  236. def user_prop():
  237. """用户道具"""
  238. userinfo = mongo.db.user_info_list.find_one({"sid":request.sid})
  239. prop_list = userinfo.get("prop_list",{})
  240. prop_id_list = []
  241. for prop_str,num in prop_list.items():
  242. pid = int(prop_str[5:])
  243. prop_id_list.append(pid)
  244.  
  245. data = []
  246. prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all()
  247. setting = Setting.query.filter(Setting.name == "td_prop_max").first()
  248. if setting is None:
  249. td_prop_max = 10
  250. else:
  251. td_prop_max = int(setting.value)
  252.  
  253. for prop_data in prop_list_data:
  254. num = int( prop_list[("prop_%s" % prop_data.id)])
  255. if td_prop_max > num:
  256. data.append({
  257. "num": num,
  258. "image": prop_data.image,
  259. "pid": prop_data.id,
  260. "pty": prop_data.prop_type,
  261. })
  262. else:
  263. padding_time = num // td_prop_max
  264. padding_last = num % td_prop_max
  265. arr = [{
  266. "num": td_prop_max,
  267. "image": prop_data.image,
  268. "pid": prop_data.id,
  269. "pty": prop_data.prop_type,
  270. }] * padding_time
  271. if padding_last != 0:
  272. arr.append({
  273. "num": padding_last,
  274. "image": prop_data.image,
  275. "pid": prop_data.id,
  276. "pty": prop_data.prop_type,
  277. })
  278. data = data + arr
  279. mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"use_package_number":len(data)}})
  280. room = request.sid
  281. fertilizer_num,pet_food_num = get_orchard_prop_list(userinfo)
  282. socketio.emit("user_prop_response", {
  283. "errno": status.CODE_OK,
  284. "errmsg": errmsg.ok,
  285. "data":data,
  286. "fertilizer_num":fertilizer_num,
  287. "pet_food_num":pet_food_num,
  288. }, namespace="/mofang",
  289. room=room)
  290.  
  291. @socketio.on("unlock_package", namespace="/mofang")
  292. def unlock_package():
  293. """解锁背包"""
  294. # 从mongo获取当前用户解锁的格子数量
  295. user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
  296. user = User.query.get(user_info.get("_id"))
  297. if user is None:
  298. socketio.emit("unlock_package_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
  299. return
  300.  
  301. package_number = int(user_info.get("package_number"))
  302. num = 7 - (32 - package_number) // 4 # 没有解锁的格子
  303.  
  304. # 从数据库中获取解锁背包的价格
  305. setting = Setting.query.filter(Setting.name == "package_unlock_price_%s" % num).first()
  306. if setting is None:
  307. unlock_price = 0
  308. else:
  309. unlock_price = int(setting.value)
  310.  
  311. # 判断是否有足够的积分或者价格
  312. room = request.sid
  313. if user.money < unlock_price:
  314. socketio.emit("unlock_package_response", {"errno": status.CODE_NO_MONEY, "errmsg": errmsg.money_no_enough},
  315. namespace="/mofang", room=room)
  316. return
  317.  
  318. # 解锁成功
  319. user.money = float(user.money) - float(unlock_price)
  320. db.session.commit()
  321.  
  322. # mongo中调整数量
  323. mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": package_number+1}})
  324. # 返回解锁的结果
  325. socketio.emit("unlock_package_response", {
  326. "errno": status.CODE_OK,
  327. "errmsg": errmsg.ok},
  328. namespace="/mofang", room=room)
  329. import math
  330. from application import redis
  331. @socketio.on("pet_show", namespace="/mofang")
  332. def pet_show():
  333. """显示宠物"""
  334. room = request.sid
  335. user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
  336. user = User.query.get(user_info.get("_id"))
  337. if user is None:
  338. socketio.emit("pet_show_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
  339. return
  340.  
  341. # 获取宠物列表
  342. pet_list = user_info.get("pet_list", [])
  343.  
  344. """
  345. pet_list: [
  346. {
  347. "pid":11,
  348. "image":"pet.png",
  349. "hp":100%,
  350. "created_time":xxxx-xx-xx xx:xx:xx,
  351. "skill":"70%",
  352. "has_time":30天
  353. },
  354. ]
  355. """
  356. # 从redis中提取当前宠物的饱食度和有效期
  357. for key,pet in enumerate(pet_list):
  358. pet["hp"] = math.ceil( redis.ttl("pet_%s_%s_hp" % (user.id,key+1)) / 86400 * 100 )
  359. pet["has_time"] = redis.ttl("pet_%s_%s_expire" % (user.id,key+1))
  360.  
  361. pet_number = user_info.get("pet_number", 1)
  362. socketio.emit(
  363. "pet_show_response",
  364. {
  365. "errno": status.CODE_OK,
  366. "errmsg": errmsg.ok,
  367. "pet_list": pet_list,
  368. "pet_number": pet_number,
  369. },
  370. namespace="/mofang",
  371. room=room
  372. )
  373.  
  374. from datetime import datetime
  375. @socketio.on("use_prop", namespace="/mofang")
  376. def use_prop(pid):
  377. """使用道具"""
  378. room = request.sid
  379. # 获取mongo中的用户信息
  380. user_info = mongo.db.user_info_list.find_one({"sid": request.sid})
  381. # 获取mysql中的用户信息
  382. user = User.query.get(user_info.get("_id"))
  383. if user is None:
  384. socketio.emit("pet_use_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists},
  385. namespace="/mofang", room=room)
  386. return
  387.  
  388. # 获取道具
  389. prop_data = Goods.query.get(pid)
  390. if prop_data is None:
  391. socketio.emit("pet_use_response", {"errno":status.CODE_NO_SUCH_PROP,"errmsg":errmsg.not_such_prop}, namespace="/mofang", room=room)
  392. return
  393.  
  394. if int(prop_data.prop_type) == 0:
  395. """使用植物道具"""
  396. # 1. 判断当前的植物数量是否有空余
  397. tree_list = user_info.get("user_tree_list", [])
  398.  
  399. # 当前用户最多可种植的数量
  400. setting = Setting.query.filter(Setting.name == "user_active_tree").first()
  401. if setting is None:
  402. user_tree_number = 3
  403. else:
  404. user_tree_number = int(setting.value)
  405. user_tree_number = user_info.get("user_tree_number", user_tree_number)
  406.  
  407. if len(tree_list) >= user_tree_number:
  408. socketio.emit("prop_use_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty},
  409. namespace="/mofang", room=room)
  410. return
  411.  
  412. # 使用道具
  413. mongo.db.user_info_list.update_one({"sid":room},{"$push":{"user_tree_list":
  414. { # 植物状态
  415. "time": int(datetime.now().timestamp()), # 种植时间
  416. "status": 2, # 植物状态,2表示幼苗状态
  417. "water_time": 0, # 浇水次数
  418. }
  419. }})
  420.  
  421. # 从种下去到浇水的时间
  422. pipe = redis.pipeline()
  423. pipe.multi()
  424. setting = Setting.query.filter(Setting.name == "tree_water_time").first()
  425. if setting is None:
  426. tree_water_time = 3600
  427. else:
  428. tree_water_time = int(setting.value)
  429. # 必须等时间到了才可以浇水
  430. pipe.setex("user_tree_water_%s_%s" % (user.id,len(tree_list)),int(tree_water_time),"_")
  431. # 必须等时间到了才可以到成长期
  432. setting = Setting.query.filter(Setting.name == "tree_growup_time").first()
  433. if setting is None:
  434. tree_growup_time = 3600
  435. else:
  436. tree_growup_time = int(setting.value)
  437. pipe.setex("user_tree_growup_%s_%s" % (user.id,len(tree_list)),tree_growup_time, "_")
  438. pipe.execute()
  439.  
  440. user_login({"uid": user.id})
  441.  
  442. if int(prop_data.prop_type) == 1:
  443. """使用宠物道具"""
  444. # 1. 判断当前的宠物数量
  445. # 获取宠物列表
  446. pet_list = user_info.get("pet_list", [])
  447. if len(pet_list) > 1:
  448. socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
  449. return
  450.  
  451. # 2. 是否有空余的宠物栏位
  452. pet_number = user_info.get("pet_number",1)
  453. if pet_number <= len(pet_list):
  454. socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
  455. return
  456.  
  457. # 3. 初始化当前宠物信息
  458. # 获取有效期和防御值
  459. exp_data = Setting.query.filter(Setting.name=="pet_expire_%s" % pid).first()
  460. ski_data = Setting.query.filter(Setting.name=="pet_skill_%s" % pid).first()
  461.  
  462. if exp_data is None:
  463. # 默认7天有效期
  464. expire = 7
  465. else:
  466. expire = exp_data.value
  467.  
  468. if ski_data is None:
  469. skill = 10
  470. else:
  471. skill = ski_data.value
  472.  
  473. # 在redis中设置当前宠物的饱食度
  474. pipe = redis.pipeline()
  475. pipe.multi()
  476. pipe.setex("pet_%s_%s_hp" % (user.id, len(pet_list)+1), 24*60*60, "_")
  477. pipe.setex("pet_%s_%s_expire" % (user.id, len(pet_list)+1), int(expire)*24*60*60, "_")
  478. pipe.execute()
  479.  
  480. # 基本保存到mongo
  481. mongo.db.user_info_list.update_one({"sid":request.sid},{"$push":{"pet_list":{
  482. "pid": pid,
  483. "image": prop_data.image,
  484. "created_time": int( datetime.now().timestamp() ),
  485. "skill": skill,
  486. }}})
  487.  
  488. """
  489. db.user_info_list.updateOne({"_id":"52"},{"$push":{"pet_list":{
  490. "pid": 2,
  491. "image": "pet1.png",
  492. "created_time": 1609727155,
  493. "skill": 30,
  494. }}})
  495. """
  496.  
  497. pet_show()
  498.  
  499. # 扣除背包中的道具数量
  500. prop_list = user_info.get("prop_list",{})
  501. for key,value in prop_list.items():
  502. if key == ("prop_%s" % pid):
  503. if int(value) > 1:
  504. prop_list[key] = int(value) - 1
  505. else:
  506. prop_list.pop(key)
  507. break
  508.  
  509. mongo.db.user_info_list.update_one({"sid":room},{"$set":{"prop_list":prop_list}})
  510. user_prop()
  511.  
  512. socketio.emit("prop_use_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
  513. namespace="/mofang", room=room)
  514.  
  515. @socketio.on("active_tree", namespace="/mofang")
  516. def active_tree():
  517. """激活树桩"""
  518. room = request.sid
  519. # 获取mongo中的用户信息
  520. user_info = mongo.db.user_info_list.find_one({"sid": request.sid})
  521. # 获取mysql中的用户信息
  522. user = User.query.get(user_info.get("_id"))
  523. if user is None:
  524. socketio.emit("active_tree_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists},
  525. namespace="/mofang", room=room)
  526. return
  527.  
  528. # 判断树桩是否达到上限
  529. tree_number_data = Setting.query.filter(Setting.name == "user_active_tree").first()
  530. total_tree_data = Setting.query.filter(Setting.name == "user_total_tree").first()
  531. if tree_number_data is None:
  532. tree_number = 1
  533. else:
  534. tree_number = tree_number_data.value
  535.  
  536. if total_tree_data is None:
  537. total_tree = 9
  538. else:
  539. total_tree = int(total_tree_data.value)
  540.  
  541. user_tree_number = int(user_info.get("user_tree_number",tree_number))
  542. if user_tree_number >= total_tree:
  543. socketio.emit("active_tree_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty},
  544. namespace="/mofang", room=room)
  545. return
  546.  
  547. # 扣除激活的果子数量
  548. ret = Setting.query.filter(Setting.name == "active_tree_price").first()
  549. if ret is None:
  550. active_tree_price = 100 * user_tree_number
  551. else:
  552. active_tree_price = int(ret.value) * user_tree_number
  553.  
  554. if active_tree_price > int(user.credit):
  555. socketio.emit("active_tree_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
  556. namespace="/mofang", room=room)
  557. return
  558.  
  559. user.credit = int(user.credit) - active_tree_price
  560. db.session.commit()
  561.  
  562. mongo.db.user_info_list.update_one({"sid":room},{"$set":{"user_tree_number":user_tree_number+1}})
  563.  
  564. socketio.emit("active_tree_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
  565. namespace="/mofang", room=room)
  566. return

服务端提供化肥和宠物粮的道具数量

2.前端显示道具数量

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>用户中心</title>
  5. <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  6. <meta charset="utf-8">
  7. <link rel="stylesheet" href="../static/css/main.css">
  8. <script src="../static/js/vue.js"></script>
  9. <script src="../static/js/axios.js"></script>
  10. <script src="../static/js/main.js"></script>
  11. <script src="../static/js/uuid.js"></script>
  12. <script src="../static/js/settings.js"></script>
  13. <script src="../static/js/socket.io.js"></script>
  14. </head>
  15. <body>
  16. <div class="app orchard" id="app">
  17. <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
  18. <div class="orchard-bg">
  19. <img src="../static/images/bg2.png">
  20. <img class="board_bg2" src="../static/images/board_bg2.png">
  21. </div>
  22. <img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
  23. <div class="header">
  24. <div class="info" @click="go_home">
  25. <div class="avatar">
  26. <img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
  27. <img class="user_avatar" src="../static/images/avatar.png" alt="">
  28. <img class="avatar_border" src="../static/images/avatar_border.png" alt="">
  29. </div>
  30. <p class="user_name">好听的昵称</p>
  31. </div>
  32. <div class="wallet">
  33. <div class="balance" @click="user_recharge">
  34. <p class="title"><img src="../static/images/money.png" alt="">钱包</p>
  35. <p class="num">{{money}}</p>
  36. </div>
  37. <div class="balance">
  38. <p class="title"><img src="../static/images/integral.png" alt="">果子</p>
  39. <p class="num">99,999.00</p>
  40. </div>
  41. </div>
  42. <div class="menu-list">
  43. <div class="menu">
  44. <img src="../static/images/menu1.png" alt="">
  45. 排行榜
  46. </div>
  47. <div class="menu">
  48. <img src="../static/images/menu2.png" alt="">
  49. 签到有礼
  50. </div>
  51. <div class="menu" @click="go_orchard_shop">
  52. <img src="../static/images/menu3.png" alt="">
  53. 道具商城
  54. </div>
  55. <div class="menu">
  56. <img src="../static/images/menu4.png" alt="">
  57. 邮件中心
  58. </div>
  59. </div>
  60. </div>
  61. <div class="footer" >
  62. <ul class="menu-list">
  63. <li class="menu">新手</li>
  64. <li class="menu" @click="go_my_package">背包</li>
  65. <li class="menu-center" @click="go_orchard_shop">商店</li>
  66. <li class="menu">消息</li>
  67. <li class="menu">好友</li>
  68. </ul>
  69. </div>
  70. </div>
  71. <script>
  72. apiready = function(){
  73. init();
  74. new Vue({
  75. el:"#app",
  76. data(){
  77. return {
  78. music_play:true,
  79. namespace: '/mofang',
  80. token:"",
  81. money:"",
  82. settings_info:{
  83. orchard: {}, // 种植园公共参数
  84. user:{}, // 用户私有相关参数
  85. },
  86. socket: null,
  87. recharge_list: ['10','20','50','100','200','500','1000'],
  88. timeout: 0,
  89. prev:{name:"",url:"",params:{}},
  90. current:{name:"orchard",url:"orchard.html",params:{}},
  91. }
  92. },
  93. beforeCreate(){
  94. this.game.goFrame("orchard","my_orchard.html", this.current,{
  95. x: 0,
  96. y: 180,
  97. w: 'auto',
  98. h: 410,
  99. },null);
  100. },
  101. created(){
  102. this.checkout();
  103. this.money = this.game.fget("money");
  104. },
  105. methods:{
  106. user_recharge(){
  107. // 发起充值请求
  108. api.actionSheet({
  109. title: '余额充值',
  110. cancelTitle: '取消',
  111. buttons: this.recharge_list
  112. }, (ret, err)=>{
  113. if( ret ){
  114. if(ret.buttonIndex <= this.recharge_list.length){
  115. // 充值金额
  116. money = this.recharge_list[ret.buttonIndex-1];
  117. // 调用支付宝充值
  118. this.create_recharge(money);
  119. }
  120. }else{
  121.  
  122. }
  123. });
  124.  
  125. },
  126. create_recharge(money){
  127. // 获取历史信息记录
  128. var token = this.game.get("access_token") || this.game.fget("access_token");
  129. this.game.checkout(this, token, (new_access_token)=>{
  130. this.axios.post("",{
  131. "jsonrpc": "2.0",
  132. "id": this.uuid(),
  133. "method": "Recharge.create",
  134. "params": {
  135. "money": money,
  136. }
  137. },{
  138. headers:{
  139. Authorization: "jwt " + token,
  140. }
  141. }).then(response=>{
  142. if(parseInt(response.data.result.errno)==1000){
  143. // 前往支付宝
  144. var aliPayPlus = api.require('aliPayPlus');
  145. aliPayPlus.payOrder({
  146. orderInfo: response.data.result.order_string,
  147. sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
  148. }, (ret, err)=>{
  149. pay_result = {
  150. 9000:"支付成功",
  151. 8000:"正在处理中",
  152. 4000:"订单支付失败",
  153. 5000:"重复请求",
  154. 6001:"取消支付",
  155. 6002:"网络连接出错",
  156. 6004:"支付结果未知",
  157. }
  158. api.alert({
  159. title: '支付结果',
  160. msg: pay_result[ret.code],
  161. buttons: ['确定']
  162. });
  163. // 通知服务端, 修改充值结果
  164. this.return_recharge(response.data.result.order_number,token);
  165. });
  166. }else{
  167. this.game.print(response.data);
  168. }
  169. }).catch(error=>{
  170. // 网络等异常
  171. this.game.print(error);
  172. });
  173. })
  174. },
  175. return_recharge(out_trade_number,token){
  176. this.axios.post("",{
  177. "jsonrpc": "2.0",
  178. "id": this.uuid(),
  179. "method": "Recharge.return",
  180. "params": {
  181. "out_trade_number": out_trade_number,
  182. }
  183. },{
  184. headers:{
  185. Authorization: "jwt " + token,
  186. }
  187. }).then(response=>{
  188. if(parseInt(response.data.result.errno)==1000){
  189. this.money = response.data.result.money.toFixed(2);
  190. }
  191. })
  192. },
  193. checkout(){
  194. var token = this.game.get("access_token") || this.game.fget("access_token");
  195. this.game.checkout(this,token,(new_access_token)=>{
  196. this.connect();
  197. this.login();
  198. this.user_package();
  199. this.buy_prop();
  200. this.unlock_package_number();
  201. this.show_pet();
  202. this.use_prop();
  203. this.active_tree();
  204. });
  205. },
  206. connect(){
  207. // socket连接
  208. this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
  209. this.socket.on('connect', ()=>{
  210. this.game.print("开始连接服务端");
  211. var id = this.game.fget("id");
  212. this.socket.emit("login",{"uid":id});
  213. this.socket.emit("user_prop");
  214. });
  215. },
  216. login(){
  217. this.socket.on("login_response",(message)=>{
  218. this.settings_info.orchard = message.orchard_settings;
  219. this.settings_info.user=message.user_settings;
  220. this.game.fsave({
  221. "orchard_settings":message.orchard_settings,
  222. "user_settings":message.user_settings,
  223. });
  224. this.game.save({
  225. "tree_total":message.tree_total,
  226. "user_tree_number":message.user_tree_number,
  227. "user_tree_list":message.user_tree_list,
  228. "tree_status":message.tree_status,
  229. "pet_food_num":message.pet_food_num,
  230. "fertilizer_num":message.fertilizer_num,
  231. });
  232. setTimeout(()=>{
  233. // 通知种植园页面获取到了当前用户的种植信息
  234. api.sendEvent({
  235. name: 'user_tree_data',
  236. extra: {}
  237. });
  238. },500);
  239. });
  240. },
  241. user_package(){
  242. // 用户背包道具列表
  243. this.socket.on("user_prop_response",(message)=>{
  244. this.game.fsave({
  245. "user_package":message.data,
  246. });
  247. // 界面中的道具信息
  248. this.game.save({
  249. "pet_food_num":message.pet_food_num,
  250. "fertilizer_num":message.fertilizer_num,
  251. });
  252. api.sendEvent({
  253. name: 'update_prop_data',
  254. extra: {
  255.  
  256. }
  257. });
  258.  
  259. })
  260. },
  261. go_index(){
  262. this.game.goWin("root");
  263. },
  264. go_friends(){
  265. this.game.goFrame("friends","friends.html",this.current);
  266. this.game.goFrame("friend_list","friend_list.html",this.current,{
  267. x: 0,
  268. y: 190,
  269. w: 'auto',
  270. h: 'auto',
  271. },null,true);
  272. },
  273. go_home(){
  274. this.game.goWin("user","user.html", this.current);
  275. },
  276. go_orchard_shop(){
  277. // 种植园商店
  278. this.game.goFrame("orchard_shop","shop.html", this.current,null,{
  279. type:"push",
  280. subType:"from_top",
  281. duration:300
  282. });
  283. },
  284. go_my_package(){
  285. // 我的背包
  286. this.game.goFrame("package","package.html", this.current,null,{
  287. type:"push",
  288. subType:"from_top",
  289. duration:300
  290. });
  291. },
  292. buy_prop(){
  293. api.addEventListener({
  294. name: 'buy_prop'
  295. }, (ret, err)=>{
  296. if( ret ){
  297. // 用户购买道具
  298. this.socket.emit("user_buy_prop",ret.value);
  299. }
  300. });
  301. this.socket.on("user_buy_prop_response",(message)=>{
  302. alert(message.errmsg);
  303.  
  304. })
  305. },
  306. use_prop(){
  307. // 使用道具
  308. var pid = 0;
  309. api.addEventListener({
  310. name: 'use_prop'
  311. }, (ret, err)=>{
  312. if( ret ){
  313. // 用户使用道具
  314. pid = ret.value.pid;
  315. this.socket.emit("use_prop",ret.value.pid);
  316. }
  317. });
  318.  
  319. this.socket.on("prop_use_response",(message)=>{
  320. if(parseInt(message.errno) === 1000){
  321. api.sendEvent({
  322. name: 'prop_use_success',
  323. extra: {
  324. pid: pid
  325. }
  326. });
  327. }else{
  328. api.alert({
  329. title: '提示',
  330. msg: message.errmsg,
  331. });
  332.  
  333. }
  334. });
  335. },
  336. unlock_package_number(){
  337. api.addEventListener({
  338. name: 'unlock_package_number'
  339. }, (ret, err)=>{
  340. if( ret ){
  341. // 用户购买道具
  342. this.socket.emit("unlock_package");
  343. }
  344. });
  345.  
  346. this.socket.on("unlock_package_response",(message)=>{
  347. if(parseInt(message.errno) === 1000){
  348. api.sendEvent({
  349. name: 'unlock_package_success',
  350. extra: {
  351. }
  352. });
  353. }else{
  354. api.alert({
  355. title: '提示',
  356. msg: message.errmsg,
  357. });
  358.  
  359. }
  360.  
  361. })
  362. },
  363. show_pet(){
  364. // 显示宠物
  365. this.socket.emit("pet_show");
  366. this.socket.on("pet_show_response",(message)=>{
  367. if(parseInt(message.errno) === 1000){
  368. // 把宠物信息保存到本地
  369. this.game.save({"pet_list":message.pet_list})
  370. this.game.save({"pet_number":message.pet_number})
  371. setTimeout(()=>{
  372. api.sendEvent({
  373. name: 'pet_show_success',
  374. extra: {}
  375. });
  376. },500);
  377. }else{
  378. api.alert({
  379. title: '提示',
  380. msg: message.errmsg,
  381. });
  382. }
  383.  
  384. })
  385. },
  386. active_tree(){
  387. // 激活树桩
  388. api.addEventListener({
  389. name: 'active_tree'
  390. }, (ret, err)=>{
  391. if( ret ){
  392. this.socket.emit("active_tree");
  393. }
  394. });
  395.  
  396. this.socket.on("active_tree_response",(message)=>{
  397. if(parseInt(message.errno) === 1000){
  398. // 更新数据到本地
  399. api.sendEvent({
  400. name: 'active_tree_success',
  401. extra: {}
  402. });
  403. }else{
  404. api.alert({
  405. title: '提示',
  406. msg: message.errmsg,
  407. });
  408. }
  409.  
  410. })
  411. }
  412. }
  413. });
  414. }
  415. </script>
  416. </body>
  417. </html>

前端显示道具数量:orchard.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>用户中心</title>
  5. <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  6. <meta charset="utf-8">
  7. <link rel="stylesheet" href="../static/css/main.css">
  8. <script src="../static/js/vue.js"></script>
  9. <script src="../static/js/axios.js"></script>
  10. <script src="../static/js/main.js"></script>
  11. <script src="../static/js/uuid.js"></script>
  12. <script src="../static/js/settings.js"></script>
  13. <script src="../static/js/socket.io.js"></script>
  14. </head>
  15. <body>
  16. <div class="app orchard orchard-frame" id="app">
  17. <div class="background">
  18. <img class="grassland2" src="../static/images/grassland2.png" alt="">
  19. <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
  20. <img class="stake1" src="../static/images/stake1.png" alt="">
  21. <img class="stake2" src="../static/images/stake2.png" alt="">
  22. </div>
  23. <div class="pet-box">
  24. <div class="pet">
  25. <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
  26. </div>
  27. <div class="pet" v-if="pet_number > 1">
  28. <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt="">
  29. </div>
  30. <div class="pet turned_off" v-if="pet_number==1">
  31. <img class="turned_image" src="../static/images/turned_off.png" alt="">
  32. <p>请购买宠物</p>
  33. </div>
  34. </div>
  35. <div class="tree-list">
  36. <div class="tree-box">
  37. <div class="tree" v-for="tree in user_tree_data.user_tree_list">
  38. <img :src="tree_img(tree.status)" alt="">
  39. </div>
  40. <!-- 已激活但是未种植的树桩列表 -->
  41. <div class="tree" v-for="i in active_tree">
  42. <img src="../static/images/tree1.png" alt="">
  43. </div>
  44. <!-- 未激活树桩列表 -->
  45. <div @click="unlock_tree" class="tree" v-for="i in lock_tree">
  46. <img src="../static/images/tree0.png" alt="">
  47. </div>
  48. </div>
  49.  
  50. </div>
  51. <div class="prop-list">
  52. <div class="prop">
  53. <img src="../static/images/prop1.png" alt="">
  54. <span>{{fertilizer_num}}</span>
  55. <p>化肥</p>
  56. </div>
  57. <div class="prop">
  58. <img src="../static/images/prop2.png" alt="">
  59. <span>0</span>
  60. <p>修剪</p>
  61. </div>
  62. <div class="prop">
  63. <img src="../static/images/prop3.png" alt="">
  64. <span>1</span>
  65. <p>浇水</p>
  66. </div>
  67. <div class="prop">
  68. <img src="../static/images/prop4.png" alt="">
  69. <span>{{pet_food_num}}</span>
  70. <p>宠物粮</p>
  71. </div>
  72. </div>
  73. <div class="pet-hp-list">
  74. <div class="pet-hp" v-for="pet in pet_list">
  75. <p>宠物1 饱食度</p>
  76. <div class="hp">
  77. <div :style="{width: pet.hp+'%'}" class="process">{{pet.hp}}%</div>
  78. </div>
  79. </div>
  80. </div>
  81. </div>
  82. <script>
  83. apiready = function(){
  84. init();
  85. new Vue({
  86. el:"#app",
  87. data(){
  88. return {
  89. namespace: '/mofang',
  90. token:"",
  91. socket: null,
  92. pet_food_num:0, // 宠物粮数量
  93. fertilizer_num:0, // 化肥数量
  94. pet_list:[],
  95. user_tree_data:{
  96. "total_tree": 9, // 总树桩数量
  97. "user_tree_number": 0, // 当前用户激活树桩数量
  98. "user_tree_list":[ // 当前种植的树桩列表状态
  99.  
  100. ],
  101. },
  102. tree_status:{
  103.  
  104. },
  105. // user_tree_data:{
  106. // "total_tree":9, // 总树桩数量
  107. // "user_tree_number": 5, // 当前用户激活树桩数量
  108. // "user_tree_list":[ // 当前种植的树桩列表状态
  109. // { // 树桩状态
  110. // "time":1609808084, // 种植时间
  111. // "status":4, // 植物状态
  112. // "has_time": 300, // 状态时间
  113. // },
  114. // ],
  115. // },
  116. // tree_status:{
  117. // "tree_status_0": "tree0.png", // 树桩
  118. // "tree_status_1": "tree1.png", // 空桩
  119. // "tree_status_2": "tree2.png", // 幼苗
  120. // "tree_status_3": "tree3.png", // 成长
  121. // "tree_status_4": "tree4.png", // 成熟
  122. // },
  123. pet_number:[],
  124. timeout: 0,
  125. prev:{name:"",url:"",params:{}},
  126. current:{name:"orchard",url:"orchard.html",params:{}},
  127. }
  128. },
  129. computed:{
  130. // 已激活但是未使用的树桩
  131. active_tree(){
  132. return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
  133. },
  134. // 未激活的剩余树桩
  135. lock_tree(){
  136. return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
  137. },
  138. },
  139. created(){
  140. this.show_pet_list();
  141. this.show_tree_list();
  142. this.get_prop_list();
  143. },
  144. methods:{
  145. get_prop_list(){
  146. // 更新道具列表信息
  147. api.addEventListener({
  148. name: 'update_prop_data'
  149. }, (ret, err)=>{
  150. if( ret ){
  151. this.pet_food_num = this.game.get("pet_food_num");
  152. this.fertilizer_num = this.game.get("fertilizer_num");
  153. }
  154. });
  155.  
  156. },
  157. tree_img(status){
  158. return '../static/images/'+this.tree_status[status];
  159. },
  160. show_pet_list(){
  161. api.addEventListener({
  162. name: 'pet_show_success'
  163. }, (ret, err)=>{
  164. if( ret ){
  165. // 用户购买道具
  166. this.pet_list = this.game.get("pet_list");
  167. this.pet_number = parseInt(this.game.get("pet_number"));
  168. }
  169. });
  170. },
  171. show_tree_list(){
  172. api.addEventListener({
  173. name: 'user_tree_data'
  174. }, (ret, err)=>{
  175. if( ret ){
  176. // 用户种植植物信息
  177. this.user_tree_data.tree_total = parseInt(this.game.get("tree_total"));
  178. this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number"));
  179. this.user_tree_data.user_tree_list = this.game.get("user_tree_list");
  180. this.tree_status = this.game.get("tree_status");
  181. this.pet_food_num = this.game.get("pet_food_num");
  182. this.fertilizer_num = this.game.get("fertilizer_num");
  183. }
  184. });
  185. },
  186. unlock_tree(){
  187. // 激活树桩通知
  188. api.confirm({
  189. title: '提示',
  190. msg: '是否激活树桩?',
  191. buttons: ['确定', '取消']
  192. }, (ret, err)=>{
  193. if( ret.buttonIndex == 1 ){
  194. api.sendEvent({
  195. name: 'active_tree',
  196. extra: {
  197.  
  198. }
  199. });
  200. }
  201. });
  202.  
  203. // 激活成功!
  204. api.addEventListener({
  205. name: 'active_tree_success'
  206. }, (ret, err)=>{
  207. if( ret ){
  208. // 新增树桩的数量
  209. this.user_tree_data.user_tree_number+=1;
  210. this.game.save({"user_tree_number": this.user_tree_data.user_tree_number});
  211. }
  212. });
  213. }
  214. }
  215. });
  216. }
  217. </script>
  218. </body>
  219. </html>

前端显示道具数量:my_orchard.html

2.显示剪刀和浇水数量

1.后端:根据植物计算剪刀和浇水的数量

  1. from application import socketio
  2. from flask import request
  3. from application.apps.users.models import User
  4. from flask_socketio import join_room, leave_room
  5. from application import mongo
  6. from .models import Goods,Setting,db
  7. from status import APIStatus as status
  8. from message import ErrorMessage as errmsg
  9. # 建立socket通信
  10. # @socketio.on("connect", namespace="/mofang")
  11. # def user_connect():
  12. # """用户连接"""
  13. # print("用户%s连接过来了!" % request.sid)
  14. # # 主动响应数据给客户端
  15. # socketio.emit("server_response","hello",namespace="/mofang")
  16.  
  17. # 断开socket通信
  18. @socketio.on("disconnect", namespace="/mofang")
  19. def user_disconnect():
  20. print("用户%s退出了种植园" % request.sid )
  21.  
  22. @socketio.on("login", namespace="/mofang")
  23. def user_login(data):
  24. # 分配房间
  25. room = data["uid"]
  26. join_room(room)
  27. # 保存当前用户和sid的绑定关系
  28. # 判断当前用户是否在mongo中有记录
  29. query = {
  30. "_id": data["uid"]
  31. }
  32. ret = mongo.db.user_info_list.find_one(query)
  33. if ret:
  34. mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}})
  35. else:
  36. mongo.db.user_info_list.insert_one({
  37. "_id": data["uid"],
  38. "sid": request.sid,
  39. })
  40.  
  41. # 返回种植园的相关配置参数
  42. orchard_settings = {}
  43. setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all()
  44. """
  45. 现在的格式:
  46. [<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>]
  47. 需要返回的格式:
  48. {
  49. package_number_base:4,
  50. package_number_max: 32,
  51. ...
  52. }
  53. """
  54. for item in setting_list:
  55. orchard_settings[item.name] = item.value
  56.  
  57. # 返回当前用户相关的配置参数
  58. user_settings = {}
  59. # 从mongo中查找用户信息,判断用户是否激活了背包格子
  60. user_dict = mongo.db.user_info_list.find_one({"sid":request.sid})
  61. # 背包格子
  62. if user_dict.get("package_number") is None:
  63. user_settings["package_number"] = orchard_settings.get("package_number_base",4)
  64. mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}})
  65. else:
  66. user_settings["package_number"] = user_dict.get("package_number")
  67.  
  68. """种植园植物信息"""
  69. # 总树桩数量
  70. setting = Setting.query.filter(Setting.name == "user_total_tree").first()
  71. if setting is None:
  72. tree_total = 9
  73. else:
  74. tree_total = int(setting.value)
  75.  
  76. # 用户已经激活的树桩
  77. setting = Setting.query.filter(Setting.name == "user_active_tree").first()
  78. if setting is None:
  79. user_tree_number = 3
  80. else:
  81. user_tree_number = int(setting.value)
  82. user_tree_number = user_dict.get("user_tree_number",user_tree_number)
  83.  
  84. # 种植的植物列表
  85. user_tree_list = user_dict.get("user_tree_list", [])
  86. key = 0
  87. for tree_item in user_tree_list:
  88. tree_item["status"] = "tree_status_%s" % int(tree_item["status"]) # 植物状态
  89. tree_item["water_time"] = redis.ttl("user_tree_water_%s_%s" % (data["uid"],key))
  90. tree_item["growup_time"] = redis.ttl("user_tree_growup_%s_%s" % (data["uid"],key))
  91. key+=1
  92. # 植物状态信息
  93. status_list = [
  94. "tree_status_0",
  95. "tree_status_1",
  96. "tree_status_2",
  97. "tree_status_3",
  98. "tree_status_4",
  99. ]
  100. setting_list = Setting.query.filter(Setting.name.in_(status_list)).all()
  101. tree_status = {}
  102. for item in setting_list:
  103. tree_status[item.name] = item.value
  104.  
  105. """显示植物相关道具"""
  106. # 获取背包中的化肥和宠物粮
  107. fertilizer_num,pet_food_num = get_package_prop_list(user_dict)
  108. # 获取剪刀和浇水
  109. # 只有植物处于成长期才会允许裁剪
  110. # 只有植物处于幼苗期才会允许浇水
  111. waters = 0
  112. shears = 0
  113. user_tree_list = user_dict.get("user_tree_list",[])
  114. if len(user_tree_list) > 0:
  115. key = 0
  116. for tree in user_tree_list:
  117. if (tree["status"] == "tree_status_%s" % 2) and int(tree.get("waters",0)) == 0:
  118. """处于幼苗期"""
  119. # 判断只有种植指定时间以后的幼苗才可以浇水
  120. interval_time = redis.ttl("user_tree_water_%s_%s" % (user_dict.get("_id"), key) )
  121. if interval_time==-2:
  122. waters+=1
  123.  
  124. elif (tree["status"] == "tree_status_%s" % 3) and int(tree.get("shears",0)) == 0:
  125. """处于成长期"""
  126. interval_time = redis.ttl("user_tree_shears_%s_%s" % (user_dict.get("_id"), key))
  127. if interval_time==-2:
  128. shears+=1
  129. key+=1
  130. message = {
  131. "errno":status.CODE_OK,
  132. "errmsg":errmsg.ok,
  133. "orchard_settings":orchard_settings,
  134. "user_settings":user_settings,
  135. "tree_total":tree_total,
  136. "user_tree_number":user_tree_number,
  137. "user_tree_list":user_tree_list,
  138. "fertilizer_num":fertilizer_num,
  139. "pet_food_num":pet_food_num,
  140. "tree_status":tree_status,
  141. "waters":waters,
  142. "shears":shears,
  143. }
  144. print(message)
  145. socketio.emit("login_response", message, namespace="/mofang", room=room)
  146.  
  147. def get_package_prop_list(user_dict):
  148. fertilizer_num = 0
  149. pet_food_num = 0
  150. prop_list = user_dict.get("prop_list", {})
  151. for prop_item, num in prop_list.items():
  152. pid = prop_item.split("_")[-1]
  153. num = int(num)
  154. prop_obj = Goods.query.get(pid)
  155. if prop_obj.prop_type == 2:
  156. # 提取化肥
  157. fertilizer_num += num
  158. elif prop_obj.prop_type == 3:
  159. # 提取宠物粮
  160. pet_food_num += num
  161.  
  162. return fertilizer_num,pet_food_num
  163.  
  164. @socketio.on("user_buy_prop", namespace="/mofang")
  165. def user_buy_prop(data):
  166. """用户购买道具"""
  167. room = request.sid
  168. # 从mongo中获取当前用户信息
  169. user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
  170. user = User.query.get(user_info.get("_id"))
  171. if user is None:
  172. socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
  173. return
  174.  
  175. # 判断背包物品存储是否达到上限
  176. use_package_number = int(user_info.get("use_package_number",0)) # 当前诗经使用的格子数量
  177. package_number = int(user_info.get("package_number",0)) # 当前用户已经解锁的格子数量
  178. # 本次购买道具需要使用的格子数量
  179. setting = Setting.query.filter(Setting.name == "td_prop_max").first()
  180. if setting is None:
  181. td_prop_max = 10
  182. else:
  183. td_prop_max = int(setting.value)
  184.  
  185. # 计算购买道具以后需要额外占用的格子数量
  186. if ("prop_%s" % data["pid"]) in user_info.get("prop_list",{}):
  187. """曾经购买过当前道具"""
  188. prop_num = int( user_info.get("prop_list")["prop_%s" % data["pid"]]) # 购买前的道具数量
  189. new_prop_num = prop_num+int(data["num"]) # 如果成功购买道具以后的数量
  190. old_td_num = prop_num // td_prop_max
  191. if prop_num % td_prop_max > 0:
  192. old_td_num+=1
  193. new_td_num = new_prop_num // td_prop_max
  194. if new_prop_num % td_prop_max > 0:
  195. new_td_num+=1
  196. td_num = new_td_num - old_td_num
  197. else:
  198. """新增购买的道具"""
  199. # 计算本次购买道具需要占用的格子数量
  200.  
  201. if int(data["num"]) > td_prop_max:
  202. """需要多个格子"""
  203. td_num = int(data["num"]) // td_prop_max
  204. if int(data["num"]) % td_prop_max > 0:
  205. td_num+=1
  206. else:
  207. """需要一个格子"""
  208. td_num = 1
  209.  
  210. if use_package_number+td_num > package_number:
  211. """超出存储上限"""
  212. socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_PACKAGE, "errmsg": errmsg.no_package},
  213. namespace="/mofang", room=room)
  214. return
  215.  
  216. # 从mysql中获取商品价格
  217. prop = Goods.query.get(data["pid"])
  218. if user.money > 0: # 当前商品需要通过RMB购买
  219. if float(user.money) < float(prop.price) * int(data["num"]):
  220. socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room)
  221. return
  222. else:
  223. """当前通过果子进行购买"""
  224. if int(user.credit) < int(prop.credit) * int(data["num"]):
  225. socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
  226. namespace="/mofang", room=room)
  227. return
  228.  
  229. # 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额
  230. query = {"sid": request.sid}
  231. if user_info.get("prop_list") is None:
  232. """此前没有购买任何道具"""
  233. message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}}
  234. mongo.db.user_info_list.update_one(query,message)
  235. else:
  236. """此前有购买了道具"""
  237. prop_list = user_info.get("prop_list") # 道具列表
  238. if ("prop_%s" % prop.id) in prop_list:
  239. """如果再次同一款道具"""
  240. prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"])
  241. else:
  242. """此前没有购买过这种道具"""
  243. prop_list[("prop_%s" % prop.id)] = int(data["num"])
  244.  
  245. mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}})
  246.  
  247. # 扣除余额或果子
  248. if prop.price > 0:
  249. user.money = float(user.money) - float(prop.price) * int(data["num"])
  250. else:
  251. user.credit = int(user.credit) - int(prop.credit) * int(data["num"])
  252.  
  253. db.session.commit()
  254.  
  255. # 返回购买成功的信息
  256. socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room)
  257. # 返回最新的用户道具列表
  258. user_prop()
  259.  
  260. @socketio.on("user_prop", namespace="/mofang")
  261. def user_prop():
  262. """用户道具"""
  263. userinfo = mongo.db.user_info_list.find_one({"sid":request.sid})
  264. prop_list = userinfo.get("prop_list",{})
  265. prop_id_list = []
  266. for prop_str,num in prop_list.items():
  267. pid = int(prop_str[5:])
  268. prop_id_list.append(pid)
  269.  
  270. data = []
  271. prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all()
  272. setting = Setting.query.filter(Setting.name == "td_prop_max").first()
  273. if setting is None:
  274. td_prop_max = 10
  275. else:
  276. td_prop_max = int(setting.value)
  277.  
  278. for prop_data in prop_list_data:
  279. num = int( prop_list[("prop_%s" % prop_data.id)])
  280. if td_prop_max > num:
  281. data.append({
  282. "num": num,
  283. "image": prop_data.image,
  284. "pid": prop_data.id,
  285. "pty": prop_data.prop_type,
  286. })
  287. else:
  288. padding_time = num // td_prop_max
  289. padding_last = num % td_prop_max
  290. arr = [{
  291. "num": td_prop_max,
  292. "image": prop_data.image,
  293. "pid": prop_data.id,
  294. "pty": prop_data.prop_type,
  295. }] * padding_time
  296. if padding_last != 0:
  297. arr.append({
  298. "num": padding_last,
  299. "image": prop_data.image,
  300. "pid": prop_data.id,
  301. "pty": prop_data.prop_type,
  302. })
  303. data = data + arr
  304. mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"use_package_number":len(data)}})
  305. room = request.sid
  306. fertilizer_num,pet_food_num = get_package_prop_list(userinfo)
  307. socketio.emit("user_prop_response", {
  308. "errno": status.CODE_OK,
  309. "errmsg": errmsg.ok,
  310. "data":data,
  311. "fertilizer_num":fertilizer_num,
  312. "pet_food_num":pet_food_num,
  313. }, namespace="/mofang",
  314. room=room)
  315.  
  316. @socketio.on("unlock_package", namespace="/mofang")
  317. def unlock_package():
  318. """解锁背包"""
  319. # 从mongo获取当前用户解锁的格子数量
  320. user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
  321. user = User.query.get(user_info.get("_id"))
  322. if user is None:
  323. socketio.emit("unlock_package_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
  324. return
  325.  
  326. package_number = int(user_info.get("package_number"))
  327. num = 7 - (32 - package_number) // 4 # 没有解锁的格子
  328.  
  329. # 从数据库中获取解锁背包的价格
  330. setting = Setting.query.filter(Setting.name == "package_unlock_price_%s" % num).first()
  331. if setting is None:
  332. unlock_price = 0
  333. else:
  334. unlock_price = int(setting.value)
  335.  
  336. # 判断是否有足够的积分或者价格
  337. room = request.sid
  338. if user.money < unlock_price:
  339. socketio.emit("unlock_package_response", {"errno": status.CODE_NO_MONEY, "errmsg": errmsg.money_no_enough},
  340. namespace="/mofang", room=room)
  341. return
  342.  
  343. # 解锁成功
  344. user.money = float(user.money) - float(unlock_price)
  345. db.session.commit()
  346.  
  347. # mongo中调整数量
  348. mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": package_number+1}})
  349. # 返回解锁的结果
  350. socketio.emit("unlock_package_response", {
  351. "errno": status.CODE_OK,
  352. "errmsg": errmsg.ok},
  353. namespace="/mofang", room=room)
  354. import math
  355. from application import redis
  356. @socketio.on("pet_show", namespace="/mofang")
  357. def pet_show():
  358. """显示宠物"""
  359. room = request.sid
  360. user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
  361. user = User.query.get(user_info.get("_id"))
  362. if user is None:
  363. socketio.emit("pet_show_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
  364. return
  365.  
  366. # 获取宠物列表
  367. pet_list = user_info.get("pet_list", [])
  368.  
  369. """
  370. pet_list: [
  371. {
  372. "pid":11,
  373. "image":"pet.png",
  374. "hp":100%,
  375. "created_time":xxxx-xx-xx xx:xx:xx,
  376. "skill":"70%",
  377. "has_time":30天
  378. },
  379. ]
  380. """
  381. # 从redis中提取当前宠物的饱食度和有效期
  382. for key,pet in enumerate(pet_list):
  383. pet["hp"] = math.ceil( redis.ttl("pet_%s_%s_hp" % (user.id,key+1)) / 86400 * 100 )
  384. pet["has_time"] = redis.ttl("pet_%s_%s_expire" % (user.id,key+1))
  385.  
  386. pet_number = user_info.get("pet_number", 1)
  387. socketio.emit(
  388. "pet_show_response",
  389. {
  390. "errno": status.CODE_OK,
  391. "errmsg": errmsg.ok,
  392. "pet_list": pet_list,
  393. "pet_number": pet_number,
  394. },
  395. namespace="/mofang",
  396. room=room
  397. )
  398.  
  399. from datetime import datetime
  400. @socketio.on("use_prop", namespace="/mofang")
  401. def use_prop(pid):
  402. """使用道具"""
  403. room = request.sid
  404. # 获取mongo中的用户信息
  405. user_info = mongo.db.user_info_list.find_one({"sid": request.sid})
  406. # 获取mysql中的用户信息
  407. user = User.query.get(user_info.get("_id"))
  408. if user is None:
  409. socketio.emit("pet_use_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists},
  410. namespace="/mofang", room=room)
  411. return
  412.  
  413. # 获取道具
  414. prop_data = Goods.query.get(pid)
  415. if prop_data is None:
  416. socketio.emit("pet_use_response", {"errno":status.CODE_NO_SUCH_PROP,"errmsg":errmsg.not_such_prop}, namespace="/mofang", room=room)
  417. return
  418.  
  419. if int(prop_data.prop_type) == 0:
  420. """使用植物道具"""
  421. # 1. 判断当前的植物数量是否有空余
  422. tree_list = user_info.get("user_tree_list", [])
  423.  
  424. # 当前用户最多可种植的数量
  425. setting = Setting.query.filter(Setting.name == "user_active_tree").first()
  426. if setting is None:
  427. user_tree_number = 3
  428. else:
  429. user_tree_number = int(setting.value)
  430. user_tree_number = user_info.get("user_tree_number", user_tree_number)
  431.  
  432. if len(tree_list) >= user_tree_number:
  433. socketio.emit("prop_use_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty},
  434. namespace="/mofang", room=room)
  435. return
  436.  
  437. # 使用道具
  438. mongo.db.user_info_list.update_one({"sid":room},{"$push":{"user_tree_list":
  439. { # 植物状态
  440. "time": int(datetime.now().timestamp()), # 种植时间
  441. "status": 2, # 植物状态,2表示幼苗状态
  442. "waters": 0, # 浇水次数
  443. "shears": 0, # 使用剪刀次数
  444. }
  445. }})
  446.  
  447. # 从种下去到浇水的时间
  448. pipe = redis.pipeline()
  449. pipe.multi()
  450. setting = Setting.query.filter(Setting.name == "tree_water_time").first()
  451. if setting is None:
  452. tree_water_time = 3600
  453. else:
  454. tree_water_time = int(setting.value)
  455. # 必须等时间到了才可以浇水
  456. pipe.setex("user_tree_water_%s_%s" % (user.id,len(tree_list)),int(tree_water_time),"_")
  457. # 必须等时间到了才可以到成长期
  458. setting = Setting.query.filter(Setting.name == "tree_growup_time").first()
  459. if setting is None:
  460. tree_growup_time = 3600
  461. else:
  462. tree_growup_time = int(setting.value)
  463. pipe.setex("user_tree_growup_%s_%s" % (user.id,len(tree_list)),tree_growup_time, "_")
  464. pipe.execute()
  465.  
  466. user_login({"uid": user.id})
  467.  
  468. if int(prop_data.prop_type) == 1:
  469. """使用宠物道具"""
  470. # 1. 判断当前的宠物数量
  471. # 获取宠物列表
  472. pet_list = user_info.get("pet_list", [])
  473. if len(pet_list) > 1:
  474. socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
  475. return
  476.  
  477. # 2. 是否有空余的宠物栏位
  478. pet_number = user_info.get("pet_number",1)
  479. if pet_number <= len(pet_list):
  480. socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
  481. return
  482.  
  483. # 3. 初始化当前宠物信息
  484. # 获取有效期和防御值
  485. exp_data = Setting.query.filter(Setting.name=="pet_expire_%s" % pid).first()
  486. ski_data = Setting.query.filter(Setting.name=="pet_skill_%s" % pid).first()
  487.  
  488. if exp_data is None:
  489. # 默认7天有效期
  490. expire = 7
  491. else:
  492. expire = exp_data.value
  493.  
  494. if ski_data is None:
  495. skill = 10
  496. else:
  497. skill = ski_data.value
  498.  
  499. # 在redis中设置当前宠物的饱食度
  500. pipe = redis.pipeline()
  501. pipe.multi()
  502. pipe.setex("pet_%s_%s_hp" % (user.id, len(pet_list)+1), 24*60*60, "_")
  503. pipe.setex("pet_%s_%s_expire" % (user.id, len(pet_list)+1), int(expire)*24*60*60, "_")
  504. pipe.execute()
  505.  
  506. # 基本保存到mongo
  507. mongo.db.user_info_list.update_one({"sid":request.sid},{"$push":{"pet_list":{
  508. "pid": pid,
  509. "image": prop_data.image,
  510. "created_time": int( datetime.now().timestamp() ),
  511. "skill": skill,
  512. }}})
  513.  
  514. """
  515. db.user_info_list.updateOne({"_id":"52"},{"$push":{"pet_list":{
  516. "pid": 2,
  517. "image": "pet1.png",
  518. "created_time": 1609727155,
  519. "skill": 30,
  520. }}})
  521. """
  522.  
  523. pet_show()
  524.  
  525. # 扣除背包中的道具数量
  526. prop_list = user_info.get("prop_list",{})
  527. for key,value in prop_list.items():
  528. if key == ("prop_%s" % pid):
  529. if int(value) > 1:
  530. prop_list[key] = int(value) - 1
  531. else:
  532. prop_list.pop(key)
  533. break
  534.  
  535. mongo.db.user_info_list.update_one({"sid":room},{"$set":{"prop_list":prop_list}})
  536. user_prop()
  537.  
  538. socketio.emit("prop_use_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
  539. namespace="/mofang", room=room)
  540.  
  541. @socketio.on("active_tree", namespace="/mofang")
  542. def active_tree():
  543. """激活树桩"""
  544. room = request.sid
  545. # 获取mongo中的用户信息
  546. user_info = mongo.db.user_info_list.find_one({"sid": request.sid})
  547. # 获取mysql中的用户信息
  548. user = User.query.get(user_info.get("_id"))
  549. if user is None:
  550. socketio.emit("active_tree_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists},
  551. namespace="/mofang", room=room)
  552. return
  553.  
  554. # 判断树桩是否达到上限
  555. tree_number_data = Setting.query.filter(Setting.name == "user_active_tree").first()
  556. total_tree_data = Setting.query.filter(Setting.name == "user_total_tree").first()
  557. if tree_number_data is None:
  558. tree_number = 1
  559. else:
  560. tree_number = tree_number_data.value
  561.  
  562. if total_tree_data is None:
  563. total_tree = 9
  564. else:
  565. total_tree = int(total_tree_data.value)
  566.  
  567. user_tree_number = int(user_info.get("user_tree_number",tree_number))
  568. if user_tree_number >= total_tree:
  569. socketio.emit("active_tree_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty},
  570. namespace="/mofang", room=room)
  571. return
  572.  
  573. # 扣除激活的果子数量
  574. ret = Setting.query.filter(Setting.name == "active_tree_price").first()
  575. if ret is None:
  576. active_tree_price = 100 * user_tree_number
  577. else:
  578. active_tree_price = int(ret.value) * user_tree_number
  579.  
  580. if active_tree_price > int(user.credit):
  581. socketio.emit("active_tree_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
  582. namespace="/mofang", room=room)
  583. return
  584.  
  585. user.credit = int(user.credit) - active_tree_price
  586. db.session.commit()
  587.  
  588. mongo.db.user_info_list.update_one({"sid":room},{"$set":{"user_tree_number":user_tree_number+1}})
  589.  
  590. socketio.emit("active_tree_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
  591. namespace="/mofang", room=room)
  592. return

服务端根据植物的状态计算剪刀和浇水的数量

2.前端:进入种植园后显示剪刀和浇水的次数

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>用户中心</title>
  5. <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  6. <meta charset="utf-8">
  7. <link rel="stylesheet" href="../static/css/main.css">
  8. <script src="../static/js/vue.js"></script>
  9. <script src="../static/js/axios.js"></script>
  10. <script src="../static/js/main.js"></script>
  11. <script src="../static/js/uuid.js"></script>
  12. <script src="../static/js/settings.js"></script>
  13. <script src="../static/js/socket.io.js"></script>
  14. </head>
  15. <body>
  16. <div class="app orchard" id="app">
  17. <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
  18. <div class="orchard-bg">
  19. <img src="../static/images/bg2.png">
  20. <img class="board_bg2" src="../static/images/board_bg2.png">
  21. </div>
  22. <img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
  23. <div class="header">
  24. <div class="info" @click="go_home">
  25. <div class="avatar">
  26. <img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
  27. <img class="user_avatar" src="../static/images/avatar.png" alt="">
  28. <img class="avatar_border" src="../static/images/avatar_border.png" alt="">
  29. </div>
  30. <p class="user_name">好听的昵称</p>
  31. </div>
  32. <div class="wallet">
  33. <div class="balance" @click="user_recharge">
  34. <p class="title"><img src="../static/images/money.png" alt="">钱包</p>
  35. <p class="num">{{money}}</p>
  36. </div>
  37. <div class="balance">
  38. <p class="title"><img src="../static/images/integral.png" alt="">果子</p>
  39. <p class="num">99,999.00</p>
  40. </div>
  41. </div>
  42. <div class="menu-list">
  43. <div class="menu">
  44. <img src="../static/images/menu1.png" alt="">
  45. 排行榜
  46. </div>
  47. <div class="menu">
  48. <img src="../static/images/menu2.png" alt="">
  49. 签到有礼
  50. </div>
  51. <div class="menu" @click="go_orchard_shop">
  52. <img src="../static/images/menu3.png" alt="">
  53. 道具商城
  54. </div>
  55. <div class="menu">
  56. <img src="../static/images/menu4.png" alt="">
  57. 邮件中心
  58. </div>
  59. </div>
  60. </div>
  61. <div class="footer" >
  62. <ul class="menu-list">
  63. <li class="menu">新手</li>
  64. <li class="menu" @click="go_my_package">背包</li>
  65. <li class="menu-center" @click="go_orchard_shop">商店</li>
  66. <li class="menu">消息</li>
  67. <li class="menu">好友</li>
  68. </ul>
  69. </div>
  70. </div>
  71. <script>
  72. apiready = function(){
  73. init();
  74. new Vue({
  75. el:"#app",
  76. data(){
  77. return {
  78. music_play:true,
  79. namespace: '/mofang',
  80. token:"",
  81. money:"",
  82. settings_info:{
  83. orchard: {}, // 种植园公共参数
  84. user:{}, // 用户私有相关参数
  85. },
  86. socket: null,
  87. recharge_list: ['10','20','50','100','200','500','1000'],
  88. timeout: 0,
  89. prev:{name:"",url:"",params:{}},
  90. current:{name:"orchard",url:"orchard.html",params:{}},
  91. }
  92. },
  93. beforeCreate(){
  94. this.game.goFrame("orchard","my_orchard.html", this.current,{
  95. x: 0,
  96. y: 180,
  97. w: 'auto',
  98. h: 410,
  99. },null);
  100. },
  101. created(){
  102. this.checkout();
  103. this.money = this.game.fget("money");
  104. },
  105. methods:{
  106. user_recharge(){
  107. // 发起充值请求
  108. api.actionSheet({
  109. title: '余额充值',
  110. cancelTitle: '取消',
  111. buttons: this.recharge_list
  112. }, (ret, err)=>{
  113. if( ret ){
  114. if(ret.buttonIndex <= this.recharge_list.length){
  115. // 充值金额
  116. money = this.recharge_list[ret.buttonIndex-1];
  117. // 调用支付宝充值
  118. this.create_recharge(money);
  119. }
  120. }else{
  121.  
  122. }
  123. });
  124.  
  125. },
  126. create_recharge(money){
  127. // 获取历史信息记录
  128. var token = this.game.get("access_token") || this.game.fget("access_token");
  129. this.game.checkout(this, token, (new_access_token)=>{
  130. this.axios.post("",{
  131. "jsonrpc": "2.0",
  132. "id": this.uuid(),
  133. "method": "Recharge.create",
  134. "params": {
  135. "money": money,
  136. }
  137. },{
  138. headers:{
  139. Authorization: "jwt " + token,
  140. }
  141. }).then(response=>{
  142. if(parseInt(response.data.result.errno)==1000){
  143. // 前往支付宝
  144. var aliPayPlus = api.require('aliPayPlus');
  145. aliPayPlus.payOrder({
  146. orderInfo: response.data.result.order_string,
  147. sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
  148. }, (ret, err)=>{
  149. pay_result = {
  150. 9000:"支付成功",
  151. 8000:"正在处理中",
  152. 4000:"订单支付失败",
  153. 5000:"重复请求",
  154. 6001:"取消支付",
  155. 6002:"网络连接出错",
  156. 6004:"支付结果未知",
  157. }
  158. api.alert({
  159. title: '支付结果',
  160. msg: pay_result[ret.code],
  161. buttons: ['确定']
  162. });
  163. // 通知服务端, 修改充值结果
  164. this.return_recharge(response.data.result.order_number,token);
  165. });
  166. }else{
  167. this.game.print(response.data);
  168. }
  169. }).catch(error=>{
  170. // 网络等异常
  171. this.game.print(error);
  172. });
  173. })
  174. },
  175. return_recharge(out_trade_number,token){
  176. this.axios.post("",{
  177. "jsonrpc": "2.0",
  178. "id": this.uuid(),
  179. "method": "Recharge.return",
  180. "params": {
  181. "out_trade_number": out_trade_number,
  182. }
  183. },{
  184. headers:{
  185. Authorization: "jwt " + token,
  186. }
  187. }).then(response=>{
  188. if(parseInt(response.data.result.errno)==1000){
  189. this.money = response.data.result.money.toFixed(2);
  190. }
  191. })
  192. },
  193. checkout(){
  194. var token = this.game.get("access_token") || this.game.fget("access_token");
  195. this.game.checkout(this,token,(new_access_token)=>{
  196. this.connect();
  197. this.login();
  198. this.user_package();
  199. this.buy_prop();
  200. this.unlock_package_number();
  201. this.show_pet();
  202. this.use_prop();
  203. this.active_tree();
  204. });
  205. },
  206. connect(){
  207. // socket连接
  208. this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
  209. this.socket.on('connect', ()=>{
  210. this.game.print("开始连接服务端");
  211. var id = this.game.fget("id");
  212. this.socket.emit("login",{"uid":id});
  213. this.socket.emit("user_prop");
  214. });
  215. },
  216. login(){
  217. this.socket.on("login_response",(message)=>{
  218. this.settings_info.orchard = message.orchard_settings;
  219. this.settings_info.user=message.user_settings;
  220. this.game.fsave({
  221. "orchard_settings":message.orchard_settings,
  222. "user_settings":message.user_settings,
  223. });
  224. this.game.save({
  225. "tree_total":message.tree_total,
  226. "user_tree_number":message.user_tree_number,
  227. "user_tree_list":message.user_tree_list,
  228. "tree_status":message.tree_status,
  229. "pet_food_num":message.pet_food_num,
  230. "fertilizer_num":message.fertilizer_num,
  231. "waters":message.waters,
  232. "shears":message.shears,
  233. });
  234. setTimeout(()=>{
  235. // 通知种植园页面获取到了当前用户的种植信息
  236. api.sendEvent({
  237. name: 'user_tree_data',
  238. extra: {}
  239. });
  240. },500);
  241. });
  242. },
  243. user_package(){
  244. // 用户背包道具列表
  245. this.socket.on("user_prop_response",(message)=>{
  246. this.game.fsave({
  247. "user_package":message.data,
  248. });
  249. // 界面中的道具信息
  250. this.game.save({
  251. "pet_food_num":message.pet_food_num,
  252. "fertilizer_num":message.fertilizer_num,
  253. });
  254. api.sendEvent({
  255. name: 'update_prop_data',
  256. extra: {
  257.  
  258. }
  259. });
  260.  
  261. })
  262. },
  263. go_index(){
  264. this.game.goWin("root");
  265. },
  266. go_friends(){
  267. this.game.goFrame("friends","friends.html",this.current);
  268. this.game.goFrame("friend_list","friend_list.html",this.current,{
  269. x: 0,
  270. y: 190,
  271. w: 'auto',
  272. h: 'auto',
  273. },null,true);
  274. },
  275. go_home(){
  276. this.game.goWin("user","user.html", this.current);
  277. },
  278. go_orchard_shop(){
  279. // 种植园商店
  280. this.game.goFrame("orchard_shop","shop.html", this.current,null,{
  281. type:"push",
  282. subType:"from_top",
  283. duration:300
  284. });
  285. },
  286. go_my_package(){
  287. // 我的背包
  288. this.game.goFrame("package","package.html", this.current,null,{
  289. type:"push",
  290. subType:"from_top",
  291. duration:300
  292. });
  293. },
  294. buy_prop(){
  295. api.addEventListener({
  296. name: 'buy_prop'
  297. }, (ret, err)=>{
  298. if( ret ){
  299. // 用户购买道具
  300. this.socket.emit("user_buy_prop",ret.value);
  301. }
  302. });
  303. this.socket.on("user_buy_prop_response",(message)=>{
  304. alert(message.errmsg);
  305.  
  306. })
  307. },
  308. use_prop(){
  309. // 使用道具
  310. var pid = 0;
  311. api.addEventListener({
  312. name: 'use_prop'
  313. }, (ret, err)=>{
  314. if( ret ){
  315. // 用户使用道具
  316. pid = ret.value.pid;
  317. this.socket.emit("use_prop",ret.value.pid);
  318. }
  319. });
  320.  
  321. this.socket.on("prop_use_response",(message)=>{
  322. if(parseInt(message.errno) === 1000){
  323. api.sendEvent({
  324. name: 'prop_use_success',
  325. extra: {
  326. pid: pid
  327. }
  328. });
  329. }else{
  330. api.alert({
  331. title: '提示',
  332. msg: message.errmsg,
  333. });
  334.  
  335. }
  336. });
  337. },
  338. unlock_package_number(){
  339. api.addEventListener({
  340. name: 'unlock_package_number'
  341. }, (ret, err)=>{
  342. if( ret ){
  343. // 用户购买道具
  344. this.socket.emit("unlock_package");
  345. }
  346. });
  347.  
  348. this.socket.on("unlock_package_response",(message)=>{
  349. if(parseInt(message.errno) === 1000){
  350. api.sendEvent({
  351. name: 'unlock_package_success',
  352. extra: {
  353. }
  354. });
  355. }else{
  356. api.alert({
  357. title: '提示',
  358. msg: message.errmsg,
  359. });
  360.  
  361. }
  362.  
  363. })
  364. },
  365. show_pet(){
  366. // 显示宠物
  367. this.socket.emit("pet_show");
  368. this.socket.on("pet_show_response",(message)=>{
  369. if(parseInt(message.errno) === 1000){
  370. // 把宠物信息保存到本地
  371. this.game.save({"pet_list":message.pet_list})
  372. this.game.save({"pet_number":message.pet_number})
  373. setTimeout(()=>{
  374. api.sendEvent({
  375. name: 'pet_show_success',
  376. extra: {}
  377. });
  378. },500);
  379. }else{
  380. api.alert({
  381. title: '提示',
  382. msg: message.errmsg,
  383. });
  384. }
  385.  
  386. })
  387. },
  388. active_tree(){
  389. // 激活树桩
  390. api.addEventListener({
  391. name: 'active_tree'
  392. }, (ret, err)=>{
  393. if( ret ){
  394. this.socket.emit("active_tree");
  395. }
  396. });
  397.  
  398. this.socket.on("active_tree_response",(message)=>{
  399. if(parseInt(message.errno) === 1000){
  400. // 更新数据到本地
  401. api.sendEvent({
  402. name: 'active_tree_success',
  403. extra: {}
  404. });
  405. }else{
  406. api.alert({
  407. title: '提示',
  408. msg: message.errmsg,
  409. });
  410. }
  411.  
  412. })
  413. }
  414. }
  415. });
  416. }
  417. </script>
  418. </body>
  419. </html>

前端显示剪刀和浇水的次数:orchard.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>用户中心</title>
  5. <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  6. <meta charset="utf-8">
  7. <link rel="stylesheet" href="../static/css/main.css">
  8. <script src="../static/js/vue.js"></script>
  9. <script src="../static/js/axios.js"></script>
  10. <script src="../static/js/main.js"></script>
  11. <script src="../static/js/uuid.js"></script>
  12. <script src="../static/js/settings.js"></script>
  13. <script src="../static/js/socket.io.js"></script>
  14. </head>
  15. <body>
  16. <div class="app orchard orchard-frame" id="app">
  17. <div class="background">
  18. <img class="grassland2" src="../static/images/grassland2.png" alt="">
  19. <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
  20. <img class="stake1" src="../static/images/stake1.png" alt="">
  21. <img class="stake2" src="../static/images/stake2.png" alt="">
  22. </div>
  23. <div class="pet-box">
  24. <div class="pet">
  25. <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
  26. </div>
  27. <div class="pet" v-if="pet_number > 1">
  28. <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt="">
  29. </div>
  30. <div class="pet turned_off" v-if="pet_number==1">
  31. <img class="turned_image" src="../static/images/turned_off.png" alt="">
  32. <p>请购买宠物</p>
  33. </div>
  34. </div>
  35. <div class="tree-list">
  36. <div class="tree-box">
  37. <div class="tree" v-for="tree in user_tree_data.user_tree_list">
  38. <img :src="tree_img(tree.status)" alt="">
  39. </div>
  40. <!-- 已激活但是未种植的树桩列表 -->
  41. <div class="tree" v-for="i in active_tree">
  42. <img src="../static/images/tree1.png" alt="">
  43. </div>
  44. <!-- 未激活树桩列表 -->
  45. <div @click="unlock_tree" class="tree" v-for="i in lock_tree">
  46. <img src="../static/images/tree0.png" alt="">
  47. </div>
  48. </div>
  49.  
  50. </div>
  51. <div class="prop-list">
  52. <div class="prop">
  53. <img src="../static/images/prop1.png" alt="">
  54. <span>{{fertilizer_num}}</span>
  55. <p>化肥</p>
  56. </div>
  57. <div class="prop">
  58. <img src="../static/images/prop2.png" alt="">
  59. <span>{{shears}}</span>
  60. <p>修剪</p>
  61. </div>
  62. <div class="prop">
  63. <img src="../static/images/prop3.png" alt="">
  64. <span>{{waters}}</span>
  65. <p>浇水</p>
  66. </div>
  67. <div class="prop">
  68. <img src="../static/images/prop4.png" alt="">
  69. <span>{{pet_food_num}}</span>
  70. <p>宠物粮</p>
  71. </div>
  72. </div>
  73. <div class="pet-hp-list">
  74. <div class="pet-hp" v-for="pet in pet_list">
  75. <p>宠物1 饱食度</p>
  76. <div class="hp">
  77. <div :style="{width: pet.hp+'%'}" class="process">{{pet.hp}}%</div>
  78. </div>
  79. </div>
  80. </div>
  81. </div>
  82. <script>
  83. apiready = function(){
  84. init();
  85. new Vue({
  86. el:"#app",
  87. data(){
  88. return {
  89. namespace: '/mofang',
  90. token:"",
  91. socket: null,
  92. pet_food_num:0, // 宠物粮数量
  93. fertilizer_num:0, // 化肥数量
  94. waters:0, // 浇水次数
  95. shears:0, // 剪刀次数
  96. pet_list:[],
  97. user_tree_data:{
  98. "total_tree": 9, // 总树桩数量
  99. "user_tree_number": 0, // 当前用户激活树桩数量
  100. "user_tree_list":[ // 当前种植的树桩列表状态
  101.  
  102. ],
  103. },
  104. tree_status:{
  105.  
  106. },
  107. // user_tree_data:{
  108. // "total_tree":9, // 总树桩数量
  109. // "user_tree_number": 5, // 当前用户激活树桩数量
  110. // "user_tree_list":[ // 当前种植的树桩列表状态
  111. // { // 树桩状态
  112. // "time":1609808084, // 种植时间
  113. // "status":4, // 植物状态
  114. // "has_time": 300, // 状态时间
  115. // },
  116. // ],
  117. // },
  118. // tree_status:{
  119. // "tree_status_0": "tree0.png", // 树桩
  120. // "tree_status_1": "tree1.png", // 空桩
  121. // "tree_status_2": "tree2.png", // 幼苗
  122. // "tree_status_3": "tree3.png", // 成长
  123. // "tree_status_4": "tree4.png", // 成熟
  124. // },
  125. pet_number:[],
  126. timeout: 0,
  127. prev:{name:"",url:"",params:{}},
  128. current:{name:"orchard",url:"orchard.html",params:{}},
  129. }
  130. },
  131. computed:{
  132. // 已激活但是未使用的树桩
  133. active_tree(){
  134. return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
  135. },
  136. // 未激活的剩余树桩
  137. lock_tree(){
  138. return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
  139. },
  140. },
  141. created(){
  142. this.show_pet_list();
  143. this.show_tree_list();
  144. this.get_prop_list();
  145. },
  146. methods:{
  147. get_prop_list(){
  148. // 更新道具列表信息
  149. api.addEventListener({
  150. name: 'update_prop_data'
  151. }, (ret, err)=>{
  152. if( ret ){
  153. this.pet_food_num = this.game.get("pet_food_num");
  154. this.fertilizer_num = this.game.get("fertilizer_num");
  155. }
  156. });
  157.  
  158. },
  159. tree_img(status){
  160. return '../static/images/'+this.tree_status[status];
  161. },
  162. show_pet_list(){
  163. api.addEventListener({
  164. name: 'pet_show_success'
  165. }, (ret, err)=>{
  166. if( ret ){
  167. // 用户购买道具
  168. this.pet_list = this.game.get("pet_list");
  169. this.pet_number = parseInt(this.game.get("pet_number"));
  170. }
  171. });
  172. },
  173. show_tree_list(){
  174. api.addEventListener({
  175. name: 'user_tree_data'
  176. }, (ret, err)=>{
  177. if( ret ){
  178. // 用户种植植物信息
  179. this.user_tree_data.tree_total = parseInt(this.game.get("tree_total"));
  180. this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number"));
  181. this.user_tree_data.user_tree_list = this.game.get("user_tree_list");
  182. this.tree_status = this.game.get("tree_status");
  183. this.pet_food_num = this.game.get("pet_food_num");
  184. this.fertilizer_num = this.game.get("fertilizer_num");
  185. this.waters = this.game.get("waters");
  186. this.shears = this.game.get("shears");
  187. }
  188. });
  189. },
  190. unlock_tree(){
  191. // 激活树桩通知
  192. api.confirm({
  193. title: '提示',
  194. msg: '是否激活树桩?',
  195. buttons: ['确定', '取消']
  196. }, (ret, err)=>{
  197. if( ret.buttonIndex == 1 ){
  198. api.sendEvent({
  199. name: 'active_tree',
  200. extra: {
  201.  
  202. }
  203. });
  204. }
  205. });
  206.  
  207. // 激活成功!
  208. api.addEventListener({
  209. name: 'active_tree_success'
  210. }, (ret, err)=>{
  211. if( ret ){
  212. // 新增树桩的数量
  213. this.user_tree_data.user_tree_number+=1;
  214. this.game.save({"user_tree_number": this.user_tree_data.user_tree_number});
  215. }
  216. });
  217. }
  218. }
  219. });
  220. }
  221. </script>
  222. </body>
  223. </html>

前端显示剪刀和浇水的次数:my_orchard.html

day118:MoFang:根据激活/未激活的状态分别显示树桩&种植植物&解锁树桩&化肥/修剪/浇水/宠物粮小图标数字的显示的更多相关文章

  1. CentOS 6.x启动时网卡eth0未激活

    简述 安装CentOS 6.x操作系统后,开机时发现没有网络,最后发现系统启动时未激活网卡 - 因为只有在激活状态的网卡才能去连接网络,进行网络通讯. 简述 激活网卡eth0 激活网卡eth0 执行& ...

  2. ecshop后台"云提醒未激活 点击激活" 补丁删除方法

    ecshop后台"云提醒未激活 点击激活" 补丁删除方法 ECSHOP教程/ ecshop教程网(www.ecshop119.com) 2015-01-15   ecshop后台提 ...

  3. firebug调试js时提示调试器未激活处理办法

    firebug是web开发中最常用的分析调试软件,不过我今天使用在调试百度在线编辑器UEditor时一直提示调试器未激活. 从使用经验来看不应该啊,我都下了断点了为什么会提示调试器未激活呢!多次载入网 ...

  4. 打开phpmyadmin显示高级功能尚未完全设置部分功能未激活

    问题:老师,打开phpmyadmin显示高级功能尚未完全设置部分功能未激活,应该如何解决? 这是前一阵子学生问过我的一个问题,今天我就在博客里解答你的疑问吧. 总共三步可以搞定 1.导入相关文件到数据 ...

  5. Ribbon Workbench 与此流程相关的流程操作未激活

    问题描述:使用Ribbon Workbench 打开解决方案时报 :与此流程相关的流程操作未激活 解决方法 :ribbon 导航--系统定置--流程中心--流程--CustomiseRibbon -- ...

  6. 优步uber司机申请了为什么一直没有通过审核,帐号也显示未激活

    优步uber现在是越来越火,申请注册成为优步uber司机的人数也日剧增多,申请了的车主都知道,申请后要等待审核,审核通过才可以激活帐号,快的运气好的,三五天不到一个星期就激活了,慢点的得大半个月,还有 ...

  7. phpMyAdmin 高级功能尚未完全设置,部分功能未激活(转载)

    phpMyAdmin 高级功能尚未完全设置,部分功能未激活.请点击这里查看原因. 第一步: 使用Mysql管理员帐号通过phpmyadmin登陆,然后点击“导入”,然后点击“浏览”按钮,找到phpmy ...

  8. day119:MoFang:宠物的状态改动&宠物粮道具的使用&宠物死亡处理

    目录 1.宠物的状态改动 2.宠物粮道具的使用 3.宠物死亡处理 1.宠物的状态改动 1.在setting表中为每个宠物配置生命周期时间 因为宠物有多个,每个宠物会有不同的初始生命的饥饿时间,所以我们 ...

  9. 产品激活 比如Windows激活 , office激活 等激活的原理是什么? KMS等激活工具安全吗?

    什么是密钥管理服务 (KMS)? 密钥管理服务 (KMS) 允许在本地网络上激活产品.这样,单台计算机不必连接至 Microsoft 便可激活产品.需要将一台计算机配置为 KMS 主机.管理员必须为 ...

  10. SpringBoot实现网站注册,邮件激活码激活功能

    项目源码:https://gitee.com/smfx1314/springbootemail 上一篇文章已经讲到如何springboot如何实现邮件的发送,趁热打铁,这篇文章实现如下功能. 很多网站 ...

随机推荐

  1. Alibaba Cloud Linux 3.2104 64位安装nginx-1.16.1

    1   下载nginx 从nginx官网 http://nginx.org/ 下载新的稳定版本nginx 并上传到linux服务器  2  安装nginx 所需要的扩展 yum -y install ...

  2. 简单了解promise

    promise是什么: JavaScript中存在很多异步操作, Promise将异步操作队列化,按照期望的顺序执行,返回 符合预期的结果.可以通过链式调用多个 Promise达到我们的目的. Pro ...

  3. 20193314白晨阳 实验一《Python程序设计》实验报告

    实验一 20193314 2020-2021-2 <Python程序设计>实验1报告 课程:<Python程序设计> 班级: 201933 姓名: 白晨阳 学号:2019331 ...

  4. MySQL分库分表原理

    转自https://www.jianshu.com/p/7aec260ca1a2 前言 在互联网还未崛起的时代,我们的传统应用都有这样一个特点:访问量.数据量都比较小,单库单表都完全可以支撑整个业务. ...

  5. MP逻辑删除

    在实体类中加上@TableLogic注解 在配置类中加入逻辑删除插件

  6. antd动态tree 自定义样式

    import React, { useEffect, useState } from 'react';import { Tree } from 'antd';import './index.less' ...

  7. Spring整合Redis学习笔记

    1 Spring-Data-Redis 1.1 Spring-Data-Redis简介   Spring-Data-Redis(简称SDR)对Redis的Key-Value数据存储操作提供了更高层次的 ...

  8. springboot项目导出excel实现

    参见:https://blog.csdn.net/duli_0105/article/details/102809936

  9. WSL/Ubutun安装GCC

    1.更新升级软件包 $ sudo apt-get update && sudo apt-get upgrade -y 2.清理关联软件包 $ sudo apt autoremove - ...

  10. Winform 应用DotnetBar

    Winform 使用NotNetBar namespace WindowsFormExample { public partial class FrmMain : Office2007Form { p ...