day118:MoFang:根据激活/未激活的状态分别显示树桩&种植植物&解锁树桩&化肥/修剪/浇水/宠物粮小图标数字的显示

2023-06-26,,

登录

1.根据激活状态和未激活状态分别显示树桩

2.用户使用植物道具进行果树种植

3.解锁树桩

4.化肥/修剪/浇水/宠物粮小图标显示

种植栏的功能实现

1. 客户端需要的植物相关参数: 总树桩数量, 当前用户激活树桩数量, 当前种植的树桩数量, 树桩列表状态

2. 客户端根据激活状态和未激活状态分别显示树桩

3. 服务端在用户进入种植园时提供上面的数据

4. 用户如果第一次进入种植园需要初始化参数

5. 数据库中必须预设树桩的相关参数

6. 用户可以使用道具对树桩进行响应的操作

7. 用户可以在背包里面进行果树的种植

1.根据激活状态和未激活状态分别显示树桩

1.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="i in active_tree">
<img src="../static/images/tree1.png" alt="">
</div>
<!-- 未激活树桩列表 -->
<div 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>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:[],
user_tree_data:{
"total_tree":9, // 总树桩数量
"user_tree_number": 5, // 当前用户激活树桩数量
"user_tree_list":[ // 当前种植的树桩列表状态 ],
},
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();
},
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>

my_orchard.html-先确定前端需要哪些数据

2.my_orchard.html-确定当前种植的树桩状态列表的数据结构

user_tree_data:{
"total_tree":9, // 总树桩数量
"user_tree_number": 5, // 当前用户激活树桩数量
"user_tree_list":[ // 当前种植的树桩列表状态
{ // 树桩状态
"time":1609808084, // 种植时间
"status":4, // 状态
"has_time": 300, // 状态时间
},
],
},
pet_number:[],
tree_status:{
"tree0": "../static/images/tree0.png", // 树桩
"tree1": "../static/images/tree1.png", // 空桩
"tree2": "../static/images/tree2.png", // 幼苗
"tree3": "../static/images/tree3.png", // 成长
"tree4": "../static/images/tree4.png", // 成熟
},

客户端显示种植植物相关的参数,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 v-if="tree.status==2" src="../static/images/tree2.png" alt="">
<img v-if="tree.status==3" src="../static/images/tree3.png" alt="">
<img v-if="tree.status==4" src="../static/images/tree4.png" alt="">
</div>
<!-- 已激活但是未种植的树桩列表 -->
<div class="tree" v-for="i in active_tree">
<img src="../static/images/tree1.png" alt="">
</div>
<!-- 未激活树桩列表 -->
<div 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>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:[],
user_tree_data:{
"total_tree":9, // 总树桩数量
"user_tree_number": 5, // 当前用户激活树桩数量
"user_tree_list":[ // 当前种植的树桩列表状态
{ // 树桩状态
"time":1609808084, // 种植时间
"status":4, // 状态
"has_time": 300, // 状态时间
},
],
},
pet_number:[],
tree_status:{
"tree0": "../static/images/tree0.png", // 树桩
"tree1": "../static/images/tree1.png", // 空桩
"tree2": "../static/images/tree2.png", // 幼苗
"tree3": "../static/images/tree3.png", // 成长
"tree4": "../static/images/tree4.png", // 成熟
},
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();
},
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>

my_orchard.html-确定当前种植的树桩状态列表的数据结构

3.服务端提供种植植物的相关数据

1.在setting表中添加种植植物的参数数据

添加参数数据,SQL代码:

INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (19, 'tree_status_0', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-未解锁', 'tree0.png');
INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (20, 'tree_status_1', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-未种植', 'tree1.png');
INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (21, 'tree_status_2', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-幼苗', 'tree2.png');
INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (22, 'tree_status_3', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-成长', 'tree3.png');
INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (23, 'tree_status_4', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-成熟', 'tree4.png');

2.后端返回当前用户种植的植物信息

服务端返回当前用户种植的植物信息,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("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", []) socketio.emit("login_response", {
"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,
}, 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 = 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
})
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
}] * padding_time
if 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.sid
socketio.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():
"""解锁背包"""
# 从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中提取当前宠物的饱食度和有效期
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.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_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 = 7
else:
expire = exp_data.value if ski_data is None:
skill = 10
else:
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() # 基本保存到mongo
mongo.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) - 1
else:
prop_list.pop(key)
break mongo.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)

用户登录种植园成功后,后端返回当前用户种植的植物信息

4.orchard.html-登陆种植园成功后将植物相关信息获取并保存在前端,并发送事件

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
<div class="header">
<div class="info" @click="go_home">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<p class="user_name">好听的昵称</p>
</div>
<div class="wallet">
<div class="balance" @click="user_recharge">
<p class="title"><img src="../static/images/money.png" alt="">钱包</p>
<p class="num">{{money}}</p>
</div>
<div class="balance">
<p class="title"><img src="../static/images/integral.png" alt="">果子</p>
<p class="num">99,999.00</p>
</div>
</div>
<div class="menu-list">
<div class="menu">
<img src="../static/images/menu1.png" alt="">
排行榜
</div>
<div class="menu">
<img src="../static/images/menu2.png" alt="">
签到有礼
</div>
<div class="menu" @click="go_orchard_shop">
<img src="../static/images/menu3.png" alt="">
道具商城
</div>
<div class="menu">
<img src="../static/images/menu4.png" alt="">
邮件中心
</div>
</div>
</div>
<div class="footer" >
<ul class="menu-list">
<li class="menu">新手</li>
<li class="menu" @click="go_my_package">背包</li>
<li class="menu-center" @click="go_orchard_shop">商店</li>
<li class="menu">消息</li>
<li class="menu">好友</li>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang',
token:"",
money:"",
settings_info:{
orchard: {}, // 种植园公共参数
user:{}, // 用户私有相关参数
},
socket: null,
recharge_list: ['10','20','50','100','200','500','1000'],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
beforeCreate(){
this.game.goFrame("orchard","my_orchard.html", this.current,{
x: 0,
y: 180,
w: 'auto',
h: 410,
},null);
},
created(){
this.checkout();
this.money = this.game.fget("money");
},
methods:{
user_recharge(){
// 发起充值请求
api.actionSheet({
title: '余额充值',
cancelTitle: '取消',
buttons: this.recharge_list
}, (ret, err)=>{
if( ret ){
if(ret.buttonIndex <= this.recharge_list.length){
// 充值金额
money = this.recharge_list[ret.buttonIndex-1];
// 调用支付宝充值
this.create_recharge(money);
}
}else{ }
}); },
create_recharge(money){
// 获取历史信息记录
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.create",
"params": {
"money": money,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// 前往支付宝
var aliPayPlus = api.require('aliPayPlus');
aliPayPlus.payOrder({
orderInfo: response.data.result.order_string,
sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
}, (ret, err)=>{
pay_result = {
9000:"支付成功",
8000:"正在处理中",
4000:"订单支付失败",
5000:"重复请求",
6001:"取消支付",
6002:"网络连接出错",
6004:"支付结果未知",
}
api.alert({
title: '支付结果',
msg: pay_result[ret.code],
buttons: ['确定']
});
// 通知服务端, 修改充值结果
this.return_recharge(response.data.result.order_number,token);
});
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
return_recharge(out_trade_number,token){
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.return",
"params": {
"out_trade_number": out_trade_number,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
this.money = response.data.result.money.toFixed(2);
}
})
},
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
this.login();
this.user_package();
this.buy_prop();
this.unlock_package_number();
this.show_pet();
this.use_prop();
});
},
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,
});
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,
})
})
},
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>

orchard.html-登陆种植园成功后将植物相关信息获取并保存在前端,并发送事件

