种植园

一、宠物栏的功能实现

1. 宠物的显示
2. 宠物的使用
3. 宠物的饱食度
4. 宠物的开锁

1.服务端提供显示宠物的api接口

socket.py,代码

...
import math
from application import redis
@socketio.on('pet_show', namespace='/mofang')
def pet_show():"""显示宠物"""room = request.siduser_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中提取当前宠物的饱食度和有效期for key, pet in enumerate(pet_list):pet['hp'] = math.ceil(redis.ttl('pet_%s_%s_hp' % (user.id, key + 1)) / 86400 * 100) pet['has_time'] = redis.ttl('pet_%s_%s_expire' % (user.id, key + 1))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)

根据服务端前面添加的宠物的id主键,在配置参数中设置宠物的相关参数,

INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (11, 'pet_expire_2', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '1号宠物的有效期', '30');
INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (12, 'pet_skill_2', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '1号宠物的防御概率', '50');
INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (13, 'pet_expire_3', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '2号宠物的有效期', '30');
INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (14, 'pet_skill_3', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '2号宠物的防御概率', '20');
INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (15, 'pet_expire_4', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '3号宠物的有效期', '30');
INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (16, 'pet_skill_4', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '3号宠物的防御概率', '70');
INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (17, 'pet_expire_5', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '4号宠物的有效期', '30');
INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (18, 'pet_skill_5', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '4号宠物的防御概率', '30');

message.py代码:

    pet_not_empty = "没有空余的宠物位置"not_such_pet = "没有该宠物"

status.py,代码:

    CODE_NO_EMPTY = 1017 # 没有空余的宠物栏位CODE_NO_SUCH_PET = 1018 # 没有该宠物

