九层妖塔功能设计及实现

九层妖塔功能设计及实现

Scroll Down

前言

功能比较复杂,但单模块主要是业务方面,生物刷新规则那块弄的久印象更深刻

需求

image.png
活动时间就用以前的活动时间管理模块,同事也直接拿来用了,写通用类还是很有用的哈

九层妖塔管理类

大部分逻辑在这实现,包括活动时间控制,奖励生物更新,玩家妖塔交互

local oo = require "class"
local server = require "server"
local lua_app = require "lua_app"
local GuildConfig = require "common.resource.GuildConfig"
local WeeklyActivityConfig = require "resource.WeeklyActivityConfig"
local GameEntityConfig = require "resource.GameEntityConfig"
local RefreshObj = require "modules.entity.RefreshObj"
local lua_util = require "lua_util"

--帮派九层妖塔活动
local GuildYaotaCtrl = oo.class()
GuildYaotaCtrl.id = WeeklyActivityConfig.ActiveType.UnionYaota

function GuildYaotaCtrl:ctor(guildid,active_data)
	self.guildid = guildid
	self.active_data = active_data
end

function GuildYaotaCtrl:onCreate()
	self:onLoad()
end

function GuildYaotaCtrl:onLoad()
	self.active_data[GuildYaotaCtrl.id] = self.active_data[GuildYaotaCtrl.id]
			or {storeyId = 1,openState = 0,isSend = 0,isEndSend = 0,openNumber = 0, openTime = 0,endTime = 0,weekday = 0}
	self.cache = self.active_data[GuildYaotaCtrl.id]
	self:onDayTimer()
	self:SecondTimer()
end

function GuildYaotaCtrl:SecondTimer()
	if self.sectimer then
		lua_app.del_timer(self.sectimer)
		self.sectimer = nil
	end
	local function _DoSecond()
		self.sectimer = lua_app.add_timer(1 * 1000, _DoSecond)
		self:CheckTerm()
	end
	self.sectimer = lua_app.add_timer(1 * 1000, _DoSecond)
end

function GuildYaotaCtrl:ServerOpen()
	if self.cache.openState == 2  then
		local nowtime = lua_app.now()
		if nowtime >= self.cache.openTime and nowtime < self.cache.endTime then
			self.obj = RefreshObj.new()
			self.obj:Create()
			self:RefreshYaota()
		else
			--活动结束
			self:AcitvityEnd()
		end
	end
end

-- 检测活动时间
function GuildYaotaCtrl:CheckTerm()
	local data = self:GetActivityTime()
	if not data then
		return
	end
	local guild = self:GetGuild()
	if not guild then
		return
	end
	local nowtime = lua_app.now()
	if self.cache.isSend == 0 then
		--提前五分钟帮派提示
		if nowtime < data.startTime and nowtime >= data.startTime - 300 then
			self.cache.isSend = 1
			server.chatCenter:sendTipMsg(1785,guild.dbid)
		end
	elseif self.cache.isSend == 1 and self.cache.openNumber == 0 then
		--帮主副帮主提示
		if nowtime >= data.startTime then
			for k,v in pairs(guild.players) do
				if v.office == GuildConfig.Office.Leader or v.office == GuildConfig.Office.AssistLeader then
					server.chatCenter:sendTipMsg(1787,k)
				end
			end
			self.cache.isSend = 2
		end
	end
	--周五晚上提示活动结束
	if self.cache.weekday == 5 then
		if self.cache.isEndSend == 0 then
			if nowtime >= data.endTime - 300 and nowtime < data.endTime then
				self.cache.isEndSend = 1
				server.chatCenter:sendTipMsg(1794,guild.dbid)
			end
		elseif  self.cache.isEndSend == 1 then
			if nowtime >= data.endTime then
				self.cache.isEndSend = 2
				server.chatCenter:sendTipMsg(1795,guild.dbid)
			end
		end
	end
	--活动时间内可以开启帮派九层妖塔
	if self.cache.openState == 2 then
		if nowtime >= self.cache.endTime - 300 and self.cache.isSend ~= 3 then
			server.chatCenter:sendTipMsg(1792,guild.dbid)
			self.cache.isSend = 3
		end
		--九层妖塔结束
		if nowtime >= self.cache.endTime then
			self:AcitvityEnd()
		end
	else
		self:YaoEnterGuild()
	end
end

