种植园

植物的状态改动

一、当果树种植以后在celery的异步任务中调整浇水的状态

在进行果树种植的时候, 在服务端设置当前果树到等待浇水的redis变量中.通过celery不断进行周期任务的处理, 改动果树的浇水状态
socket.py

...
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)returnif 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 = 3else: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表示幼苗状态'allow_water': False,  # 是否允许浇水"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 = 3600else: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 = 3600else:tree_growup_time = int(setting.value)pipe.setex('user_tree_growup_%s_%s' % (user.id, len(tree_list)), tree_growup_time, '_')pipe.execute()# 设置定时任务进行浇水redis.append('tree_list_water', '%s_%s,' % (user.id, len(tree_list)))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 and pet_list[0]['is_die'] == 0 and pet_list[1]['is_die'] == 0: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)length = len(pet_list)if length == 2:live_leng = 0if pet_list[0]['is_die'] == 0:live_leng += 1if pet_list[1]['is_die'] == 0:live_leng += 1elif length == 1 and pet_list[0]['is_die'] == 0:live_leng = 1else:live_leng = 0if live_leng >= pet_number: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 = 7else:expire = exp_data.valueif ski_data is None:skill = 10else: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 = 7200else:pet_hp_max = int(setting.value)# 保存到mongo# 判断是否有挂了的宠物在列表中pet_data = {'pid': pid,'image': prop_data.image,'created_time': int(datetime.now().timestamp()),'skill': skill,'is_die': 0,}# 如果第一个宠物是挂了的if len(pet_list) == 0:mongo.db.user_info_list.update_one({'sid': room}, {'$set': {'pet_list': [pet_data]}})pipe.setex('pet_%s_%s_hp' % (user.id, 1), pet_hp_max, '_')pipe.setex('pet_%s_%s_expire' % (user.id, 1), int(expire) * 24 * 60 * 60, '_')elif len(pet_list) == 1 and int(pet_list[0]['is_die']) == 1:"""只有一个挂了的宠物"""mongo.db.user_info_list.update_one({'sid': room}, {'$set': {'pet_list': [pet_data]}})pipe.setex('pet_%s_%s_hp' % (user.id, 1), pet_hp_max, '_')pipe.setex('pet_%s_%s_expire' % (user.id, 1), int(expire) * 24 * 60 * 60, '_')elif len(pet_list) == 1 and int(pet_list[0]['is_die']) == 0:"""只有一个活着的宠物"""mongo.db.user_info_list.update_one({'sid': room}, {'$push': {'pet_list': pet_data}})pipe.setex('pet_%s_%s_hp' % (user.id, 2), pet_hp_max, '_')pipe.setex('pet_%s_%s_expire' % (user.id, 2), int(expire) * 24 * 60 * 60, '_')elif len(pet_list) == 2 and int(pet_list[0]['is_die']) == 1:"""有2个宠物,但是第1个挂了"""pet_list[0] = pet_datamongo.db.user_info_list.update_one({'sid': room}, {'$set': {'pet_list': pet_list}})pipe.setex('pet_%s_%s_hp' % (user.id, 1), pet_hp_max, '_')pipe.setex('pet_%s_%s_expire' % (user.id, 1), int(expire) * 24 * 60 * 60, '_')elif len(pet_list) == 2 and int(pet_list[1]['is_die']) == 1:"""有2个宠物,但是第2个挂了"""pet_list[1] = pet_datapipe.setex('pet_%s_%s_hp' % (user.id, 2), pet_hp_max, '_')pipe.setex('pet_%s_%s_expire' % (user.id, 2), int(expire) * 24 * 60 * 60, '_')mongo.db.user_info_list.update_one({'sid': room}, {'$set': {'pet_list': pet_list}})pipe.execute()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)returncurrent_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 = 7200else: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)returnif current_pet_hp <= 0:socketio.emit('pet_use_response', {'errno': status.CODE_NO_PET, 'errmsg': errmsg.not_pet}, namespace='/mofang', room=room)returnsetting = Setting.query.filter(Setting.name == 'pet_feed_number').first()if setting is None:pet_feed_number = 0.1else:pet_feed_number = float(setting.value)prop_time = pet_hp_max * pet_feed_numbertime = int(current_hp_time + prop_time)redis.expire('pet_%s_%s_hp' % (user.id, pet_key + 1), 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) - 1else:prop_list.pop(key)breakmongo.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 = 1else:tree_number = tree_number_data.valueif total_tree_data is None:total_tree = 9else: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_numberelse:active_tree_price = int(ret.value) * user_tree_numberif 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)returnuser.credit = int(user.credit) - active_tree_pricedb.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

