成就系统是游戏常见的功能模块,主要就是在用户达成某种条件时进行判定记录成就,难点在于嵌入各种各样的其它功能。
--保存数据字段
{ "achievement" ,"mediumblob" ,{
achievements = {},
point = 0,
pointReward = {},
countdata = {},
isrecord = {},
} ,"成就数据" },
--成就类加载数据
function Achievement:onLoad()
self.cache = self.player.cache.achievement
self.cache.countdata = self.cache.countdata or {}
self.cache.isrecord = self.cache.isrecord or {}
self.ConfigList = groupby(server.configCenter.AchievementConfig)
end
统计类型的数据种类繁多,还在不断更新增加中,其它的不需统计数据发成就id和value值即可
#成就信息
sc_achievement_info 31301 {
request {
achievements 0 : *group_data #成就分组
point 1 : integer #成就点
countdata 2 : valuedata #成就达成值
}
}
# 成就数据
.achievement_data {
id 0 : integer #成就id
time 1 : integer #获得时间
groupID 2 : integer #组id
typeID 3 : integer #子类型id
value 4 : integer #达到数值 --更新成就才有此字段
point 5 : integer #成就点
}
.type_data {
typeid 0: integer
achievedata 1: *achievement_data
}
# 成就类型数据
.group_data {
groupid 0 : integer
typedata 1 : *type_data #组类型数据
}
#更新成就
sc_achievement_update 31302 {
request {
achievementdata 0 : achievement_data
}
}
# 成就统计达成数据
.valuedata {
equip_gem 0 : integer #宝石等级
equip_jinglian 1 : integer #精炼等级
equip_wash 2 : integer #重铸次数
pet_compose 3 : integer #宠物合成次数
partner_unlock 4 : integer #仙君解锁个数
partner_call 5 : integer #仙君召唤次数
sign 6 : integer #累计签到次数
sect 7 : integer #门派技能
union 8 : integer #帮派技能
xiulian_role 9 : integer #角色修炼技能
xiulian_pet 10 : integer #宠物修炼技能
chat_speak 11 : integer #世界聊天次数
money 12 : integer #累计获得银币
fengyao 13 : integer #封妖任务次数
chumo 14 : integer #除魔任务次数
chumo_leader 15 : integer #带队除魔任务次数
yaowang 16 : integer #击杀妖王统计
shengxiao 17 : integer #累计击杀十二生肖
xingguan 18 : integer #累计击杀九曜星官
union_guildcontribute 19 : integer #累计帮派贡献
union_donate 20 : integer #累计上交帮派日志
union_daytask 21 : integer #累计帮派日常
kid_num 22 : integer #拥有子女数量
pet_wash 23 : integer #宠物洗练次数
pet_skill_learn 24 : integer #宠物技能学习次数
pet_catch 25 : integer #捕捉宠物累积次数
partner_call_ten 26 : integer #仙君十连召唤次数
mount_num 27 : integer #激活坐骑数
fashion_dress 28 : integer #同时激活时装数
fashion_headshow 29 : integer #同时激活头像数
fashion_touxiangkuang 30 : integer #同时激活头像框数
fashion_qipao 31 : integer #同时激活气泡数
bianshen_count 32 : integer #使用变身卡次数
union_qiyuan 33 : integer #帮派祈愿次数
pet_skill_special 34 : integer #变异宠物数量
partner_quality4 35 : integer #橙色仙君数量
partner_quality5 36 : integer #红色仙君数量
kid_growup_num 37 : integer #拥有成年子女数量
mount_top_num 38 : integer #拥有9阶坐骑数量
fashion_have 39 : integer #永久装扮数量
industry1 40 : integer #采矿等级
industry2 41 : integer #炼药等级
industry3 42 : integer #钓鱼等级
energyUse1 43 : integer #累计烹饪次数
energyUse2 44 : integer #累计炼药次数
energyUse3 45 : integer #累计附魔次数
worldAnswer_num 46 : integer #累计世界答题次数
friend_num 47 : integer #好友数量
vipCard_count1 48 : integer #累计购买周卡次数
vipCard_count2 49 : integer #累计购买月卡次数
building_level1 50 : integer #房屋等级
building_level2 51 : integer #农田等级
building_level3 52 : integer #牧场等级
building_level4 53 : integer #树圃等级
building_speed 54 : integer #累计使用家园卡次数
house_plant 55 : integer #累计种植农田次数
house_animal 56 : integer #累计牧场养殖次数
house_tree 57 : integer #累计家园祈愿次数
house_environment 58 : integer #家园环境度
active_point_150 59 : integer #每日活跃度达成150点累计
battle_all_shengxiao 60 : integer #累计击败十二生肖种类
battle_id_all_shengxiao 61 : integer #累计击败五星十二生肖
battle_all_xingguan 62 : integer #累计击败星官种类
battle_id_all_xingguan 63 : integer #累计击败五星星官
mission_list 64 : integer #累计完成师门任务
mission_yabiao 65 : integer #累计完成运镖任务
mission_xuncheng 66 : integer #累计完成巡城任务
mission_xianyuan 67 : integer #累计完成仙缘任务
achieve_all_xianyuan 68 : integer #累计完成仙缘种类
baotu_count 69 : integer #累计挖宝次数
baotuxiaoyao_count 70 : integer #累计逍遥宝图挖宝次数
baotu_item_low 71 : integer #累计挖到低阶兽决
baotu_item_high 72 : integer #累计挖到高阶兽决
}
配置表格:
项目代码对应配置:
local AchievementConfig = {}
--成就类型
AchievementConfig.Type = {
level = {groupID=1,typeID=1}, --等级
prop = {groupID=1,typeID=2}, --属性
equip = {groupID=1,typeID=3}, --装备
pet = {groupID=1,typeID=4}, --宠物
partner = {groupID=1,typeID=5}, --仙君
child = {groupID=1,typeID=6}, --子女
mount = {groupID=1,typeID=7}, --坐骑
sign = {groupID=1,typeID=8}, --累计签到
guanzhi = {groupID=1,typeID=9}, --官职
skill = {groupID=2,typeID=1}, --门派技能
guildskill = {groupID=2,typeID=2}, --帮派技能
xiulian = {groupID=2,typeID=3}, --修炼技能
fishdrug = {groupID=2,typeID=4}, --副职业技能
bianshen = {groupID=2,typeID=6}, --变身
worldchat = {groupID=3,typeID=1}, --世界聊天
friend = {groupID=3,typeID=2}, --统计好友数量
money = {groupID=3,typeID=3}, --统计银币数量
vipCard = {groupID=3,typeID=4}, --统计vip信息
house = {groupID=3,typeID=5}, --统计家园信息
houseplan = {groupID=3,typeID=6}, --统计家园农作信息
environment = {groupID=3,typeID=7}, --统计家园风水环境信息
mission = {groupID=4,typeID=1}, --主线任务完成
fb = {groupID=4,typeID=2}, --副本完成
active = {groupID=5,typeID=1}, --活跃度
chumofengyao = {groupID=5,typeID=2}, --封妖除魔任务
yaowang = {groupID=5,typeID=3}, --妖王
shengxiao = {groupID=5,typeID=4}, --生肖
xingguan = {groupID=5,typeID=5}, --星官
sect = {groupID=5,typeID=6}, --门派任务
baotu = {groupID=5,typeID=7}, --挖宝
xuncheng = {groupID=5,typeID=8}, --累计完成运镖皇城任务
mission3 = {groupID=5,typeID=9}, --累计完成300环任务链
xianyuan = {groupID=5,typeID=10}, --累计完成仙缘任务
union = {groupID=6,typeID=1}, --帮派
}
--tupe为1统计数+1,type=2特殊处理
AchievementConfig.CountType = {
equip_wash = {name = "equip_wash",type = 1}, --锻造装备次数
pet_wash = {name = "pet_wash",type = 1}, --宠物洗练次数
pet_compose = {name = "pet_compose",type = 1}, --宠物合成次数
pet_skill_learn = {name = "pet_skill_learn",type = 1}, --宠物技能学习次数
pet_catch = {name = "pet_catch",type = 1}, --捕捉宠物累积次数
partner_call = {name = "partner_call",type = 1}, --仙君召唤次数
partner_call_ten = {name = "partner_call_ten",type = 1}, --仙君十连召唤次数
chat_speak = {name = "chat_speak",type = 1}, --世界频道发言
money = {name = "money",type = 1}, --累计获得银币
active_point = {name = "active_point",type = 2},--每日活跃度达成
fengyao = {name = "fengyao",type = 1}, --累计制服捣乱妖怪
chumo = {name = "chumo",type = 1}, --除魔任务统计
chumo_leader = {name = "chumo_leader",type = 1}, --带队完成除魔任务统计
yaowang = {name = "yaowang",type = 1}, --击杀妖王统计
shengxiao = {name = "shengxiao",type = 1}, --累计击杀十二生肖
xingguan = {name = "xingguan",type = 1}, --累计击杀九曜星官
union_guildcontribute = {name = "union_guildcontribute",type = 1}, --累计帮派贡献
union_rob = {name = "union_rob",type = 1}, --累计击杀帮派强盗
union_donate = {name = "union_donate",type = 1}, --累计上交帮派日志
union_store = {name = "union_store",type = 1}, --累计帮派跑商
union_daytask = {name = "union_daytask",type = 1}, --累计帮派日常
union_qiyuan = {name = "union_qiyuan",type = 1}, --累计帮派祈愿次数
bianshen_count = {name = "bianshen_count",type = 1}, --累计使用变身卡次数
fashion_have = {name = "fashion_have",type = 1}, --累计激活永久装扮
energyUse1 = {name = "energyUse1",type = 1}, --累计烹饪次数
energyUse2 = {name = "energyUse2",type = 1}, --累计炼药次数
energyUse3 = {name = "energyUse3",type = 1}, --累计附魔次数
worldAnswer_num = {name = "worldAnswer_num",type = 1}, --累计世界答题次数
worldAnswer_lucky = {name = "worldAnswer_lucky",type = 1}, --累计世界答题幸运奖励次数
vipCard_count1 = {name = "vipCard_count1",type = 1}, --累计购买周卡次数
vipCard_count2 = {name = "vipCard_count2",type = 1}, --累计购买月卡次数
building_speed = {name = "building_speed",type = 1}, --累计使用家园卡次数
house_plant = {name = "house_plant",type = 1}, --累计种植农田次数
house_animal = {name = "house_animal",type = 1}, --累计牧场养殖次数
house_tree = {name = "house_tree",type = 1}, --累计家园祈愿次数
fbdifu1 = {name = "fbdifu1",type = 1}, --累计通关副本普通地府次数
fbdifu2 = {name = "fbdifu2",type = 1}, --累计通关副本精英地府次数
active_point_150 = {name = "active_point_150",type = 1,isrecord = true}, --累计每日活跃度达到150次数,每日只能达成一次
battle_all_shengxiao = {name = "battle_all_shengxiao",type = 1}, --这个手动特殊存,累计击败所有种类的十二生肖
battle_id_all_shengxiao = {name = "battle_id_all_shengxiao",type = 1,all={2003,2013,2023,2033,2043,2053,2063,2073,2083,2093,2103,2113}}, --这个手动特殊存,累计击败所有五星十二生肖
battle_all_xingguan = {name = "battle_all_xingguan",type = 1}, --这个手动特殊存,累计击败所有种类的星官
battle_id_all_xingguan = {name = "battle_id_all_xingguan",type = 1,all={2003,2013,2023,2033,2043,2053,2063,2073,2083,2093,2103,2113}}, --这个手动特殊存,累计击败所有五星星官
mission_list = {name = "mission_list",type = 1}, --累计师门任务次数
mission_yabiao = {name = "mission_yabiao",type = 1}, --累计运镖任务次数
mission_xuncheng = {name = "mission_xuncheng",type = 1}, --累计巡城任务次数
mission_xianyuan = {name = "mission_xianyuan",type = 1}, --累计仙缘任务次数
achieve_all_xianyuan = {name = "achieve_all_xianyuan",type = 1,all={5701,5702,5703,5704,5705,5706,5707,5708,5709,5710,5711,5712,5713,5714}}, --这个手动特殊存,累计完成所有仙缘
baotu_count = {name = "baotu_count",type = 1}, --累计挖宝次数
baotuxiaoyao_count = {name = "baotuxiaoyao_count",type = 1}, --累计逍遥宝图挖宝次数
baotu_item_low = {name = "baotu_item_low",type = 1,all={1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,1515,1516,1517,1518,1519,1520,1521,1522,1523,1524,1525,1526,1527,1528,1529,1530,1531,1532,1533,1534,1535,1536,1537,1538,1539,1540,1541,1542,1543,1544,1545,1546,1547}},--累计挖到兽决
baotu_item_high = {name = "baotu_item_high",type = 1,all={1551,1552,1553,1554,1555,1556,1557,1558,1559,1560,1561,1562,1563,1564,1565,1566,1567,1568,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587,1588,1589,1590,1591,1592,1593,1594,1595,1596,1597}},--累计挖到高级兽决
roleaddexp_count = {name = "roleaddexp_count",type = 1}, --累计人物经验书
}
成就按类型分组发送给客户端,也可以按组验证查询成就
local groupby = function(list)
local achievelist = {}
for _,v in pairs(list) do
if not achievelist[v.groupID] then
achievelist[v.groupID] = {}
end
if not achievelist[v.groupID][v.typeID] then
achievelist[v.groupID][v.typeID] = {}
end
table.insert(achievelist[v.groupID][v.typeID],v)
end
return achievelist
end
function Achievement:onLoad()
self.cache = self.player.cache.achievement
self.cache.countdata = self.cache.countdata or {}
self.cache.isrecord = self.cache.isrecord or {}
self.ConfigList = groupby(server.configCenter.AchievementConfig)
end
--获取分组list
function Achievement:GetConfigList(groupID,typeID)
return self.ConfigList[groupID][typeID]
end
登陆注册事件,触发成就验证方法为onEventCheck,事件详情请看事件系统设计
function Achievement:onLogin()
if not self.achievementEvent then
self.achievementEvent = self.player:SubEvent(server.enEvent.PlayerEvnt.EVENTID_ACHIEVEMENT_CHECK,self.onEventCheck,self)
end
if not self.propChangeE then
self.propChangeE = self.player:SubEvent(server.enEvent.PlayerEvnt.EVENTID_ENTITY_RPOP_CHANGE,self.onEntityPropChange,self)
end
if not self.TaskEvent then
self.TaskEvent = self.player:SubEvent(server.enEvent.PlayerEvnt.EVENTID_ENTITY_SUBMIT_TASK,self.onTaskSubmit,self)
end
if not self.BattleEndEvent then
self.BattleEndEvent = self.player:SubEvent(server.enEvent.PlayerEvnt.EVENTID_ENTITY_BATTLE_END,self.onBattleEnd,self)
end
end
1.写入方法内,以仙君抽奖为例,在仙君抽奖方法中触发抽奖统计和解锁仙君数量验证
--触发仙君抽奖
local args = {}
args.myEntityID = self.player.entityID
args.require = AchievementConfig.CountType.partner_call.name
args.countType = AchievementConfig.CountType.partner_call.name
args.groupdata = AchievementConfig.Type.partner
args.value = itemcfg.num
args.EventID = server.enEvent.PlayerEvnt.EVENTID_ACHIEVEMENT_CHECK
self.player:FireEvent(args.EventID,args)
--触发仙君增加
local args = {}
args.myEntityID = self.player.entityID
args.require = "partner_unlock"
args.value = count
args.groupdata = AchievementConfig.Type.partner
args.EventID = server.enEvent.PlayerEvnt.EVENTID_ACHIEVEMENT_CHECK
self.player:FireEvent(args.EventID,args)
2.原有事件触发
--触发人物升级
function Achievement:onLevelUp()
local args = {require = "level",value = self.player.cache.level,groupdata = AchievementConfig.Type.level}--对应finishRequire的 {"level",type,value=100}
self:onEventCheck(args)
end
--任务提交判定成就
function Achievement:onTaskSubmit(args)
local isachieve = false
--正常主线是这样的
local config = server.configCenter.missionConfig[args.missionID]
local tab = {require = "mission",id = args.missionID, value = 1}
--主线
if config.mType == "main_line" then
tab.groupdata = AchievementConfig.Type.mission
isachieve = true
--封妖
elseif args.missionID == 113 then
tab.groupdata = AchievementConfig.Type.chumofengyao
tab.countType = AchievementConfig.CountType.fengyao.name
isachieve = true
--除魔
elseif args.missionID == 112 then
tab.groupdata = AchievementConfig.Type.chumofengyao
tab.countType = AchievementConfig.CountType.chumo.name
isachieve = true
--队长除魔触发带队除魔统计统计
local team = server.teamMgr:getTeam(self.player.prop.teamid)
if team and team.leader.dbid == self.player.dbid and #team.memlist >= 2 then
local tab2 = {groupdata = AchievementConfig.Type.chumofengyao,require = AchievementConfig.CountType.chumo_leader.name,countType = AchievementConfig.CountType.chumo_leader.name}
self:onEventCheck(tab2)
end
--帮派日常
elseif args.missionID == 116 then
tab.groupdata = AchievementConfig.Type.union
tab.countType = AchievementConfig.CountType.union_daytask.name
isachieve = true
--运镖日常
elseif args.missionID == 114then
tab.groupdata = AchievementConfig.Type.xuncheng
tab.countType = AchievementConfig.CountType.mission_yabiao.name
isachieve = true
--巡城日常
elseif args.missionID == 115then
tab.groupdata = AchievementConfig.Type.xuncheng
tab.countType = AchievementConfig.CountType.mission_xuncheng.name
isachieve = true
--师门任务累计
elseif config.name == "师门任务" then
local tab2 = {groupdata = AchievementConfig.Type.sect,require = AchievementConfig.CountType.mission_list.name,id = args.missionID,countType = AchievementConfig.CountType.mission_list.name}
self:onEventCheck(tab2)
--仙缘
elseif config.mType == "xianyuan" then
tab.groupdata = AchievementConfig.Type.xianyuan
isachieve = true
--完成累计仙缘次数
local tab2 = {groupdata = AchievementConfig.Type.xianyuan,require = "mission",type = "xianyuan",countType = AchievementConfig.CountType.xianyuan.name}
self:onEventCheck(tab2)
--判定是仙缘id
if lua_util.inArray(args.cfgId,AchievementConfig.CountType.achieve_all_xianyuan.all) then
local idname = AchievementConfig.CountType.achieve_all_xianyuan.name
self.cache.countdata[idname] = self.cache.countdata[idname] or {}
if not self.cache.countdata[idname][args.cfgId] then
self.cache.countdata[idname][args.cfgId] = true
local tab2 = {require = "achieve_all",groupdata = AchievementConfig.Type.xianyuan,countType = AchievementConfig.CountType.achieve_all_xianyuan.name}
self:onEventCheck(tab2)
end
end
end
--触发misson任务
if isachieve then
self:onEventCheck(tab)
end
end
当触发成就验证事件时,如果有对应的countType则统计,并将value值改为最新的统计值 成就判定,判定成就条件,type,id,value值,配置若有则需要相同,后续兼容包含,战斗回合等相反数值验证
-- 任务条件检查
function Achievement:onEventCheck(args)
local achievetype = args.groupdata
if not achievetype then
return
end
local configlist = self:GetConfigList(achievetype.groupID,achievetype.typeID)
if not configlist then
return
end
--需统计成就数据
args.value = args.value or 1
if args.countType then
for _, v in pairs(AchievementConfig.CountType) do
if args.countType == v.name then
--是否限时计数
if v.isrecord then
if self.cache.isrecord[AchievementConfig.CountType.active_point_150.name] then
return
end
self.cache.isrecord[AchievementConfig.CountType.active_point_150.name] = true
end
--type1累加统计
if v.type == 1 then
local count = self.cache.countdata[args.countType] or 0
self.cache.countdata[args.countType] = count + args.value
args.value = self.cache.countdata[args.countType]
break
elseif v.type == 2 then
--type2超过历史统计则替换,不过返回
local count = self.cache.countdata[args.countType] or 0
if args.value > count then
self.cache.countdata[args.countType] = args.value
break
end
return
end
end
end
end
for _,v in pairs(configlist) do
--未获得成就,完成条件一致
local require = v.finishRequire
if not self.cache.achievements[v.id] and require and require[1] == args.require then
local issuccess = true
--判定存在的完成条件
if require.type and args.type ~= require.type then
issuccess = false
end
if require.id then
if type(require.id) == "table" then
if not lua_util.inArray(args.id,require.id) then
issuccess = false
end
elseif args.id ~= require.id then
issuccess = false
end
end
if require.value then
if args.require == "battle_speed" then
if args.value > require.value then
issuccess = false
end
else
if args.value < require.value then
issuccess = false
end
end
end
if issuccess then
self:AchievementComplete(v.id,args.value)
end
end
end
end
-- 完成成就
function Achievement:AchievementComplete(achievementid,value)
local achievementconfig = server.configCenter.AchievementConfig[achievementid]
if not achievementconfig then
lua_app.log_info("Achievement:achievementconfig not id ", achievementid)
return false
end
if self.cache.achievements[achievementid] then
lua_app.log_info("Achievement:achievementconfig exist id ", achievementid)
return false
end
local achievementdata = {id = achievementid,time = lua_app.now(),groupID = achievementconfig.groupID,typeID = achievementconfig.typeID,value = value or 0,point = achievementconfig.point }
--发送更新数据
self:GetReward(achievementid)
self.cache.point = self.cache.point + achievementconfig.point
server.sendReq(self.player, "sc_achievement_update", {achievementdata = achievementdata })
achievementdata.value = nil
self.cache.achievements[achievementid] = achievementdata
--成就点奖励
local pointcfg = server.configCenter.AchievementGiftConfig
for _,cfg in pairs(pointcfg) do
if self.cache.point > cfg.point and not lua_util.inArray(cfg.id,self.cache.pointReward) then
local mailconfig = server.configCenter.MailMsgConfig[cfg.mail]
server.mailCenter:SendMail(self.player.dbid, mailconfig.title, mailconfig.desc, mailconfig.item)
table.insert(self.cache.pointReward,cfg.id)
end
end
end
-- 领取奖励
function Achievement:GetReward(achievementid)
local achievementconfig = server.configCenter.AchievementConfig[achievementid]
if achievementconfig then
if achievementconfig.drop then
local rewards = server.dropCenter:DropGroup(achievementconfig.drop)
if rewards then
self.player:GiveItem(rewards, 1, 1,server.baseConfig.YuanbaoRecordType.Achievement)
end
end
end
if achievementconfig.title then
self.role.titleeffect:ActivatePart({id = achievementconfig.title,time = 0})
end
if achievementconfig.tipMsg1 then
for _,v in pairs(achievementconfig.tipMsg1) do
local dbid = self.player.dbid
if server.configCenter.TipMsgConfig[v].target == "union" then
dbid = self.player.prop.guildid
end
if dbid ~= 0 then
--超链接
server.chatCenter:ChatLink(v,self.player,nil,achievementconfig.name,{server.ChatLinkType.achievement,self.player.entityID,achievementid,lua_app.now()})
end
end
end
if achievementconfig.tipMsg2 then
for _,v in pairs(achievementconfig.tipMsg2) do
local dbid = self.player.dbid
if server.configCenter.TipMsgConfig[v].target == "union" then
dbid = self.player.prop.guildid
end
if dbid ~= 0 then
--server.chatCenter:sendTipMsg(v,dbid,self.player.cache.name,achievementconfig.name)
--超链接
server.chatCenter:ChatLink(v,self.player,nil,self.player.cache.name,achievementconfig.name,{server.ChatLinkType.achievement,self.player.entityID,achievementid,lua_app.now()})
end
end
end
end
--重置当天累计
function Achievement:onDayTimer()
local isrecord = self.cache.isrecord
if isrecord[AchievementConfig.CountType.active_point_150.name] then
isrecord[AchievementConfig.CountType.active_point_150.name] = false
end
--经验书
local countdata = self.cache.countdata
countdata[AchievementConfig.CountType.roleaddexp_count.name] = 0
end
成就的难点,在于多种复合条件的验证判定,也囊括各种任务,帮派,活动,战斗等事件,非自己做的模块需要多花费时间操作