2.客户端中展示宠物栏和宠物列表以及饱食度

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:{}},}},beforeCreated(){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();});},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});});},user_package(){// 用户背包道具列表this.socket.on('user_prop_response', (message)=>{this.game.fsave({'user_package': message.data,})})},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);})},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',}, function(ret, err){});}})}}});}</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"><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"><img src="../static/images/tree4.png" alt=""></div><div class="tree"><img src="../static/images/tree3.png" alt=""></div><div class="tree"><img src="../static/images/tree4.png" alt=""></div></div><div class="tree-box"><div class="tree"><img src="../static/images/tree3.png" alt=""></div><div class="tree"><img src="../static/images/tree2.png" alt=""></div><div class="tree"><img src="../static/images/tree2.png" alt=""></div></div><div class="tree-box"><div class="tree"><img src="../static/images/tree1.png" alt=""></div><div class="tree"><img src="../static/images/tree0.png" alt=""></div><div class="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>1</span><p>化肥</p></div><div class="prop"><img src="../static/images/prop2.png" alt=""><span>0</span><p>修剪</p></div><div class="prop"><img src="../static/images/prop3.png" alt=""><span>1</span><p>浇水</p></div><div class="prop"><img src="../static/images/prop4.png" alt=""><span>1</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+'%'}" 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_list: [],pet_number: [],timeout: 0,prev:{name:"",url:"",params:{}},current:{name:"orchard",url:"orchard.html",params:{}},}},created(){this.show_pet_list();},methods:{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'));this.game.print(this.pet_list);}});}}});}</script>
</body>
</html>

3.宠物道具的使用

调整业务模型, orchard/models.py代码;


from application.utils.models import BaseModel, db
class Goods(BaseModel):"""商品基本信息"""__tablename__ = 'mf_goods'PROP_TYPE = ((0, '果树'),(1, '宠物'),(2, '植物生长道具'),(3, '宠物粮'),)remark = db.Column(db.String(255), comment="商品描述")price = db.Column(db.Numeric(7, 2), comment="商品价格[余额]")prop_type = db.Column(db.Integer, default=0, comment='道具类型')credit = db.Column(db.Integer, comment="商品价格[果子]")image = db.Column(db.String(255), comment="商品图片")class Setting(BaseModel):"""参数信息"""__tablename__ = 'mf_orchard_setting'title = db.Column(db.String(255), comment="提示文本")value = db.Column(db.String(255), comment="数值")

数据迁移, 命令:

python manage.py db migrate -m "add goods column"
python manage.py db upgrade

客户端发起使用道具的请求 ,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: "", // 当前登陆用户Idorchard_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: 'pet_use_success'}, (ret, err)=>{if( ret ){// 扣除指定道具var pid = ret.value.pid;for(var i in this.user_package){if(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>

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"><img src="../static/images/tree4.png" alt=""></div><div class="tree"><img src="../static/images/tree3.png" alt=""></div><div class="tree"><img src="../static/images/tree4.png" alt=""></div></div><div class="tree-box"><div class="tree"><img src="../static/images/tree3.png" alt=""></div><div class="tree"><img src="../static/images/tree2.png" alt=""></div><div class="tree"><img src="../static/images/tree2.png" alt=""></div></div><div class="tree-box"><div class="tree"><img src="../static/images/tree1.png" alt=""></div><div class="tree"><img src="../static/images/tree0.png" alt=""></div><div class="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>1</span><p>化肥</p></div><div class="prop"><img src="../static/images/prop2.png" alt=""><span>0</span><p>修剪</p></div><div class="prop"><img src="../static/images/prop3.png" alt=""><span>1</span><p>浇水</p></div><div class="prop"><img src="../static/images/prop4.png" alt=""><span>1</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+'%'}" 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_list: [],pet_number: [],timeout: 0,prev:{name:"",url:"",params:{}},current:{name:"orchard",url:"orchard.html",params:{}},}},created(){this.show_pet_list();},methods:{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'));}});}}});}</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();});},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});});},user_package(){// 用户背包道具列表this.socket.on('user_prop_response', (message)=>{this.game.fsave({'user_package': message.data,})})},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;this.socket.emit('use_prop', ret.value.pid);}});this.socket.on('pet_use_response', (message)=>{if(parseInt(message.errno) === 1000){api.sendEvent({name: 'pet_use_success',extra: {pid: pid}});}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',});}})}}});}</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中查找用户信息,判断用户是否激活了背包格子dict = mongo.db.user_info_list.find_one({'sid': request.sid})# 背包格子if 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'] = dict.get('package_number')socketio.emit('login_response', {'errno': status.CODE_OK,'errmsg': errmsg.ok,'orchard_settings': orchard_settings,'user_settings': user_settings,}, namespace='/mofang', room=room)@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 = 10else: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_maxif prop_num % td_prop_max > 0:old_td_num += 1new_td_num = new_prop_num // td_prop_maxif new_prop_num % td_prop_max > 0:new_td_num += 1td_num = new_td_num - old_td_numelse:"""新增购买的道具"""# 计算本次购买道具需要占用的格子数量if int(data['num']) > td_prop_max:"""需要多个格子"""td_num = int(data['num']) // td_prop_maxif int(data['num']) % td_prop_max > 0:td_num += 1else:"""需要一个格子"""td_num = 1if 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)returnelse:"""当前通过果子进行购买"""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 = 10else: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})else:padding_time = num // td_prop_maxpadding_last = num % td_prop_maxarr = [{'num': td_prop_max,'image': prop_data.image,'pid': prop_data.id}] * padding_timeif padding_last != 0:arr.append({'num': padding_last,'image': prop_data.image,'pid': prop_data.id})data = data + arr# 保存当前用户已经使用的格子数量mongo.db.user_info_list.update_one({'sid':request.sid}, {'$set': {'use_package_number': len(data)}})room = request.sidsocketio.emit('user_prop_response', {'errno': status.CODE_OK,'errmsg': errmsg.ok,'data': data,}, 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)returnpackage_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 = 0else:unlock_price = int(setting.value)# 判断是否有足够的积分或者价格room = request.sidif 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.siduser_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中提取当前宠物的饱食度和有效期for key, pet in enumerate(pet_list):pet['hp'] = math.ceil(redis.ttl('pet_%s_%s_hp' % (user.id, key + 1)) / 86400 * 100)pet['has_time'] = redis.ttl('pet_%s_%s_expire' % (user.id, key + 1))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):"""使用宠物"""# 1.判断当前的宠物数量room = request.siduser_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_use_response', {'errno': status.CODE_NO_USER, 'errmsg': errmsg.user_not_exists}, namespace='/mofang', room=room)return # 获取宠物列表pet_list = user_info.get('pet_list', [])if len(pet_list) > 1:socketio.emit('pet_use_response', {'errno': status.CODE_NO_EMPTY, 'errmsg': errmsg.pet_not_empty}, namespace='/mofang', room=room)return# 2. 是否有空余的宠物栏位pet_number = user_info.get('pet_number', 1)if pet_number <= len(pet_list):socketio.emit('pet_use_response', {'errno': status.CODE_NO_EMPTY, 'errmsg': errmsg.pet_not_empty}, namespace='/mofang', room=room)return# 3. 初始化当前宠物信息pet = Goods.query.get(pid)if pet is None:socketio.emit('pet_use_response', {'errno': status.CODE_NO_SUCH_PET, 'errmsg': errmsg.not_such_pet}, namespace='/mofang', room=room)return# 获取有效期和防御值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()pipe.setex('pet_%s_%s_hp' % (user.id, len(pet_list)+1), 24*60*60, '_')pipe.setex('pet_%s_%s_expire' % (user.id, len(pet_list)+1), int(expire)*24*60*60, '_')pipe.execute()# 保存到mongomongo.db.user_info_list.update_one({'sid': request.sid}, {'$push': {'pet_list': {'pid': pid,'image': pet.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,}}})"""# 扣除背包中的道具数量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()pet_show()socketio.emit('pet_use_response', {'errno': status.CODE_OK, 'errmsg': errmsg.ok}, namespace='/mofang', room=room)

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

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

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

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

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

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

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

  4. 魔方APP项目-06-用户注册,完成短信验证码的校验、基于Celery实现短信异步发送、用户登录,jwt登陆认证、服务端提供用户登录的API接口

    一.用户注册- 1.完成短信验证码的校验 application.utils.language.message,代码: class ErrorMessage():ok = "ok" ...

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

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

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

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

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

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

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

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

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

    种植园 植物的状态改动 一.当果树种植以后在celery的异步任务中调整浇水的状态 在进行果树种植的时候, 在服务端设置当前果树到等待浇水的redis变量中.通过celery不断进行周期任务的处理, ...