mycelery/tree/tasks.py代码:


from application import redis
from mycelery.main import app, flask_app
from application import mongo@app.task(name='tree_write', bind=True)
def tree_write(self):"""允许浇水"""try:tree_list_water = redis.get('tree_list_water')if tree_list_water is None:return tree_list_water = tree_list_water.decode()tree_list = tree_list_water.split(',')[:-1]for tree in tree_list:timer = redis.ttl('user_tree_water_%s' % tree)if timer == -2:print('%s可以浇水了' % tree)treeinfo = tree.split('_')query = {'_id': treeinfo[0]}user_info = mongo.db.user_info_list.find_one(query)user_tree_list = user_info.get('user_tree_list', [])user_tree_list[int(treeinfo[1])]['allow_water'] = Truemongo.db.user_info_list.update_one(query, {'$set': {'user_tree_list': user_tree_list}})tree_list_water_str = ''.join(tree_list_water.split(tree+','))redis.set('tree_list_water', tree_list_water_str)except Exception as exc:print(exc)

mycelery/main.py,代码:

from celery import Celery
from application import init_app
# 初始化celery对象
app = Celery("flask")# 初始化flask
flask_app = init_app("application.settings.dev").app
# 加载配置
app.config_from_object("mycelery.config")
# 自动注册任务
app.autodiscover_tasks(["mycelery.sms", 'mycelery.tree'])# 运行celery
# 主程序终端下启动: celery -A mycelery.main worker -l info
# 调度器终端下启动: celery -A mycelery.main beat

mycelery/config.py,代码:

# 任务队列地址
broker_url = 'redis://127.0.0.1:6379/15'
# 结果队列地址
result_backend = "redis://127.0.0.1:6379/14"# 周期任务
from mycelery.main import app
app.conf.beat_schedule = {'tree_write_task': {'task': 'tree_write','schedule': 5,},
}

接下来在终端下运行celery

celery -A mycelery.main worker -l info
celery -A mycelery.main beat

二、客户端通过倒计时判断时间,显示浇水道具

客户端显示浇水图标, main.css,代码:

...
.tree-popped{top: 4rem;right: 3rem;height: 3.4rem;width: 3.4rem;border-radius: 3.4rem;
}
.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!important;height: 3rem!important;position: absolute;top: 0;left: 0;right: 0;bottom: 0;margin: auto;
}
.tree-popped img{width: 2.2rem!important;height: 2.2rem!important;
}
.orchard-frame .pet-box .turned_off .turned_image{width: 5.14rem;height: 6.83rem;position: absolute;top: 0;left: 0;right: 0;bottom: 0;margin: auto;
}
.orchard-frame .pet-box .turned_off p{position: absolute;top: 0;left: 0;right: 0;bottom: 0;margin: auto;border: 1px solid #fff;border-radius: 1rem;width: 8rem;height: 3rem;line-height: 3rem;font-size: 1.5rem;word-wrap: break-word;padding: 1rem;color: #000;text-align: center;background: rgba(255,255,255,.6);
}
.orchard-frame .pet-box .pet-item{width: 10rem;height: 10rem;position: absolute;top: 0;left: 0;right: 0;bottom: 0;margin: auto;
}
.orchard-frame .tree-list{position: absolute;top: 9rem;width: 100%;
}
.orchard-frame .tree-box{margin-left: 3rem;margin-right: 3rem;
}
.orchard-frame .tree-box .tree{width: 9rem;height: 4rem;margin-bottom: 2rem;float: left;position: relative;
}
...

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 && pet_list[0].hp>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 && pet_list[1].hp>0' 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'><span class="popped tree-popped"><img src="../static/images/prop3.png" alt=""></span><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, key in pet_list'><p>宠物{{key+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.pet_list[ret.value.pet_key].hp_time = ret.value.hp_time;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 timer = null;setInterval(()=>{// 保证定时器中每次读取的都是最新的宠物信息this.pet_list = this.game.get('pet_list');for(let i in this.pet_list){if(this.pet_list[i].hp_time<1 && this.pet_list[i].is_die==0){// 宠物挂了api.sendEvent({name: 'pet_die',extra: {}});}if(this.pet_list[i].hp_time>0){this.pet_list[i].hp_time -= 0.5;this.pet_list[i].hp = Math.ceil(this.pet_list[i].hp_time / this.pet_list[i].pet_hp_max * 100)}}this.game.save({'pet_list': this.pet_list});}, 500);}});},show_tree_list(){api.addEventListener({name: 'user_tree_data'}, (ret, err)=>{if( ret ){// 用户种植植物信息this.user_tree_data.tree_total = parseInt(this.game.get('tree_total'));this.user_tree_data.user_tree_number = parseInt(this.game.get('user_tree_number'));this.user_tree_data.user_tree_list = this.game.get('user_tree_list');this.tree_status = this.game.get('tree_status');this.pet_food_num = this.game.get('pet_food_num');this.fertilizer_num = this.game.get('fertilizer_num');this.waters = this.game.get('waters');this.shears = this.game.get('shears');}});},unlock_tree(){// 激活树桩通知api.confirm({title: '提示',msg: '是否激活树桩?',buttons: ['确定', '取消']}, (ret, err)=>{if( ret.buttonIndex == 1 ){api.sendEvent({name: 'active_tree',extra: {}});}});// 激活成功!api.addEventListener({name: 'active_tree_success'}, (ret, err)=>{if( ret ){// 新增树桩的数量this.user_tree_data.user_tree_number+=1;this.game.save({'user_tree_number': this.user_tree_data.user_tree_number});}});}}});}</script>
</body>
</html>