5.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 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>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:[],
user_tree_data:{
"total_tree": 9, // 总树桩数量
"user_tree_number": 0, // 当前用户激活树桩数量
"user_tree_list":[ // 当前种植的树桩列表状态 ],
},
tree_status:{ },
// user_tree_data:{
// "total_tree":9, // 总树桩数量
// "user_tree_number": 5, // 当前用户激活树桩数量
// "user_tree_list":[ // 当前种植的树桩列表状态
// { // 树桩状态
// "time":1609808084, // 种植时间
// "status":4, // 植物状态
// "has_time": 300, // 状态时间
// },
// ],
// },
// tree_status:{
// "tree_status_0": "tree0.png", // 树桩
// "tree_status_1": "tree1.png", // 空桩
// "tree_status_2": "tree2.png", // 幼苗
// "tree_status_3": "tree3.png", // 成长
// "tree_status_4": "tree4.png", // 成熟
// },
pet_number:[],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
computed:{
// 已激活但是未使用的树桩
active_tree(){
return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
},
// 未激活的剩余树桩
lock_tree(){
return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
},
},
created(){
this.show_pet_list();
this.show_tree_list();
},
methods:{
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"));
}
});
},
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");
}
});
}
}
});
}
</script>
</body>
</html>

my_orchard.html-监听事件,并获取前端已经保存好的植物数据

2.用户使用植物道具进行果树种植

1.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 = 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>

package.html-用户点击背包中的道具,前端发起使用道具的请求

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">好友</li>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang',
token:"",
money:"",
settings_info:{
orchard: {}, // 种植园公共参数
user:{}, // 用户私有相关参数
},
socket: null,
recharge_list: ['10','20','50','100','200','500','1000'],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
beforeCreate(){
this.game.goFrame("orchard","my_orchard.html", this.current,{
x: 0,
y: 180,
w: 'auto',
h: 410,
},null);
},
created(){
this.checkout();
this.money = this.game.fget("money");
},
methods:{
user_recharge(){
// 发起充值请求
api.actionSheet({
title: '余额充值',
cancelTitle: '取消',
buttons: this.recharge_list
}, (ret, err)=>{
if( ret ){
if(ret.buttonIndex <= this.recharge_list.length){
// 充值金额
money = this.recharge_list[ret.buttonIndex-1];
// 调用支付宝充值
this.create_recharge(money);
}
}else{ }
}); },
create_recharge(money){
// 获取历史信息记录
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.create",
"params": {
"money": money,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// 前往支付宝
var aliPayPlus = api.require('aliPayPlus');
aliPayPlus.payOrder({
orderInfo: response.data.result.order_string,
sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
}, (ret, err)=>{
pay_result = {
9000:"支付成功",
8000:"正在处理中",
4000:"订单支付失败",
5000:"重复请求",
6001:"取消支付",
6002:"网络连接出错",
6004:"支付结果未知",
}
api.alert({
title: '支付结果',
msg: pay_result[ret.code],
buttons: ['确定']
});
// 通知服务端, 修改充值结果
this.return_recharge(response.data.result.order_number,token);
});
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
return_recharge(out_trade_number,token){
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.return",
"params": {
"out_trade_number": out_trade_number,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
this.money = response.data.result.money.toFixed(2);
}
})
},
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
this.login();
this.user_package();
this.buy_prop();
this.unlock_package_number();
this.show_pet();
this.use_prop();
});
},
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,
});
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,
})
})
},
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("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,
}); }
});
},
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>

orchard.html-监听使用道具事件

3.my_orchard.html-增加tree_img方法,前端根据后端传过来的status显示植物的不同形态

<!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 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>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:[],
user_tree_data:{
"total_tree": 9, // 总树桩数量
"user_tree_number": 0, // 当前用户激活树桩数量
"user_tree_list":[ // 当前种植的树桩列表状态 ],
},
tree_status:{ },
// user_tree_data:{
// "total_tree":9, // 总树桩数量
// "user_tree_number": 5, // 当前用户激活树桩数量
// "user_tree_list":[ // 当前种植的树桩列表状态
// { // 树桩状态
// "time":1609808084, // 种植时间
// "status":4, // 植物状态
// "has_time": 300, // 状态时间
// },
// ],
// },
// tree_status:{
// "tree_status_0": "tree0.png", // 树桩
// "tree_status_1": "tree1.png", // 空桩
// "tree_status_2": "tree2.png", // 幼苗
// "tree_status_3": "tree3.png", // 成长
// "tree_status_4": "tree4.png", // 成熟
// },
pet_number:[],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
computed:{
// 已激活但是未使用的树桩
active_tree(){
return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
},
// 未激活的剩余树桩
lock_tree(){
return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
},
},
created(){
this.show_pet_list();
this.show_tree_list();
},
methods:{
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"));
}
});
},
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");
}
});
}
}
});
}
</script>
</body>
</html>

my_orchard.html-增加tree_img方法,前端根据后端传过来的status显示植物的不同形态

4.服务端调整使用道具的后端接口和进入种植园的登录接口

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" % 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
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,
"tree_status":tree_status,
}
socketio.emit("login_response", message, 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 = 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
socketio.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():
"""解锁背包"""
# 从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中提取当前宠物的饱食度和有效期
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):
"""使用道具"""
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表示幼苗状态
"water_time": 0, # 浇水次数
}
}}) # 从种下去到浇水的时间
pipe = redis.pipeline()
pipe.multi()
setting = Setting.query.filter(Setting.name == "tree_water_time").first()
if setting is None:
tree_water_time = 3600
else:
tree_water_time = int(setting.value)
# 必须等时间到了才可以浇水
pipe.setex("user_tree_water_%s_%s" % (user.id,len(tree_list)),int(tree_water_time),"_")
# 必须等时间到了才可以到成长期
setting = Setting.query.filter(Setting.name == "tree_growup_time").first()
if setting is None:
tree_growup_time = 3600
else:
tree_growup_time = int(setting.value)
pipe.setex("user_tree_growup_%s_%s" % (user.id,len(tree_list)),tree_growup_time, "_")
pipe.execute() user_login({"uid": user.id}) if int(prop_data.prop_type) == 1:
"""使用宠物道具"""
# 1. 判断当前的宠物数量
# 获取宠物列表
pet_list = user_info.get("pet_list", [])
if len(pet_list) > 1:
socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
return # 2. 是否有空余的宠物栏位
pet_number = user_info.get("pet_number",1)
if pet_number <= len(pet_list):
socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
return # 3. 初始化当前宠物信息
# 获取有效期和防御值
exp_data = Setting.query.filter(Setting.name=="pet_expire_%s" % pid).first()
ski_data = Setting.query.filter(Setting.name=="pet_skill_%s" % pid).first() if exp_data is None:
# 默认7天有效期
expire = 7
else:
expire = exp_data.value if ski_data is None:
skill = 10
else:
skill = ski_data.value # 在redis中设置当前宠物的饱食度
pipe = redis.pipeline()
pipe.multi()
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() # 基本保存到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)

服务端调整使用道具后端接口&种植园登录接口

3.解锁树桩

1.在Setting表中添加解锁树桩的费用

INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (28, 'active_tree_price', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '激活树桩的递增果子费用', '1000');

2.后端提供解锁树桩的接口