--通过妖塔宝箱
function GuildYaotaCtrl:CreateBox()
	--通关刷宝箱
	local function _CreateBox()
		local guild = self:GetGuild()
		local cfg = server.configCenter.UnionYaotaConfig[1]
		local onlineplayers = guild:GetOnlineplayers()
		local minnum = cfg.refreshNum.min
		local num = minnum.a * onlineplayers +  minnum.b
		if num > cfg.max then
			num = cfg.max
		end
		local npcId = cfg.logic
		self.objbox = RefreshObj.new()
		self.objbox:Create()
		self.objbox:OperatCreateMapNpc(GuildConfig.MapID,num,guild.sceneid,npcId,GameEntityConfig.EntityType.LogicObj,cfg.survivalTime)
		server.chatCenter:sendTipMsg(1789,guild.dbid)
	end
	lua_app.add_timer(60 * 1000, _CreateBox)
end

--本层奖励资金
function GuildYaotaCtrl:SendUnionFund()
	local guild = self:GetGuild()
	local cfg = server.configCenter.UnionYaotaConfig[1]
	local num = cfg.unionFund.a * guild.cache.level + cfg.unionFund.b
	guild:UpdateFund(num)
end

--通过妖塔怪物
function GuildYaotaCtrl:ThroughMonster()
	local count = 0
	for _, _ in pairs(self.obj.entityMap) do
		count = count + 1
	end
	if count <= 0 then
		if self.cache.openState == 2 then
			self:SendUnionFund()
		end
		local guild = self:GetGuild()
		if not guild then
			return
		end
		if server.configCenter.YaotaRefreshConfig[self.cache.storeyId + 1] then
			self.cache.storeyId = self.cache.storeyId + 1
			self:RefreshYaota()
			server.chatCenter:sendTipMsg(1788,guild.dbid)
			--三秒后再传送进妖塔
			local function _EnterYao()
				self:FloorEnerYaota()
			end
			lua_app.add_timer(3 * 1000, _EnterYao)
		else
			--活动时间内结束活动
			if self.cache.openState == 2 then
				self:AcitvityEnd()
				server.chatCenter:sendTipMsg(1791,guild.dbid)
				--三秒后再传送进妖塔
				local function _EnterGUild()
					self:YaoEnterGuild()
				end
				lua_app.add_timer(3 * 1000, _EnterGUild)
				self:CreateBox()
			end
		end
	end
end

--更新妖塔层
function GuildYaotaCtrl:RefreshYaota()
	local guild = self:GetGuild()
	--创建副本和npc
	local cfg = server.configCenter.YaotaRefreshConfig[self.cache.storeyId]
	if not cfg then
		return false
	end
	server.mapMgr:createMap(cfg.mapID, guild.sceneid)
	local onlineplayers = guild:GetOnlineplayers()
	local minnum = math.floor(cfg.refreshNum.min.a * onlineplayers + cfg.refreshNum.min.b)
	local maxnum = math.floor(cfg.refreshNum.max.a * onlineplayers + cfg.refreshNum.max.b)
	local num = math.random(minnum,maxnum)
	if num > cfg.maxNum then
		num = cfg.maxNum
	end
	if self.obj then
		self.obj:RemoveAllEntity()
	else
		self.obj = RefreshObj.new()
		self.obj:Create()
	end
	for _ = 1,num,1 do
		local npcId = self.obj:getRandomNpc(cfg.npcId)
		self.obj:OperatCreateMapNpc(cfg.mapID,1,guild.sceneid,npcId,GameEntityConfig.EntityType.Monster,3600,3)
	end
	self:SendClientMsg()
end