客户端判断当前种植物状态控制图标的显示和隐藏
my_orhcard.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 && pet_list[0].hp>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 && pet_list[1].hp>0' 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'><span class="popped tree-popped" v-if='tree.allow_water && tree.waters<1'><img src="../static/images/prop3.png" alt=""></span><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, key in pet_list'><p>宠物{{key+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.pet_list[ret.value.pet_key].hp_time = ret.value.hp_time;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(){var pet_timer = null;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'));clearInterval(pet_timer);pet_timer = setInterval(()=>{// 保证定时器中每次读取的都是最新的宠物信息this.pet_list = this.game.get('pet_list');for(let i in this.pet_list){if(this.pet_list[i].hp_time<1 && this.pet_list[i].is_die==0){// 宠物挂了api.sendEvent({name: 'pet_die',extra: {}});}if(this.pet_list[i].hp_time>0){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(){var tree_timer = null;  // 定时器标记符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');clearInterval(tree_timer);tree_timer = setInterval(()=>{var user_tree_list = this.game.get('user_tree_list');for(let tree of user_tree_list){if(tree.water_time>-2){tree.water_time-=1;}if(tree.water_time<=-2 && tree.allow_water==false){// 通知服务端允许用户浇水tree.allow_water = true;this.waters+=1;}}this.user_tree_data.user_tree_list = user_tree_list;this.game.save({'user_tree_list': user_tree_list});},1000);}});},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,代码:

...
@socketio.on('water_tree', namespace='/mofang')
def water_tree(key):"""浇水"""room = request.sid# 获取mongo中的用户信息query = {'sid': request.sid}user_info = mongo.db.user_info_list.find_one(query)# 获取mysql中的用户信息user = User.query.get(user_info.get('_id'))if user is None:socketio.emit('water_tree_response', {'errno': status.CODE_NO_USER, 'errmsg': errmsg.user_not_exists}, namespace='/mofang', room=room)returnprint('给%s的植物%s' % (user.id, key))user_tree_list = user_info.get('user_tree_list', [])try:tree_data = user_tree_list[key]except Exception as e:socketio.emit('water_tree_response', {'errno': status.CODE_NO_SUCH_TREE, 'errmsg': errmsg.tree_not_exists}, namespace='/mofang', room=room)return if tree_data.get('allow_water', False) and int(tree_data.get('waters', 1)) == 0:"""允许浇水"""tree_data['waters'] = 1# 如果种植物的种植时间达到成长期,则修改种植物的成长状态growup = redis.ttl('user_tree_growup_%s_%s' % (user.id, key))if growup == -2:tree_data['status'] = 3mongo.db.user_info_list.update_one(query, {'$set': {'user_tree_list': user_tree_list}})socketio.emit('water_tree_response', {'errno': status.CODE_OK, 'errmsg': errmsg.ok}, namespace='/mofang', room=room)user_login({'uid': user.id})

status.py

...CODE_NO_SUCH_TREE = 1021  # 没有对应的种植物

message.py

...tree_not_exists = "没有对应的种植物"

mycelery/tree/tasks.py,代码:


from application import redis
from mycelery.main import app, flask_app
from application import mongo@app.task(name='tree_write', bind=True)
def tree_write(self):"""允许浇水"""try:tree_list_water = redis.get('tree_list_water')if tree_list_water is None:return tree_list_water = tree_list_water.decode()tree_list = tree_list_water.split(',')[:-1]print(tree_list)for tree in tree_list:treeinfo = tree.split('_')query = {'_id': treeinfo[0]}user_info = mongo.db.user_info_list.find_one(query)user_tree_list = user_info.get('user_tree_list', [])# 是否允许浇水timer = redis.ttl('user_tree_water_%s' % tree)if timer == -2 and user_tree_list[int(treeinfo[1])]['allow_water'] == False:user_tree_list[int(treeinfo[1])]['allow_water'] = True# 是否进入成长期timer = redis.ttl('user_tree_growup_%s' % tree)if timer == -2 and user_tree_list[int(treeinfo[1])]['status'] == 2:if user_tree_list[int(treeinfo[1])]['waters'] > 0:user_tree_list[int(treeinfo[1])]['status'] = 3# tree_list_water_str = ''.join(tree_list_water.split(tree + ','))# redis.set('tree_list_water', tree_list_water_str)mongo.db.user_info_list.update_one(query, {'$set': {'user_tree_list': user_tree_list}})except Exception as exc:# 重新尝试执行失败任务print(exc)

客户端发起浇水通知,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 && pet_list[0].hp>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 && pet_list[1].hp>0' 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, key in user_tree_data.user_tree_list'><span @click='water_tree(key)' class="popped tree-popped" v-if='tree.allow_water && tree.waters<1'><img src="../static/images/prop3.png" alt=""></span><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, key in pet_list'><p>宠物{{key+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:{water_tree(tree_key){// 给种植物浇水api.sendEvent({name: 'water_tree',extra: {key: tree_key,}});api.addEventListener({name: 'water_tree_success'}, (ret, err)=>{if( ret ){this.user_tree_data.user_tree_list[ret.value.key]['waters'] = 1;this.waters-=1;this.game.save({'user_tree_list': this.user_tree_data.user_tree_list});}});},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.pet_list[ret.value.pet_key].hp_time = ret.value.hp_time;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(){var pet_timer = null;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'));clearInterval(pet_timer);pet_timer = setInterval(()=>{// 保证定时器中每次读取的都是最新的宠物信息this.pet_list = this.game.get('pet_list');for(let i in this.pet_list){if(this.pet_list[i].hp_time<1 && this.pet_list[i].is_die==0){// 宠物挂了api.sendEvent({name: 'pet_die',extra: {}});}if(this.pet_list[i].hp_time>0){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(){var tree_timer = null;  // 定时器标记符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');clearInterval(tree_timer);tree_timer = setInterval(()=>{var user_tree_list = this.game.get('user_tree_list');for(let tree of user_tree_list){if(tree.water_time>-2){tree.water_time-=1;}if(tree.water_time<=-2 && tree.allow_water==false){// 通知服务端允许用户浇水tree.allow_water = true;this.waters+=1;}if(tree.growup_time>=-2){tree.growup_time-=1;}if(tree.growup_time<=-2 && tree.waters>0 && tree.status=='tree_status_2'){// 通知服务端植物成长了tree.status='tree_status_3';}}this.user_tree_data.user_tree_list = user_tree_list;this.game.print(this.user_tree_data.user_tree_list)this.game.save({'user_tree_list': user_tree_list});},1000);}});},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();this.water_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,});}})},water_tree(){var key = null;api.addEventListener({name: 'water_tree'}, (ret, err)=>{if( ret ){key = ret.value.key;this.socket.emit('water_tree', key=key);}});this.socket.on('water_tree_response', (message)=>{if(parseInt(message.errno) == 1000){// 更新数据到本地api.sendEvent({name: 'water_tree_success',extra: {key: key}});}else {api.alert({title: '提示',msg: 'message.errmsg',});}})}}});}</script>
</body>
</html>

魔坊APP项目-25-种植园,植物的状态改动、当果树种植以后在celery的异步任务中调整浇水的状态、客户端通过倒计时判断时间,显示浇水道具相关推荐

  1. 魔坊APP项目-22-种植园,种植栏的功能实现,客户端根据激活状态和未激活状态分别显示树桩、服务端提供种植植物的相关数据、解锁树桩、植物相关道具使用

    种植园 一.种植栏的功能实现 1. 客户端需要的植物相关参数: 总树桩数量, 当前用户激活树桩数量, 当前种植的树桩数量, 树桩列表状态 2. 客户端根据激活状态和未激活状态分别显示树桩 3. 服务端 ...

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

    种植园 宠物和种植物的状态改变 1. 宠物的状态改动 2. 种植物的状态改动 3. 道具的使用 宠物的状态改动 因为宠物有多个,每个宠物会有不同的初始生命的饥饿时间,所以我们提前在mysql中进行配置 ...

  3. 魔坊APP项目-20-种植园,背包显示道具、用户购买道具的时候,判断背包存储是否达到上限、背包解锁

    种植园 一.背包显示道具 在背包中显示道具,会涉及到用户的背包格子的显示以及解锁问题,所以我们需要在服务端准备一个参数信息, 用于保存种植园中用户的业务参数,例如: 格子的初始化数量, 每次解锁背包格 ...

  4. 魔坊APP项目-24-种植园,修复宠物喂食时出现的饱食度没有增加的bug、宠物挂了的bug问题

    种植园 修复宠物喂食时出现的饱食度没有增加的bug 在feed方法中监听是否喂食成功的pet_feed_success通知中, 保存更新后的hp_time. my_orchard.html代码: &l ...

  5. 魔坊APP项目-21-种植园,宠物栏的功能实现、服务端提供显示宠物的api接口、客户端中展示宠物栏和宠物列表以及饱食度、宠物道具的使用

    种植园 一.宠物栏的功能实现 1. 宠物的显示 2. 宠物的使用 3. 宠物的饱食度 4. 宠物的开锁 1.服务端提供显示宠物的api接口 socket.py,代码 ... import math f ...

  6. 魔坊APP项目-16-种植园、websocket协议、服务端基于socket提供服务(基于房间管理分发信息)、种植园页面展示

    种植园 我们需要完成的种植园,是一个互动频繁,并且要求有一定即时性的模块,所以如果继续基于http协议开发,那么需要通过ajax发送大量http请求,同时因为http本身属于单向通讯,所以服务端无法主 ...

  7. 魔坊APP项目-19-种植园,我的背包、道具购买

    种植园 一.我的背包 打开背包,orchard.html,代码: <!DOCTYPE html> <html> <head><title>用户中心< ...

  8. 魔坊APP项目-17-种植园,商城页面、服务端提供商品api,解决App打包编译以后的跨域限制、客户端获取商品列表并进行展示,集成Alipayplus模块完成支付

    种植园 一.商城页面 orchard.html,代码: <!DOCTYPE html> <html> <head><title>用户中心</tit ...

  9. 魔坊APP项目-15-邀请好友(业务逻辑流程图、服务端提供邀请好友的二维码生成接口、客户端通过第三方识别微信二维码,服务端提供接口允许访问、App配置私有协议,允许第三方应用通过私有协议,唤醒APP)

    邀请好友 1.业务逻辑流程图 客户端提供点击"邀请好友"以后的页面frame,html/invite.html,代码: <!DOCTYPE html> <html ...

最新文章

  1. java正立三角形_JAVA 打印三角形
  2. 我只会python、怎么搞个软件出来-python---很多行代码做一个自动打开软件的程序...
  3. hihoCoder太阁最新面经算法竞赛18
  4. Brocade NOS学习笔记(第一章——第三章)
  5. PAT (Basic Level) 1045 快速排序(思维)
  6. 前端String类型转JSON类型
  7. 【博士招生】卢森堡大学​SnT(CVI²)研究小组,DeepFake 检测领域
  8. (二)OpenCV Mat常用属性和方法
  9. linux下源码安装apache服务
  10. Java面经-海康威视
  11. 梦想家CMS内容管理系统(毕业设计)
  12. 怎么查询逆水寒服务器角色信息,逆水寒服务器状态
  13. v4l2_async_subdev_notifier_register 分析
  14. candidate expects 1 argument, 0 provided 错误解决
  15. mysql查询手机号199开头_从今天起,你将能用上199开头的手机号了……
  16. AMD皓龙系列服务器CPU,霄龙PK皓龙!AMD CPU 10年历史对比:一步登天
  17. CellID 基站定位
  18. 九招教你完全了解液晶拼接屏
  19. 数据嗨客 | 第4期:逻辑回归
  20. 2020身高体重标准表儿童_儿童0一18岁青少年身高体重标准表-2020年儿童身高体重表图(中国标准版)下载最新比例表-西西软件下载...

热门文章

  1. Android内存优化总结
  2. import 和 from … import 模块的变量、方法引用差异
  3. 0基础快速入门WebPack(3)——图解详述plugins(插件)的安装及sourceMap的使用及WebpackDevServer正向代理和模块热更新等(附详细案例源码解析过程及版本迭代过程)
  4. GL-HOOK-ERR: 本项目开启了commitlint检查,您的提交不符合规范,具体规则可以查看:http://commitlint.js.org/
  5. 那些著名的软件都是用什么语言编写的?
  6. 谷歌PR更新到2,双喜临门?
  7. ROK 万国觉醒2D资源分析
  8. 电视直播录播系统,多路电视直播录播解决方案
  9. ACM概率期望dp刷题总结
  10. ubuntu mldonkey 设置