@socketio.on("active_tree", namespace="/mofang")
def active_tree():
"""激活树桩"""
room = request.sid
# 获取mongo中的用户信息
user_info = mongo.db.user_info_list.find_one({"sid": request.sid})
# 获取mysql中的用户信息
user = User.query.get(user_info.get("_id"))
if user is None:
socketio.emit("active_tree_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists},
namespace="/mofang", room=room)
return # 判断树桩是否达到上限
tree_number_data = Setting.query.filter(Setting.name == "user_active_tree").first()
total_tree_data = Setting.query.filter(Setting.name == "user_total_tree").first()
if tree_number_data is None:
tree_number = 1
else:
tree_number = tree_number_data.value if total_tree_data is None:
total_tree = 9
else:
total_tree = int(total_tree_data.value) user_tree_number = int(user_info.get("user_tree_number",tree_number))
if user_tree_number >= total_tree:
socketio.emit("active_tree_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty},
namespace="/mofang", room=room)
return # 扣除激活的果子数量
ret = Setting.query.filter(Setting.name == "active_tree_price").first()
if ret is None:
active_tree_price = 100 * user_tree_number
else:
active_tree_price = int(ret.value) * user_tree_number if active_tree_price > int(user.credit):
socketio.emit("active_tree_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
namespace="/mofang", room=room)
return user.credit = int(user.credit) - active_tree_price
db.session.commit() mongo.db.user_info_list.update_one({"sid":room},{"$set":{"user_tree_number":user_tree_number+1}}) socketio.emit("active_tree_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
namespace="/mofang", room=room)
return

3.前端发起请求解锁树桩

1.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>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:[],
user_tree_data:{
"total_tree": 9, // 总树桩数量
"user_tree_number": 0, // 当前用户激活树桩数量
"user_tree_list":[ // 当前种植的树桩列表状态 ],
},
tree_status:{ },
// user_tree_data:{
// "total_tree":9, // 总树桩数量
// "user_tree_number": 5, // 当前用户激活树桩数量
// "user_tree_list":[ // 当前种植的树桩列表状态
// { // 树桩状态
// "time":1609808084, // 种植时间
// "status":4, // 植物状态
// "has_time": 300, // 状态时间
// },
// ],
// },
// tree_status:{
// "tree_status_0": "tree0.png", // 树桩
// "tree_status_1": "tree1.png", // 空桩
// "tree_status_2": "tree2.png", // 幼苗
// "tree_status_3": "tree3.png", // 成长
// "tree_status_4": "tree4.png", // 成熟
// },
pet_number:[],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
computed:{
// 已激活但是未使用的树桩
active_tree(){
return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
},
// 未激活的剩余树桩
lock_tree(){
return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
},
},
created(){
this.show_pet_list();
this.show_tree_list();
},
methods:{
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"));
}
});
},
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");
}
});
},
unlock_tree(){
// 激活树桩通知
api.confirm({
title: '提示',
msg: '是否激活树桩?',
buttons: ['确定', '取消']
}, (ret, err)=>{
if( ret.buttonIndex == 1 ){
api.sendEvent({
name: 'active_tree',
extra: { }
});
}
}); // 激活成功!
api.addEventListener({
name: 'active_tree_success'
}, (ret, err)=>{
if( ret ){
// 新增树桩的数量
this.user_tree_data.user_tree_number+=1;
this.game.save({"user_tree_number": this.user_tree_data.user_tree_number});
}
}); }
}
});
}
</script>
</body>
</html>

解锁树桩:my_orchard.html

2.orchard.html

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
<div class="header">
<div class="info" @click="go_home">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<p class="user_name">好听的昵称</p>
</div>
<div class="wallet">
<div class="balance" @click="user_recharge">
<p class="title"><img src="../static/images/money.png" alt="">钱包</p>
<p class="num">{{money}}</p>
</div>
<div class="balance">
<p class="title"><img src="../static/images/integral.png" alt="">果子</p>
<p class="num">99,999.00</p>
</div>
</div>
<div class="menu-list">
<div class="menu">
<img src="../static/images/menu1.png" alt="">
排行榜
</div>
<div class="menu">
<img src="../static/images/menu2.png" alt="">
签到有礼
</div>
<div class="menu" @click="go_orchard_shop">
<img src="../static/images/menu3.png" alt="">
道具商城
</div>
<div class="menu">
<img src="../static/images/menu4.png" alt="">
邮件中心
</div>
</div>
</div>
<div class="footer" >
<ul class="menu-list">
<li class="menu">新手</li>
<li class="menu" @click="go_my_package">背包</li>
<li class="menu-center" @click="go_orchard_shop">商店</li>
<li class="menu">消息</li>
<li class="menu">好友</li>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang',
token:"",
money:"",
settings_info:{
orchard: {}, // 种植园公共参数
user:{}, // 用户私有相关参数
},
socket: null,
recharge_list: ['10','20','50','100','200','500','1000'],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
beforeCreate(){
this.game.goFrame("orchard","my_orchard.html", this.current,{
x: 0,
y: 180,
w: 'auto',
h: 410,
},null);
},
created(){
this.checkout();
this.money = this.game.fget("money");
},
methods:{
user_recharge(){
// 发起充值请求
api.actionSheet({
title: '余额充值',
cancelTitle: '取消',
buttons: this.recharge_list
}, (ret, err)=>{
if( ret ){
if(ret.buttonIndex <= this.recharge_list.length){
// 充值金额
money = this.recharge_list[ret.buttonIndex-1];
// 调用支付宝充值
this.create_recharge(money);
}
}else{ }
}); },
create_recharge(money){
// 获取历史信息记录
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.create",
"params": {
"money": money,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// 前往支付宝
var aliPayPlus = api.require('aliPayPlus');
aliPayPlus.payOrder({
orderInfo: response.data.result.order_string,
sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
}, (ret, err)=>{
pay_result = {
9000:"支付成功",
8000:"正在处理中",
4000:"订单支付失败",
5000:"重复请求",
6001:"取消支付",
6002:"网络连接出错",
6004:"支付结果未知",
}
api.alert({
title: '支付结果',
msg: pay_result[ret.code],
buttons: ['确定']
});
// 通知服务端, 修改充值结果
this.return_recharge(response.data.result.order_number,token);
});
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
return_recharge(out_trade_number,token){
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.return",
"params": {
"out_trade_number": out_trade_number,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
this.money = response.data.result.money.toFixed(2);
}
})
},
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
this.login();
this.user_package();
this.buy_prop();
this.unlock_package_number();
this.show_pet();
this.use_prop();
this.active_tree();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
var id = this.game.fget("id");
this.socket.emit("login",{"uid":id});
this.socket.emit("user_prop");
});
},
login(){
this.socket.on("login_response",(message)=>{
this.settings_info.orchard = message.orchard_settings;
this.settings_info.user=message.user_settings;
this.game.fsave({
"orchard_settings":message.orchard_settings,
"user_settings":message.user_settings,
});
this.game.save({
"tree_total":message.tree_total,
"user_tree_number":message.user_tree_number,
"user_tree_list":message.user_tree_list,
"tree_status":message.tree_status,
});
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,
})
})
},
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("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,
}); }
});
},
unlock_package_number(){
api.addEventListener({
name: 'unlock_package_number'
}, (ret, err)=>{
if( ret ){
// 用户购买道具
this.socket.emit("unlock_package");
}
}); this.socket.on("unlock_package_response",(message)=>{
if(parseInt(message.errno) === 1000){
api.sendEvent({
name: 'unlock_package_success',
extra: {
}
});
}else{
api.alert({
title: '提示',
msg: message.errmsg,
}); } })
},
show_pet(){
// 显示宠物
this.socket.emit("pet_show");
this.socket.on("pet_show_response",(message)=>{
if(parseInt(message.errno) === 1000){
// 把宠物信息保存到本地
this.game.save({"pet_list":message.pet_list})
this.game.save({"pet_number":message.pet_number})
setTimeout(()=>{
api.sendEvent({
name: 'pet_show_success',
extra: {}
});
},500);
}else{
api.alert({
title: '提示',
msg: message.errmsg,
});
} })
},
active_tree(){
// 激活树桩
api.addEventListener({
name: 'active_tree'
}, (ret, err)=>{
if( ret ){
this.socket.emit("active_tree");
}
}); this.socket.on("active_tree_response",(message)=>{
if(parseInt(message.errno) === 1000){
// 更新数据到本地
api.sendEvent({
name: 'active_tree_success',
extra: {}
});
}else{
api.alert({
title: '提示',
msg: message.errmsg,
});
} })
}
}
});
}
</script>
</body>
</html>