--下层玩家进入最新层
function GuildYaotaCtrl:FloorEnerYaota()
	--如果下层存在
	if self.cache.storeyId <= 1 then
		return
	end
	local guild = self:GetGuild()
	for i = 1,self.cache.storeyId - 1,1 do
		local floorcfg = server.configCenter.YaotaRefreshConfig[i]
		if floorcfg then
			--玩家进入新层
			local players = server.mapMgr:GetScenePlayers(floorcfg.mapID, guild.sceneid)
			local fubencfg = server.configCenter.YaotaRefreshConfig[self.cache.storeyId]
			local mapcfg = server.configCenter.ScenesConfig[fubencfg.mapID]
			if not mapcfg then
				print("MapCenter:GetPos no this map", "guildYaota")
				return false
			end
			local index = math.random(1,#mapcfg.enter)
			local x, y = mapcfg.enter[index][1],mapcfg.enter[index][2]
			for _,player in pairs(players) do
				if player.entityType and player.entityType == GameEntityConfig.EntityType.Role then
					--进行队伍判定
					if player.prop.teamid == 0 then
						self:EnterMap(player)
					else
						local team = server.teamMgr:getTeam(player.prop.teamid)
						if team:isLeader(player.entityID) then
							server.mapMgr:TransTeam(player.entityID,fubencfg.mapID,guild.sceneid,x,y)
						end
					end
				end
			end
		end
	end
end

function GuildYaotaCtrl:EnterMap(player)
	if self.cache.openState ~= 2 then
		server.chatCenter:sendTipMsg(1951,player.dbid)
		return false
	end

	local fubencfg = server.configCenter.YaotaRefreshConfig[self.cache.storeyId]
	local mapcfg = server.configCenter.ScenesConfig[fubencfg.mapID]
	if not mapcfg then
		print("MapCenter:GetPos no this map", "guildYaota")
		return false
	end
	local index = math.random(1,#mapcfg.enter)
	local x, y = mapcfg.enter[index][1],mapcfg.enter[index][2]
	--普通的使用方式不用创建对象,直接调用函数就行了
	local teamid = player.prop.teamid
	local guild = self:GetGuild()

	if teamid == 0 then
		if not self:CheckEnterYaot(player) then
			return false
		end
		if not server.mapMgr:Fly(player.entityID,fubencfg.mapID,guild.sceneid,x,y) then
			server.sendErr(player,"当前状态无法进入妖塔")
			return false
		end
	else
		--进行队伍判定
		local team = server.teamMgr:getTeam(teamid)
		if not team:isLeader(player.entityID) then
			server.sendErr(player,"你不是队长,无权进入")
			return false
		end
		--验证队伍
		for _,mem in pairs(team.memlist) do
			if guild.dbid ~= mem.prop.guildid then
				server.sendErr(player,"队员帮派不一致:"..mem.cache.name)
				return false
			end
			if not self:CheckEnterYaot(mem) then
				server.sendErr(player,"队员无法进入妖塔:"..mem.cache.name)
				return false
			end
		end
		if not server.mapMgr:TransTeam(player.entityID,fubencfg.mapID,guild.sceneid,x,y) then
			server.sendErr(player,"当前状态无法进入妖塔")
			return false
		end
	end
	return true
end

function GuildYaotaCtrl:CheckEnterYaot(player)
	local guild = self:GetGuild()
	local guildplayer = guild.players[player.dbid]
	if not guildplayer then
		server.sendErr(player,"帮派玩家信息异常")
		return false
	end
	if lua_util.intervalDays(guildplayer.jointime) < 3 then
		server.sendErr(player,"加入帮派不足三天")
		return false
	end
	if not player.missionSystem:canFly() then
		server.sendErr(player,"当前任务下无法进入妖塔")
		return false
	end
	return true
end

function GuildYaotaCtrl:GetGuild()
	return server.guildCenter:GetGuild(self.guildid)
end

--openState 0:未开启 1:允许开启 2:活动中
function GuildYaotaCtrl:ChangeOpenState(player,state)
	local guild = self:GetGuild()
	if not guild.players[player.dbid] then
		server.sendErr(player,"不在帮派中")
		return false
	end
	local office = guild.players[player.dbid].office
	if office ~= GuildConfig.Office.Leader and office ~= GuildConfig.Office.AssistLeader then
		server.sendErr(player,"权限不足")
		return false
	end
	--活动时间内可以开启帮派九层妖塔
	local data = self:GetActivityTime()
	if not data or data.state ~= 1 then
		server.sendErr(player,"不在活动时间内")
		return false
	end
	if self.cache.openNumber > 0 then
		server.sendErr(player,"本周已开启")
		return false
	end
	if self.cache.openState == 2 then
		server.sendErr(player,"活动已开启")
		return false
	end
	self.cache.openState = state
	if self.cache.openState == 1 then
		--是否需要重置 下次活动开始时间
		local nowtime = lua_app.now()
		if nowtime >= data.startTime and nowtime < data.endTime then
			self:AcitvityResert()
			return true
		end
	end
	self:SendClientMsg()
	return true
end

--每天重置
function GuildYaotaCtrl:onDayTimer()
	self.cache.isSend = 0
	self.cache.weekday = lua_util.GetWeekDay()
end

--每周重置
function GuildYaotaCtrl:onWeekTimer()
	self.cache.openNumber = 0
	self.cache.isEndSend = 0
end

--获得活动时间数据
function GuildYaotaCtrl:GetActivityTime()
	local data = server.weeklyActivityMgr:GetDataTime(GuildYaotaCtrl.id)
	return data
end

--活动开始初始化数据
function GuildYaotaCtrl:AcitvityResert()
	self.cache.storeyId 	= 1
	self.cache.openNumber 	= 1
	self.cache.openState 	= 2
	self.cache.openTime = lua_app.now()
	local cfg = server.configCenter.UnionYaotaConfig[1]
	self.cache.endTime = self.cache.openTime + cfg.remainTime * 60
	local guild = self:GetGuild()
	server.chatCenter:sendTipMsg(1786,guild.dbid)
	self:RefreshYaota()
end

--活动结束事件
function GuildYaotaCtrl:AcitvityEnd()
	local guild = self:GetGuild()
	self.cache.storeyId = 1
	self.cache.openState = 0
	self.cache.isSend = 0
	if self.obj then
		self.obj:RemoveAllEntity()
		self.obj = nil
	end
	self:SendClientMsg()
	server.chatCenter:sendTipMsg(1793,guild.dbid)

end

function GuildYaotaCtrl:YaoEnterGuild()
	--妖塔玩家进入帮派地图
	local guild = self:GetGuild()
	for _,cfg in pairs(server.configCenter.YaotaRefreshConfig) do
		local players = server.mapMgr:GetScenePlayers(cfg.mapID, guild.sceneid)
		for _,player in pairs(players) do
			if player.entityType and player.entityType == GameEntityConfig.EntityType.Role then
				--进行队伍判定
				if player.prop.teamid == 0 then
					server.guildCenter:enterGuild(player)
				else
					local team = server.teamMgr:getTeam(player.prop.teamid)
					if team:isLeader(player.entityID) then
						server.guildCenter:enterGuild(player)
					end
				end
			end
		end
	end
end

function GuildYaotaCtrl:Quit(player)
	--如果在妖塔内,踢出
	local cfg = server.configCenter.YaotaRefreshConfig[self.cache.storeyId]
	if cfg then
		local guild = self:GetGuild()
		local players = server.mapMgr:GetScenePlayers(cfg.mapID, guild.sceneid)
		for _,data in pairs(players) do
			if player.entityType and player.entityType == GameEntityConfig.EntityType.Role then
				if data.dbid == player.dbid then
					local pos = server.configCenter.ScenesConfig[21].enter[1]
					server.mapMgr:Fly(player.entityID, 21, 1,  pos[1], pos[2])
				end
			end
		end
	end
end

function GuildYaotaCtrl:SendClientMsg(player)
	local guild = self:GetGuild()
	if not guild then
		return false
	end
	local msg = {storeyId = self.cache.storeyId,openState = self.cache.openState,openNumber = self.cache.openNumber,openTime = self.cache.openTime,endTime = self.cache.endTime}
	if player then
		server.sendReq(player, "sc_guild_yaota_activity_info", msg)
	else
		guild:Broadcast("sc_guild_yaota_activity_info",msg)
	end
end

return GuildYaotaCtrl

玩家九层妖塔类

这里比较简单,玩家数据和帮派简单的交互逻辑

local oo = require "class"
local server = require "server"
local lua_app = require "lua_app"

--帮派九层妖塔
local GuildYaota = oo.class()

function GuildYaota:ctor(player, guildctrl)
	self.player = player
	self.guildctrl = guildctrl
end

function GuildYaota:onJoinGuild(guild)
	local playerinfo = guild:GetPlayerInfo(self.player.dbid)
	if not playerinfo then
		return
	end
	guild.guildYaotaCtrl:SendClientMsg(self.player)
end

function GuildYaota:EnterMap()
	local guild = self:GetGuild()
	local playerinfo = guild:GetPlayerInfo(self.player.dbid)
	if not playerinfo then
		return false
	end
	guild.guildYaotaCtrl:EnterMap(self.player)
end

function GuildYaota:onLogin()
	--推送一些消息
	local guild  =  self:GetGuild()
	if not guild then
		return
	end
	--单独推送
	guild.guildYaotaCtrl:SendClientMsg(self.player)
end

function GuildYaota:GetGuild()
	return server.guildCenter:GetGuild(self.player.prop.guildid)
end

return GuildYaota

客户端协议

有些逻辑,比如进入妖塔就陪在npc交互表了

--[[
#九层妖塔开启状态变更
cs_guild_yaota_change_state 3830 {
	request {
		state   0 : integer #0:关闭状态 1:开启状态
	}
	response {
		ret 			0 : boolean
	}
}
]]
function server.cs_guild_yaota_change_state(socketid, msg)
	local player = server.playerCenter:GetPlayerBySocket(socketid)
	local guild = server.guildCenter:GetGuild(player and player.prop.guildid or 0)
	local result = false
	if guild then
		result = guild.guildYaotaCtrl:ChangeOpenState(player,msg.state)
	end
	return {ret = result}
end

#帮派九层妖塔活动信息
sc_guild_yaota_activity_info 3825 {
	request {
		storeyId	    0: integer     #妖塔层数
		openState       1: integer     #妖塔开启状态 0:未开启 1:允许开启 2:活动中
		openNumber      2: integer     #本周开启次数
		openTime	    3: integer     #妖塔开启时间
        endTime         4: integer     #妖塔结束时间
	}
}

function MutualEffect.enterYaota(srcEntityID,targetEntityID,effectCfg,effectSrc,info)
	local player = server.gameEntityCenter:GetEntityByID(targetEntityID)
	if not player then
		return
	end
	if player.prop.entityType ~= GameEntityConfig.EntityType.Role then
		return
	end
	local guild = server.guildCenter:GetGuild(player.prop.guildid)
	if not guild then
		return
	end
	guild.guildYaotaCtrl:EnterMap(player)
	return true
end

function MutualEffect.yaota(srcEntityID,targetEntityID,effectCfg,effectSrc,info)
	local player = server.gameEntityCenter:GetEntityByID(targetEntityID)
	if not player then
		return
	end
	if player.prop.entityType ~= GameEntityConfig.EntityType.Role then
		return
	end
	local guild = server.guildCenter:GetGuild(player.prop.guildid)
	if not guild then
		return
	end
	guild.guildYaotaCtrl:ThroughMonster()
	return true
end

实体创建和刷新规则

懒不多说,直接贴代码

配置表

image.png

刷新实体管理类

local server = require "server"
local lua_app = require "lua_app"
local RefreshObj = require "modules.entity.RefreshObj"


local RefreshMgr = {}

function RefreshMgr:Init()
	
end

function RefreshMgr:ServerOpen()
	self:Create()
end

function RefreshMgr:Create()
	self.map = {}
	for id, config in pairs(server.configCenter.MonsterRefreshConfig) do 
		--if id == 100 then
		local obj = RefreshObj.new(config)
		self.map[id] = obj
		obj:Create()
		--end
	end
end


server.SetCenter(RefreshMgr, "refreshMgr")
return RefreshMgr

local oo = require "class"
local server = require "server"
local lua_app = require "lua_app"
local GameEntityConfig = require "resource.GameEntityConfig"
local GuildConfig = require "resource.GuildConfig"

local RefreshObj = oo.class()

function RefreshObj:ctor(config)
	self.config = config
	self.entityMap = {}
	self.refreshCiShu = 0
	self.refreshTime = 0
	self.nextRefreshTime = 0
	self.npcMap = {}
	self.mapposlist = {}
	self.mapidList = {}
	self.nextRefreshTimeList = {}
end

function RefreshObj:Create()
	local function _RefreshObjCheck()
		self:onTimer()
		self.check_timer = lua_app.add_timer(1 * 1000, _RefreshObjCheck)
	end
	self.check_timer = lua_app.add_timer(1 * 1000, _RefreshObjCheck)
end

--当前时间是否在活动刷新时间内
function RefreshObj:isRefresh()
	local isRefresh = false
	local weekDay = tonumber(os.date("%w")) 	--0,6 0星期天
	local dateNow = tonumber(os.date("%Y%m%d")) 	--20191225 2019/12/25  20200101 2020/01/01
	local minuteNow = tonumber(os.date("%H%M")) --2021 20:21
	--当前服务器时间判断
	local inWeekDay = false
	if self.config.week then
		for _, day in pairs(self.config.week) do
			if day == weekDay then
				inWeekDay = true
				break
			end
		end
	end
	if not inWeekDay then
		return isRefresh
	end
	local dateStart = self.config.date[1][1] * 10000 + self.config.date[1][2] * 100 + self.config.date[1][3]
	local dateEnd = self.config.date[2][1] * 10000 + self.config.date[2][2] * 100 + self.config.date[2][3]
	if dateStart <= dateNow and dateNow <= dateEnd then
		local minuteStart = self.config.time[1][1] * 100 + self.config.time[1][2]
		local minuteEnd = self.config.time[2][1] * 100 + self.config.time[2][2]
		if minuteStart <= minuteNow and minuteNow <= minuteEnd then
			isRefresh = true
		end
	end
	return isRefresh
end

function RefreshObj:onTimer()
	--过期删除
	self:RemoveEntityOfoverTime()
	--没有刷新间隔,无法自动更新
	if not self.config or not self.config.refreshCell then
		return
	end
	local secNow =  os.time()
	local isRefresh = self:isRefresh()
	local minuteNow = tonumber(os.date("%H%M"))
	if not isRefresh then
		--不是刷新时间就要回收
		self:RemoveAllEntity()
		self:RemoveNpcs()
		return
	end
	--判断是不是固定分钟刷新
	if self.config.refreshLimit then
		local minute = tonumber(os.date("%M"))
		if minute ~= self.config.refreshLimit then
			return
		end
	end
	-- 特殊刷新
	if self.config.active then
		if #self.nextRefreshTimeList > 0 then
			-- 处理刷新
			if secNow >= self.nextRefreshTimeList[1] then
				self:doSpecialRefresh()
				-- 获取任务间隔60秒
				self.refreshTime = secNow + 60
				-- 设置下次刷新
				table.remove(self.nextRefreshTimeList,1)
			end
		elseif #self.nextRefreshTimeList == 0 and secNow >= self.refreshTime then
			-- 判断开始时间
			local openflag = false
			for _,vTime in pairs(self.config.active.time) do
				local opentime = vTime[1]  * 100  + vTime[2]
				if minuteNow == opentime then
					openflag = true
					break
				end
			end
			if openflag then
				self.refreshCiShu = 0
				self.mapidList = {}
				--发预告
				if self.config.msgTip then
					-- 随机地图
					local mapGroupCount = #(self.config.mapId)
					local index = math.random(1, mapGroupCount)
					self.mapidList = self.config.mapId[index]
					--地图提示推送
					self:SendMapTipMsg(self.mapidList)
				end
				-- 是否子任务
				local mainid =  self.config.active.main
				if mainid then
					--获取地图
					self.mapidList = server.refreshMgr.map[mainid].mapidList
				end
				-- 设置刷新时间列表
				for _,v in pairs(self.config.active.refreshTime) do
					local task = secNow + v * 60
					table.insert(self.nextRefreshTimeList,#self.nextRefreshTimeList + 1,task)
				end
			end
		end
	else
		-- 常规刷新
		if secNow >= self.nextRefreshTime then
			self:doRefresh()
			if self.refreshCiShu == 0 then
				--npc刷新
				self:doRefreshNpc()
			end
			self.refreshTime = secNow
			self.nextRefreshTime = secNow + math.random(self.config.refreshCell[1], self.config.refreshCell[2])
		end
	end
end

--刷新npc数量
function RefreshObj:GetRefreshNum()
	local num = 0
	if self.config.refreshNum.type == 0 then
		--计算玩家数量
		local onlineCount = server.playerCenter:GetOnlinePlayerCount()
		local minCount = math.floor(onlineCount * self.config.refreshNum.min.a + self.config.refreshNum.min.b)
		local maxCount = math.floor(onlineCount * self.config.refreshNum.max.a + self.config.refreshNum.max.b)
		num = math.random(minCount, maxCount)
	elseif self.config.refreshNum.type == 1 then
		local oldCount = self:getTableRealNum(self.entityMap)
		if self.refreshCiShu <= 0 then
			num = self.config.refreshNum.baseNum
		else
			if oldCount <= 0 then
				num = math.floor(self.config.refreshNum.baseNum * self.config.refreshNum.xishu)
			else
				num = self.config.refreshNum.baseNum - oldCount
			end
		end
	end
	if self.config.maxNum and num > self.config.maxNum then
		num = self.config.maxNum
	end
	return num
end

--刷新地图npc
function RefreshObj:RefreshMapNum(mapid_list,num)
	local hasGuildMap = false
	for i = 1, #mapid_list do
		local mapId = mapid_list[i]
		if mapId == GuildConfig.MapID then
			hasGuildMap = true
			break
		end
	end
	if hasGuildMap then
		for i = 1, #mapid_list do
			local mapId = mapid_list[i]
			if mapId == GuildConfig.MapID then
				--所有帮派更新npc
				for _, guild in pairs(server.guildCenter.guildList) do
					self:CreateMapNpc(mapId,num,guild.sceneid)
					self:SendMapTipMsg(mapid_list,0,guild.dbid)
				end
			end
		end
	elseif #mapid_list >= 1 then
		local i = 1
		while num > 0 do
			num = num - 1
			local mapId = mapid_list[i]
			--每个地图轮着刷一只,所有地图总共刷num个
			self:CreateMapNpc(mapId, 1)

			i = i + 1
			if i > #mapid_list then
				i = 1
			end
		end
	end
end

-- 创建地图npc
function RefreshObj:CreateMapNpc(mapId,num,sceneid,snpcid)
	for _ = 1, num ,1 do
		local npcId = 0
		if self.config.active then
			if self.config.active.type and self.config.active.type == 1 then
				npcId = self:getRandomNpc()
			else
				npcId = self:getRandomNpcBybatch(self.refreshCiShu)
			end
		else
			npcId = self:getRandomNpc()
		end
		if snpcid and snpcid > 0 then
			npcId = snpcid
		end
		if npcId > 0 then
			--local posCfg = server.taskMgr:getRandPosCfgByMapid(mapId)
			local posCfg = self:GetRandomPosCfg(mapId, num)
			local GameEntityType
			local CreateObj = {}
			if self.config.type == 1 then
				GameEntityType = GameEntityConfig.EntityType.Monster
				--生物
				CreateObj = {
					npcid		= npcId,
					x 			= posCfg.mapX,
					y			= posCfg.mapY,
					dir			= math.random(0,7),
					mapid		= mapId,
					entityType = GameEntityType}
			elseif self.config.type == 2 then
				--逻辑物件
				GameEntityType = GameEntityConfig.EntityType.LogicObj
				CreateObj = {
					logicID		= npcId,
					x 			= posCfg.mapX,
					y			= posCfg.mapY,
					dir			= math.random(0,7),
					mapid		= mapId,
					entityType = GameEntityType}
			end
			if GameEntityType then
				if sceneid and sceneid > 0 then
					CreateObj.sceneid = sceneid
				end
				local entity = server.gameEntityCenter:CreateEntity(CreateObj)
				if entity then
					local enteyinfo = {}
					enteyinfo["entity"] = entity
					local overtime = 0
					if self.config.survivalTime then
						overtime = os.time() +  self.config.survivalTime - 5
					end
					enteyinfo["OverTime"] =  overtime
					self.entityMap[entity.entityID] = enteyinfo
					entity.prop.worldIndex = posCfg.randomid
					entity:SubEvent(server.enEvent.PlayerEvnt.EVENTID_ENTITY_DESTROY,self.onEntityDestroy,self)
					if self.config.move then
						entity.prop.moveSpeed =  self.config.move
						server.npcMoveMgr:addEntitytoAIControl(mapId,entity.entityID)
					end
				end
			end
		end
	end
end

--手动创建刷新地图npc
function RefreshObj:OperatCreateMapNpc(mapId,num,sceneid,npcId,npctype,survivalTime,move)
	for _ = 1, num ,1 do
		if npcId > 0 then
			local posCfg = self:GetRandomPosCfg(mapId, num)
			local CreateObj = {
				x 			= posCfg.mapX,
				y			= posCfg.mapY,
				dir			= math.random(0,7),
				mapid		= mapId,
				entityType = npctype}
			if npctype == GameEntityConfig.EntityType.Monster then
					CreateObj.npcid	= npcId
			elseif npctype == GameEntityConfig.EntityType.LogicObj then
				CreateObj.logicID	= npcId
			end
			if sceneid and sceneid > 0 then
				CreateObj.sceneid = sceneid
			end
			local entity = server.gameEntityCenter:CreateEntity(CreateObj)
			if entity then
				local enteyinfo = {}
				enteyinfo["entity"] = entity
				local overtime = 0
				if survivalTime then
					overtime = os.time() +  survivalTime - 5
				end
				enteyinfo["OverTime"] = overtime
				self.entityMap[entity.entityID] = enteyinfo
				entity.prop.worldIndex = posCfg.randomid
				entity:SubEvent(server.enEvent.PlayerEvnt.EVENTID_ENTITY_DESTROY,self.onEntityDestroy,self)
				if move then
					entity.prop.moveSpeed =  move
					server.npcMoveMgr:addEntitytoAIControl(mapId,entity.entityID)
				end
			end

		end
	end
end

-- 特殊刷新
function RefreshObj:doSpecialRefresh()
	--刷新数量
	local num = self:GetRefreshNum()
	self.refreshCiShu = self.refreshCiShu + 1
	--封妖类不移除已存在的怪,只补全数量
	if self.config.refreshNum.type == 0 then
		self:RemoveAllEntity()
	end
	-- 特殊刷新 地图 不变
	if #self.mapidList == 0 then
		local mapGroupCount = #(self.config.mapId)
		local index = math.random(1, mapGroupCount)
		self.mapidList = self.config.mapId[index]
	end
	--根据地图和npc数量创建
	self:RefreshMapNum(self.mapidList,num)
	--地图提示推送
	if self.config.active.tip then
		local tipsid = self.config.active.tip[self.refreshCiShu]
		if tipsid and tipsid > 0  then
			self:SendMapTipMsg(self.mapidList,tipsid)
		end
	end
end

function RefreshObj:doRefresh()
	--刷新数量
	local num = self:GetRefreshNum()
	self.refreshCiShu = self.refreshCiShu + 1
	--封妖类不移除已存在的怪,只补全数量
	if self.config.refreshNum.type == 0 then
		self:RemoveAllEntity()
	end
	local index = math.random(1, #(self.config.mapId))
	local mapid_list = self.config.mapId[index] --刷新的地图列表{21,22,23}
	--根据地图和npc数量创建
	self:RefreshMapNum(mapid_list,num)
	--地图提示推送
	self:SendMapTipMsg(mapid_list)
end

--地图提示
function RefreshObj:SendMapTipMsg(mapid_list,tipsid,dbid)
	local msgtip
	--不传则默认msgTip
	if tipsid and tipsid > 0 then
		msgtip = tipsid
	elseif self.config.msgTip then
		msgtip = self.config.msgTip
	end
	if msgtip then
		--地图作参数
		local str = ""
		for i = 1, #mapid_list do
			local mapId = mapid_list[i]
			local mapName = server.configCenter.ScenesConfig[mapId].mapName
			str = str..mapName
			if i < #mapid_list then
				str = str.."、"
			end
		end
		local sendid = 0
		if dbid then
			sendid = dbid
		end
		server.chatCenter:sendTipMsg(msgtip,sendid,str)
	end
end

--num仅仅初始化数量
function RefreshObj:GetRandomPosCfg(mapId, num)
	local pos =  server.mapMgr:GetMosnterRandPos(mapId)
	if pos then
		return pos
	end
	print("获取随机点失败了吗")
	if not self.mapposlist[mapId] then
		self.mapposlist[mapId] = server.taskMgr:getBatchWorldPos(mapId,num)
	end
	local poslist = self.mapposlist[mapId]
	if #poslist <= 0 then
		self.mapposlist[mapId] = server.taskMgr:getBatchWorldPos(mapId,num)
		poslist = self.mapposlist[mapId]
	end
	local cfg
	if #poslist >= 1 then
		local index = math.random(1, #poslist)
		cfg = server.configCenter.MapRandomConfig[poslist[index]]
		table.remove(poslist, index)
	end
	return cfg
end

function RefreshObj:onEntityDestroy(args)
	local entityID = args.entityID
	self.entityMap[entityID].obj = nil
	self.entityMap[entityID] = nil
end

function RefreshObj:RemoveAllEntity()
	for entityID, obj in pairs(self.entityMap) do
		obj.entity:unSubEvent(server.enEvent.PlayerEvnt.EVENTID_ENTITY_DESTROY,self.onEntityDestroy,self)
		server.gameEntityCenter:ReleaseEntity(entityID)
	end
	self.entityMap = {}
end

function RefreshObj:doRefreshNpc()
	if not self.config.npcFixedId then
		return
	end
	self.npcMap = {}
	--{{3101,22,22,35,3},{3101,22,22,35,3}} id,map,x,y,dir
	for _, cfg in pairs(self.config.npcFixedId) do
		local entity =
		server.gameEntityCenter:CreateEntity({
			npcid		= cfg[1],
			x 			= cfg[3],
			y			= cfg[4],
			dir			= cfg[5],
			mapid		= cfg[2],
			entityType = GameEntityConfig.EntityType.Monster})
		if entity then
			self.npcMap[entity.entityID] = entity.entityID
		end
	end
end

function RefreshObj:RemoveNpcs()
	for entityID, _ in pairs(self.npcMap) do
		server.gameEntityCenter:ReleaseEntity(entityID)
	end
	self.npcMap = {}
end

function RefreshObj:getTableRealNum(_table)
	local count = 0
	for _, _ in pairs (_table) do
		count = count + 1
	end
	return count
end

function RefreshObj:getRandomNpc(npcMonsterId)
	local rate = 0
	local num = math.random(1, 10000)
	local npclist
	if npcMonsterId then
		npclist = npcMonsterId
	else
		npclist = self.config.npcMonsterId
	end
	for _, npcs in pairs(npclist) do
		if #npcs >= 2 then
			rate = rate + npcs[1]
			if num <= rate then
				local index = math.random(2, #npcs)
				return npcs[index]
			end
		end
	end
	return 0
end

-- 分批刷新
function RefreshObj:getRandomNpcBybatch(batchCount)
	--lua_app.log_debug("getRandomNpcBybatch",batchCount)
	local npcs = self.config.npcMonsterId[batchCount]
	--  npcs[1] 概率为0 不刷新
	if npcs and #npcs >= 2 and npcs[1] ~= 0 then
		local index = math.random(2, #npcs)
		return npcs[index]
	end
	return 0
end

-- 到期删除
function RefreshObj:RemoveEntityOfoverTime()
	local secNow = os.time()
	for entityId, obj in pairs(self.entityMap) do
		if obj.OverTime ~= 0 and secNow > obj.OverTime then
			obj.entity:unSubEvent(server.enEvent.PlayerEvnt.EVENTID_ENTITY_DESTROY,self.onEntityDestroy,self)
			server.gameEntityCenter:ReleaseEntity(entityId)
			self.entityMap[entityId] = nil
			--lua_app.log_debug("RemoveEntityOfoverTime=",entityId)
		end
	end
end

return RefreshObj