魔坊APP项目-23-种植园,宠物和种植物的状态改变、宠物的状态改动

种植园

宠物和种植物的状态改变

1. 宠物的状态改动
2. 种植物的状态改动
3. 道具的使用

宠物的状态改动

因为宠物有多个,每个宠物会有不同的初始生命的饥饿时间,所以我们提前在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');
INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (33,'pet_feed_number',0,1,1,'2020-12-30 17:40:46','2020-12-30 17:40:44','宠物粮饱食度','0.1');

socket.py调整使用宠物以后的相关参数设置,以及在用户进入种植园时显示宠物列表的相关参数.


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():
    """解锁背包"""
    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('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.prop_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

客户端进行倒计时对宠物的状态进行改动.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">
        <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,  // 状态时间
					// 		},
					// 	],
					// },
					// pet_number: [],
					// 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 '#foo';
					}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

      <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: 3rem;
  height: 3rem;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: auto;
}

在倒计时中阻止宠物饱食度进入负数状态
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">
					<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,  // 状态时间
					// 		},
					// 	],
					// },
					// pet_number: [],
					// 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, 代码:

<!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,  // 状态时间
					// 		},
					// 	],
					// },
					// pet_number: [],
					// 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>

道具使用,socket.py实现宠物粮的道具使用逻辑, 代码:


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():
    """解锁背包"""
    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('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.prop_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.prop_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_hp_max_%s' % (pet_list[pet_key]['pid']))).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

message.py代码:

class ErrorMessage():
    ok = "ok"
    mobile_format_error = "手机号码格式有误!"
    mobile_is_use = "对不起,当前手机已经被注册!"
    username_is_use = "对不起,当前用户名已经被使用!"
    password_not_match = "密码和验证密码不匹配!"
    sms_send_error = "短信发送失败!"
    sms_interval_time = "短信发送冷却中!"
    sms_code_expired = "短信验证码已过期!"
    sms_code_error = "短信验证码不正确!"
    sms_is_send = "短信发送中,请留意您的手机短信。"
    no_authorization = "用户认证信息校验失败!"
    authorization_has_expired = "用户认证信息已过期,请重新登录!"
    authorization_is_invalid = "无效的认证信息!"
    account_no_data = "对不起,用户账户必须填写!"
    user_not_exists = "用户不存在!"
    password_error = "密码错误!"
    captcaht_no_match = "验证码验证失败!"
    avatar_save_success = '用户头像保存成功!'
    transaction_password_not_match = '交易密码和确认密码不一致!'
    transaction_password_error = '原交易密码输入有误!'
    receive_user_not_exists = "搜索的用户不存在!"
    update_user_relation_fail = "好友申请处理失败!"
    update_success = "好友申请处理成功!"
    recharge_not_exists = "充值记录不存在!"
    money_no_enough = "余额不足!"
    no_package = "背包存储达到上限!"
    credit_no_enough = "果子不足!"
    prop_not_empty = "没有空余位置"
    not_such_prop = "没有该道具"
    not_pet = "没有宠物"
    no_feed = "饱食度超过90%,不能喂养"

status.py代码:

class APIStatus():
    CODE_OK = 1000  # 接口操作成功
    CODE_VALIDATE_ERROR = 1001   # 验证有误!
    CODE_SMS_ERROR = 1002  # 短信功能执行失败
    CODE_INTERVAL_TIME = 1003    # 短信发送冷却中
    CODE_NO_AUTHORIZATION = 1004  # 请求中没有附带认证信息
    CODE_SIGNATURE_EXPIRED = 1005  # 请求中的认证信息已过期
    CODE_INVALID_AUTHORIZATION = 1006  # 请求中的认证信息无效
    CODE_NO_ACCOUNT = 1007  # 请求中没有账户信息
    CODE_NO_USER = 1008  # 用户不存在
    CODE_PASSWORD_ERROR = 1009  # 密码错误
    CODE_CAPTCHA_ERROR = 1010  # 验证码验证失败
    CODE_TRANSACTION_PASSWORD_ERROR = 1011  # 交易密码和确认密码不一致
    CODE_UPDATE_USER_RELATION_ERROR = 1012  # 更新用户好友申请状态失败
    CODE_RECHARGE_ERROR = 1013  # 充值发生错误
    CODE_NO_MONEY = 1014  # 余额不足
    CODE_NO_PACKAGE = 1015  # 背包存储达到上限
    CODE_NO_CREDIT = 1016  # 果子不足
    CODE_NO_EMPTY = 1017  # 没有空余的宠物栏位
    CODE_NO_SUCH_PROP = 1018  # 没有该道具
    CODE_NO_PET = 1019  # 没有宠物
    CODE_NO_FEED = 1020  # 饱食度超过90%,不能喂养

客户端在用户打开背包点击宠物粮时, 发送通知, 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>

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" @click='go_friends'>好友</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=>{
							// this.game.print(response.data);
							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>

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,  // 状态时间
					// 		},
					// 	],
					// },
					// pet_number: [],
					// 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>

到宠物饱食度低于0, 则清除宠物信息
服务端中, 当用户进入种植园读取宠物信息时,如果宠物死亡则直接清除宠物信息.socket.py, 代码:


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():
    """解锁背包"""
    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('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.prop_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.prop_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_hp_max_%s' % (pet_list[pet_key]['pid']))).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

客户端在饱食度消耗完以后, 发起宠物死亡的通知,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,  // 状态时间
					// 		},
					// 	],
					// },
					// pet_number: [],
					// 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>

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" @click='go_friends'>好友</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=>{
							// this.game.print(response.data);
							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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值