解锁树桩:orchard.html

4.化肥/修剪/浇水/宠物粮小图标显示

1.显示化肥和宠物粮的道具数量

1.后端返回道具数量

服务端提供化肥和宠物粮的道具数量

from application import socketio
from flask import request
from application.apps.users.models import User
from flask_socketio import join_room, leave_room
from application import mongo
from .models import Goods,Setting,db
from status import APIStatus as status
from message import ErrorMessage as errmsg
# 建立socket通信
# @socketio.on("connect", namespace="/mofang")
# def user_connect():
# """用户连接"""
# print("用户%s连接过来了!" % request.sid)
# # 主动响应数据给客户端
# socketio.emit("server_response","hello",namespace="/mofang") # 断开socket通信
@socketio.on("disconnect", namespace="/mofang")
def user_disconnect():
print("用户%s退出了种植园" % request.sid ) @socketio.on("login", namespace="/mofang")
def user_login(data):
# 分配房间
room = data["uid"]
join_room(room)
# 保存当前用户和sid的绑定关系
# 判断当前用户是否在mongo中有记录
query = {
"_id": data["uid"]
}
ret = mongo.db.user_info_list.find_one(query)
if ret:
mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}})
else:
mongo.db.user_info_list.insert_one({
"_id": data["uid"],
"sid": request.sid,
}) # 返回种植园的相关配置参数
orchard_settings = {}
setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all()
"""
现在的格式:
[<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>]
需要返回的格式:
{
package_number_base:4,
package_number_max: 32,
...
}
"""
for item in setting_list:
orchard_settings[item.name] = item.value # 返回当前用户相关的配置参数
user_settings = {}
# 从mongo中查找用户信息,判断用户是否激活了背包格子
user_dict = mongo.db.user_info_list.find_one({"sid":request.sid})
# 背包格子
if user_dict.get("package_number") is None:
user_settings["package_number"] = orchard_settings.get("package_number_base",4)
mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}})
else:
user_settings["package_number"] = user_dict.get("package_number") """种植园植物信息"""
# 总树桩数量
setting = Setting.query.filter(Setting.name == "user_total_tree").first()
if setting is None:
tree_total = 9
else:
tree_total = int(setting.value) # 用户已经激活的树桩
setting = Setting.query.filter(Setting.name == "user_active_tree").first()
if setting is None:
user_tree_number = 3
else:
user_tree_number = int(setting.value)
user_tree_number = user_dict.get("user_tree_number",user_tree_number) # 种植的植物列表
user_tree_list = user_dict.get("user_tree_list", [])
key = 0
for tree_item in user_tree_list:
tree_item["status"] = "tree_status_%s" % 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_orchard_prop_list(user_dict) 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,
}
socketio.emit("login_response", message, namespace="/mofang", room=room) def get_orchard_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_orchard_prop_list(userinfo)
socketio.emit("user_prop_response", {
"errno": status.CODE_OK,
"errmsg": errmsg.ok,
"data":data,
"fertilizer_num":fertilizer_num,
"pet_food_num":pet_food_num,
}, namespace="/mofang",
room=room) @socketio.on("unlock_package", namespace="/mofang")
def unlock_package():
"""解锁背包"""
# 从mongo获取当前用户解锁的格子数量
user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
user = User.query.get(user_info.get("_id"))
if user is None:
socketio.emit("unlock_package_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
return package_number = int(user_info.get("package_number"))
num = 7 - (32 - package_number) // 4 # 没有解锁的格子 # 从数据库中获取解锁背包的价格
setting = Setting.query.filter(Setting.name == "package_unlock_price_%s" % num).first()
if setting is None:
unlock_price = 0
else:
unlock_price = int(setting.value) # 判断是否有足够的积分或者价格
room = request.sid
if user.money < unlock_price:
socketio.emit("unlock_package_response", {"errno": status.CODE_NO_MONEY, "errmsg": errmsg.money_no_enough},
namespace="/mofang", room=room)
return # 解锁成功
user.money = float(user.money) - float(unlock_price)
db.session.commit() # mongo中调整数量
mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": package_number+1}})
# 返回解锁的结果
socketio.emit("unlock_package_response", {
"errno": status.CODE_OK,
"errmsg": errmsg.ok},
namespace="/mofang", room=room)
import math
from application import redis
@socketio.on("pet_show", namespace="/mofang")
def pet_show():
"""显示宠物"""
room = request.sid
user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
user = User.query.get(user_info.get("_id"))
if user is None:
socketio.emit("pet_show_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
return # 获取宠物列表
pet_list = user_info.get("pet_list", []) """
pet_list: [
{
"pid":11,
"image":"pet.png",
"hp":100%,
"created_time":xxxx-xx-xx xx:xx:xx,
"skill":"70%",
"has_time":30天
},
]
"""
# 从redis中提取当前宠物的饱食度和有效期
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):
"""使用道具"""
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表示幼苗状态
"water_time": 0, # 浇水次数
}
}}) # 从种下去到浇水的时间
pipe = redis.pipeline()
pipe.multi()
setting = Setting.query.filter(Setting.name == "tree_water_time").first()
if setting is None:
tree_water_time = 3600
else:
tree_water_time = int(setting.value)
# 必须等时间到了才可以浇水
pipe.setex("user_tree_water_%s_%s" % (user.id,len(tree_list)),int(tree_water_time),"_")
# 必须等时间到了才可以到成长期
setting = Setting.query.filter(Setting.name == "tree_growup_time").first()
if setting is None:
tree_growup_time = 3600
else:
tree_growup_time = int(setting.value)
pipe.setex("user_tree_growup_%s_%s" % (user.id,len(tree_list)),tree_growup_time, "_")
pipe.execute() user_login({"uid": user.id}) if int(prop_data.prop_type) == 1:
"""使用宠物道具"""
# 1. 判断当前的宠物数量
# 获取宠物列表
pet_list = user_info.get("pet_list", [])
if len(pet_list) > 1:
socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
return # 2. 是否有空余的宠物栏位
pet_number = user_info.get("pet_number",1)
if pet_number <= len(pet_list):
socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
return # 3. 初始化当前宠物信息
# 获取有效期和防御值
exp_data = Setting.query.filter(Setting.name=="pet_expire_%s" % pid).first()
ski_data = Setting.query.filter(Setting.name=="pet_skill_%s" % pid).first() if exp_data is None:
# 默认7天有效期
expire = 7
else:
expire = exp_data.value if ski_data is None:
skill = 10
else:
skill = ski_data.value # 在redis中设置当前宠物的饱食度
pipe = redis.pipeline()
pipe.multi()
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() # 基本保存到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

服务端提供化肥和宠物粮的道具数量

