day119:MoFang:宠物的状态改动&宠物粮道具的使用&宠物死亡处理
目录
1.宠物的状态改动
1.在setting表中为每个宠物配置生命周期时间
因为宠物有多个,每个宠物会有不同的初始生命的饥饿时间,所以我们提前在mysql中进行配置参数.
INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (29, 'pet_hp_max_2', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '1号宠物的最大生命周期时间', '86400'); INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (30, 'pet_hp_max_3', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '2号宠物的最大生命周期时间', '3600'); INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (31, 'pet_hp_max_4', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '3号宠物的最大生命周期时间', '86400'); INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (32, 'pet_hp_max_5', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '4号宠物的最大生命周期时间', '86400');
from application import socketio from flask import request from application.apps.users.models import User from flask_socketio import join_room, leave_room from application import mongo from .models import Goods,Setting,db from status import APIStatus as status from message import ErrorMessage as errmsg # 建立socket通信 # @socketio.on("connect", namespace="/mofang") # def user_connect(): # """用户连接""" # print("用户%s连接过来了!" % request.sid) # # 主动响应数据给客户端 # socketio.emit("server_response","hello",namespace="/mofang") # 断开socket通信 @socketio.on("disconnect", namespace="/mofang") def user_disconnect(): print("用户%s退出了种植园" % request.sid ) @socketio.on("login", namespace="/mofang") def user_login(data): # 分配房间 room = data["uid"] join_room(room) # 保存当前用户和sid的绑定关系 # 判断当前用户是否在mongo中有记录 query = { "_id": data["uid"] } ret = mongo.db.user_info_list.find_one(query) if ret: mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}}) else: mongo.db.user_info_list.insert_one({ "_id": data["uid"], "sid": request.sid, }) # 返回种植园的相关配置参数 orchard_settings = {} setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all() """ 现在的格式: [<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>] 需要返回的格式: { package_number_base:4, package_number_max: 32, ... } """ for item in setting_list: orchard_settings[item.name] = item.value # 返回当前用户相关的配置参数 user_settings = {} # 从mongo中查找用户信息,判断用户是否激活了背包格子 user_dict = mongo.db.user_info_list.find_one({"sid":request.sid}) # 背包格子 if user_dict.get("package_number") is None: user_settings["package_number"] = orchard_settings.get("package_number_base",4) mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}}) else: user_settings["package_number"] = user_dict.get("package_number") """种植园植物信息""" # 总树桩数量 setting = Setting.query.filter(Setting.name == "user_total_tree").first() if setting is None: tree_total = 9 else: tree_total = int(setting.value) # 用户已经激活的树桩 setting = Setting.query.filter(Setting.name == "user_active_tree").first() if setting is None: user_tree_number = 3 else: user_tree_number = int(setting.value) user_tree_number = user_dict.get("user_tree_number",user_tree_number) # 种植的植物列表 user_tree_list = user_dict.get("user_tree_list", []) key = 0 for tree_item in user_tree_list: tree_item["status"] = "tree_status_%s" % int(tree_item["status"]) # 植物状态 tree_item["water_time"] = redis.ttl("user_tree_water_%s_%s" % (data["uid"],key)) tree_item["growup_time"] = redis.ttl("user_tree_growup_%s_%s" % (data["uid"],key)) key+=1 # 植物状态信息 status_list = [ "tree_status_0", "tree_status_1", "tree_status_2", "tree_status_3", "tree_status_4", ] setting_list = Setting.query.filter(Setting.name.in_(status_list)).all() tree_status = {} for item in setting_list: tree_status[item.name] = item.value """显示植物相关道具""" # 获取背包中的化肥和宠物粮 fertilizer_num,pet_food_num = get_package_prop_list(user_dict) # 获取剪刀和浇水 # 只有植物处于成长期才会允许裁剪 # 只有植物处于幼苗期才会允许浇水 waters = 0 shears = 0 user_tree_list = user_dict.get("user_tree_list",[]) if len(user_tree_list) > 0: key = 0 for tree in user_tree_list: if (tree["status"] == "tree_status_%s" % 2) and int(tree.get("waters",0)) == 0: """处于幼苗期""" # 判断只有种植指定时间以后的幼苗才可以浇水 interval_time = redis.ttl("user_tree_water_%s_%s" % (user_dict.get("_id"), key) ) if interval_time==-2: waters+=1 elif (tree["status"] == "tree_status_%s" % 3) and int(tree.get("shears",0)) == 0: """处于成长期""" interval_time = redis.ttl("user_tree_shears_%s_%s" % (user_dict.get("_id"), key)) if interval_time==-2: shears+=1 key+=1 message = { "errno":status.CODE_OK, "errmsg":errmsg.ok, "orchard_settings":orchard_settings, "user_settings":user_settings, "tree_total":tree_total, "user_tree_number":user_tree_number, "user_tree_list":user_tree_list, "fertilizer_num":fertilizer_num, "pet_food_num":pet_food_num, "tree_status":tree_status, "waters":waters, "shears":shears, } print(message) socketio.emit("login_response", message, namespace="/mofang", room=room) def get_package_prop_list(user_dict): fertilizer_num = 0 pet_food_num = 0 prop_list = user_dict.get("prop_list", {}) for prop_item, num in prop_list.items(): pid = prop_item.split("_")[-1] num = int(num) prop_obj = Goods.query.get(pid) if prop_obj.prop_type == 2: # 提取化肥 fertilizer_num += num elif prop_obj.prop_type == 3: # 提取宠物粮 pet_food_num += num return fertilizer_num,pet_food_num @socketio.on("user_buy_prop", namespace="/mofang") def user_buy_prop(data): """用户购买道具""" room = request.sid # 从mongo中获取当前用户信息 user_info = mongo.db.user_info_list.find_one({"sid":request.sid}) user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room) return # 判断背包物品存储是否达到上限 use_package_number = int(user_info.get("use_package_number",0)) # 当前诗经使用的格子数量 package_number = int(user_info.get("package_number",0)) # 当前用户已经解锁的格子数量 # 本次购买道具需要使用的格子数量 setting = Setting.query.filter(Setting.name == "td_prop_max").first() if setting is None: td_prop_max = 10 else: td_prop_max = int(setting.value) # 计算购买道具以后需要额外占用的格子数量 if ("prop_%s" % data["pid"]) in user_info.get("prop_list",{}): """曾经购买过当前道具""" prop_num = int( user_info.get("prop_list")["prop_%s" % data["pid"]]) # 购买前的道具数量 new_prop_num = prop_num+int(data["num"]) # 如果成功购买道具以后的数量 old_td_num = prop_num // td_prop_max if prop_num % td_prop_max > 0: old_td_num+=1 new_td_num = new_prop_num // td_prop_max if new_prop_num % td_prop_max > 0: new_td_num+=1 td_num = new_td_num - old_td_num else: """新增购买的道具""" # 计算本次购买道具需要占用的格子数量 if int(data["num"]) > td_prop_max: """需要多个格子""" td_num = int(data["num"]) // td_prop_max if int(data["num"]) % td_prop_max > 0: td_num+=1 else: """需要一个格子""" td_num = 1 if use_package_number+td_num > package_number: """超出存储上限""" socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_PACKAGE, "errmsg": errmsg.no_package}, namespace="/mofang", room=room) return # 从mysql中获取商品价格 prop = Goods.query.get(data["pid"]) if user.money > 0: # 当前商品需要通过RMB购买 if float(user.money) < float(prop.price) * int(data["num"]): socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room) return else: """当前通过果子进行购买""" if int(user.credit) < int(prop.credit) * int(data["num"]): socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough}, namespace="/mofang", room=room) return # 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额 query = {"sid": request.sid} if user_info.get("prop_list") is None: """此前没有购买任何道具""" message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}} mongo.db.user_info_list.update_one(query,message) else: """此前有购买了道具""" prop_list = user_info.get("prop_list") # 道具列表 if ("prop_%s" % prop.id) in prop_list: """如果再次同一款道具""" prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"]) else: """此前没有购买过这种道具""" prop_list[("prop_%s" % prop.id)] = int(data["num"]) mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}}) # 扣除余额或果子 if prop.price > 0: user.money = float(user.money) - float(prop.price) * int(data["num"]) else: user.credit = int(user.credit) - int(prop.credit) * int(data["num"]) db.session.commit() # 返回购买成功的信息 socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room) # 返回最新的用户道具列表 user_prop() @socketio.on("user_prop", namespace="/mofang") def user_prop(): """用户道具""" userinfo = mongo.db.user_info_list.find_one({"sid":request.sid}) prop_list = userinfo.get("prop_list",{}) prop_id_list = [] for prop_str,num in prop_list.items(): pid = int(prop_str[5:]) prop_id_list.append(pid) data = [] prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all() setting = Setting.query.filter(Setting.name == "td_prop_max").first() if setting is None: td_prop_max = 10 else: td_prop_max = int(setting.value) for prop_data in prop_list_data: num = int( prop_list[("prop_%s" % prop_data.id)]) if td_prop_max > num: data.append({ "num": num, "image": prop_data.image, "pid": prop_data.id, "pty": prop_data.prop_type, }) else: padding_time = num // td_prop_max padding_last = num % td_prop_max arr = [{ "num": td_prop_max, "image": prop_data.image, "pid": prop_data.id, "pty": prop_data.prop_type, }] * padding_time if padding_last != 0: arr.append({ "num": padding_last, "image": prop_data.image, "pid": prop_data.id, "pty": prop_data.prop_type, }) data = data + arr mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"use_package_number":len(data)}}) room = request.sid fertilizer_num,pet_food_num = get_package_prop_list(userinfo) socketio.emit("user_prop_response", { "errno": status.CODE_OK, "errmsg": errmsg.ok, "data":data, "fertilizer_num":fertilizer_num, "pet_food_num":pet_food_num, }, namespace="/mofang", room=room) @socketio.on("unlock_package", namespace="/mofang") def unlock_package(): """解锁背包""" # 从mongo获取当前用户解锁的格子数量 user_info = mongo.db.user_info_list.find_one({"sid":request.sid}) user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("unlock_package_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room) return package_number = int(user_info.get("package_number")) num = 7 - (32 - package_number) // 4 # 没有解锁的格子 # 从数据库中获取解锁背包的价格 setting = Setting.query.filter(Setting.name == "package_unlock_price_%s" % num).first() if setting is None: unlock_price = 0 else: unlock_price = int(setting.value) # 判断是否有足够的积分或者价格 room = request.sid if user.money < unlock_price: socketio.emit("unlock_package_response", {"errno": status.CODE_NO_MONEY, "errmsg": errmsg.money_no_enough}, namespace="/mofang", room=room) return # 解锁成功 user.money = float(user.money) - float(unlock_price) db.session.commit() # mongo中调整数量 mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": package_number+1}}) # 返回解锁的结果 socketio.emit("unlock_package_response", { "errno": status.CODE_OK, "errmsg": errmsg.ok}, namespace="/mofang", room=room) import math from application import redis @socketio.on("pet_show", namespace="/mofang") def pet_show(): """显示宠物""" room = request.sid user_info = mongo.db.user_info_list.find_one({"sid":request.sid}) user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("pet_show_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room) return # 获取宠物列表 pet_list = user_info.get("pet_list", []) """ pet_list: [ { "pid":11, "image":"pet.png", "hp":100%, "created_time":xxxx-xx-xx xx:xx:xx, "skill":"70%", "has_time":30天 }, ] """ # 从redis中提取当前宠物的饱食度和有效期 # 初始化宠物的生命周期 pet_hp_max = 10000 for key,pet in enumerate(pet_list): setting = Setting.query.filter(Setting.name == "pet_hp_max_%s" % pet["pid"]).first() if setting is None: pet_hp_max = 7200 else: pet_hp_max = int(setting.value) pet["hp"] = math.ceil( redis.ttl("pet_%s_%s_hp" % (user.id,key+1)) / pet_hp_max * 100 ) pet["has_time"] = redis.ttl("pet_%s_%s_expire" % (user.id,key+1)) pet["hp_time"] = redis.ttl("pet_%s_%s_hp" % (user.id,key+1)) pet["pet_hp_max"] = pet_hp_max pet_number = user_info.get("pet_number", 1) socketio.emit( "pet_show_response", { "errno": status.CODE_OK, "errmsg": errmsg.ok, "pet_list": pet_list, "pet_number": pet_number, }, namespace="/mofang", room=room ) from datetime import datetime @socketio.on("use_prop", namespace="/mofang") def use_prop(pid): """使用道具""" room = request.sid # 获取mongo中的用户信息 user_info = mongo.db.user_info_list.find_one({"sid": request.sid}) # 获取mysql中的用户信息 user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("pet_use_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists}, namespace="/mofang", room=room) return # 获取道具 prop_data = Goods.query.get(pid) if prop_data is None: socketio.emit("pet_use_response", {"errno":status.CODE_NO_SUCH_PROP,"errmsg":errmsg.not_such_prop}, namespace="/mofang", room=room) return if int(prop_data.prop_type) == 0: """使用植物道具""" # 1. 判断当前的植物数量是否有空余 tree_list = user_info.get("user_tree_list", []) # 当前用户最多可种植的数量 setting = Setting.query.filter(Setting.name == "user_active_tree").first() if setting is None: user_tree_number = 3 else: user_tree_number = int(setting.value) user_tree_number = user_info.get("user_tree_number", user_tree_number) if len(tree_list) >= user_tree_number: socketio.emit("prop_use_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty}, namespace="/mofang", room=room) return # 使用道具 mongo.db.user_info_list.update_one({"sid":room},{"$push":{"user_tree_list": { # 植物状态 "time": int(datetime.now().timestamp()), # 种植时间 "status": 2, # 植物状态,2表示幼苗状态 "waters": 0, # 浇水次数 "shears": 0, # 使用剪刀次数 } }}) # 从种下去到浇水的时间 pipe = redis.pipeline() pipe.multi() setting = Setting.query.filter(Setting.name == "tree_water_time").first() if setting is None: tree_water_time = 3600 else: tree_water_time = int(setting.value) # 必须等时间到了才可以浇水 pipe.setex("user_tree_water_%s_%s" % (user.id,len(tree_list)),int(tree_water_time),"_") # 必须等时间到了才可以到成长期 setting = Setting.query.filter(Setting.name == "tree_growup_time").first() if setting is None: tree_growup_time = 3600 else: tree_growup_time = int(setting.value) pipe.setex("user_tree_growup_%s_%s" % (user.id,len(tree_list)),tree_growup_time, "_") pipe.execute() user_login({"uid": user.id}) if int(prop_data.prop_type) == 1: """使用宠物道具""" # 1. 判断当前的宠物数量 # 获取宠物列表 pet_list = user_info.get("pet_list", []) if len(pet_list) > 1: socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room) return # 2. 是否有空余的宠物栏位 pet_number = user_info.get("pet_number",1) if pet_number <= len(pet_list): socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room) return # 3. 初始化当前宠物信息 # 获取有效期和防御值 exp_data = Setting.query.filter(Setting.name=="pet_expire_%s" % pid).first() ski_data = Setting.query.filter(Setting.name=="pet_skill_%s" % pid).first() if exp_data is None: # 默认7天有效期 expire = 7 else: expire = exp_data.value if ski_data is None: skill = 10 else: skill = ski_data.value # 在redis中设置当前宠物的饱食度 pipe = redis.pipeline() pipe.multi() setting = Setting.query.filter(Setting.name == ("pet_hp_max_%s" % pid)).first() if setting is None: pet_hp_max = 7200 else: pet_hp_max = int(setting.value) pipe.setex("pet_%s_%s_hp" % (user.id, len(pet_list)+1), pet_hp_max, "_") pipe.setex("pet_%s_%s_expire" % (user.id, len(pet_list)+1), int(expire)*24*60*60, "_") pipe.execute() # 基本保存到mongo mongo.db.user_info_list.update_one({"sid":request.sid},{"$push":{"pet_list":{ "pid": pid, "image": prop_data.image, "created_time": int( datetime.now().timestamp() ), "skill": skill, }}}) """ db.user_info_list.updateOne({"_id":"52"},{"$push":{"pet_list":{ "pid": 2, "image": "pet1.png", "created_time": 1609727155, "skill": 30, }}}) """ pet_show() # 扣除背包中的道具数量 prop_list = user_info.get("prop_list",{}) for key,value in prop_list.items(): if key == ("prop_%s" % pid): if int(value) > 1: prop_list[key] = int(value) - 1 else: prop_list.pop(key) break mongo.db.user_info_list.update_one({"sid":room},{"$set":{"prop_list":prop_list}}) user_prop() socketio.emit("prop_use_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok}, namespace="/mofang", room=room) @socketio.on("active_tree", namespace="/mofang") def active_tree(): """激活树桩""" room = request.sid # 获取mongo中的用户信息 user_info = mongo.db.user_info_list.find_one({"sid": request.sid}) # 获取mysql中的用户信息 user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("active_tree_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists}, namespace="/mofang", room=room) return # 判断树桩是否达到上限 tree_number_data = Setting.query.filter(Setting.name == "user_active_tree").first() total_tree_data = Setting.query.filter(Setting.name == "user_total_tree").first() if tree_number_data is None: tree_number = 1 else: tree_number = tree_number_data.value if total_tree_data is None: total_tree = 9 else: total_tree = int(total_tree_data.value) user_tree_number = int(user_info.get("user_tree_number",tree_number)) if user_tree_number >= total_tree: socketio.emit("active_tree_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty}, namespace="/mofang", room=room) return # 扣除激活的果子数量 ret = Setting.query.filter(Setting.name == "active_tree_price").first() if ret is None: active_tree_price = 100 * user_tree_number else: active_tree_price = int(ret.value) * user_tree_number if active_tree_price > int(user.credit): socketio.emit("active_tree_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough}, namespace="/mofang", room=room) return user.credit = int(user.credit) - active_tree_price db.session.commit() mongo.db.user_info_list.update_one({"sid":room},{"$set":{"user_tree_number":user_tree_number+1}}) socketio.emit("active_tree_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok}, namespace="/mofang", room=room) return
3.前端:进行倒计时,对宠物的状态进行改动
<!DOCTYPE html> <html> <head> <title>用户中心</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> <script src="../static/js/socket.io.js"></script> </head> <body> <div class="app orchard orchard-frame" id="app"> <div class="background"> <img class="grassland2" src="../static/images/grassland2.png" alt=""> <img class="mushroom1" src="../static/images/mushroom1.png" alt=""> <img class="stake1" src="../static/images/stake1.png" alt=""> <img class="stake2" src="../static/images/stake2.png" alt=""> </div> <div class="pet-box"> <div class="pet"> <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt=""> </div> <div class="pet" v-if="pet_number > 1"> <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt=""> </div> <div class="pet turned_off" v-if="pet_number==1"> <img class="turned_image" src="../static/images/turned_off.png" alt=""> <p>请购买宠物</p> </div> </div> <div class="tree-list"> <div class="tree-box"> <div class="tree" v-for="tree in user_tree_data.user_tree_list"> <img :src="tree_img(tree.status)" alt=""> </div> <!-- 已激活但是未种植的树桩列表 --> <div class="tree" v-for="i in active_tree"> <img src="../static/images/tree1.png" alt=""> </div> <!-- 未激活树桩列表 --> <div @click="unlock_tree" class="tree" v-for="i in lock_tree"> <img src="../static/images/tree0.png" alt=""> </div> </div> </div> <div class="prop-list"> <div class="prop"> <img src="../static/images/prop1.png" alt=""> <span>{{fertilizer_num}}</span> <p>化肥</p> </div> <div class="prop"> <img src="../static/images/prop2.png" alt=""> <span>{{shears}}</span> <p>修剪</p> </div> <div class="prop"> <img src="../static/images/prop3.png" alt=""> <span>{{waters}}</span> <p>浇水</p> </div> <div class="prop"> <img src="../static/images/prop4.png" alt=""> <span>{{pet_food_num}}</span> <p>宠物粮</p> </div> </div> <div class="pet-hp-list"> <div class="pet-hp" v-for="pet in pet_list"> <p>宠物1 饱食度</p> <div class="hp"> <div :style="{width: pet.hp+'%',backgroundColor:bg(pet.hp)}" class="process">{{pet.hp}}%</div> </div> </div> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { namespace: '/mofang', token:"", socket: null, pet_food_num:0, // 宠物粮数量 fertilizer_num:0, // 化肥数量 waters:0, // 浇水次数 shears:0, // 剪刀次数 pet_list:[], user_tree_data:{ "total_tree": 9, // 总树桩数量 "user_tree_number": 0, // 当前用户激活树桩数量 "user_tree_list":[ // 当前种植的树桩列表状态 ], }, tree_status:{ }, // user_tree_data:{ // "total_tree":9, // 总树桩数量 // "user_tree_number": 5, // 当前用户激活树桩数量 // "user_tree_list":[ // 当前种植的树桩列表状态 // { // 树桩状态 // "time":1609808084, // 种植时间 // "status":4, // 植物状态 // "has_time": 300, // 状态时间 // }, // ], // }, // tree_status:{ // "tree_status_0": "tree0.png", // 树桩 // "tree_status_1": "tree1.png", // 空桩 // "tree_status_2": "tree2.png", // 幼苗 // "tree_status_3": "tree3.png", // 成长 // "tree_status_4": "tree4.png", // 成熟 // }, pet_number:[], timeout: 0, prev:{name:"",url:"",params:{}}, current:{name:"orchard",url:"orchard.html",params:{}}, } }, computed:{ // 已激活但是未使用的树桩 active_tree(){ return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length); }, // 未激活的剩余树桩 lock_tree(){ return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number); }, }, created(){ this.show_pet_list(); this.show_tree_list(); this.get_prop_list(); }, methods:{ bg(hp){ if(hp>90){ return "#f00"; }else if(hp>60){ return "#a00"; }else if(hp>30){ return "#600"; }else{ return "#300"; } }, get_prop_list(){ // 更新道具列表信息 api.addEventListener({ name: 'update_prop_data' }, (ret, err)=>{ if( ret ){ this.pet_food_num = this.game.get("pet_food_num"); this.fertilizer_num = this.game.get("fertilizer_num"); } }); }, tree_img(status){ return '../static/images/'+this.tree_status[status]; }, show_pet_list(){ api.addEventListener({ name: 'pet_show_success' }, (ret, err)=>{ if( ret ){ // 显示宠物相关 this.pet_list = this.game.get("pet_list"); this.pet_number = parseInt(this.game.get("pet_number")); for( let pet of this.pet_list){ setInterval(()=>{ pet.hp_time-=0.5; pet.hp = Math.ceil( pet.hp_time / pet.pet_hp_max * 100 ) },500); } } }); }, show_tree_list(){ api.addEventListener({ name: 'user_tree_data' }, (ret, err)=>{ if( ret ){ // 用户种植植物信息 this.user_tree_data.tree_total = parseInt(this.game.get("tree_total")); this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number")); this.user_tree_data.user_tree_list = this.game.get("user_tree_list"); this.tree_status = this.game.get("tree_status"); this.pet_food_num = this.game.get("pet_food_num"); this.fertilizer_num = this.game.get("fertilizer_num"); this.waters = this.game.get("waters"); this.shears = this.game.get("shears"); } }); }, unlock_tree(){ // 激活树桩通知 api.confirm({ title: '提示', msg: '是否激活树桩?', buttons: ['确定', '取消'] }, (ret, err)=>{ if( ret.buttonIndex == 1 ){ api.sendEvent({ name: 'active_tree', extra: { } }); } }); // 激活成功! api.addEventListener({ name: 'active_tree_success' }, (ret, err)=>{ if( ret ){ // 新增树桩的数量 this.user_tree_data.user_tree_number+=1; this.game.save({"user_tree_number": this.user_tree_data.user_tree_number}); } }); } } }); } </script> </body> </html>
4.前端:在倒计时中阻止宠物饱食度进入负数状态
<!DOCTYPE html> <html> <head> <title>用户中心</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> <script src="../static/js/socket.io.js"></script> </head> <body> <div class="app orchard orchard-frame" id="app"> <div class="background"> <img class="grassland2" src="../static/images/grassland2.png" alt=""> <img class="mushroom1" src="../static/images/mushroom1.png" alt=""> <img class="stake1" src="../static/images/stake1.png" alt=""> <img class="stake2" src="../static/images/stake2.png" alt=""> </div> <div class="pet-box"> <div class="pet"> <span class="popped"> <img class="pet-prop" src="../static/images/prop4.png" alt=""> </span> <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt=""> </div> <div class="pet" v-if="pet_number > 1"> <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt=""> </div> <div class="pet turned_off" v-if="pet_number==1"> <img class="turned_image" src="../static/images/turned_off.png" alt=""> <p>请购买宠物</p> </div> </div> <div class="tree-list"> <div class="tree-box"> <div class="tree" v-for="tree in user_tree_data.user_tree_list"> <img :src="tree_img(tree.status)" alt=""> </div> <!-- 已激活但是未种植的树桩列表 --> <div class="tree" v-for="i in active_tree"> <img src="../static/images/tree1.png" alt=""> </div> <!-- 未激活树桩列表 --> <div @click="unlock_tree" class="tree" v-for="i in lock_tree"> <img src="../static/images/tree0.png" alt=""> </div> </div> </div> <div class="prop-list"> <div class="prop"> <img src="../static/images/prop1.png" alt=""> <span>{{fertilizer_num}}</span> <p>化肥</p> </div> <div class="prop"> <img src="../static/images/prop2.png" alt=""> <span>{{shears}}</span> <p>修剪</p> </div> <div class="prop"> <img src="../static/images/prop3.png" alt=""> <span>{{waters}}</span> <p>浇水</p> </div> <div class="prop"> <img src="../static/images/prop4.png" alt=""> <span>{{pet_food_num}}</span> <p>宠物粮</p> </div> </div> <div class="pet-hp-list"> <div class="pet-hp" v-for="pet in pet_list"> <p>宠物1 饱食度</p> <div class="hp"> <div :style="{width: pet.hp+'%',backgroundColor:bg(pet.hp)}" class="process">{{pet.hp}}%</div> </div> </div> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { namespace: '/mofang', token:"", socket: null, pet_food_num:0, // 宠物粮数量 fertilizer_num:0, // 化肥数量 waters:0, // 浇水次数 shears:0, // 剪刀次数 pet_list:[], user_tree_data:{ "total_tree": 9, // 总树桩数量 "user_tree_number": 0, // 当前用户激活树桩数量 "user_tree_list":[ // 当前种植的树桩列表状态 ], }, tree_status:{ }, // user_tree_data:{ // "total_tree":9, // 总树桩数量 // "user_tree_number": 5, // 当前用户激活树桩数量 // "user_tree_list":[ // 当前种植的树桩列表状态 // { // 树桩状态 // "time":1609808084, // 种植时间 // "status":4, // 植物状态 // "has_time": 300, // 状态时间 // }, // ], // }, // tree_status:{ // "tree_status_0": "tree0.png", // 树桩 // "tree_status_1": "tree1.png", // 空桩 // "tree_status_2": "tree2.png", // 幼苗 // "tree_status_3": "tree3.png", // 成长 // "tree_status_4": "tree4.png", // 成熟 // }, pet_number:[], timeout: 0, prev:{name:"",url:"",params:{}}, current:{name:"orchard",url:"orchard.html",params:{}}, } }, computed:{ // 已激活但是未使用的树桩 active_tree(){ return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length); }, // 未激活的剩余树桩 lock_tree(){ return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number); }, }, created(){ this.show_pet_list(); this.show_tree_list(); this.get_prop_list(); }, methods:{ bg(hp){ if(hp>90){ return "#f00"; }else if(hp>60){ return "#a00"; }else if(hp>30){ return "#600"; }else{ return "#300"; } }, get_prop_list(){ // 更新道具列表信息 api.addEventListener({ name: 'update_prop_data' }, (ret, err)=>{ if( ret ){ this.pet_food_num = this.game.get("pet_food_num"); this.fertilizer_num = this.game.get("fertilizer_num"); } }); }, tree_img(status){ return '../static/images/'+this.tree_status[status]; }, show_pet_list(){ api.addEventListener({ name: 'pet_show_success' }, (ret, err)=>{ if( ret ){ // 显示宠物相关 this.pet_list = this.game.get("pet_list"); this.pet_number = parseInt(this.game.get("pet_number")); for( let pet of this.pet_list){ pet.timer = setInterval(()=>{ if(pet.hp<=0){ // 宠物挂了 clearInterval(pet.timer); } pet.hp_time-=0.5; pet.hp = Math.ceil( pet.hp_time / pet.pet_hp_max * 100 ) },500); } } }); }, show_tree_list(){ api.addEventListener({ name: 'user_tree_data' }, (ret, err)=>{ if( ret ){ // 用户种植植物信息 this.user_tree_data.tree_total = parseInt(this.game.get("tree_total")); this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number")); this.user_tree_data.user_tree_list = this.game.get("user_tree_list"); this.tree_status = this.game.get("tree_status"); this.pet_food_num = this.game.get("pet_food_num"); this.fertilizer_num = this.game.get("fertilizer_num"); this.waters = this.game.get("waters"); this.shears = this.game.get("shears"); } }); }, unlock_tree(){ // 激活树桩通知 api.confirm({ title: '提示', msg: '是否激活树桩?', buttons: ['确定', '取消'] }, (ret, err)=>{ if( ret.buttonIndex == 1 ){ api.sendEvent({ name: 'active_tree', extra: { } }); } }); // 激活成功! api.addEventListener({ name: 'active_tree_success' }, (ret, err)=>{ if( ret ){ // 新增树桩的数量 this.user_tree_data.user_tree_number+=1; this.game.save({"user_tree_number": this.user_tree_data.user_tree_number}); } }); } } }); } </script> </body> </html>
2.宠物粮道具的使用
1.宠物喂养的半透明图标
添加道具样式, my_orchard.html
<div class="pet"> <span class="popped"> <img class="pet-prop" src="../static/images/prop4.png" alt=""> </span> <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt=""> </div>
css样式, main.css, 代码;
.popped { transform: translate(-50%, -50%); border-radius: 5rem; height: 5rem; width: 5rem; display: block; position: absolute; opacity: 1; transition: box-shadow .5s ease-in-out, transform .07s ease-out, opacity .04s ease-in; top: 6rem; right: 0rem; box-shadow: rgb(255, 200, 200) 0px 0px 3rem inset; z-index: 100; } .popped:after { content: ''; position: absolute; top: 18%; left: 18%; background-color: rgba(191, 255, 255, 0.6); width: 1.2rem; height: 1.5rem; border-radius: 50%; transform: rotate(45deg) scale(0.8); } .popped img{ width: 4rem; height: 4rem; position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: auto; }
2.前端:根据宠物的饱食度和道具数量来控制是否显示喂食的图标-my_orchard.html
<!DOCTYPE html> <html> <head> <title>用户中心</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> <script src="../static/js/socket.io.js"></script> </head> <body> <div class="app orchard orchard-frame" id="app"> <div class="background"> <img class="grassland2" src="../static/images/grassland2.png" alt=""> <img class="mushroom1" src="../static/images/mushroom1.png" alt=""> <img class="stake1" src="../static/images/stake1.png" alt=""> <img class="stake2" src="../static/images/stake2.png" alt=""> </div> <div class="pet-box"> <div class="pet"> <span class="popped" v-if="pet_list[0] && pet_list[0].hp<50 && pet_list[0].hp>0 && pet_food_num > 0"> <img class="pet-prop" src="../static/images/prop4.png" alt=""> </span> <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt=""> </div> <div class="pet" v-if="pet_number > 1"> <span class="popped" v-if="pet_list[1] && pet_list[1].hp<50 && pet_list[1].hp>0 && pet_food_num > 0"> <img class="pet-prop" src="../static/images/prop4.png" alt=""> </span> <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt=""> </div> <div class="pet turned_off" v-if="pet_number==1"> <img class="turned_image" src="../static/images/turned_off.png" alt=""> <p>请购买宠物</p> </div> </div> <div class="tree-list"> <div class="tree-box"> <div class="tree" v-for="tree in user_tree_data.user_tree_list"> <img :src="tree_img(tree.status)" alt=""> </div> <!-- 已激活但是未种植的树桩列表 --> <div class="tree" v-for="i in active_tree"> <img src="../static/images/tree1.png" alt=""> </div> <!-- 未激活树桩列表 --> <div @click="unlock_tree" class="tree" v-for="i in lock_tree"> <img src="../static/images/tree0.png" alt=""> </div> </div> </div> <div class="prop-list"> <div class="prop"> <img src="../static/images/prop1.png" alt=""> <span>{{fertilizer_num}}</span> <p>化肥</p> </div> <div class="prop"> <img src="../static/images/prop2.png" alt=""> <span>{{shears}}</span> <p>修剪</p> </div> <div class="prop"> <img src="../static/images/prop3.png" alt=""> <span>{{waters}}</span> <p>浇水</p> </div> <div class="prop"> <img src="../static/images/prop4.png" alt=""> <span>{{pet_food_num}}</span> <p>宠物粮</p> </div> </div> <div class="pet-hp-list"> <div class="pet-hp" v-for="pet in pet_list"> <p>宠物1 饱食度</p> <div class="hp"> <div :style="{width: pet.hp+'%',backgroundColor:bg(pet.hp)}" class="process">{{pet.hp}}%</div> </div> </div> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { namespace: '/mofang', token:"", socket: null, pet_food_num:0, // 宠物粮数量 fertilizer_num:0, // 化肥数量 waters:0, // 浇水次数 shears:0, // 剪刀次数 pet_list:[], user_tree_data:{ "total_tree": 9, // 总树桩数量 "user_tree_number": 0, // 当前用户激活树桩数量 "user_tree_list":[ // 当前种植的树桩列表状态 ], }, tree_status:{ }, // user_tree_data:{ // "total_tree":9, // 总树桩数量 // "user_tree_number": 5, // 当前用户激活树桩数量 // "user_tree_list":[ // 当前种植的树桩列表状态 // { // 树桩状态 // "time":1609808084, // 种植时间 // "status":4, // 植物状态 // "has_time": 300, // 状态时间 // }, // ], // }, // tree_status:{ // "tree_status_0": "tree0.png", // 树桩 // "tree_status_1": "tree1.png", // 空桩 // "tree_status_2": "tree2.png", // 幼苗 // "tree_status_3": "tree3.png", // 成长 // "tree_status_4": "tree4.png", // 成熟 // }, pet_number:[], timeout: 0, prev:{name:"",url:"",params:{}}, current:{name:"orchard",url:"orchard.html",params:{}}, } }, computed:{ // 已激活但是未使用的树桩 active_tree(){ return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length); }, // 未激活的剩余树桩 lock_tree(){ return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number); }, }, created(){ this.show_pet_list(); this.show_tree_list(); this.get_prop_list(); }, methods:{ bg(hp){ if(hp>90){ return "#f00"; }else if(hp>60){ return "#a00"; }else if(hp>30){ return "#600"; }else{ return "#300"; } }, get_prop_list(){ // 更新道具列表信息 api.addEventListener({ name: 'update_prop_data' }, (ret, err)=>{ if( ret ){ this.pet_food_num = this.game.get("pet_food_num"); this.fertilizer_num = this.game.get("fertilizer_num"); } }); }, tree_img(status){ return '../static/images/'+this.tree_status[status]; }, show_pet_list(){ api.addEventListener({ name: 'pet_show_success' }, (ret, err)=>{ if( ret ){ // 显示宠物相关 this.pet_list = this.game.get("pet_list"); this.pet_number = parseInt(this.game.get("pet_number")); for( let pet of this.pet_list){ pet.timer = setInterval(()=>{ if(pet.hp<=0){ // 宠物挂了 clearInterval(pet.timer); } pet.hp_time-=0.5; pet.hp = Math.ceil( pet.hp_time / pet.pet_hp_max * 100 ) },500); } } }); }, show_tree_list(){ api.addEventListener({ name: 'user_tree_data' }, (ret, err)=>{ if( ret ){ // 用户种植植物信息 this.user_tree_data.tree_total = parseInt(this.game.get("tree_total")); this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number")); this.user_tree_data.user_tree_list = this.game.get("user_tree_list"); this.tree_status = this.game.get("tree_status"); this.pet_food_num = this.game.get("pet_food_num"); this.fertilizer_num = this.game.get("fertilizer_num"); this.waters = this.game.get("waters"); this.shears = this.game.get("shears"); } }); }, unlock_tree(){ // 激活树桩通知 api.confirm({ title: '提示', msg: '是否激活树桩?', buttons: ['确定', '取消'] }, (ret, err)=>{ if( ret.buttonIndex == 1 ){ api.sendEvent({ name: 'active_tree', extra: { } }); } }); // 激活成功! api.addEventListener({ name: 'active_tree_success' }, (ret, err)=>{ if( ret ){ // 新增树桩的数量 this.user_tree_data.user_tree_number+=1; this.game.save({"user_tree_number": this.user_tree_data.user_tree_number}); } }); } } }); } </script> </body> </html>
3.后端:实现使用宠物粮道具的逻辑
from application import socketio from flask import request from application.apps.users.models import User from flask_socketio import join_room, leave_room from application import mongo from .models import Goods,Setting,db from status import APIStatus as status from message import ErrorMessage as errmsg # 建立socket通信 # @socketio.on("connect", namespace="/mofang") # def user_connect(): # """用户连接""" # print("用户%s连接过来了!" % request.sid) # # 主动响应数据给客户端 # socketio.emit("server_response","hello",namespace="/mofang") # 断开socket通信 @socketio.on("disconnect", namespace="/mofang") def user_disconnect(): print("用户%s退出了种植园" % request.sid ) @socketio.on("login", namespace="/mofang") def user_login(data): # 分配房间 room = data["uid"] join_room(room) # 保存当前用户和sid的绑定关系 # 判断当前用户是否在mongo中有记录 query = { "_id": data["uid"] } ret = mongo.db.user_info_list.find_one(query) if ret: mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}}) else: mongo.db.user_info_list.insert_one({ "_id": data["uid"], "sid": request.sid, }) # 返回种植园的相关配置参数 orchard_settings = {} setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all() """ 现在的格式: [<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>] 需要返回的格式: { package_number_base:4, package_number_max: 32, ... } """ for item in setting_list: orchard_settings[item.name] = item.value # 返回当前用户相关的配置参数 user_settings = {} # 从mongo中查找用户信息,判断用户是否激活了背包格子 user_dict = mongo.db.user_info_list.find_one({"sid":request.sid}) # 背包格子 if user_dict.get("package_number") is None: user_settings["package_number"] = orchard_settings.get("package_number_base",4) mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}}) else: user_settings["package_number"] = user_dict.get("package_number") """种植园植物信息""" # 总树桩数量 setting = Setting.query.filter(Setting.name == "user_total_tree").first() if setting is None: tree_total = 9 else: tree_total = int(setting.value) # 用户已经激活的树桩 setting = Setting.query.filter(Setting.name == "user_active_tree").first() if setting is None: user_tree_number = 3 else: user_tree_number = int(setting.value) user_tree_number = user_dict.get("user_tree_number",user_tree_number) # 种植的植物列表 user_tree_list = user_dict.get("user_tree_list", []) key = 0 for tree_item in user_tree_list: tree_item["status"] = "tree_status_%s" % int(tree_item["status"]) # 植物状态 tree_item["water_time"] = redis.ttl("user_tree_water_%s_%s" % (data["uid"],key)) tree_item["growup_time"] = redis.ttl("user_tree_growup_%s_%s" % (data["uid"],key)) key+=1 # 植物状态信息 status_list = [ "tree_status_0", "tree_status_1", "tree_status_2", "tree_status_3", "tree_status_4", ] setting_list = Setting.query.filter(Setting.name.in_(status_list)).all() tree_status = {} for item in setting_list: tree_status[item.name] = item.value """显示植物相关道具""" # 获取背包中的化肥和宠物粮 fertilizer_num,pet_food_num = get_package_prop_list(user_dict) # 获取剪刀和浇水 # 只有植物处于成长期才会允许裁剪 # 只有植物处于幼苗期才会允许浇水 waters = 0 shears = 0 user_tree_list = user_dict.get("user_tree_list",[]) if len(user_tree_list) > 0: key = 0 for tree in user_tree_list: if (tree["status"] == "tree_status_%s" % 2) and int(tree.get("waters",0)) == 0: """处于幼苗期""" # 判断只有种植指定时间以后的幼苗才可以浇水 interval_time = redis.ttl("user_tree_water_%s_%s" % (user_dict.get("_id"), key) ) if interval_time==-2: waters+=1 elif (tree["status"] == "tree_status_%s" % 3) and int(tree.get("shears",0)) == 0: """处于成长期""" interval_time = redis.ttl("user_tree_shears_%s_%s" % (user_dict.get("_id"), key)) if interval_time==-2: shears+=1 key+=1 message = { "errno":status.CODE_OK, "errmsg":errmsg.ok, "orchard_settings":orchard_settings, "user_settings":user_settings, "tree_total":tree_total, "user_tree_number":user_tree_number, "user_tree_list":user_tree_list, "fertilizer_num":fertilizer_num, "pet_food_num":pet_food_num, "tree_status":tree_status, "waters":waters, "shears":shears, } print(message) socketio.emit("login_response", message, namespace="/mofang", room=room) def get_package_prop_list(user_dict): fertilizer_num = 0 pet_food_num = 0 prop_list = user_dict.get("prop_list", {}) for prop_item, num in prop_list.items(): pid = prop_item.split("_")[-1] num = int(num) prop_obj = Goods.query.get(pid) if prop_obj.prop_type == 2: # 提取化肥 fertilizer_num += num elif prop_obj.prop_type == 3: # 提取宠物粮 pet_food_num += num return fertilizer_num,pet_food_num @socketio.on("user_buy_prop", namespace="/mofang") def user_buy_prop(data): """用户购买道具""" room = request.sid # 从mongo中获取当前用户信息 user_info = mongo.db.user_info_list.find_one({"sid":request.sid}) user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room) return # 判断背包物品存储是否达到上限 use_package_number = int(user_info.get("use_package_number",0)) # 当前诗经使用的格子数量 package_number = int(user_info.get("package_number",0)) # 当前用户已经解锁的格子数量 # 本次购买道具需要使用的格子数量 setting = Setting.query.filter(Setting.name == "td_prop_max").first() if setting is None: td_prop_max = 10 else: td_prop_max = int(setting.value) # 计算购买道具以后需要额外占用的格子数量 if ("prop_%s" % data["pid"]) in user_info.get("prop_list",{}): """曾经购买过当前道具""" prop_num = int( user_info.get("prop_list")["prop_%s" % data["pid"]]) # 购买前的道具数量 new_prop_num = prop_num+int(data["num"]) # 如果成功购买道具以后的数量 old_td_num = prop_num // td_prop_max if prop_num % td_prop_max > 0: old_td_num+=1 new_td_num = new_prop_num // td_prop_max if new_prop_num % td_prop_max > 0: new_td_num+=1 td_num = new_td_num - old_td_num else: """新增购买的道具""" # 计算本次购买道具需要占用的格子数量 if int(data["num"]) > td_prop_max: """需要多个格子""" td_num = int(data["num"]) // td_prop_max if int(data["num"]) % td_prop_max > 0: td_num+=1 else: """需要一个格子""" td_num = 1 if use_package_number+td_num > package_number: """超出存储上限""" socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_PACKAGE, "errmsg": errmsg.no_package}, namespace="/mofang", room=room) return # 从mysql中获取商品价格 prop = Goods.query.get(data["pid"]) if user.money > 0: # 当前商品需要通过RMB购买 if float(user.money) < float(prop.price) * int(data["num"]): socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room) return else: """当前通过果子进行购买""" if int(user.credit) < int(prop.credit) * int(data["num"]): socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough}, namespace="/mofang", room=room) return # 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额 query = {"sid": request.sid} if user_info.get("prop_list") is None: """此前没有购买任何道具""" message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}} mongo.db.user_info_list.update_one(query,message) else: """此前有购买了道具""" prop_list = user_info.get("prop_list") # 道具列表 if ("prop_%s" % prop.id) in prop_list: """如果再次同一款道具""" prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"]) else: """此前没有购买过这种道具""" prop_list[("prop_%s" % prop.id)] = int(data["num"]) mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}}) # 扣除余额或果子 if prop.price > 0: user.money = float(user.money) - float(prop.price) * int(data["num"]) else: user.credit = int(user.credit) - int(prop.credit) * int(data["num"]) db.session.commit() # 返回购买成功的信息 socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room) # 返回最新的用户道具列表 user_prop() @socketio.on("user_prop", namespace="/mofang") def user_prop(): """用户道具""" userinfo = mongo.db.user_info_list.find_one({"sid":request.sid}) prop_list = userinfo.get("prop_list",{}) prop_id_list = [] for prop_str,num in prop_list.items(): pid = int(prop_str[5:]) prop_id_list.append(pid) data = [] prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all() setting = Setting.query.filter(Setting.name == "td_prop_max").first() if setting is None: td_prop_max = 10 else: td_prop_max = int(setting.value) for prop_data in prop_list_data: num = int( prop_list[("prop_%s" % prop_data.id)]) if td_prop_max > num: data.append({ "num": num, "image": prop_data.image, "pid": prop_data.id, "pty": prop_data.prop_type, }) else: padding_time = num // td_prop_max padding_last = num % td_prop_max arr = [{ "num": td_prop_max, "image": prop_data.image, "pid": prop_data.id, "pty": prop_data.prop_type, }] * padding_time if padding_last != 0: arr.append({ "num": padding_last, "image": prop_data.image, "pid": prop_data.id, "pty": prop_data.prop_type, }) data = data + arr mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"use_package_number":len(data)}}) room = request.sid fertilizer_num,pet_food_num = get_package_prop_list(userinfo) socketio.emit("user_prop_response", { "errno": status.CODE_OK, "errmsg": errmsg.ok, "data":data, "fertilizer_num":fertilizer_num, "pet_food_num":pet_food_num, }, namespace="/mofang", room=room) @socketio.on("unlock_package", namespace="/mofang") def unlock_package(): """解锁背包""" # 从mongo获取当前用户解锁的格子数量 user_info = mongo.db.user_info_list.find_one({"sid":request.sid}) user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("unlock_package_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room) return package_number = int(user_info.get("package_number")) num = 7 - (32 - package_number) // 4 # 没有解锁的格子 # 从数据库中获取解锁背包的价格 setting = Setting.query.filter(Setting.name == "package_unlock_price_%s" % num).first() if setting is None: unlock_price = 0 else: unlock_price = int(setting.value) # 判断是否有足够的积分或者价格 room = request.sid if user.money < unlock_price: socketio.emit("unlock_package_response", {"errno": status.CODE_NO_MONEY, "errmsg": errmsg.money_no_enough}, namespace="/mofang", room=room) return # 解锁成功 user.money = float(user.money) - float(unlock_price) db.session.commit() # mongo中调整数量 mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": package_number+1}}) # 返回解锁的结果 socketio.emit("unlock_package_response", { "errno": status.CODE_OK, "errmsg": errmsg.ok}, namespace="/mofang", room=room) import math from application import redis @socketio.on("pet_show", namespace="/mofang") def pet_show(): """显示宠物""" room = request.sid user_info = mongo.db.user_info_list.find_one({"sid":request.sid}) user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("pet_show_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room) return # 获取宠物列表 pet_list = user_info.get("pet_list", []) """ pet_list: [ { "pid":11, "image":"pet.png", "hp":100%, "created_time":xxxx-xx-xx xx:xx:xx, "skill":"70%", "has_time":30天 }, ] """ # 从redis中提取当前宠物的饱食度和有效期 # 初始化宠物的生命周期 pet_hp_max = 10000 for key,pet in enumerate(pet_list): setting = Setting.query.filter(Setting.name == "pet_hp_max_%s" % pet["pid"]).first() if setting is None: pet_hp_max = 7200 else: pet_hp_max = int(setting.value) pet["hp"] = math.ceil( redis.ttl("pet_%s_%s_hp" % (user.id,key+1)) / pet_hp_max * 100 ) pet["has_time"] = redis.ttl("pet_%s_%s_expire" % (user.id,key+1)) pet["hp_time"] = redis.ttl("pet_%s_%s_hp" % (user.id,key+1)) pet["pet_hp_max"] = pet_hp_max pet_number = user_info.get("pet_number", 1) socketio.emit( "pet_show_response", { "errno": status.CODE_OK, "errmsg": errmsg.ok, "pet_list": pet_list, "pet_number": pet_number, }, namespace="/mofang", room=room ) from datetime import datetime @socketio.on("use_prop", namespace="/mofang") def use_prop(data): """使用道具""" pid = data.get("pid") pet_key = data.get("pet_key",0) room = request.sid # 获取mongo中的用户信息 user_info = mongo.db.user_info_list.find_one({"sid": request.sid}) # 获取mysql中的用户信息 user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("pet_use_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists}, namespace="/mofang", room=room) return # 获取道具 prop_data = Goods.query.get(pid) if prop_data is None: socketio.emit("pet_use_response", {"errno":status.CODE_NO_SUCH_PROP,"errmsg":errmsg.not_such_prop}, namespace="/mofang", room=room) return if int(prop_data.prop_type) == 0: """使用植物道具""" # 1. 判断当前的植物数量是否有空余 tree_list = user_info.get("user_tree_list", []) # 当前用户最多可种植的数量 setting = Setting.query.filter(Setting.name == "user_active_tree").first() if setting is None: user_tree_number = 3 else: user_tree_number = int(setting.value) user_tree_number = user_info.get("user_tree_number", user_tree_number) if len(tree_list) >= user_tree_number: socketio.emit("prop_use_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty}, namespace="/mofang", room=room) return # 使用道具 mongo.db.user_info_list.update_one({"sid":room},{"$push":{"user_tree_list": { # 植物状态 "time": int(datetime.now().timestamp()), # 种植时间 "status": 2, # 植物状态,2表示幼苗状态 "waters": 0, # 浇水次数 "shears": 0, # 使用剪刀次数 } }}) # 从种下去到浇水的时间 pipe = redis.pipeline() pipe.multi() setting = Setting.query.filter(Setting.name == "tree_water_time").first() if setting is None: tree_water_time = 3600 else: tree_water_time = int(setting.value) # 必须等时间到了才可以浇水 pipe.setex("user_tree_water_%s_%s" % (user.id,len(tree_list)),int(tree_water_time),"_") # 必须等时间到了才可以到成长期 setting = Setting.query.filter(Setting.name == "tree_growup_time").first() if setting is None: tree_growup_time = 3600 else: tree_growup_time = int(setting.value) pipe.setex("user_tree_growup_%s_%s" % (user.id,len(tree_list)),tree_growup_time, "_") pipe.execute() user_login({"uid": user.id}) if int(prop_data.prop_type) == 1: """使用宠物道具""" # 1. 判断当前的宠物数量 # 获取宠物列表 pet_list = user_info.get("pet_list", []) if len(pet_list) > 1: socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room) return # 2. 是否有空余的宠物栏位 pet_number = user_info.get("pet_number",1) if pet_number <= len(pet_list): socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room) return # 3. 初始化当前宠物信息 # 获取有效期和防御值 exp_data = Setting.query.filter(Setting.name=="pet_expire_%s" % pid).first() ski_data = Setting.query.filter(Setting.name=="pet_skill_%s" % pid).first() if exp_data is None: # 默认7天有效期 expire = 7 else: expire = exp_data.value if ski_data is None: skill = 10 else: skill = ski_data.value # 在redis中设置当前宠物的饱食度 pipe = redis.pipeline() pipe.multi() setting = Setting.query.filter(Setting.name == ("pet_hp_max_%s" % pid)).first() if setting is None: pet_hp_max = 7200 else: pet_hp_max = int(setting.value) pipe.setex("pet_%s_%s_hp" % (user.id, len(pet_list)+1), pet_hp_max, "_") pipe.setex("pet_%s_%s_expire" % (user.id, len(pet_list)+1), int(expire)*24*60*60, "_") pipe.execute() # 基本保存到mongo mongo.db.user_info_list.update_one({"sid":request.sid},{"$push":{"pet_list":{ "pid": pid, "image": prop_data.image, "created_time": int( datetime.now().timestamp() ), "skill": skill, }}}) """ db.user_info_list.updateOne({"_id":"52"},{"$push":{"pet_list":{ "pid": 2, "image": "pet1.png", "created_time": 1609727155, "skill": 30, }}}) """ pet_show() if int(prop_data.prop_type) == 3: """宠物喂食""" pet_list = user_info.get("pet_list") if len(pet_list) < 1: socketio.emit("pet_use_response", {"errno": status.CODE_NO_PET, "errmsg": errmsg.not_pet}, namespace="/mofang", room=room) return current_hp_time = redis.ttl("pet_%s_%s_hp" % (user.id,pet_key+1)) setting = Setting.query.filter(Setting.name== ("pet_hp_max_%s" % (pet_list[pet_key]["pid"]))).first() if setting is None: pet_hp_max = 7200 else: pet_hp_max = int(setting.value) current_pet_hp = math.ceil(current_hp_time / pet_hp_max * 100) if current_pet_hp > 90: """饱食度高于90%无法喂养""" socketio.emit("pet_use_response", {"errno": status.CODE_NO_FEED, "errmsg": errmsg.no_feed}, namespace="/mofang", room=room) return if current_pet_hp <= 0: socketio.emit("pet_use_response", {"errno": status.CODE_NO_PET, "errmsg": errmsg.not_pet}, namespace="/mofang", room=room) return setting = Setting.query.filter(Setting.name == "pet_feed_number").first() if setting is None: pet_feed_number = 0.1 else: pet_feed_number = float(setting.value) prop_time = pet_hp_max * pet_feed_number time = int( current_hp_time + prop_time ) redis.expire("pet_%s_%s_hp" % (user.id,pet_key+1),time) print({"pet_key":pet_key,"hp_time":time}) socketio.emit("pet_feed_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok,"pet_key":pet_key,"hp_time":time}, namespace="/mofang", room=room) # 扣除背包中的道具数量 prop_list = user_info.get("prop_list",{}) for key,value in prop_list.items(): if key == ("prop_%s" % pid): if int(value) > 1: prop_list[key] = int(value) - 1 else: prop_list.pop(key) break mongo.db.user_info_list.update_one({"sid":room},{"$set":{"prop_list":prop_list}}) user_prop() socketio.emit("prop_use_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok}, namespace="/mofang", room=room) @socketio.on("active_tree", namespace="/mofang") def active_tree(): """激活树桩""" room = request.sid # 获取mongo中的用户信息 user_info = mongo.db.user_info_list.find_one({"sid": request.sid}) # 获取mysql中的用户信息 user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("active_tree_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists}, namespace="/mofang", room=room) return # 判断树桩是否达到上限 tree_number_data = Setting.query.filter(Setting.name == "user_active_tree").first() total_tree_data = Setting.query.filter(Setting.name == "user_total_tree").first() if tree_number_data is None: tree_number = 1 else: tree_number = tree_number_data.value if total_tree_data is None: total_tree = 9 else: total_tree = int(total_tree_data.value) user_tree_number = int(user_info.get("user_tree_number",tree_number)) if user_tree_number >= total_tree: socketio.emit("active_tree_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty}, namespace="/mofang", room=room) return # 扣除激活的果子数量 ret = Setting.query.filter(Setting.name == "active_tree_price").first() if ret is None: active_tree_price = 100 * user_tree_number else: active_tree_price = int(ret.value) * user_tree_number if active_tree_price > int(user.credit): socketio.emit("active_tree_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough}, namespace="/mofang", room=room) return user.credit = int(user.credit) - active_tree_price db.session.commit() mongo.db.user_info_list.update_one({"sid":room},{"$set":{"user_tree_number":user_tree_number+1}}) socketio.emit("active_tree_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok}, namespace="/mofang", room=room) return
4.前端:点击宠物粮道具,向后端发起通知-package.html
<!DOCTYPE html> <html> <head> <title>我的背包</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> </head> <body> <div class="app frame avatar add_friend package" id="app"> <div class="box"> <p class="title">我的背包</p> <img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt=""> <div class="prop_list"> <div class="item" v-for="prop in user_package" @click="use_prop(prop.pid)"> <img :src="settings.static_url+prop.image" alt=""> <span>{{prop.num}}</span> </div> <div class="item" v-for="number in unlock_td_number"></div> <div class="item lock" @click="unlock_package()" v-for="number in lock_td_number"></div> </div> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { td: 36, // 背包格子总数量 user_id: "", // 当前登陆用户Id orchard_settings:{}, // 种植园相关公共参数 user_settings:{}, // 用户相关私有参数 user_package:[], // 用户背包信息 prev:{name:"",url:"",params:{}}, current:{name:"package",url:"package.html",params:{}}, } }, computed:{// 计算属性 lock_td_number(){ // 未解锁的格子 return parseInt(this.orchard_settings.package_number_max-this.user_settings.package_number); }, unlock_td_number(){ // 解锁的格子 return parseInt( this.user_settings.package_number - this.user_package.length); } }, created(){ this.user_id = this.game.get("id") || this.game.fget("id"); this.orchard_settings = JSON.parse(this.game.fget("orchard_settings")); this.user_settings = JSON.parse(this.game.fget("user_settings")); this.user_package = JSON.parse(this.game.fget("user_package")); }, methods:{ use_prop(pid){ // 发起使用道具的通知 api.confirm({ title: '提示', msg: '确认使用当前道具吗?', buttons: ['确定', '取消'] }, (ret, err)=>{ if( ret.buttonIndex == 1 ){ api.sendEvent({ name: "use_prop", extra: { pid: pid, } }); } }); api.addEventListener({ name: 'prop_use_success' }, (ret, err)=>{ if( ret ){ // 扣除指定道具 var pid = parseInt(ret.value.pid); for(var i in this.user_package){ if(parseInt(this.user_package[i].pid) == pid){ this.user_package[i].num-=1; if(this.user_package[i].num == 0){ this.user_package.splice(i,1); } } } this.game.fsave({"user_package":this.user_package}); }else{ alert( JSON.stringify( err ) ); } }); }, unlock_package(){ // 解锁格子上限 api.confirm({ title: '提示', msg: '解锁背包上限', buttons: ['确定', '取消'] }, (ret, err)=>{ if( ret.buttonIndex == 1 ){ api.sendEvent({ name: 'unlock_package_number', extra: {} }); api.addEventListener({ name: 'unlock_package_success' }, (ret, err)=>{ this.user_settings.package_number=parseInt(this.user_settings.package_number)+1; this.game.fsave({"user_settings":this.user_settings}); }); } }); }, close_frame(){ this.game.outFrame("package"); }, } }); } </script> </body> </html>
5.前端:接收道具使用成功的通知-orchard.html
<!DOCTYPE html> <html> <head> <title>用户中心</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> <script src="../static/js/socket.io.js"></script> </head> <body> <div class="app orchard" id="app"> <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png"> <div class="orchard-bg"> <img src="../static/images/bg2.png"> <img class="board_bg2" src="../static/images/board_bg2.png"> </div> <img class="back" @click="go_index" src="../static/images/user_back.png" alt=""> <div class="header"> <div class="info" @click="go_home"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" src="../static/images/avatar.png" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <p class="user_name">好听的昵称</p> </div> <div class="wallet"> <div class="balance" @click="user_recharge"> <p class="title"><img src="../static/images/money.png" alt="">钱包</p> <p class="num">{{money}}</p> </div> <div class="balance"> <p class="title"><img src="../static/images/integral.png" alt="">果子</p> <p class="num">99,999.00</p> </div> </div> <div class="menu-list"> <div class="menu"> <img src="../static/images/menu1.png" alt=""> 排行榜 </div> <div class="menu"> <img src="../static/images/menu2.png" alt=""> 签到有礼 </div> <div class="menu" @click="go_orchard_shop"> <img src="../static/images/menu3.png" alt=""> 道具商城 </div> <div class="menu"> <img src="../static/images/menu4.png" alt=""> 邮件中心 </div> </div> </div> <div class="footer" > <ul class="menu-list"> <li class="menu">新手</li> <li class="menu" @click="go_my_package">背包</li> <li class="menu-center" @click="go_orchard_shop">商店</li> <li class="menu">消息</li> <li class="menu">好友</li> </ul> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { music_play:true, namespace: '/mofang', token:"", money:"", settings_info:{ orchard: {}, // 种植园公共参数 user:{}, // 用户私有相关参数 }, socket: null, recharge_list: ['10','20','50','100','200','500','1000'], timeout: 0, prev:{name:"",url:"",params:{}}, current:{name:"orchard",url:"orchard.html",params:{}}, } }, beforeCreate(){ this.game.goFrame("orchard","my_orchard.html", this.current,{ x: 0, y: 180, w: 'auto', h: 410, },null); }, created(){ this.checkout(); this.money = this.game.fget("money"); }, methods:{ user_recharge(){ // 发起充值请求 api.actionSheet({ title: '余额充值', cancelTitle: '取消', buttons: this.recharge_list }, (ret, err)=>{ if( ret ){ if(ret.buttonIndex <= this.recharge_list.length){ // 充值金额 money = this.recharge_list[ret.buttonIndex-1]; // 调用支付宝充值 this.create_recharge(money); } }else{ } }); }, create_recharge(money){ // 获取历史信息记录 var token = this.game.get("access_token") || this.game.fget("access_token"); this.game.checkout(this, token, (new_access_token)=>{ this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "Recharge.create", "params": { "money": money, } },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ if(parseInt(response.data.result.errno)==1000){ // 前往支付宝 var aliPayPlus = api.require('aliPayPlus'); aliPayPlus.payOrder({ orderInfo: response.data.result.order_string, sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false }, (ret, err)=>{ pay_result = { 9000:"支付成功", 8000:"正在处理中", 4000:"订单支付失败", 5000:"重复请求", 6001:"取消支付", 6002:"网络连接出错", 6004:"支付结果未知", } api.alert({ title: '支付结果', msg: pay_result[ret.code], buttons: ['确定'] }); // 通知服务端, 修改充值结果 this.return_recharge(response.data.result.order_number,token); }); }else{ this.game.print(response.data); } }).catch(error=>{ // 网络等异常 this.game.print(error); }); }) }, return_recharge(out_trade_number,token){ this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "Recharge.return", "params": { "out_trade_number": out_trade_number, } },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ if(parseInt(response.data.result.errno)==1000){ this.money = response.data.result.money.toFixed(2); } }) }, checkout(){ var token = this.game.get("access_token") || this.game.fget("access_token"); this.game.checkout(this,token,(new_access_token)=>{ this.connect(); this.login(); this.user_package(); this.buy_prop(); this.unlock_package_number(); this.show_pet(); this.use_prop(); this.active_tree(); }); }, connect(){ // socket连接 this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']}); this.socket.on('connect', ()=>{ this.game.print("开始连接服务端"); var id = this.game.fget("id"); this.socket.emit("login",{"uid":id}); this.socket.emit("user_prop"); }); }, login(){ this.socket.on("login_response",(message)=>{ this.settings_info.orchard = message.orchard_settings; this.settings_info.user=message.user_settings; this.game.fsave({ "orchard_settings":message.orchard_settings, "user_settings":message.user_settings, }); this.game.save({ "tree_total":message.tree_total, "user_tree_number":message.user_tree_number, "user_tree_list":message.user_tree_list, "tree_status":message.tree_status, "pet_food_num":message.pet_food_num, "fertilizer_num":message.fertilizer_num, "waters":message.waters, "shears":message.shears, }); setTimeout(()=>{ // 通知种植园页面获取到了当前用户的种植信息 api.sendEvent({ name: 'user_tree_data', extra: {} }); },500); }); }, user_package(){ // 用户背包道具列表 this.socket.on("user_prop_response",(message)=>{ this.game.fsave({ "user_package":message.data, }); // 界面中的道具信息 this.game.save({ "pet_food_num":message.pet_food_num, "fertilizer_num":message.fertilizer_num, }); api.sendEvent({ name: 'update_prop_data', extra: { } }); }) }, go_index(){ this.game.goWin("root"); }, go_friends(){ this.game.goFrame("friends","friends.html",this.current); this.game.goFrame("friend_list","friend_list.html",this.current,{ x: 0, y: 190, w: 'auto', h: 'auto', },null,true); }, go_home(){ this.game.goWin("user","user.html", this.current); }, go_orchard_shop(){ // 种植园商店 this.game.goFrame("orchard_shop","shop.html", this.current,null,{ type:"push", subType:"from_top", duration:300 }); }, go_my_package(){ // 我的背包 this.game.goFrame("package","package.html", this.current,null,{ type:"push", subType:"from_top", duration:300 }); }, buy_prop(){ api.addEventListener({ name: 'buy_prop' }, (ret, err)=>{ if( ret ){ // 用户购买道具 this.socket.emit("user_buy_prop",ret.value); } }); this.socket.on("user_buy_prop_response",(message)=>{ alert(message.errmsg); }) }, use_prop(){ // 使用道具 var pid = 0; api.addEventListener({ name: 'use_prop' }, (ret, err)=>{ if( ret ){ // 用户使用道具 pid = ret.value.pid; var pet_key = this.game.get("pet_key"); if(pet_key<1){ pet_key = 0; } this.socket.emit("use_prop",{pid:ret.value.pid,pet_key:pet_key}); } }); this.socket.on("prop_use_response",(message)=>{ if(parseInt(message.errno) === 1000){ api.sendEvent({ name: 'prop_use_success', extra: { pid: pid } }); }else{ api.alert({ title: '提示', msg: message.errmsg, }); } }); this.socket.on("pet_feed_response",(message)=>{ if(parseInt(message.errno) === 1000){ api.sendEvent({ name: 'pet_feed_success', extra: { pet_key: message.pet_key, hp_time: message.hp_time } }); }else{ api.alert({ title: '提示', msg: message.errmsg, }); } }) }, unlock_package_number(){ api.addEventListener({ name: 'unlock_package_number' }, (ret, err)=>{ if( ret ){ // 用户购买道具 this.socket.emit("unlock_package"); } }); this.socket.on("unlock_package_response",(message)=>{ if(parseInt(message.errno) === 1000){ api.sendEvent({ name: 'unlock_package_success', extra: { } }); }else{ api.alert({ title: '提示', msg: message.errmsg, }); } }) }, show_pet(){ // 显示宠物 this.socket.emit("pet_show"); this.socket.on("pet_show_response",(message)=>{ if(parseInt(message.errno) === 1000){ // 把宠物信息保存到本地 this.game.save({"pet_list":message.pet_list}) this.game.save({"pet_number":message.pet_number}) setTimeout(()=>{ api.sendEvent({ name: 'pet_show_success', extra: {} }); },500); }else{ api.alert({ title: '提示', msg: message.errmsg, }); } }) }, active_tree(){ // 激活树桩 api.addEventListener({ name: 'active_tree' }, (ret, err)=>{ if( ret ){ this.socket.emit("active_tree"); } }); this.socket.on("active_tree_response",(message)=>{ if(parseInt(message.errno) === 1000){ // 更新数据到本地 api.sendEvent({ name: 'active_tree_success', extra: {} }); }else{ api.alert({ title: '提示', msg: message.errmsg, }); } }) } } }); } </script> </body> </html>
6.前端:点击宠物喂养小透明图标,也可以进行喂养-my_orchard.html
<!DOCTYPE html> <html> <head> <title>用户中心</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> <script src="../static/js/socket.io.js"></script> </head> <body> <div class="app orchard orchard-frame" id="app"> <div class="background"> <img class="grassland2" src="../static/images/grassland2.png" alt=""> <img class="mushroom1" src="../static/images/mushroom1.png" alt=""> <img class="stake1" src="../static/images/stake1.png" alt=""> <img class="stake2" src="../static/images/stake2.png" alt=""> </div> <div class="pet-box"> <div class="pet"> <span @click="feed(0)" class="popped" v-if="pet_list[0] && pet_list[0].hp<90 && pet_list[0].hp>0 && pet_food_num > 0"> <img class="pet-prop" src="../static/images/prop4.png" alt=""> </span> <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt=""> </div> <div class="pet" v-if="pet_number > 1"> <span @click="feed(1)" class="popped" v-if="pet_list[1] && pet_list[1].hp<90 && pet_list[1].hp>0 && pet_food_num > 0"> <img class="pet-prop" src="../static/images/prop4.png" alt=""> </span> <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt=""> </div> <div class="pet turned_off" v-if="pet_number==1"> <img class="turned_image" src="../static/images/turned_off.png" alt=""> <p>请购买宠物</p> </div> </div> <div class="tree-list"> <div class="tree-box"> <div class="tree" v-for="tree in user_tree_data.user_tree_list"> <img :src="tree_img(tree.status)" alt=""> </div> <!-- 已激活但是未种植的树桩列表 --> <div class="tree" v-for="i in active_tree"> <img src="../static/images/tree1.png" alt=""> </div> <!-- 未激活树桩列表 --> <div @click="unlock_tree" class="tree" v-for="i in lock_tree"> <img src="../static/images/tree0.png" alt=""> </div> </div> </div> <div class="prop-list"> <div class="prop"> <img src="../static/images/prop1.png" alt=""> <span>{{fertilizer_num}}</span> <p>化肥</p> </div> <div class="prop"> <img src="../static/images/prop2.png" alt=""> <span>{{shears}}</span> <p>修剪</p> </div> <div class="prop"> <img src="../static/images/prop3.png" alt=""> <span>{{waters}}</span> <p>浇水</p> </div> <div class="prop"> <img src="../static/images/prop4.png" alt=""> <span>{{pet_food_num}}</span> <p>宠物粮</p> </div> </div> <div class="pet-hp-list"> <div class="pet-hp" v-for="pet in pet_list"> <p>宠物1 饱食度</p> <div class="hp"> <div :style="{width: pet.hp+'%',backgroundColor:bg(pet.hp)}" class="process">{{pet.hp}}%</div> </div> </div> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { namespace: '/mofang', token:"", socket: null, pet_food_num:0, // 宠物粮数量 fertilizer_num:0, // 化肥数量 waters:0, // 浇水次数 shears:0, // 剪刀次数 pet_list:[], user_tree_data:{ "total_tree": 9, // 总树桩数量 "user_tree_number": 0, // 当前用户激活树桩数量 "user_tree_list":[ // 当前种植的树桩列表状态 ], }, tree_status:{ }, // user_tree_data:{ // "total_tree":9, // 总树桩数量 // "user_tree_number": 5, // 当前用户激活树桩数量 // "user_tree_list":[ // 当前种植的树桩列表状态 // { // 树桩状态 // "time":1609808084, // 种植时间 // "status":4, // 植物状态 // "has_time": 300, // 状态时间 // }, // ], // }, // tree_status:{ // "tree_status_0": "tree0.png", // 树桩 // "tree_status_1": "tree1.png", // 空桩 // "tree_status_2": "tree2.png", // 幼苗 // "tree_status_3": "tree3.png", // 成长 // "tree_status_4": "tree4.png", // 成熟 // }, pet_number:[], timeout: 0, prev:{name:"",url:"",params:{}}, current:{name:"orchard",url:"orchard.html",params:{}}, } }, computed:{ // 已激活但是未使用的树桩 active_tree(){ return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length); }, // 未激活的剩余树桩 lock_tree(){ return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number); }, }, created(){ this.show_pet_list(); this.show_tree_list(); this.get_prop_list(); }, methods:{ feed(pet_key){ // 我的背包 this.game.save({"pet_key":pet_key}); // 记录本次喂养的宠物下标 this.game.goFrame("package","package.html", this.current,null,{ type:"push", subType:"from_top", duration:300 }); api.addEventListener({ name: 'pet_feed_success' }, (ret, err)=>{ if( ret ){ this.pet_list[ret.value.pet_key].hp = Math.ceil( ret.value.hp_time / this.pet_list[ret.value.pet_key].pet_hp_max * 100 ) this.game.save({"pet_list":this.pet_list}) } }); }, bg(hp){ if(hp>90){ return "#f00"; }else if(hp>60){ return "#a00"; }else if(hp>30){ return "#600"; }else{ return "#300"; } }, get_prop_list(){ // 更新道具列表信息 api.addEventListener({ name: 'update_prop_data' }, (ret, err)=>{ if( ret ){ this.pet_food_num = this.game.get("pet_food_num"); this.fertilizer_num = this.game.get("fertilizer_num"); } }); }, tree_img(status){ return '../static/images/'+this.tree_status[status]; }, show_pet_list(){ api.addEventListener({ name: 'pet_show_success' }, (ret, err)=>{ if( ret ){ // 显示宠物相关 this.pet_list = this.game.get("pet_list"); this.pet_number = parseInt(this.game.get("pet_number")); for( let i in this.pet_list){ this.pet_list[i].timer = setInterval(()=>{ this.pet_list = this.game.get("pet_list"); if(this.pet_list[i].hp<=0){ // 宠物挂了 clearInterval(this.pet_list[i].timer); } this.pet_list[i].hp_time-=0.5; this.pet_list[i].hp = Math.ceil( this.pet_list[i].hp_time / this.pet_list[i].pet_hp_max * 100 ) },500); } } }); }, show_tree_list(){ api.addEventListener({ name: 'user_tree_data' }, (ret, err)=>{ if( ret ){ // 用户种植植物信息 this.user_tree_data.tree_total = parseInt(this.game.get("tree_total")); this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number")); this.user_tree_data.user_tree_list = this.game.get("user_tree_list"); this.tree_status = this.game.get("tree_status"); this.pet_food_num = this.game.get("pet_food_num"); this.fertilizer_num = this.game.get("fertilizer_num"); this.waters = this.game.get("waters"); this.shears = this.game.get("shears"); } }); }, unlock_tree(){ // 激活树桩通知 api.confirm({ title: '提示', msg: '是否激活树桩?', buttons: ['确定', '取消'] }, (ret, err)=>{ if( ret.buttonIndex == 1 ){ api.sendEvent({ name: 'active_tree', extra: { } }); } }); // 激活成功! api.addEventListener({ name: 'active_tree_success' }, (ret, err)=>{ if( ret ){ // 新增树桩的数量 this.user_tree_data.user_tree_number+=1; this.game.save({"user_tree_number": this.user_tree_data.user_tree_number}); } }); } } }); } </script> </body> </html>
3.宠物死亡处理
1.后端读取宠物信息,若死亡,则清除宠物信息
from application import socketio from flask import request from application.apps.users.models import User from flask_socketio import join_room, leave_room from application import mongo from .models import Goods,Setting,db from status import APIStatus as status from message import ErrorMessage as errmsg # 建立socket通信 # @socketio.on("connect", namespace="/mofang") # def user_connect(): # """用户连接""" # print("用户%s连接过来了!" % request.sid) # # 主动响应数据给客户端 # socketio.emit("server_response","hello",namespace="/mofang") # 断开socket通信 @socketio.on("disconnect", namespace="/mofang") def user_disconnect(): print("用户%s退出了种植园" % request.sid ) @socketio.on("login", namespace="/mofang") def user_login(data): # 分配房间 room = data["uid"] join_room(room) # 保存当前用户和sid的绑定关系 # 判断当前用户是否在mongo中有记录 query = { "_id": data["uid"] } ret = mongo.db.user_info_list.find_one(query) if ret: mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}}) else: mongo.db.user_info_list.insert_one({ "_id": data["uid"], "sid": request.sid, }) # 返回种植园的相关配置参数 orchard_settings = {} setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all() """ 现在的格式: [<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>] 需要返回的格式: { package_number_base:4, package_number_max: 32, ... } """ for item in setting_list: orchard_settings[item.name] = item.value # 返回当前用户相关的配置参数 user_settings = {} # 从mongo中查找用户信息,判断用户是否激活了背包格子 user_dict = mongo.db.user_info_list.find_one({"sid":request.sid}) # 背包格子 if user_dict.get("package_number") is None: user_settings["package_number"] = orchard_settings.get("package_number_base",4) mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}}) else: user_settings["package_number"] = user_dict.get("package_number") """种植园植物信息""" # 总树桩数量 setting = Setting.query.filter(Setting.name == "user_total_tree").first() if setting is None: tree_total = 9 else: tree_total = int(setting.value) # 用户已经激活的树桩 setting = Setting.query.filter(Setting.name == "user_active_tree").first() if setting is None: user_tree_number = 3 else: user_tree_number = int(setting.value) user_tree_number = user_dict.get("user_tree_number",user_tree_number) # 种植的植物列表 user_tree_list = user_dict.get("user_tree_list", []) key = 0 for tree_item in user_tree_list: tree_item["status"] = "tree_status_%s" % int(tree_item["status"]) # 植物状态 tree_item["water_time"] = redis.ttl("user_tree_water_%s_%s" % (data["uid"],key)) tree_item["growup_time"] = redis.ttl("user_tree_growup_%s_%s" % (data["uid"],key)) key+=1 # 植物状态信息 status_list = [ "tree_status_0", "tree_status_1", "tree_status_2", "tree_status_3", "tree_status_4", ] setting_list = Setting.query.filter(Setting.name.in_(status_list)).all() tree_status = {} for item in setting_list: tree_status[item.name] = item.value """显示植物相关道具""" # 获取背包中的化肥和宠物粮 fertilizer_num,pet_food_num = get_package_prop_list(user_dict) # 获取剪刀和浇水 # 只有植物处于成长期才会允许裁剪 # 只有植物处于幼苗期才会允许浇水 waters = 0 shears = 0 user_tree_list = user_dict.get("user_tree_list",[]) if len(user_tree_list) > 0: key = 0 for tree in user_tree_list: if (tree["status"] == "tree_status_%s" % 2) and int(tree.get("waters",0)) == 0: """处于幼苗期""" # 判断只有种植指定时间以后的幼苗才可以浇水 interval_time = redis.ttl("user_tree_water_%s_%s" % (user_dict.get("_id"), key) ) if interval_time==-2: waters+=1 elif (tree["status"] == "tree_status_%s" % 3) and int(tree.get("shears",0)) == 0: """处于成长期""" interval_time = redis.ttl("user_tree_shears_%s_%s" % (user_dict.get("_id"), key)) if interval_time==-2: shears+=1 key+=1 message = { "errno":status.CODE_OK, "errmsg":errmsg.ok, "orchard_settings":orchard_settings, "user_settings":user_settings, "tree_total":tree_total, "user_tree_number":user_tree_number, "user_tree_list":user_tree_list, "fertilizer_num":fertilizer_num, "pet_food_num":pet_food_num, "tree_status":tree_status, "waters":waters, "shears":shears, } socketio.emit("login_response", message, namespace="/mofang", room=room) def get_package_prop_list(user_dict): fertilizer_num = 0 pet_food_num = 0 prop_list = user_dict.get("prop_list", {}) for prop_item, num in prop_list.items(): pid = prop_item.split("_")[-1] num = int(num) prop_obj = Goods.query.get(pid) if prop_obj.prop_type == 2: # 提取化肥 fertilizer_num += num elif prop_obj.prop_type == 3: # 提取宠物粮 pet_food_num += num return fertilizer_num,pet_food_num @socketio.on("user_buy_prop", namespace="/mofang") def user_buy_prop(data): """用户购买道具""" room = request.sid # 从mongo中获取当前用户信息 user_info = mongo.db.user_info_list.find_one({"sid":request.sid}) user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room) return # 判断背包物品存储是否达到上限 use_package_number = int(user_info.get("use_package_number",0)) # 当前诗经使用的格子数量 package_number = int(user_info.get("package_number",0)) # 当前用户已经解锁的格子数量 # 本次购买道具需要使用的格子数量 setting = Setting.query.filter(Setting.name == "td_prop_max").first() if setting is None: td_prop_max = 10 else: td_prop_max = int(setting.value) # 计算购买道具以后需要额外占用的格子数量 if ("prop_%s" % data["pid"]) in user_info.get("prop_list",{}): """曾经购买过当前道具""" prop_num = int( user_info.get("prop_list")["prop_%s" % data["pid"]]) # 购买前的道具数量 new_prop_num = prop_num+int(data["num"]) # 如果成功购买道具以后的数量 old_td_num = prop_num // td_prop_max if prop_num % td_prop_max > 0: old_td_num+=1 new_td_num = new_prop_num // td_prop_max if new_prop_num % td_prop_max > 0: new_td_num+=1 td_num = new_td_num - old_td_num else: """新增购买的道具""" # 计算本次购买道具需要占用的格子数量 if int(data["num"]) > td_prop_max: """需要多个格子""" td_num = int(data["num"]) // td_prop_max if int(data["num"]) % td_prop_max > 0: td_num+=1 else: """需要一个格子""" td_num = 1 if use_package_number+td_num > package_number: """超出存储上限""" socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_PACKAGE, "errmsg": errmsg.no_package}, namespace="/mofang", room=room) return # 从mysql中获取商品价格 prop = Goods.query.get(data["pid"]) if user.money > 0: # 当前商品需要通过RMB购买 if float(user.money) < float(prop.price) * int(data["num"]): socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room) return else: """当前通过果子进行购买""" if int(user.credit) < int(prop.credit) * int(data["num"]): socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough}, namespace="/mofang", room=room) return # 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额 query = {"sid": request.sid} if user_info.get("prop_list") is None: """此前没有购买任何道具""" message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}} mongo.db.user_info_list.update_one(query,message) else: """此前有购买了道具""" prop_list = user_info.get("prop_list") # 道具列表 if ("prop_%s" % prop.id) in prop_list: """如果再次同一款道具""" prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"]) else: """此前没有购买过这种道具""" prop_list[("prop_%s" % prop.id)] = int(data["num"]) mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}}) # 扣除余额或果子 if prop.price > 0: user.money = float(user.money) - float(prop.price) * int(data["num"]) else: user.credit = int(user.credit) - int(prop.credit) * int(data["num"]) db.session.commit() # 返回购买成功的信息 socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room) # 返回最新的用户道具列表 user_prop() @socketio.on("user_prop", namespace="/mofang") def user_prop(): """用户道具""" userinfo = mongo.db.user_info_list.find_one({"sid":request.sid}) prop_list = userinfo.get("prop_list",{}) prop_id_list = [] for prop_str,num in prop_list.items(): pid = int(prop_str[5:]) prop_id_list.append(pid) data = [] prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all() setting = Setting.query.filter(Setting.name == "td_prop_max").first() if setting is None: td_prop_max = 10 else: td_prop_max = int(setting.value) for prop_data in prop_list_data: num = int( prop_list[("prop_%s" % prop_data.id)]) if td_prop_max > num: data.append({ "num": num, "image": prop_data.image, "pid": prop_data.id, "pty": prop_data.prop_type, }) else: padding_time = num // td_prop_max padding_last = num % td_prop_max arr = [{ "num": td_prop_max, "image": prop_data.image, "pid": prop_data.id, "pty": prop_data.prop_type, }] * padding_time if padding_last != 0: arr.append({ "num": padding_last, "image": prop_data.image, "pid": prop_data.id, "pty": prop_data.prop_type, }) data = data + arr mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"use_package_number":len(data)}}) room = request.sid fertilizer_num,pet_food_num = get_package_prop_list(userinfo) socketio.emit("user_prop_response", { "errno": status.CODE_OK, "errmsg": errmsg.ok, "data":data, "fertilizer_num":fertilizer_num, "pet_food_num":pet_food_num, }, namespace="/mofang", room=room) @socketio.on("unlock_package", namespace="/mofang") def unlock_package(): """解锁背包""" # 从mongo获取当前用户解锁的格子数量 user_info = mongo.db.user_info_list.find_one({"sid":request.sid}) user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("unlock_package_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room) return package_number = int(user_info.get("package_number")) num = 7 - (32 - package_number) // 4 # 没有解锁的格子 # 从数据库中获取解锁背包的价格 setting = Setting.query.filter(Setting.name == "package_unlock_price_%s" % num).first() if setting is None: unlock_price = 0 else: unlock_price = int(setting.value) # 判断是否有足够的积分或者价格 room = request.sid if user.money < unlock_price: socketio.emit("unlock_package_response", {"errno": status.CODE_NO_MONEY, "errmsg": errmsg.money_no_enough}, namespace="/mofang", room=room) return # 解锁成功 user.money = float(user.money) - float(unlock_price) db.session.commit() # mongo中调整数量 mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": package_number+1}}) # 返回解锁的结果 socketio.emit("unlock_package_response", { "errno": status.CODE_OK, "errmsg": errmsg.ok}, namespace="/mofang", room=room) import math from application import redis @socketio.on("pet_show", namespace="/mofang") def pet_show(): """显示宠物""" room = request.sid user_info = mongo.db.user_info_list.find_one({"sid":request.sid}) user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("pet_show_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room) return # 获取宠物列表 pet_list = user_info.get("pet_list", []) """ pet_list: [ { "pid":11, "image":"pet.png", "hp":100%, "created_time":xxxx-xx-xx xx:xx:xx, "skill":"70%", "has_time":30天 }, ] """ # 从redis中提取当前宠物的饱食度和有效期 # 初始化宠物的生命周期 pet_hp_max = 10000 die_pet = [] for key,pet in enumerate(pet_list): setting = Setting.query.filter(Setting.name == "pet_hp_max_%s" % pet["pid"]).first() if setting is None: pet_hp_max = 7200 else: pet_hp_max = int(setting.value) pet["hp"] = math.ceil( redis.ttl("pet_%s_%s_hp" % (user.id,key+1)) / pet_hp_max * 100 ) pet["has_time"] = redis.ttl("pet_%s_%s_expire" % (user.id,key+1)) pet["hp_time"] = redis.ttl("pet_%s_%s_hp" % (user.id,key+1)) pet["pet_hp_max"] = pet_hp_max if pet["hp"] <=0: die_pet.append(pet) redis.delete("pet_%s_%s_expire" % (user.id,key+1)) # 清除死亡的宠物信息 if len(die_pet)>0: for del_pet in die_pet: pet_list.remove(del_pet) mongo.db.user_info_list.update_one({"sid":room},{"$set":{"pet_list":pet_list}}) pet_number = user_info.get("pet_number", 1) socketio.emit( "pet_show_response", { "errno": status.CODE_OK, "errmsg": errmsg.ok, "pet_list": pet_list, "pet_number": pet_number, }, namespace="/mofang", room=room ) from datetime import datetime @socketio.on("use_prop", namespace="/mofang") def use_prop(data): """使用道具""" pid = data.get("pid") pet_key = data.get("pet_key",0) room = request.sid # 获取mongo中的用户信息 user_info = mongo.db.user_info_list.find_one({"sid": request.sid}) # 获取mysql中的用户信息 user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("pet_use_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists}, namespace="/mofang", room=room) return # 获取道具 prop_data = Goods.query.get(pid) if prop_data is None: socketio.emit("pet_use_response", {"errno":status.CODE_NO_SUCH_PROP,"errmsg":errmsg.not_such_prop}, namespace="/mofang", room=room) return if int(prop_data.prop_type) == 0: """使用植物道具""" # 1. 判断当前的植物数量是否有空余 tree_list = user_info.get("user_tree_list", []) # 当前用户最多可种植的数量 setting = Setting.query.filter(Setting.name == "user_active_tree").first() if setting is None: user_tree_number = 3 else: user_tree_number = int(setting.value) user_tree_number = user_info.get("user_tree_number", user_tree_number) if len(tree_list) >= user_tree_number: socketio.emit("prop_use_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty}, namespace="/mofang", room=room) return # 使用道具 mongo.db.user_info_list.update_one({"sid":room},{"$push":{"user_tree_list": { # 植物状态 "time": int(datetime.now().timestamp()), # 种植时间 "status": 2, # 植物状态,2表示幼苗状态 "waters": 0, # 浇水次数 "shears": 0, # 使用剪刀次数 } }}) # 从种下去到浇水的时间 pipe = redis.pipeline() pipe.multi() setting = Setting.query.filter(Setting.name == "tree_water_time").first() if setting is None: tree_water_time = 3600 else: tree_water_time = int(setting.value) # 必须等时间到了才可以浇水 pipe.setex("user_tree_water_%s_%s" % (user.id,len(tree_list)),int(tree_water_time),"_") # 必须等时间到了才可以到成长期 setting = Setting.query.filter(Setting.name == "tree_growup_time").first() if setting is None: tree_growup_time = 3600 else: tree_growup_time = int(setting.value) pipe.setex("user_tree_growup_%s_%s" % (user.id,len(tree_list)),tree_growup_time, "_") pipe.execute() user_login({"uid": user.id}) if int(prop_data.prop_type) == 1: """使用宠物道具""" # 1. 判断当前的宠物数量 # 获取宠物列表 pet_list = user_info.get("pet_list", []) if len(pet_list) > 1: socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room) return # 2. 是否有空余的宠物栏位 pet_number = user_info.get("pet_number",1) if pet_number <= len(pet_list): socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room) return # 3. 初始化当前宠物信息 # 获取有效期和防御值 exp_data = Setting.query.filter(Setting.name=="pet_expire_%s" % pid).first() ski_data = Setting.query.filter(Setting.name=="pet_skill_%s" % pid).first() if exp_data is None: # 默认7天有效期 expire = 7 else: expire = exp_data.value if ski_data is None: skill = 10 else: skill = ski_data.value # 在redis中设置当前宠物的饱食度 pipe = redis.pipeline() pipe.multi() setting = Setting.query.filter(Setting.name == ("pet_hp_max_%s" % pid)).first() if setting is None: pet_hp_max = 7200 else: pet_hp_max = int(setting.value) pipe.setex("pet_%s_%s_hp" % (user.id, len(pet_list)+1), pet_hp_max, "_") pipe.setex("pet_%s_%s_expire" % (user.id, len(pet_list)+1), int(expire)*24*60*60, "_") pipe.execute() # 基本保存到mongo mongo.db.user_info_list.update_one({"sid":request.sid},{"$push":{"pet_list":{ "pid": pid, "image": prop_data.image, "created_time": int( datetime.now().timestamp() ), "skill": skill, }}}) """ db.user_info_list.updateOne({"_id":"52"},{"$push":{"pet_list":{ "pid": 2, "image": "pet1.png", "created_time": 1609727155, "skill": 30, }}}) """ pet_show() if int(prop_data.prop_type) == 3: """宠物喂食""" pet_list = user_info.get("pet_list") if len(pet_list) < 1: socketio.emit("pet_use_response", {"errno": status.CODE_NO_PET, "errmsg": errmsg.not_pet}, namespace="/mofang", room=room) return current_hp_time = redis.ttl("pet_%s_%s_hp" % (user.id,pet_key+1)) setting = Setting.query.filter(Setting.name== ("pet_hp_max_%s" % (pet_list[pet_key]["pid"]))).first() if setting is None: pet_hp_max = 7200 else: pet_hp_max = int(setting.value) current_pet_hp = math.ceil(current_hp_time / pet_hp_max * 100) if current_pet_hp > 90: """饱食度高于90%无法喂养""" socketio.emit("pet_use_response", {"errno": status.CODE_NO_FEED, "errmsg": errmsg.no_feed}, namespace="/mofang", room=room) return if current_pet_hp <= 0: socketio.emit("pet_use_response", {"errno": status.CODE_NO_PET, "errmsg": errmsg.not_pet}, namespace="/mofang", room=room) return setting = Setting.query.filter(Setting.name == "pet_feed_number").first() if setting is None: pet_feed_number = 0.1 else: pet_feed_number = float(setting.value) prop_time = pet_hp_max * pet_feed_number time = int( current_hp_time + prop_time ) redis.expire("pet_%s_%s_hp" % (user.id,pet_key+1),time) print({"pet_key":pet_key,"hp_time":time}) socketio.emit("pet_feed_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok,"pet_key":pet_key,"hp_time":time}, namespace="/mofang", room=room) # 扣除背包中的道具数量 prop_list = user_info.get("prop_list",{}) for key,value in prop_list.items(): if key == ("prop_%s" % pid): if int(value) > 1: prop_list[key] = int(value) - 1 else: prop_list.pop(key) break mongo.db.user_info_list.update_one({"sid":room},{"$set":{"prop_list":prop_list}}) user_prop() socketio.emit("prop_use_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok}, namespace="/mofang", room=room) @socketio.on("active_tree", namespace="/mofang") def active_tree(): """激活树桩""" room = request.sid # 获取mongo中的用户信息 user_info = mongo.db.user_info_list.find_one({"sid": request.sid}) # 获取mysql中的用户信息 user = User.query.get(user_info.get("_id")) if user is None: socketio.emit("active_tree_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists}, namespace="/mofang", room=room) return # 判断树桩是否达到上限 tree_number_data = Setting.query.filter(Setting.name == "user_active_tree").first() total_tree_data = Setting.query.filter(Setting.name == "user_total_tree").first() if tree_number_data is None: tree_number = 1 else: tree_number = tree_number_data.value if total_tree_data is None: total_tree = 9 else: total_tree = int(total_tree_data.value) user_tree_number = int(user_info.get("user_tree_number",tree_number)) if user_tree_number >= total_tree: socketio.emit("active_tree_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty}, namespace="/mofang", room=room) return # 扣除激活的果子数量 ret = Setting.query.filter(Setting.name == "active_tree_price").first() if ret is None: active_tree_price = 100 * user_tree_number else: active_tree_price = int(ret.value) * user_tree_number if active_tree_price > int(user.credit): socketio.emit("active_tree_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough}, namespace="/mofang", room=room) return user.credit = int(user.credit) - active_tree_price db.session.commit() mongo.db.user_info_list.update_one({"sid":room},{"$set":{"user_tree_number":user_tree_number+1}}) socketio.emit("active_tree_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok}, namespace="/mofang", room=room) return
2.前端:宠物饱食度消耗完之后,发起宠物死亡的通知
<!DOCTYPE html> <html> <head> <title>用户中心</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> <script src="../static/js/socket.io.js"></script> </head> <body> <div class="app orchard orchard-frame" id="app"> <div class="background"> <img class="grassland2" src="../static/images/grassland2.png" alt=""> <img class="mushroom1" src="../static/images/mushroom1.png" alt=""> <img class="stake1" src="../static/images/stake1.png" alt=""> <img class="stake2" src="../static/images/stake2.png" alt=""> </div> <div class="pet-box"> <div class="pet"> <span @click="feed(0)" class="popped" v-if="pet_list[0] && pet_list[0].hp<90 && pet_list[0].hp>0 && pet_food_num > 0"> <img class="pet-prop" src="../static/images/prop4.png" alt=""> </span> <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt=""> </div> <div class="pet" v-if="pet_number > 1"> <span @click="feed(1)" class="popped" v-if="pet_list[1] && pet_list[1].hp<90 && pet_list[1].hp>0 && pet_food_num > 0"> <img class="pet-prop" src="../static/images/prop4.png" alt=""> </span> <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt=""> </div> <div class="pet turned_off" v-if="pet_number==1"> <img class="turned_image" src="../static/images/turned_off.png" alt=""> <p>请购买宠物</p> </div> </div> <div class="tree-list"> <div class="tree-box"> <div class="tree" v-for="tree in user_tree_data.user_tree_list"> <img :src="tree_img(tree.status)" alt=""> </div> <!-- 已激活但是未种植的树桩列表 --> <div class="tree" v-for="i in active_tree"> <img src="../static/images/tree1.png" alt=""> </div> <!-- 未激活树桩列表 --> <div @click="unlock_tree" class="tree" v-for="i in lock_tree"> <img src="../static/images/tree0.png" alt=""> </div> </div> </div> <div class="prop-list"> <div class="prop"> <img src="../static/images/prop1.png" alt=""> <span>{{fertilizer_num}}</span> <p>化肥</p> </div> <div class="prop"> <img src="../static/images/prop2.png" alt=""> <span>{{shears}}</span> <p>修剪</p> </div> <div class="prop"> <img src="../static/images/prop3.png" alt=""> <span>{{waters}}</span> <p>浇水</p> </div> <div class="prop"> <img src="../static/images/prop4.png" alt=""> <span>{{pet_food_num}}</span> <p>宠物粮</p> </div> </div> <div class="pet-hp-list"> <div class="pet-hp" v-for="pet in pet_list"> <p>宠物1 饱食度</p> <div class="hp"> <div :style="{width: pet.hp+'%',backgroundColor:bg(pet.hp)}" class="process">{{pet.hp}}%</div> </div> </div> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { namespace: '/mofang', token:"", socket: null, pet_food_num:0, // 宠物粮数量 fertilizer_num:0, // 化肥数量 waters:0, // 浇水次数 shears:0, // 剪刀次数 pet_list:[], user_tree_data:{ "total_tree": 9, // 总树桩数量 "user_tree_number": 0, // 当前用户激活树桩数量 "user_tree_list":[ // 当前种植的树桩列表状态 ], }, tree_status:{ }, // user_tree_data:{ // "total_tree":9, // 总树桩数量 // "user_tree_number": 5, // 当前用户激活树桩数量 // "user_tree_list":[ // 当前种植的树桩列表状态 // { // 树桩状态 // "time":1609808084, // 种植时间 // "status":4, // 植物状态 // "has_time": 300, // 状态时间 // }, // ], // }, // tree_status:{ // "tree_status_0": "tree0.png", // 树桩 // "tree_status_1": "tree1.png", // 空桩 // "tree_status_2": "tree2.png", // 幼苗 // "tree_status_3": "tree3.png", // 成长 // "tree_status_4": "tree4.png", // 成熟 // }, pet_number:[], timeout: 0, prev:{name:"",url:"",params:{}}, current:{name:"orchard",url:"orchard.html",params:{}}, } }, computed:{ // 已激活但是未使用的树桩 active_tree(){ return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length); }, // 未激活的剩余树桩 lock_tree(){ return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number); }, }, created(){ this.show_pet_list(); this.show_tree_list(); this.get_prop_list(); }, methods:{ feed(pet_key){ // 我的背包 this.game.save({"pet_key":pet_key}); // 记录本次喂养的宠物下标 this.game.goFrame("package","package.html", this.current,null,{ type:"push", subType:"from_top", duration:300 }); api.addEventListener({ name: 'pet_feed_success' }, (ret, err)=>{ if( ret ){ this.pet_list[ret.value.pet_key].hp = Math.ceil( ret.value.hp_time / this.pet_list[ret.value.pet_key].pet_hp_max * 100 ) this.game.save({"pet_list":this.pet_list}) } }); }, bg(hp){ if(hp>90){ return "#f00"; }else if(hp>60){ return "#a00"; }else if(hp>30){ return "#600"; }else{ return "#300"; } }, get_prop_list(){ // 更新道具列表信息 api.addEventListener({ name: 'update_prop_data' }, (ret, err)=>{ if( ret ){ this.pet_food_num = this.game.get("pet_food_num"); this.fertilizer_num = this.game.get("fertilizer_num"); } }); }, tree_img(status){ return '../static/images/'+this.tree_status[status]; }, show_pet_list(){ api.addEventListener({ name: 'pet_show_success' }, (ret, err)=>{ if( ret ){ // 显示宠物相关 this.pet_list = this.game.get("pet_list"); this.pet_number = parseInt(this.game.get("pet_number")); var last_timer = null; for( let i in this.pet_list){ this.pet_list[i].timer = setInterval(()=>{ // 保证定时器中每次读取的都是最新的宠物信息 this.pet_list = this.game.get("pet_list"); if(this.pet_list.length<1){ clearInterval(last_timer); return } if(this.pet_list[i].hp_time<1){ // 宠物挂了 api.sendEvent({ name: 'pet_die', extra: { } }); last_timer = this.pet_list[i].timer clearInterval(this.pet_list[i].timer); } this.pet_list[i].hp_time-=0.5; this.pet_list[i].hp = Math.ceil( this.pet_list[i].hp_time / this.pet_list[i].pet_hp_max * 100 ) this.game.save({"pet_list":this.pet_list}); },500); } } }); }, show_tree_list(){ api.addEventListener({ name: 'user_tree_data' }, (ret, err)=>{ if( ret ){ // 用户种植植物信息 this.user_tree_data.tree_total = parseInt(this.game.get("tree_total")); this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number")); this.user_tree_data.user_tree_list = this.game.get("user_tree_list"); this.tree_status = this.game.get("tree_status"); this.pet_food_num = this.game.get("pet_food_num"); this.fertilizer_num = this.game.get("fertilizer_num"); this.waters = this.game.get("waters"); this.shears = this.game.get("shears"); } }); }, unlock_tree(){ // 激活树桩通知 api.confirm({ title: '提示', msg: '是否激活树桩?', buttons: ['确定', '取消'] }, (ret, err)=>{ if( ret.buttonIndex == 1 ){ api.sendEvent({ name: 'active_tree', extra: { } }); } }); // 激活成功! api.addEventListener({ name: 'active_tree_success' }, (ret, err)=>{ if( ret ){ // 新增树桩的数量 this.user_tree_data.user_tree_number+=1; this.game.save({"user_tree_number": this.user_tree_data.user_tree_number}); } }); } } }); } </script> </body> </html>
<!DOCTYPE html> <html> <head> <title>用户中心</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> <script src="../static/js/socket.io.js"></script> </head> <body> <div class="app orchard" id="app"> <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png"> <div class="orchard-bg"> <img src="../static/images/bg2.png"> <img class="board_bg2" src="../static/images/board_bg2.png"> </div> <img class="back" @click="go_index" src="../static/images/user_back.png" alt=""> <div class="header"> <div class="info" @click="go_home"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" src="../static/images/avatar.png" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <p class="user_name">好听的昵称</p> </div> <div class="wallet"> <div class="balance" @click="user_recharge"> <p class="title"><img src="../static/images/money.png" alt="">钱包</p> <p class="num">{{money}}</p> </div> <div class="balance"> <p class="title"><img src="../static/images/integral.png" alt="">果子</p> <p class="num">99,999.00</p> </div> </div> <div class="menu-list"> <div class="menu"> <img src="../static/images/menu1.png" alt=""> 排行榜 </div> <div class="menu"> <img src="../static/images/menu2.png" alt=""> 签到有礼 </div> <div class="menu" @click="go_orchard_shop"> <img src="../static/images/menu3.png" alt=""> 道具商城 </div> <div class="menu"> <img src="../static/images/menu4.png" alt=""> 邮件中心 </div> </div> </div> <div class="footer" > <ul class="menu-list"> <li class="menu">新手</li> <li class="menu" @click="go_my_package">背包</li> <li class="menu-center" @click="go_orchard_shop">商店</li> <li class="menu">消息</li> <li class="menu">好友</li> </ul> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { music_play:true, namespace: '/mofang', token:"", money:"", settings_info:{ orchard: {}, // 种植园公共参数 user:{}, // 用户私有相关参数 }, socket: null, recharge_list: ['10','20','50','100','200','500','1000'], timeout: 0, prev:{name:"",url:"",params:{}}, current:{name:"orchard",url:"orchard.html",params:{}}, } }, beforeCreate(){ this.game.goFrame("orchard","my_orchard.html", this.current,{ x: 0, y: 180, w: 'auto', h: 410, },null); }, created(){ this.checkout(); this.money = this.game.fget("money"); }, methods:{ user_recharge(){ // 发起充值请求 api.actionSheet({ title: '余额充值', cancelTitle: '取消', buttons: this.recharge_list }, (ret, err)=>{ if( ret ){ if(ret.buttonIndex <= this.recharge_list.length){ // 充值金额 money = this.recharge_list[ret.buttonIndex-1]; // 调用支付宝充值 this.create_recharge(money); } }else{ } }); }, create_recharge(money){ // 获取历史信息记录 var token = this.game.get("access_token") || this.game.fget("access_token"); this.game.checkout(this, token, (new_access_token)=>{ this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "Recharge.create", "params": { "money": money, } },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ if(parseInt(response.data.result.errno)==1000){ // 前往支付宝 var aliPayPlus = api.require('aliPayPlus'); aliPayPlus.payOrder({ orderInfo: response.data.result.order_string, sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false }, (ret, err)=>{ pay_result = { 9000:"支付成功", 8000:"正在处理中", 4000:"订单支付失败", 5000:"重复请求", 6001:"取消支付", 6002:"网络连接出错", 6004:"支付结果未知", } api.alert({ title: '支付结果', msg: pay_result[ret.code], buttons: ['确定'] }); // 通知服务端, 修改充值结果 this.return_recharge(response.data.result.order_number,token); }); }else{ this.game.print(response.data); } }).catch(error=>{ // 网络等异常 this.game.print(error); }); }) }, return_recharge(out_trade_number,token){ this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "Recharge.return", "params": { "out_trade_number": out_trade_number, } },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ if(parseInt(response.data.result.errno)==1000){ this.money = response.data.result.money.toFixed(2); } }) }, checkout(){ var token = this.game.get("access_token") || this.game.fget("access_token"); this.game.checkout(this,token,(new_access_token)=>{ this.connect(); this.login(); this.user_package(); this.buy_prop(); this.unlock_package_number(); this.show_pet(); this.use_prop(); this.active_tree(); }); }, connect(){ // socket连接 this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']}); this.socket.on('connect', ()=>{ this.game.print("开始连接服务端"); var id = this.game.fget("id"); this.socket.emit("login",{"uid":id}); this.socket.emit("user_prop"); }); }, login(){ this.socket.on("login_response",(message)=>{ this.settings_info.orchard = message.orchard_settings; this.settings_info.user=message.user_settings; this.game.fsave({ "orchard_settings":message.orchard_settings, "user_settings":message.user_settings, }); this.game.save({ "tree_total":message.tree_total, "user_tree_number":message.user_tree_number, "user_tree_list":message.user_tree_list, "tree_status":message.tree_status, "pet_food_num":message.pet_food_num, "fertilizer_num":message.fertilizer_num, "waters":message.waters, "shears":message.shears, }); setTimeout(()=>{ // 通知种植园页面获取到了当前用户的种植信息 api.sendEvent({ name: 'user_tree_data', extra: {} }); },500); }); }, user_package(){ // 用户背包道具列表 this.socket.on("user_prop_response",(message)=>{ this.game.fsave({ "user_package":message.data, }); // 界面中的道具信息 this.game.save({ "pet_food_num":message.pet_food_num, "fertilizer_num":message.fertilizer_num, }); api.sendEvent({ name: 'update_prop_data', extra: { } }); }) }, go_index(){ this.game.goWin("root"); }, go_friends(){ this.game.goFrame("friends","friends.html",this.current); this.game.goFrame("friend_list","friend_list.html",this.current,{ x: 0, y: 190, w: 'auto', h: 'auto', },null,true); }, go_home(){ this.game.goWin("user","user.html", this.current); }, go_orchard_shop(){ // 种植园商店 this.game.goFrame("orchard_shop","shop.html", this.current,null,{ type:"push", subType:"from_top", duration:300 }); }, go_my_package(){ // 我的背包 this.game.goFrame("package","package.html", this.current,null,{ type:"push", subType:"from_top", duration:300 }); }, buy_prop(){ api.addEventListener({ name: 'buy_prop' }, (ret, err)=>{ if( ret ){ // 用户购买道具 this.socket.emit("user_buy_prop",ret.value); } }); this.socket.on("user_buy_prop_response",(message)=>{ alert(message.errmsg); }); }, use_prop(){ // 使用道具 var pid = 0; api.addEventListener({ name: 'use_prop' }, (ret, err)=>{ if( ret ){ // 用户使用道具 pid = ret.value.pid; var pet_key = this.game.get("pet_key"); if(pet_key<1){ pet_key = 0; } this.socket.emit("use_prop",{pid:ret.value.pid,pet_key:pet_key}); } }); this.socket.on("prop_use_response",(message)=>{ if(parseInt(message.errno) === 1000){ api.sendEvent({ name: 'prop_use_success', extra: { pid: pid } }); }else{ api.alert({ title: '提示', msg: message.errmsg, }); } }); this.socket.on("pet_feed_response",(message)=>{ if(parseInt(message.errno) === 1000){ api.sendEvent({ name: 'pet_feed_success', extra: { pet_key: message.pet_key, hp_time: message.hp_time } }); }else{ api.alert({ title: '提示', msg: message.errmsg, }); } }) }, unlock_package_number(){ api.addEventListener({ name: 'unlock_package_number' }, (ret, err)=>{ if( ret ){ // 用户购买道具 this.socket.emit("unlock_package"); } }); this.socket.on("unlock_package_response",(message)=>{ if(parseInt(message.errno) === 1000){ api.sendEvent({ name: 'unlock_package_success', extra: { } }); }else{ api.alert({ title: '提示', msg: message.errmsg, }); } }) }, show_pet(){ // 显示宠物 this.socket.emit("pet_show"); this.socket.on("pet_show_response",(message)=>{ if(parseInt(message.errno) === 1000){ // 把宠物信息保存到本地 this.game.save({"pet_list":message.pet_list}) this.game.save({"pet_number":message.pet_number}) setTimeout(()=>{ api.sendEvent({ name: 'pet_show_success', extra: {} }); },500); }else{ api.alert({ title: '提示', msg: message.errmsg, }); } }); api.addEventListener({ name: 'pet_die' }, (ret, err)=>{ if( ret ){ this.socket.emit("pet_show"); } }); }, active_tree(){ // 激活树桩 api.addEventListener({ name: 'active_tree' }, (ret, err)=>{ if( ret ){ this.socket.emit("active_tree"); } }); this.socket.on("active_tree_response",(message)=>{ if(parseInt(message.errno) === 1000){ // 更新数据到本地 api.sendEvent({ name: 'active_tree_success', extra: {} }); }else{ api.alert({ title: '提示', msg: message.errmsg, }); } }) } } }); } </script> </body> </html>