最新文章

  1. RAP Mock.js语法规范
  2. HQL中的Like查询需要注意的地方
  3. [EF Core]数据迁移(二)
  4. 从零开始编写自己的C#框架(17)——Web层后端首页
  5. C++阶段02笔记【通讯录管理系统 完整代码(系统需求、创建项目、添加/显示/删除/查找/修改/清空)】
  6. matlab6432有什么区别,在同一台机器上使用Matlab 32和64位,如何在不同的地方存储设置?...
  7. 上周热点回顾(7.10-7.16)
  8. 计算机能做什么英语对话,计算机英语会话∣实用电脑英语
  9. 苹果悬赏100万美元找漏洞 辞职的理由找到了!
  10. 关于不使用漫游配置文件解决方案保持登陆域后保持原来的配置文件不变
  11. 2019上半年教资综合素质——主观题
  12. vue实现一个简单的表情包组件
  13. 【折半查找二叉判定树】
  14. Scrapy是什么?Scrapy怎么用?Scrapy进阶使用[链接提取器、自动登录、图片(文件)下载器](基于scrapy2.0+编写) ๑乛◡乛๑ Scrapy框架使用方法
  15. Structured Program I – Print a Frame
  16. 研究生录取数据分析 Python123
  17. php鼠标移过图片放大代码,鼠标移上去,图片会自动原地放大CSS写法
  18. 一、mysql数据库基本框架
  19. win、linux、unix查看系统主机名
  20. Echarts清空图表:There is a chart instance already initialized on the dom.

热门文章

  1. 飞机大战------功能更新
  2. Excel常用功能和常用函数学习笔记
  3. 计算机毕业设计ssm校园排球联赛管理系统y513u系统+程序+源码+lw+远程部署
  4. 首届“十大最具价值”机器人创业项目遴选榜单丨Xtecher联合中投协权威发布
  5. js判断移动端浏览器
  6. JZOJ 3912. 超氧化钾
  7. 计算机女生吃香的职业,女生最吃香的九大专业,这9个选择最适合女孩子,跟着时代走...
  8. Lists.newArrayListWithExpectedSize( int estimatedSize)
  9. 年龄到底怎么算才对_专升本的年纪是怎么算的(年龄到底怎么算才对)
  10. 众达说两化融合管理体系A015C=数字化转型框架五个过程方法