2.前端显示道具数量

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
<div class="header">
<div class="info" @click="go_home">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<p class="user_name">好听的昵称</p>
</div>
<div class="wallet">
<div class="balance" @click="user_recharge">
<p class="title"><img src="../static/images/money.png" alt="">钱包</p>
<p class="num">{{money}}</p>
</div>
<div class="balance">
<p class="title"><img src="../static/images/integral.png" alt="">果子</p>
<p class="num">99,999.00</p>
</div>
</div>
<div class="menu-list">
<div class="menu">
<img src="../static/images/menu1.png" alt="">
排行榜
</div>
<div class="menu">
<img src="../static/images/menu2.png" alt="">
签到有礼
</div>
<div class="menu" @click="go_orchard_shop">
<img src="../static/images/menu3.png" alt="">
道具商城
</div>
<div class="menu">
<img src="../static/images/menu4.png" alt="">
邮件中心
</div>
</div>
</div>
<div class="footer" >
<ul class="menu-list">
<li class="menu">新手</li>
<li class="menu" @click="go_my_package">背包</li>
<li class="menu-center" @click="go_orchard_shop">商店</li>
<li class="menu">消息</li>
<li class="menu">好友</li>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang',
token:"",
money:"",
settings_info:{
orchard: {}, // 种植园公共参数
user:{}, // 用户私有相关参数
},
socket: null,
recharge_list: ['10','20','50','100','200','500','1000'],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
beforeCreate(){
this.game.goFrame("orchard","my_orchard.html", this.current,{
x: 0,
y: 180,
w: 'auto',
h: 410,
},null);
},
created(){
this.checkout();
this.money = this.game.fget("money");
},
methods:{
user_recharge(){
// 发起充值请求
api.actionSheet({
title: '余额充值',
cancelTitle: '取消',
buttons: this.recharge_list
}, (ret, err)=>{
if( ret ){
if(ret.buttonIndex <= this.recharge_list.length){
// 充值金额
money = this.recharge_list[ret.buttonIndex-1];
// 调用支付宝充值
this.create_recharge(money);
}
}else{ }
}); },
create_recharge(money){
// 获取历史信息记录
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.create",
"params": {
"money": money,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// 前往支付宝
var aliPayPlus = api.require('aliPayPlus');
aliPayPlus.payOrder({
orderInfo: response.data.result.order_string,
sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
}, (ret, err)=>{
pay_result = {
9000:"支付成功",
8000:"正在处理中",
4000:"订单支付失败",
5000:"重复请求",
6001:"取消支付",
6002:"网络连接出错",
6004:"支付结果未知",
}
api.alert({
title: '支付结果',
msg: pay_result[ret.code],
buttons: ['确定']
});
// 通知服务端, 修改充值结果
this.return_recharge(response.data.result.order_number,token);
});
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
return_recharge(out_trade_number,token){
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.return",
"params": {
"out_trade_number": out_trade_number,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
this.money = response.data.result.money.toFixed(2);
}
})
},
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
this.login();
this.user_package();
this.buy_prop();
this.unlock_package_number();
this.show_pet();
this.use_prop();
this.active_tree();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
var id = this.game.fget("id");
this.socket.emit("login",{"uid":id});
this.socket.emit("user_prop");
});
},
login(){
this.socket.on("login_response",(message)=>{
this.settings_info.orchard = message.orchard_settings;
this.settings_info.user=message.user_settings;
this.game.fsave({
"orchard_settings":message.orchard_settings,
"user_settings":message.user_settings,
});
this.game.save({
"tree_total":message.tree_total,
"user_tree_number":message.user_tree_number,
"user_tree_list":message.user_tree_list,
"tree_status":message.tree_status,
"pet_food_num":message.pet_food_num,
"fertilizer_num":message.fertilizer_num,
});
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;
this.socket.emit("use_prop",ret.value.pid);
}
}); 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,
}); }
});
},
unlock_package_number(){
api.addEventListener({
name: 'unlock_package_number'
}, (ret, err)=>{
if( ret ){
// 用户购买道具
this.socket.emit("unlock_package");
}
}); this.socket.on("unlock_package_response",(message)=>{
if(parseInt(message.errno) === 1000){
api.sendEvent({
name: 'unlock_package_success',
extra: {
}
});
}else{
api.alert({
title: '提示',
msg: message.errmsg,
}); } })
},
show_pet(){
// 显示宠物
this.socket.emit("pet_show");
this.socket.on("pet_show_response",(message)=>{
if(parseInt(message.errno) === 1000){
// 把宠物信息保存到本地
this.game.save({"pet_list":message.pet_list})
this.game.save({"pet_number":message.pet_number})
setTimeout(()=>{
api.sendEvent({
name: 'pet_show_success',
extra: {}
});
},500);
}else{
api.alert({
title: '提示',
msg: message.errmsg,
});
} })
},
active_tree(){
// 激活树桩
api.addEventListener({
name: 'active_tree'
}, (ret, err)=>{
if( ret ){
this.socket.emit("active_tree");
}
}); this.socket.on("active_tree_response",(message)=>{
if(parseInt(message.errno) === 1000){
// 更新数据到本地
api.sendEvent({
name: 'active_tree_success',
extra: {}
});
}else{
api.alert({
title: '提示',
msg: message.errmsg,
});
} })
}
}
});
}
</script>
</body>
</html>

前端显示道具数量:orchard.html

<!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>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>{{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+'%'}" 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, // 化肥数量
pet_list:[],
user_tree_data:{
"total_tree": 9, // 总树桩数量
"user_tree_number": 0, // 当前用户激活树桩数量
"user_tree_list":[ // 当前种植的树桩列表状态 ],
},
tree_status:{ },
// user_tree_data:{
// "total_tree":9, // 总树桩数量
// "user_tree_number": 5, // 当前用户激活树桩数量
// "user_tree_list":[ // 当前种植的树桩列表状态
// { // 树桩状态
// "time":1609808084, // 种植时间
// "status":4, // 植物状态
// "has_time": 300, // 状态时间
// },
// ],
// },
// tree_status:{
// "tree_status_0": "tree0.png", // 树桩
// "tree_status_1": "tree1.png", // 空桩
// "tree_status_2": "tree2.png", // 幼苗
// "tree_status_3": "tree3.png", // 成长
// "tree_status_4": "tree4.png", // 成熟
// },
pet_number:[],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
computed:{
// 已激活但是未使用的树桩
active_tree(){
return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
},
// 未激活的剩余树桩
lock_tree(){
return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
},
},
created(){
this.show_pet_list();
this.show_tree_list();
this.get_prop_list();
},
methods:{
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"));
}
});
},
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");
}
});
},
unlock_tree(){
// 激活树桩通知
api.confirm({
title: '提示',
msg: '是否激活树桩?',
buttons: ['确定', '取消']
}, (ret, err)=>{
if( ret.buttonIndex == 1 ){
api.sendEvent({
name: 'active_tree',
extra: { }
});
}
}); // 激活成功!
api.addEventListener({
name: 'active_tree_success'
}, (ret, err)=>{
if( ret ){
// 新增树桩的数量
this.user_tree_data.user_tree_number+=1;
this.game.save({"user_tree_number": this.user_tree_data.user_tree_number});
}
});
}
}
});
}
</script>
</body>
</html>

前端显示道具数量:my_orchard.html

2.显示剪刀和浇水数量

1.后端:根据植物计算剪刀和浇水的数量

