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

目录

1.宠物的状态改动

2.宠物粮道具的使用

3.宠物死亡处理

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');

2.调整后端接口:使用宠物后的相关参数设置&用户进入种植园后显示宠物列表相关参数

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>
前端:进行倒计时,对宠物的状态进行改动-my_orchard.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>
在倒计时中阻止宠物饱食度进入负数状态-my_orchard.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>
根据宠物的饱食度和道具数量来控制是否显示喂食的图标-my_orchard.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>
前端点击宠物粮道具向后端发起通知-package.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>
接收道具使用成功的通知-orchard.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>
点击喂养透明图标,也可以进行喂养-my_orchard.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>
客户端在饱食度消耗完以后, 发起宠物死亡的通知-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" 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>
客户端在饱食度消耗完以后, 发起宠物死亡的通知-orchard.html

 

posted @ 2021-01-11 10:42  iR-Poke  阅读(142)  评论(0编辑  收藏  举报