--扭蛋机 local MsgPack = MsgPack local Capsule = class("Capsule", require("shared.ModelBase")) function Capsule:ctor(properties) Capsule.super.ctor(self, properties) end RewardType = { GOODS = 1, SPECIAL = 2, INCENTIVE = 3, } SpecialType = { TOP = 1, CORE = 2, LAST = 3, JOKER = 4, KING = 5, } CapsuleType = { PRIVATE = 0, PUBLIC = 1, } --[[ --通知数据结构 { [roleId] = { [good_id1] = { }, [good_id2] = { }, } } ]]-- Capsule.schema = { id = {"number", 0}, --扭蛋机key,配置读取 room = {"number", 0}, --房间号, 配置读取 name = {"string"}, typ = {"number", 1}, -- 1=共享,2=独享 coin = {"number", 0}, --货币代号 token = {"table", {}}, --抽一次,货币=消耗 register = {"table", {}}, --人数 {["id"]=0}, 0 围观, 1 已报名 record = {"table", {}}, --抽取记录 列表 recordByRole = {"table", {}}, -- 抽取记录,hash, {roleid=record} rank = {"table", {}}, --排行 goods = {"table", {}}, --奖励池 specials = {"table", {}}, --特殊赏 incentive = {"table", {}}, --激励奖 incentiveRecord = {"table", {}}, --激励奖记录 specialsRecord= {"table", {}}, --特殊赏领取记录 resetTimes = {"number", 0}, --每日一次手动重置的机会 hideTime = {"number", 0} , --隐藏时间 drawEndTime = {"number", 0}, --抽完时间 } function Capsule:getResetFields() return { id = self:getProperty("id"), room = self:getProperty("room"), typ = self:getProperty("typ"), coin = 0, token = {}, register = {}, record = {}, recordByRole = {}, rank = {}, goods = {}, specials = {}, incentive = {}, incentiveRecord = {}, specialsRecord= {}, resetTimes = 0, hideTime = 0, drawEndTime = 0, } end function Capsule:init() local id = self:getProperty("id") local room = self:getProperty("room") self:setProperties(self:getResetFields()) local ichibankuji = csvdb["ichibankuji_mainCsv"][id][room] --奖励池 local goods_id = ichibankuji["goods_id"] local goods, specials, incentive = {}, {}, {} for _, data in pairs(csvdb["ichibankuji_goodsCsv"]) do for _, val in pairs(data) do if val.key == goods_id then goods[goods_id] = clone(val) end end end for _, v in pairs(goods) do v.weight = (v.weight or 0) * v.amount end --特殊赏 local special_ids = ichibankuji["special_id"] if special_ids ~= "" then for _, special_id in ipairs(special_ids:toArray(true, "=")) do local val = csvdb["ichibankuji_specialCsv"][special_id] if type(val.type) == "number" then specials[special_id] = {np= 1, amount = val.amount, award = val.award, quality = tonumber(val.type), showIndex = val.showIndex} elseif type(val.type) == "string" then local pos = val.type:find("=") if pos then for k, v in pairs(val.type:toNumMap()) do specials[special_id] = {np= v, amount = val.amount, award = val.award, quality = k, showIndex = val.showIndex} end else specials[special_id] = {np= 1, amount = val.amount, award = val.award, quality = tonumber(val.type), showIndex = val.showIndex} end end end end --激励奖 local incentive_ids = ichibankuji["incentive_id"] if incentive_ids ~= "" then for _, incentive_id in ipairs(incentive_ids:toArray(true, "=")) do local val = csvdb["ichibankuji_incentiveCsv"][incentive_id] if type(val.type) == "number" then incentive["last"] = {np=val.type, award = val.award} elseif type(val.type) == "string" then for k, v in pairs(val.type:toNumMap()) do if k == 2 then incentive["amount"] = {np= v, award = val.award} elseif k==3 then incentive["probabilities"] = {np= v, award = val.award} end end end end end --货币类型 local coin = ichibankuji["token"]:toArray(true, "=") self:setProperties({coin = coin[1] or 0, token = coin, hideTime = ichibankuji.hide_time, goods = goods, specials = specials, incentive = incentive}) end function Capsule:isShow() if skynet.timex() >= self:getProperty("hideTime") then return false end return true end function Capsule:refreshing(now) local id = self:getProperty("id") local room = self:getProperty("room") local ichibankuji = csvdb["ichibankuji_mainCsv"][id][room] local reset = tostring(ichibankuji.reset) if reset == "0" then return false elseif reset == "1" then if self:getProperty("resetTimes") == 1 then return true end return false else local resetArr = reset:toArray(true, "=") if not next(resetArr) then return false end if resetArr[1] == "2" then if self:getGoodsAmount() > 0 then return false end local drawEndTime = self:getProperty("drawEndTime") or 0 if drawEndTime == 0 then return false end if now - drawEndTime >= resetArr[2] then return true end return false elseif resetArr[1] == "3" then elseif resetArr[1] == "4" then if now >= resetArr[2] then return true end end end return false end function Capsule:getOnlineCount() local register = self:getProperty("register") or {} local reg, onlookers = 0, 0 for _, v in pairs(register) do if v == 1 then reg = reg + 1 else onlookers = onlookers + 1 end end return {[0]=onlookers, [1]=reg, [2] = reg+onlookers} end function Capsule:join(roleId) --一个房间最多人数 TODO local register = self:getProperty("register") or {} register[roleId] = 0 self:setProperty("register", register) return self:data(roleId) end function Capsule:getRegisterByRoleId(roleId) local register = self:getProperty("register") or {} return register[roleId] or 0 end function Capsule:isRegister(roleId) return self:getRegisterByRoleId(roleId) == 1 end function Capsule:register(roleId) local register = self:getProperty("register") or {} register[roleId] = 1 self:setProperty("register", register) return self:data(roleId) end function Capsule:exit(roleId) local register = self:getProperty("register") or {} if next(register) then register[roleId] = nil return true end return false end function Capsule:confirmed(cares) local goods = self:getProperty("goods") or {} local specials = self:getProperty("specials") or {} local change = {} for k, v in pairs(cares) do if v.typ == 1 then if goods[k] and goods[k].amount ~= v.count then change[k] = {typ=1, count = goods[k].amount} end else if specials[k] and specials[k].amount ~= v.count then change[k] = {typ=1, count = specials[k].amount} end end end return change end function Capsule:getGoodsAmount() local goods = self:getProperty("goods") or {} local amount = 0 for _, v in pairs(goods) do amount = amount + v.amount end return amount end function Capsule:getSpecialByType(typ) local specials = self:getProperty("specials") or {} for k, v in pairs(specials) do if v.quality == typ then return k, v end end return nil end function Capsule:checkSpecialFlag(typ) local spKey, special = self:getSpecialByType(typ) if not special then return nil end if special["amount"] <= 0 then return nil end return spKey, special end local function getSpecialRoleNotify(rewardRecord, count, award, spKey, typ, now) local rewardByRole = {} while(count > 0 and next(rewardRecord)) do local roleId = math.randWeight(rewardRecord, "amount") if roleId then local tmp = rewardRecord[roleId] tmp["amount"] = tmp["amount"] - 1 if tmp["amount"] <= 0 then rewardRecord[roleId] = nil end tmp = rewardByRole[roleId] or {} if not next(tmp) then local name = getNameByRoleId(roleId) tmp[spKey] = {name = name, good_id = spKey, typ = RewardType.SPECIAL, award = award, amount = 1, quality = typ, create_time= now} else if not tmp[spKey] then local name = getNameByRoleId(roleId) tmp[spKey] = {name = name, good_id = spKey, typ = RewardType.SPECIAL, award = award, amount = 1, quality = typ, create_time= now} else tmp[spKey].amount = tmp[spKey].amount + 1 end end rewardByRole[roleId] = tmp count = count - 1 end end return rewardByRole, count end local rewardToNtyFunc = function(notify, tmpReward) for key, val in pairs(tmpReward or {}) do if not notify[key] then notify[key] = clone(val) else for k, v in pairs(val) do if not notify[key][k] then notify[key][k] = v else notify[key][k] = notify[key][k].amount + v.amount end end end end end local function getRecordAmount(record) local amount = 0 for _, v in ipairs(record) do amount = amount + v.amount end return amount end function Capsule:getTop(record, recordAmount,now) local spKey, special = self:checkSpecialFlag(SpecialType.TOP) if not special then return nil end local specials = self:getProperty("specials") or {} local specialsRecord = self:getProperty("specialsRecord") or {} if recordAmount < special["np"] then return nil end local topRecord = {} local count = special["np"] for _, v in ipairs(record) do if count <= 0 then break end local tmpCount = 0 if count >= v.amount then count = count - v.amount tmpCount = v.amount else tmpCount = count count = 0 end if not topRecord[v.roleId]then topRecord[v.roleId] = {amount = v.amount } else topRecord[v.roleId] = {amount = (topRecord[v.roleId]["amount"] or 0) + tmpCount} end end local rewardByRole, count = getSpecialRoleNotify(topRecord, special["amount"], special["award"], spKey, SpecialType.TOP,now) special["amount"] = count specials[spKey] = special rewardToNtyFunc(specialsRecord, rewardByRole) self:setProperties({specialsRecord = specialsRecord, specials = specials}) return rewardByRole end --TODO function Capsule:getCore(record, recordAmount,now) local spKey, special = self:checkSpecialFlag(SpecialType.CORE) if not special then return nil end local specials = self:getProperty("specials") or {} local specialsRecord = self:getProperty("specialsRecord") or {} if self:getGoodsAmount() > 0 then return nil end local np = special["np"] if np > recordAmount then return nil end local left = math.ceil((np - recordAmount)/2) or 0 local count = np local roleRecord = {} for i, v in ipairs(record) do if count <= 0 then break end if i > left then local tmpCount = 0 if count >= v.amount then count = count - v.amount tmpCount = v.amount else tmpCount = count count = 0 end if not roleRecord[v.roleId]then roleRecord[v.roleId] = {amount = v.amount } else roleRecord[v.roleId] = {amount = (roleRecord[v.roleId]["amount"] or 0) + tmpCount} end end end local rewardByRole, count = getSpecialRoleNotify(roleRecord, special["amount"], special["award"], spKey, SpecialType.CORE,now) special["amount"] = count specials[spKey] = special rewardToNtyFunc(specialsRecord, rewardByRole) self:setProperties({specialsRecord = specialsRecord, specials = specials}) return rewardByRole end function Capsule:getLast(record,now) local spKey, special = self:checkSpecialFlag(SpecialType.LAST) if not special then return nil end local specials = self:getProperty("specials") or {} local specialsRecord = self:getProperty("specialsRecord") or {} if self:getGoodsAmount() > 0 then return nil end table.sort(record, function(a, b) return a.create_time > b.create_time end) local np = special["np"] local count = np local roleRecord = {} for _, v in ipairs(record) do if count <= 0 then break end local tmpCount = 0 if count >= v.amount then count = count - v.amount tmpCount = v.amount else tmpCount = count count = 0 end if not roleRecord[v.roleId]then roleRecord[v.roleId] = {amount = v.amount } else roleRecord[v.roleId] = {amount = (roleRecord[v.roleId]["amount"] or 0) + tmpCount} end end local rewardByRole, count = getSpecialRoleNotify(roleRecord, special["amount"], special["award"], spKey, SpecialType.LAST,now) special["amount"] = count specials[spKey] = special rewardToNtyFunc(specialsRecord, rewardByRole) self:setProperties({specialsRecord = specialsRecord, specials = specials}) return rewardByRole end function Capsule:getJoker(record,now) local spKey, special = self:checkSpecialFlag(SpecialType.JOKER) if not special then return nil end local specials = self:getProperty("specials") or {} local specialsRecord = self:getProperty("specialsRecord") or {} if self:getGoodsAmount() > 0 then return nil end local roleRecord = {} for _, v in ipairs(record) do if not roleRecord[v.roleId]then roleRecord[v.roleId] = {amount = v.amount } else roleRecord[v.roleId] = {amount = (roleRecord[v.roleId]["amount"] or 0) + v.amount} end end local rewardByRole, count = getSpecialRoleNotify(roleRecord, special["amount"], special["award"], spKey, SpecialType.JOKER,now) special["amount"] = count specials[spKey] = special rewardToNtyFunc(specialsRecord, rewardByRole) self:setProperties({specialsRecord = specialsRecord, specials = specials}) return rewardByRole end function Capsule:getKing(record,now) local spKey, special = self:checkSpecialFlag(SpecialType.KING) if not special then return nil end local specials = self:getProperty("specials") or {} local specialsRecord = self:getProperty("specialsRecord") or {} if self:getGoodsAmount() > 0 then return nil end local roleRecord = {} for _, v in ipairs(record) do if not roleRecord[v.roleId]then roleRecord[v.roleId] = {amount = v.amount } else roleRecord[v.roleId] = {amount = (roleRecord[v.roleId]["amount"] or 0) + v.amount} end end local rewardByRole, count = getSpecialRoleNotify(roleRecord, special["amount"], special["award"], spKey, SpecialType.KING,now) special["amount"] = count specials[spKey] = special rewardToNtyFunc(specialsRecord, rewardByRole) self:setProperties({specialsRecord = specialsRecord, specials = specials}) return rewardByRole end function Capsule:checkSpecialReward( now) local specials = self:getProperty("specials") or {} if not next(specials) then return nil end local record = self:getProperty("record") or {} if not next(record) then return nil end table.sort(record, function(a, b) return a.create_time < b.create_time end ) local recordAmount = getRecordAmount(record) local notify = self:getTop(record, recordAmount,now) or {} local coreReward = self:getCore(record, recordAmount, now) rewardToNtyFunc(notify, coreReward) local lastReward = self:getLast(record, now) rewardToNtyFunc(notify, lastReward) local jokerReward = self:getJoker(record, now) rewardToNtyFunc(notify, jokerReward) local kingReward = self:getKing(record, now) rewardToNtyFunc(notify, kingReward) return notify end function Capsule:checkIncentive(roleId, name, now) local goods = self:getProperty("goods") or {} local recordByRole = self:getProperty("recordByRole") or {} local roleRecord = recordByRole[roleId] or {} local incentiveRecord = self:getProperty("incentiveRecord") or {} local incentiveByRole = incentiveRecord[roleId] or {} local incentive = self:getProperty("incentive") local notify = {} -- 最后一抽 TODO if incentive["last"] then local last = true for k, v in pairs(goods) do if v and v.amount then last = false break end end if last then notify["last"] = {name = name, good_id = "last", typ = RewardType.INCENTIVE, award = incentive["last"]["award"], amount = 1, quality = 1, create_time= now} end end --次数 if incentive["amount"] then local amount = 0 for _, v in pairs(roleRecord) do if (v.calculated or 0) == 0 then amount = amount + v.amount end end local count = math.floor(amount / incentive["amount"]["np"]) local tmpCount = count * incentive["amount"]["np"] notify["amount"] = {name = name, roleId= roleId, good_id = "amount", typ = RewardType.INCENTIVE, award = incentive["amount"]["award"], amount = count, quality = 2, create_time= now} --填充v.calculated 字段,标识已经用于每x抽的计算中。 for _, v in pairs(roleRecord) do if tmpCount <= 0 then break end v.calculated = v.calculated or 0 if v.calculated ~= v.amount then if tmpCount <= v.amount then v.calculated = tmpCount tmpCount = 0 else v.calculated = v.amount tmpCount = tmpCount - v.amount end end end end --概率 if incentive["probabilities"] then local probabilities = math.randomInt(1, 100) if probabilities <= incentive["probabilities"]["np"] then notify["probabilities"] = {name = name, good_id = "probabilities", typ = RewardType.INCENTIVE, award = incentive["probabilities"]["award"], amount = 1, quality = 3, create_time= now} end end table.insert(incentiveByRole, notify) incentiveRecord[roleId] = incentiveByRole self:setProperty("incentiveRecord", incentiveRecord) --TODO 先屏蔽 return {} end function Capsule:drawByCount(roleId, count) if count <= 0 then return nil end local goods = self:getProperty("goods") or {} local record = self:getProperty("record") or {} local recordByRole = self:getProperty("recordByRole") or {} local roleRecord = recordByRole[roleId] or {} local id = self:getProperty("id") local room = self:getProperty("room") local ichibankuji = csvdb["ichibankuji_mainCsv"][id][room] local goods_id = ichibankuji["goods_id"] local now = skynet.timex() --奖励, 通知信息 local notify= {} notify[roleId] = {} local name = getNameByRoleId(roleId) while (goods and next(goods) and count > 0) do local good_id = math.randWeight(goods, "weight") if not good_id then break end local good = goods[good_id] or {} if good and good.amount > 0 then good.amount = good.amount - 1 --插入记录 local tmpNotify = {roleId = roleId, name= name, good_id = good_id, typ = RewardType.GOODS, award = good.award, amount = 1, quality = good.quality, create_time= now} table.insert(record, tmpNotify) --作为奖励记录+通知 if not notify[roleId][good_id] then notify[roleId][good_id] = tmpNotify else notify[roleId][good_id].amount = notify[roleId][good_id].amount + 1 end --记录角色的抽奖记录 计算激励奖需要用到 if not roleRecord[good_id] then roleRecord[good_id] = tmpNotify else roleRecord[good_id].amount = roleRecord[good_id].amount + 1 end good.weight = good.weight - csvdb["ichibankuji_goodsCsv"][goods_id][good.id].weight count = count - 1 end end recordByRole[roleId] = roleRecord self:setProperties({recordByRole = recordByRole, record = record, goods = goods}) local tmpNotify = self:checkIncentive(roleId, name, now) for k, v in pairs(tmpNotify) do if not notify[roleId][k] then notify[roleId][k] = v else notify[roleId][k].amount = notify[roleId][k].amount + v.amount end end local speciNotify = self:checkSpecialReward(now) rewardToNtyFunc(notify, speciNotify) local reward, rewardByGoods = {}, {} for key, val in pairs(notify) do if key == roleId then for k, v in pairs(val) do for id, count in pairs(v.award:toNumMap()) do reward[id] = (reward[id] or 0) + count end rewardByGoods[k] = v end end end return reward, rewardByGoods, notify end function Capsule:drawAll(roleId) local goods = self:getProperty("goods") or {} local record = self:getProperty("record") or {} local recordByRole = self:getProperty("recordByRole") or {} local roleRecord = recordByRole[roleId] or {} local now = skynet.timex() local name = getNameByRoleId(roleId) local notify = {} notify[roleId] = {} for good_id, good in pairs(goods) do if good.amount > 0 then --插入记录 local tmpNotify = {roleId = roleId, name= name, good_id = good_id, typ = RewardType.GOODS, award = good.award, amount = good.amount, quality = good.quality, create_time = now} table.insert(record, tmpNotify) --作为奖励记录+通知 if not notify[roleId][good_id] then notify[roleId][good_id] = tmpNotify else notify[roleId][good_id].amount = notify[roleId][good_id].amount + good.award end --记录角色的抽奖记录 if not roleRecord[good_id] then roleRecord[good_id] = tmpNotify else roleRecord[good_id].amount = roleRecord[good_id].amount + good.amount end good.amount = 0 end end recordByRole[roleId] = roleRecord self:setProperties({recordByRole = recordByRole, record = record, goods = goods}) local tmpNotify = self:checkIncentive(roleId, name, now) for k, v in pairs(tmpNotify) do if not notify[roleId][k] then notify[roleId][k] = v else notify[roleId][k].amount = notify[roleId][k].amount + v.amount end end local speciNotify = self:checkSpecialReward(now) rewardToNtyFunc(notify, speciNotify) local reward, rewardByGoods = {}, {} for key, val in pairs(notify) do if key == roleId then for k, v in pairs(val) do for id, count in pairs(v.award:toNumMap()) do reward[id] = (reward[id] or 0) + count end rewardByGoods[k] = v end end end return reward, rewardByGoods, notify end --@param --[[ @roleId @typ 0=独享,1=公开 @cares 关注{k=v} ]]-- function Capsule:draw(roleId, full, cares) if self:getProperty("typ") == 1 then --是否报名 if self:isRegister(roleId) == false then return 4 end --关注的奖品的数量发生了变化 if cares then local change = self:confirmed(cares) if next(change) then return 5, change end end end if full == 0 then return 6, self:drawByCount(roleId, 1) elseif full == 1 then return 6, self:drawByCount(roleId, 10) elseif full == 2 then return 6, self:drawAll(roleId) end end function Capsule:data(roleId) return { id = self:getProperty("id"), room = self:getProperty("room"), typ = self:getProperty("typ"), name = self:getProperty("name"), coin = self:getProperty("coin"), onlineCount = self:getOnlineCount(), playerStatus = self:getRegisterByRoleId(roleId), record = self:getProperty("record"), rank = self:getProperty("rank"), goods = self:getProperty("goods"), specials = self:getProperty("specials"), incentive = self:getProperty("incentive"), specialsRecord= self:getProperty("specialsRecord"), } end return Capsule