from application import socketio
from flask import request
from application.apps.users.models import User
from flask_socketio import join_room, leave_room
from application import mongo
from .models import Goods,Setting,db
from status import APIStatus as status
from message import ErrorMessage as errmsg
# 建立socket通信
# @socketio.on("connect", namespace="/mofang")
# def user_connect():
# """用户连接"""
# print("用户%s连接过来了!" % request.sid)
# # 主动响应数据给客户端
# socketio.emit("server_response","hello",namespace="/mofang") # 断开socket通信
@socketio.on("disconnect", namespace="/mofang")
def user_disconnect():
print("用户%s退出了种植园" % request.sid ) @socketio.on("login", namespace="/mofang")
def user_login(data):
# 分配房间
room = data["uid"]
join_room(room)
# 保存当前用户和sid的绑定关系
# 判断当前用户是否在mongo中有记录
query = {
"_id": data["uid"]
}
ret = mongo.db.user_info_list.find_one(query)
if ret:
mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}})
else:
mongo.db.user_info_list.insert_one({
"_id": data["uid"],
"sid": request.sid,
}) # 返回种植园的相关配置参数
orchard_settings = {}
setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all()
"""
现在的格式:
[<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>]
需要返回的格式:
{
package_number_base:4,
package_number_max: 32,
...
}
"""
for item in setting_list:
orchard_settings[item.name] = item.value # 返回当前用户相关的配置参数
user_settings = {}
# 从mongo中查找用户信息,判断用户是否激活了背包格子
user_dict = mongo.db.user_info_list.find_one({"sid":request.sid})
# 背包格子
if user_dict.get("package_number") is None:
user_settings["package_number"] = orchard_settings.get("package_number_base",4)
mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}})
else:
user_settings["package_number"] = user_dict.get("package_number") """种植园植物信息"""
# 总树桩数量
setting = Setting.query.filter(Setting.name == "user_total_tree").first()
if setting is None:
tree_total = 9
else:
tree_total = int(setting.value) # 用户已经激活的树桩
setting = Setting.query.filter(Setting.name == "user_active_tree").first()
if setting is None:
user_tree_number = 3
else:
user_tree_number = int(setting.value)
user_tree_number = user_dict.get("user_tree_number",user_tree_number) # 种植的植物列表
user_tree_list = user_dict.get("user_tree_list", [])
key = 0
for tree_item in user_tree_list:
tree_item["status"] = "tree_status_%s" % int(tree_item["status"]) # 植物状态
tree_item["water_time"] = redis.ttl("user_tree_water_%s_%s" % (data["uid"],key))
tree_item["growup_time"] = redis.ttl("user_tree_growup_%s_%s" % (data["uid"],key))
key+=1
# 植物状态信息
status_list = [
"tree_status_0",
"tree_status_1",
"tree_status_2",
"tree_status_3",
"tree_status_4",
]
setting_list = Setting.query.filter(Setting.name.in_(status_list)).all()
tree_status = {}
for item in setting_list:
tree_status[item.name] = item.value """显示植物相关道具"""
# 获取背包中的化肥和宠物粮
fertilizer_num,pet_food_num = get_package_prop_list(user_dict)
# 获取剪刀和浇水
# 只有植物处于成长期才会允许裁剪
# 只有植物处于幼苗期才会允许浇水
waters = 0
shears = 0
user_tree_list = user_dict.get("user_tree_list",[])
if len(user_tree_list) > 0:
key = 0
for tree in user_tree_list:
if (tree["status"] == "tree_status_%s" % 2) and int(tree.get("waters",0)) == 0:
"""处于幼苗期"""
# 判断只有种植指定时间以后的幼苗才可以浇水
interval_time = redis.ttl("user_tree_water_%s_%s" % (user_dict.get("_id"), key) )
if interval_time==-2:
waters+=1 elif (tree["status"] == "tree_status_%s" % 3) and int(tree.get("shears",0)) == 0:
"""处于成长期"""
interval_time = redis.ttl("user_tree_shears_%s_%s" % (user_dict.get("_id"), key))
if interval_time==-2:
shears+=1
key+=1
message = {
"errno":status.CODE_OK,
"errmsg":errmsg.ok,
"orchard_settings":orchard_settings,
"user_settings":user_settings,
"tree_total":tree_total,
"user_tree_number":user_tree_number,
"user_tree_list":user_tree_list,
"fertilizer_num":fertilizer_num,
"pet_food_num":pet_food_num,
"tree_status":tree_status,
"waters":waters,
"shears":shears,
}
print(message)
socketio.emit("login_response", message, namespace="/mofang", room=room) def get_package_prop_list(user_dict):
fertilizer_num = 0
pet_food_num = 0
prop_list = user_dict.get("prop_list", {})
for prop_item, num in prop_list.items():
pid = prop_item.split("_")[-1]
num = int(num)
prop_obj = Goods.query.get(pid)
if prop_obj.prop_type == 2:
# 提取化肥
fertilizer_num += num
elif prop_obj.prop_type == 3:
# 提取宠物粮
pet_food_num += num return fertilizer_num,pet_food_num @socketio.on("user_buy_prop", namespace="/mofang")
def user_buy_prop(data):
"""用户购买道具"""
room = request.sid
# 从mongo中获取当前用户信息
user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
user = User.query.get(user_info.get("_id"))
if user is None:
socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
return # 判断背包物品存储是否达到上限
use_package_number = int(user_info.get("use_package_number",0)) # 当前诗经使用的格子数量
package_number = int(user_info.get("package_number",0)) # 当前用户已经解锁的格子数量
# 本次购买道具需要使用的格子数量
setting = Setting.query.filter(Setting.name == "td_prop_max").first()
if setting is None:
td_prop_max = 10
else:
td_prop_max = int(setting.value) # 计算购买道具以后需要额外占用的格子数量
if ("prop_%s" % data["pid"]) in user_info.get("prop_list",{}):
"""曾经购买过当前道具"""
prop_num = int( user_info.get("prop_list")["prop_%s" % data["pid"]]) # 购买前的道具数量
new_prop_num = prop_num+int(data["num"]) # 如果成功购买道具以后的数量
old_td_num = prop_num // td_prop_max
if prop_num % td_prop_max > 0:
old_td_num+=1
new_td_num = new_prop_num // td_prop_max
if new_prop_num % td_prop_max > 0:
new_td_num+=1
td_num = new_td_num - old_td_num
else:
"""新增购买的道具"""
# 计算本次购买道具需要占用的格子数量 if int(data["num"]) > td_prop_max:
"""需要多个格子"""
td_num = int(data["num"]) // td_prop_max
if int(data["num"]) % td_prop_max > 0:
td_num+=1
else:
"""需要一个格子"""
td_num = 1 if use_package_number+td_num > package_number:
"""超出存储上限"""
socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_PACKAGE, "errmsg": errmsg.no_package},
namespace="/mofang", room=room)
return # 从mysql中获取商品价格
prop = Goods.query.get(data["pid"])
if user.money > 0: # 当前商品需要通过RMB购买
if float(user.money) < float(prop.price) * int(data["num"]):
socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room)
return
else:
"""当前通过果子进行购买"""
if int(user.credit) < int(prop.credit) * int(data["num"]):
socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
namespace="/mofang", room=room)
return # 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额
query = {"sid": request.sid}
if user_info.get("prop_list") is None:
"""此前没有购买任何道具"""
message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}}
mongo.db.user_info_list.update_one(query,message)
else:
"""此前有购买了道具"""
prop_list = user_info.get("prop_list") # 道具列表
if ("prop_%s" % prop.id) in prop_list:
"""如果再次同一款道具"""
prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"])
else:
"""此前没有购买过这种道具"""
prop_list[("prop_%s" % prop.id)] = int(data["num"]) mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}}) # 扣除余额或果子
if prop.price > 0:
user.money = float(user.money) - float(prop.price) * int(data["num"])
else:
user.credit = int(user.credit) - int(prop.credit) * int(data["num"]) db.session.commit() # 返回购买成功的信息
socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room)
# 返回最新的用户道具列表
user_prop() @socketio.on("user_prop", namespace="/mofang")
def user_prop():
"""用户道具"""
userinfo = mongo.db.user_info_list.find_one({"sid":request.sid})
prop_list = userinfo.get("prop_list",{})
prop_id_list = []
for prop_str,num in prop_list.items():
pid = int(prop_str[5:])
prop_id_list.append(pid) data = []
prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all()
setting = Setting.query.filter(Setting.name == "td_prop_max").first()
if setting is None:
td_prop_max = 10
else:
td_prop_max = int(setting.value) for prop_data in prop_list_data:
num = int( prop_list[("prop_%s" % prop_data.id)])
if td_prop_max > num:
data.append({
"num": num,
"image": prop_data.image,
"pid": prop_data.id,
"pty": prop_data.prop_type,
})
else:
padding_time = num // td_prop_max
padding_last = num % td_prop_max
arr = [{
"num": td_prop_max,
"image": prop_data.image,
"pid": prop_data.id,
"pty": prop_data.prop_type,
}] * padding_time
if padding_last != 0:
arr.append({
"num": padding_last,
"image": prop_data.image,
"pid": prop_data.id,
"pty": prop_data.prop_type,
})
data = data + arr
mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"use_package_number":len(data)}})
room = request.sid
fertilizer_num,pet_food_num = get_package_prop_list(userinfo)
socketio.emit("user_prop_response", {
"errno": status.CODE_OK,
"errmsg": errmsg.ok,
"data":data,
"fertilizer_num":fertilizer_num,
"pet_food_num":pet_food_num,
}, namespace="/mofang",
room=room) @socketio.on("unlock_package", namespace="/mofang")
def unlock_package():
"""解锁背包"""
# 从mongo获取当前用户解锁的格子数量
user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
user = User.query.get(user_info.get("_id"))
if user is None:
socketio.emit("unlock_package_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
return package_number = int(user_info.get("package_number"))
num = 7 - (32 - package_number) // 4 # 没有解锁的格子 # 从数据库中获取解锁背包的价格
setting = Setting.query.filter(Setting.name == "package_unlock_price_%s" % num).first()
if setting is None:
unlock_price = 0
else:
unlock_price = int(setting.value) # 判断是否有足够的积分或者价格
room = request.sid
if user.money < unlock_price:
socketio.emit("unlock_package_response", {"errno": status.CODE_NO_MONEY, "errmsg": errmsg.money_no_enough},
namespace="/mofang", room=room)
return # 解锁成功
user.money = float(user.money) - float(unlock_price)
db.session.commit() # mongo中调整数量
mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": package_number+1}})
# 返回解锁的结果
socketio.emit("unlock_package_response", {
"errno": status.CODE_OK,
"errmsg": errmsg.ok},
namespace="/mofang", room=room)
import math
from application import redis
@socketio.on("pet_show", namespace="/mofang")
def pet_show():
"""显示宠物"""
room = request.sid
user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
user = User.query.get(user_info.get("_id"))
if user is None:
socketio.emit("pet_show_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
return # 获取宠物列表
pet_list = user_info.get("pet_list", []) """
pet_list: [
{
"pid":11,
"image":"pet.png",
"hp":100%,
"created_time":xxxx-xx-xx xx:xx:xx,
"skill":"70%",
"has_time":30天
},
]
"""
# 从redis中提取当前宠物的饱食度和有效期
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):
"""使用道具"""
room = request.sid
# 获取mongo中的用户信息
user_info = mongo.db.user_info_list.find_one({"sid": request.sid})
# 获取mysql中的用户信息
user = User.query.get(user_info.get("_id"))
if user is None:
socketio.emit("pet_use_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists},
namespace="/mofang", room=room)
return # 获取道具
prop_data = Goods.query.get(pid)
if prop_data is None:
socketio.emit("pet_use_response", {"errno":status.CODE_NO_SUCH_PROP,"errmsg":errmsg.not_such_prop}, namespace="/mofang", room=room)
return if int(prop_data.prop_type) == 0:
"""使用植物道具"""
# 1. 判断当前的植物数量是否有空余
tree_list = user_info.get("user_tree_list", []) # 当前用户最多可种植的数量
setting = Setting.query.filter(Setting.name == "user_active_tree").first()
if setting is None:
user_tree_number = 3
else:
user_tree_number = int(setting.value)
user_tree_number = user_info.get("user_tree_number", user_tree_number) if len(tree_list) >= user_tree_number:
socketio.emit("prop_use_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty},
namespace="/mofang", room=room)
return # 使用道具
mongo.db.user_info_list.update_one({"sid":room},{"$push":{"user_tree_list":
{ # 植物状态
"time": int(datetime.now().timestamp()), # 种植时间
"status": 2, # 植物状态,2表示幼苗状态
"waters": 0, # 浇水次数
"shears": 0, # 使用剪刀次数
}
}}) # 从种下去到浇水的时间
pipe = redis.pipeline()
pipe.multi()
setting = Setting.query.filter(Setting.name == "tree_water_time").first()
if setting is None:
tree_water_time = 3600
else:
tree_water_time = int(setting.value)
# 必须等时间到了才可以浇水
pipe.setex("user_tree_water_%s_%s" % (user.id,len(tree_list)),int(tree_water_time),"_")
# 必须等时间到了才可以到成长期
setting = Setting.query.filter(Setting.name == "tree_growup_time").first()
if setting is None:
tree_growup_time = 3600
else:
tree_growup_time = int(setting.value)
pipe.setex("user_tree_growup_%s_%s" % (user.id,len(tree_list)),tree_growup_time, "_")
pipe.execute() user_login({"uid": user.id}) if int(prop_data.prop_type) == 1:
"""使用宠物道具"""
# 1. 判断当前的宠物数量
# 获取宠物列表
pet_list = user_info.get("pet_list", [])
if len(pet_list) > 1:
socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
return # 2. 是否有空余的宠物栏位
pet_number = user_info.get("pet_number",1)
if pet_number <= len(pet_list):
socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
return # 3. 初始化当前宠物信息
# 获取有效期和防御值
exp_data = Setting.query.filter(Setting.name=="pet_expire_%s" % pid).first()
ski_data = Setting.query.filter(Setting.name=="pet_skill_%s" % pid).first() if exp_data is None:
# 默认7天有效期
expire = 7
else:
expire = exp_data.value if ski_data is None:
skill = 10
else:
skill = ski_data.value # 在redis中设置当前宠物的饱食度
pipe = redis.pipeline()
pipe.multi()
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() # 基本保存到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

服务端根据植物的状态计算剪刀和浇水的数量

2.前端:进入种植园后显示剪刀和浇水的次数

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
<div class="header">
<div class="info" @click="go_home">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<p class="user_name">好听的昵称</p>
</div>
<div class="wallet">
<div class="balance" @click="user_recharge">
<p class="title"><img src="../static/images/money.png" alt="">钱包</p>
<p class="num">{{money}}</p>
</div>
<div class="balance">
<p class="title"><img src="../static/images/integral.png" alt="">果子</p>
<p class="num">99,999.00</p>
</div>
</div>
<div class="menu-list">
<div class="menu">
<img src="../static/images/menu1.png" alt="">
排行榜
</div>
<div class="menu">
<img src="../static/images/menu2.png" alt="">
签到有礼
</div>
<div class="menu" @click="go_orchard_shop">
<img src="../static/images/menu3.png" alt="">
道具商城
</div>
<div class="menu">
<img src="../static/images/menu4.png" alt="">
邮件中心
</div>
</div>
</div>
<div class="footer" >
<ul class="menu-list">
<li class="menu">新手</li>
<li class="menu" @click="go_my_package">背包</li>
<li class="menu-center" @click="go_orchard_shop">商店</li>
<li class="menu">消息</li>
<li class="menu">好友</li>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang',
token:"",
money:"",
settings_info:{
orchard: {}, // 种植园公共参数
user:{}, // 用户私有相关参数
},
socket: null,
recharge_list: ['10','20','50','100','200','500','1000'],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
beforeCreate(){
this.game.goFrame("orchard","my_orchard.html", this.current,{
x: 0,
y: 180,
w: 'auto',
h: 410,
},null);
},
created(){
this.checkout();
this.money = this.game.fget("money");
},
methods:{
user_recharge(){
// 发起充值请求
api.actionSheet({
title: '余额充值',
cancelTitle: '取消',
buttons: this.recharge_list
}, (ret, err)=>{
if( ret ){
if(ret.buttonIndex <= this.recharge_list.length){
// 充值金额
money = this.recharge_list[ret.buttonIndex-1];
// 调用支付宝充值
this.create_recharge(money);
}
}else{ }
}); },
create_recharge(money){
// 获取历史信息记录
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.create",
"params": {
"money": money,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// 前往支付宝
var aliPayPlus = api.require('aliPayPlus');
aliPayPlus.payOrder({
orderInfo: response.data.result.order_string,
sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
}, (ret, err)=>{
pay_result = {
9000:"支付成功",
8000:"正在处理中",
4000:"订单支付失败",
5000:"重复请求",
6001:"取消支付",
6002:"网络连接出错",
6004:"支付结果未知",
}
api.alert({
title: '支付结果',
msg: pay_result[ret.code],
buttons: ['确定']
});
// 通知服务端, 修改充值结果
this.return_recharge(response.data.result.order_number,token);
});
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
return_recharge(out_trade_number,token){
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.return",
"params": {
"out_trade_number": out_trade_number,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
this.money = response.data.result.money.toFixed(2);
}
})
},
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
this.login();
this.user_package();
this.buy_prop();
this.unlock_package_number();
this.show_pet();
this.use_prop();
this.active_tree();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
var id = this.game.fget("id");
this.socket.emit("login",{"uid":id});
this.socket.emit("user_prop");
});
},
login(){
this.socket.on("login_response",(message)=>{
this.settings_info.orchard = message.orchard_settings;
this.settings_info.user=message.user_settings;
this.game.fsave({
"orchard_settings":message.orchard_settings,
"user_settings":message.user_settings,
});
this.game.save({
"tree_total":message.tree_total,
"user_tree_number":message.user_tree_number,
"user_tree_list":message.user_tree_list,
"tree_status":message.tree_status,
"pet_food_num":message.pet_food_num,
"fertilizer_num":message.fertilizer_num,
"waters":message.waters,
"shears":message.shears,
});
setTimeout(()=>{
// 通知种植园页面获取到了当前用户的种植信息
api.sendEvent({
name: 'user_tree_data',
extra: {}
});
},500);
});
},
user_package(){
// 用户背包道具列表
this.socket.on("user_prop_response",(message)=>{
this.game.fsave({
"user_package":message.data,
});
// 界面中的道具信息
this.game.save({
"pet_food_num":message.pet_food_num,
"fertilizer_num":message.fertilizer_num,
});
api.sendEvent({
name: 'update_prop_data',
extra: { }
}); })
},
go_index(){
this.game.goWin("root");
},
go_friends(){
this.game.goFrame("friends","friends.html",this.current);
this.game.goFrame("friend_list","friend_list.html",this.current,{
x: 0,
y: 190,
w: 'auto',
h: 'auto',
},null,true);
},
go_home(){
this.game.goWin("user","user.html", this.current);
},
go_orchard_shop(){
// 种植园商店
this.game.goFrame("orchard_shop","shop.html", this.current,null,{
type:"push",
subType:"from_top",
duration:300
});
},
go_my_package(){
// 我的背包
this.game.goFrame("package","package.html", this.current,null,{
type:"push",
subType:"from_top",
duration:300
});
},
buy_prop(){
api.addEventListener({
name: 'buy_prop'
}, (ret, err)=>{
if( ret ){
// 用户购买道具
this.socket.emit("user_buy_prop",ret.value);
}
});
this.socket.on("user_buy_prop_response",(message)=>{
alert(message.errmsg); })
},
use_prop(){
// 使用道具
var pid = 0;
api.addEventListener({
name: 'use_prop'
}, (ret, err)=>{
if( ret ){
// 用户使用道具
pid = ret.value.pid;
this.socket.emit("use_prop",ret.value.pid);
}
}); 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,
}); }
});
},
unlock_package_number(){
api.addEventListener({
name: 'unlock_package_number'
}, (ret, err)=>{
if( ret ){
// 用户购买道具
this.socket.emit("unlock_package");
}
}); this.socket.on("unlock_package_response",(message)=>{
if(parseInt(message.errno) === 1000){
api.sendEvent({
name: 'unlock_package_success',
extra: {
}
});
}else{
api.alert({
title: '提示',
msg: message.errmsg,
}); } })
},
show_pet(){
// 显示宠物
this.socket.emit("pet_show");
this.socket.on("pet_show_response",(message)=>{
if(parseInt(message.errno) === 1000){
// 把宠物信息保存到本地
this.game.save({"pet_list":message.pet_list})
this.game.save({"pet_number":message.pet_number})
setTimeout(()=>{
api.sendEvent({
name: 'pet_show_success',
extra: {}
});
},500);
}else{
api.alert({
title: '提示',
msg: message.errmsg,
});
} })
},
active_tree(){
// 激活树桩
api.addEventListener({
name: 'active_tree'
}, (ret, err)=>{
if( ret ){
this.socket.emit("active_tree");
}
}); this.socket.on("active_tree_response",(message)=>{
if(parseInt(message.errno) === 1000){
// 更新数据到本地
api.sendEvent({
name: 'active_tree_success',
extra: {}
});
}else{
api.alert({
title: '提示',
msg: message.errmsg,
});
} })
}
}
});
}
</script>
</body>
</html>

前端显示剪刀和浇水的次数:orchard.html

<!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+'%'}" class="process">{{pet.hp}}%</div>
</div>
</div>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
namespace: '/mofang',
token:"",
socket: null,
pet_food_num:0, // 宠物粮数量
fertilizer_num:0, // 化肥数量
waters:0, // 浇水次数
shears:0, // 剪刀次数
pet_list:[],
user_tree_data:{
"total_tree": 9, // 总树桩数量
"user_tree_number": 0, // 当前用户激活树桩数量
"user_tree_list":[ // 当前种植的树桩列表状态 ],
},
tree_status:{ },
// user_tree_data:{
// "total_tree":9, // 总树桩数量
// "user_tree_number": 5, // 当前用户激活树桩数量
// "user_tree_list":[ // 当前种植的树桩列表状态
// { // 树桩状态
// "time":1609808084, // 种植时间
// "status":4, // 植物状态
// "has_time": 300, // 状态时间
// },
// ],
// },
// tree_status:{
// "tree_status_0": "tree0.png", // 树桩
// "tree_status_1": "tree1.png", // 空桩
// "tree_status_2": "tree2.png", // 幼苗
// "tree_status_3": "tree3.png", // 成长
// "tree_status_4": "tree4.png", // 成熟
// },
pet_number:[],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
computed:{
// 已激活但是未使用的树桩
active_tree(){
return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
},
// 未激活的剩余树桩
lock_tree(){
return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
},
},
created(){
this.show_pet_list();
this.show_tree_list();
this.get_prop_list();
},
methods:{
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"));
}
});
},
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

day118:MoFang:根据激活/未激活的状态分别显示树桩&种植植物&解锁树桩&化肥/修剪/浇水/宠物粮小图标数字的显示的相关教程结束。

《day118:MoFang:根据激活/未激活的状态分别显示树桩&种植植物&解锁树桩&化肥/修剪/浇水/宠物粮小图标数字的显示.doc》

下载本文的Word格式文档,以方便收藏与打印。