require("luaScript.Tools.EvalServerScript"); require("luaScript.Tools.List") require("luaScript.Tools.ConfigConverter"); bit32 = bit32 or require("bit") -- 异或 function getNumXor(num1, num2) local ret=bit32.bxor(num1,num2); return ret; end --或 function getNumOr(num1,num2) local ret=bit32.bor(num1,num2); return ret; end --与 function getNumBand(num1,num2) local ret=bit32.band(num1,num2); return ret; end --取反 function getNumNot(num1,num2) local ret=bit32.bnot(num1,num2); return ret; end -- 格式化时间函数, 把以秒为单位的数字转换成 -- {day = num, hours = num, min = num, second = num的形式} function formatTime(second) if second < 0 then return { day = 0; hours = 0; min = 0; second = 0;}; end; local t = {}; t.day = math.floor(second / 86400); second = second % 86400; t.hours = math.floor(second / 3600); second = second % 3600; t.min = math.floor(second / 60); t.second = second % 60; return t; end --返回的时间表格式化一下 function formatTime2(second) local t = formatTime(second); t.hours = string.format("%02d",t.hours); t.min = string.format("%02d",t.min); t.second = string.format("%02d",t.second); return t; end -- 格式化时间成字符串 -- 如果是1秒则返回是01 -- 返回的是xx:xx:xx:xx:xx function formatTimeToStr(second) local data = formatTime(second) if data.day > 0 then return string.format("%.2d天%.2d:%.2d:%.2d", data.day, data.hours, data.min, data.second) elseif data.hours > 0 then return string.format("%.2d:%.2d:%.2d", data.hours, data.min, data.second) else return string.format("%.2d:%.2d", data.min, data.second) end end -- 格式化时间成字符串,排行榜格式 function formatTimeToRankStr(second) local data = formatTime(second) if data.day > 0 then return string.format(LN.SEASON_TIME_DAY_HOUR, data.day, data.hours) elseif data.hours > 0 then return string.format(LN.SEASON_TIME_HOUR, data.hours) elseif data.min > 0 then return string.format(LN.SEASON_TIME_MINUTE, data.min) else return string.format(LN.SEASON_TIME_SECOND, data.second) end end -- 获取今天的日期,返回的结果格式是"2017-12-23" function getTodayString() local t = os.time() local d = os.date('*t', t) local str = string.format("%04d-%02d-%02d", d.year, d.month, d.day); return str; end -- AES 128/ECB/PKCS5Padding加密 function AES_encode(postData, aesKey) if postData == nil or aesKey == nil then return "" end local jsonData = json.encode(postData); --print("加密前的数据", table.tostring(jsonData)) -- 使用aes加密 local aesData = aes.encode(aesKey, jsonData) local base64Data = base64.encode(aesData) --print("加密后的数据", base64Data) return base64Data end -- AES 128/ECB/PKCS5Padding解密 function AES_decode(data, aesKey) if data == nil or aesKey == nil then return "" end --print("解密前的原始数据", data) local jsonData = json.decode(aes.decode(aesKey, base64.decode(data))); --print("解密后的jsonData", table.tostring(jsonData)) return jsonData end -- 把123#123格式转换成ccp(123,123) function toPosition(str) local numberArray = toNumberArray("#")(str) if #numberArray ~= 2 then return cc.p(0,0) else return cc.p(numberArray[1] , numberArray[2]); end end -- 传入一个相机和屏幕的像素位置,获取xz平面的一个世界位置 function convertScreenToWorldPos(Camera, ScreenX, ScreenY) local floorPlane = cc.Plane:new(cc.vec3(0,1,0) , 0); -- 计算出mainlayer的世界位置 local screenSize = cc.Director:getInstance():getWinSizeInPixels(); local ray = Camera:pickRay(ScreenX / screenSize.width , ScreenY / screenSize.height); local distance = ray:intersectsPlane(floorPlane); if distance >= 0 then return ray:getPoint(distance); end return ray:getPoint(0); end -- 根据一个世界位置和一个相机计算投影后的位置 function projectPosByWorldPos(camera, worldPos) local screenSize = cc.Director:getInstance():getWinSizeInPixels() -- 获取对应的屏幕位置 local clientPos = camera:project(cc.Rectangle:new(screenSize.width, screenSize.height), worldPos); return cc.p(clientPos.x, screenSize.height - clientPos.y); end -- 创建顺序播放的动画序列 function createActionArray(...) local arg = {...} if #arg == 1 then local v = arg[1]; if type(v) == "function" then return cc.CallFunc:create(v); else return v; end else local action = nil; local actionArray = {}; for i,v in ipairs(arg) do if type(v) == "function" then table.insert(actionArray , cc.CallFunc:create(v)); else table.insert(actionArray , v); end end local action = cc.Sequence:create(actionArray); return action; end end -- 创建同时播放的动画序列 function createSpawnActions(...) local arg = {...} if #arg == 1 then local v = arg[1]; if type(v) == "function" then return cc.CallFunc:create(v); else return v; end else local action = nil; local actionArray = {}; for i,v in ipairs(arg) do if type(v) == "function" then table.insert(actionArray , cc.CallFunc:create(v)); else table.insert(actionArray , v); end end local action = cc.Spawn:create(actionArray); return action; end end -- 创建动态时间动画序列 function createDynamicTimeActionArray(...) local arg = {...} if #arg == 1 then local v = arg[1]; if type(v) == "function" then return cc.CallFunc:create(v); else return v; end else local action = nil; local actionArray = {}; for i,v in ipairs(arg) do if type(v) == "function" then table.insert(actionArray , cc.CallFunc:create(v)); else table.insert(actionArray , v); end end local action = cc.DynamicTimeSequence:create(actionArray); return action; end end -- 尚未实现的弹出提示 function onImplementClick() showTooltip(RT("not implement")); end EaseType = { [1] = function(action , param)return action end, [2] = function(action , param)return cc.EaseRateAction:create(action , param) end, [3] = function(action , param)return cc.EaseIn:create(action , param) end, [4] = function(action , param)return cc.EaseOut:create(action , param) end, [5] = function(action , param)return cc.EaseInOut:create(action , param) end, [6] = function(action , param)return cc.EaseExponentialIn:create(action) end, [7] = function(action , param)return cc.EaseExponentialOut:create(action) end, [8] = function(action , param)return cc.EaseExponentialInOut:create(action) end, [9] = function(action , param)return cc.EaseSineIn:create(action) end, [10] = function(action , param)return cc.EaseSineOut:create(action) end, [11] = function(action , param)return cc.EaseSineInOut:create(action) end, [12] = function(action , param)return cc.EaseElastic:create(action , param) end, [13] = function(action , param)return cc.EaseElasticIn:create(action , param) end, [14] = function(action , param)return cc.EaseElasticOut:create(action , param) end, [15] = function(action , param)return cc.EaseElasticInOut:create(action , param) end, [16] = function(action , param)return cc.EaseBounce:create(action) end, [17] = function(action , param)return cc.EaseBounceIn:create(action) end, [18] = function(action , param)return cc.EaseBounceOut:create(action) end, [19] = function(action , param)return cc.EaseBounceInOut:create(action) end, [20] = function(action , param)return cc.EaseBackIn:create(action) end, [21] = function(action , param)return cc.EaseBackOut:create(action) end, [22] = function(action , param)return cc.EaseBackInOut:create(action) end, } --[[ 创建节点的淡入淡出效果 --]] function createEaseAction(easeType , param , action) local easeFunction = EaseType[easeType]; if easeFunction then return easeFunction(action , param); else return action; end end -- 创建震动Action -- @rate 频率 每秒多少次 -- @range 振动幅度 {x=10, y=0} 左右振动10像素,上下振动0像素 -- @times 播放次数,-1表示无限 function createShakeAction(rate , range , times) -- 每次所需时间 local oneSeconds = 1 / rate / 4; local oneShakeAction = createActionArray(cc.EaseSineOut:create(cc.MoveBy:create(oneSeconds , cc.p(-range.width / 2 , -range.height / 2))) , cc.EaseSineInOut:create(cc.MoveBy:create(oneSeconds * 2 , cc.p(range.width , range.height))) , cc.EaseSineIn:create(cc.MoveBy:create(oneSeconds , cc.p(-range.width / 2 , -range.height / 2))) ) if times == -1 then return cc.RepeatForever:create(oneShakeAction); else return cc.Repeat:create(oneShakeAction , times); end end -- 创建旋转Action -- @roundPerSecond 每秒转动多少圈 -- @rounds 需要旋转多少圈,-1则无限 -- @reverse 是否反方向旋转 function createRotationAction(roundPerSecond , rounds , reverse) local angle; if reverse then angle = -360; else angle = 360; end local action = cc.RotateBy:create(1 / roundPerSecond , angle); if rounds == -1 then return cc.RepeatForever:create(action); else return cc.Repeat:create(action , rounds); end end -- 创建旋转Action -- @angle 旋转多少度 -- @speed 每秒转动多少度 function createRotationReverseAction(angle , speed , easeType , easeParam) local oneRoundTime = math.abs(angle) / speed; return createEaseAction(easeType , easeParam , createActionArray(cc.RotateBy:create(oneRoundTime , angle) , cc.RotateBy:create(oneRoundTime , -angle))); end -- 创建缩放Action -- @scale 放大到多少 -- @seconds 动画时长 function createScaleReverseAction(scale , seconds , easeType , easeParam) return createEaseAction(easeType , easeParam , createActionArray(cc.ScaleTo:create(seconds / 2 , scale) , cc.ScaleTo:create(seconds / 2 , 1))); end -- 在下一帧执行一次函数func function runInNextFrame(func , ...) local arg = {...} local function exec() func(unpack(arg)); end cc.Director:getInstance():getScheduler():performFunctionInCocosThread(exec) end -- 延迟delaySeconds这么多秒后执行 function runDelay(delaySeconds, func , ...) local id local arg = {...} local function unschedule() if id then cc.Director:getInstance():getScheduler():unscheduleScriptEntry(id); id = nil; end end local function exec() if id then cc.Director:getInstance():getScheduler():unscheduleScriptEntry(id); id = nil; end func(unpack(arg)); end id = cc.Director:getInstance():getScheduler():scheduleScriptFunc(exec, delaySeconds, false) return unschedule; end -- 在下一帧延迟多少秒后执行 function runDelayInNextFrame(delaySeconds, func) local function nextFrameFunc() runDelay(delaySeconds, func) end runInNextFrame(nextFrameFunc) end -- 使当前页面在屏幕居中 function centerWindow(ui) local size = ui:getContentSize(); local sizeScreen = cc.Director:getInstance():getWinSize(); ui:setPosition(ui.Parent:convertToNodeSpace(ccp((sizeScreen.width - size.width) / 2 , (sizeScreen.height - size.height) / 2))); end -- 把字符串替换成replaceTable中的词义 function replaceStringByTable(str , replaceTable) local newString = str; for i , v in pairs(replaceTable) do if type(v) == "string" or type(v) == "number" then local newV = string.gsub(v , "%%" , "%%%%"); newString = string.gsub(newString , "%[" .. i .. "%]" , newV); end end return newString; end -- 创建一个下划线按钮 -- @title 名字 -- @fontSize 字体大小 -- @color 字体颜色 -- @onClick 点击回调的函数 function createLabelButtonHtmlText(title, fontSize, color, onClick) if not title then return ""; end local function createLuaControl() local label = cc.Label:createWithTTF(title, "res/default/msyh.ttc" , fontSize); local size = label:getContentSize(); local layer = cc.Layer:create(); layer:setContentSize(label:getContentSize()); local ui = loadUI("res/ui/ui_tongyong/tongyong_xiahuaxian.ui") ui.Items.HtmlCtrl_wenzi:setText(title); ui.Items.HtmlCtrl_wenzi:setColor(color); ui.Items.Layout_1:setTouchEnabled(true); ui.Items.Layout_1:registerClick(onClick); local lineSize = ui.Items.Layout_xian:getSize() ui.Items.Layout_xian:setSize(cc.size(size.width, lineSize.height)) ui.Items.Layout_xian:setBackGroundColor(color) layer:addChild(ui) return layer end return ""; end -- str 格式这样 r_g_b_a 255_232_139_255 function stringToColorWithSeparate(str) local colorStr = string.split(str, "_"); if(#colorStr==3)then local color = cc.c3b(tonumber(colorStr[1]),tonumber(colorStr[2]),tonumber(colorStr[3])); return color; elseif(#colorStr==4)then local color = cc.c4b(tonumber(colorStr[1]),tonumber(colorStr[2]),tonumber(colorStr[3]),tonumber(colorStr[3])); return color; end end function createLabelButtonHtmlText2(title, color, onClick) if not title then return ""; end local function createLuaControl() local label = cc.HtmlCtrl:create() label:setAutoSize(true); label:setText(title); local size = label:getContentSize(); local layer = cc.Layer:create(); layer:setContentSize(label:getContentSize()); local ui = loadUI("res/ui/ui_tongyong/tongyong_xiahuaxian.ui") ui.Items.HtmlCtrl_wenzi:setText(title); ui.Items.Layout_1:setTouchEnabled(true); ui.Items.Layout_1:registerClick(onClick); local lineSize = ui.Items.Layout_xian:getSize() ui.Items.Layout_xian:setSize(cc.size(size.width, lineSize.height)) ui.Items.Layout_xian:setBackGroundColor(color) layer:addChild(ui) return layer end return ""; end -- 显示退出确认对话框 function showExitDialog(text) local function onOk(id) cc.Director:getInstance():endToLua(); end local function onCancel() end showConfirmDialog(text, onOk, onCancel) end -- 重启游戏 function restartGame() cc.Application:getInstance():restart(); end -- 显示重启确认对话框 function showRestartDialog(text) local function onOk() restartGame(); end local function onCancel() end showConfirmDialog(text, onOk, onCancel) end local XmlFilter = { ["<"] = "<"; [">"] = ">"; ["&"] = "&"; ["'"] = "'"; ["\""] = """; } local function replaceIt(c) return XmlFilter[c] or c; end -- 把XML转义符全部替换 function replaceXmlByFilter(str) return string.gsub(str , "[%&%>%<%'%\"]" , replaceIt); end --[[ -- 获取首个屏蔽字出现的位置和结束位置 -- 没有屏蔽字出现则返回nil, nil; function findMaskWord(str) local wordList = Config.MaskWord; local startPos; local endPos; for k, v in pairs(wordList) do startPos, endPos = string.find(str, v.maskWord); if startPos or endPos then print(v.maskWord); return startPos, endPos; end end return nil, nil; end ]] -- 把一个utf8的字符串转换成utf32的table数组 function Utf8To32(utf8str) assert(type(utf8str) == "string") local res, seq, val = {}, 0, nil for i = 1, #utf8str do local c = string.byte(utf8str, i) if seq == 0 then table.insert(res, val) seq = c < 0x80 and 1 or c < 0xE0 and 2 or c < 0xF0 and 3 or c < 0xF8 and 4 or --c < 0xFC and 5 or c < 0xFE and 6 or error("invalid UTF-8 character sequence") val = bit32.band(c, 2^(8-seq) - 1) else val = bit32.bor(bit32.lshift(val, 6), bit32.band(c, 0x3F)) end seq = seq - 1 end table.insert(res, val) return res end function Utf8Table(utf8str) assert(type(utf8str) == "string") local res = {} for i = 1, #utf8str do local c = string.byte(utf8str, i) table.insert(res, c) end table.insert(res, val) return res end local char = string.char local function tail(n, k) local u, r='' for i=1,k do n,r = math.floor(n/0x40), n%0x40 u = char(r+0x80) .. u end return u, n end -- 把一个utf32字符数字,转换成utf8字符串 function Utf32To8(a) local n = a; local r; local u; if n<0x80 then -- 1 byte return char(n) elseif n<0x800 then -- 2 byte u, n = tail(n, 1) return char(n+0xc0) .. u elseif n<0x10000 then -- 3 byte u, n = tail(n, 2) return char(n+0xe0) .. u elseif n<0x200000 then -- 4 byte u, n = tail(n, 3) return char(n+0xf0) .. u elseif n<0x4000000 then -- 5 byte u, n = tail(n, 4) return char(n+0xf8) .. u else -- 6 byte u, n = tail(n, 5) return char(n+0xfc) .. u end end MaskCodeType = { NAME = 1; CHAT = 2; } local emojiTable = require("luaScript.Tools.Emoji"); -- 保存所有正则表达式 local g_batchRegex = {}; -- 动态构造正则表达式批量处理 -- maskCode 屏蔽范围:1表示命名2表示聊天3表示聊天和命名 function getBatchRegex(maskCode) maskCode = maskCode or 0xffffffff; -- 构造正则表达式批量处理 local batchRegex = g_batchRegex[maskCode]; if batchRegex == nil then batchRegex = cc.BatchRegex:new(); g_batchRegex[maskCode] = batchRegex; if maskCode == 3 or maskCode == 2 then for i , v in ipairs(string.split(LN.MASK_PATTERNS , "\n")) do if v ~= "" then batchRegex:add(v); end end end -- 名字 if maskCode == 3 or maskCode == 1 then for i , v in ipairs(string.split(LN.MASK_PATTERNS_NAME , "\n")) do if v ~= "" then batchRegex:add(v); end end end end return batchRegex; end -- 检测字符串里是否有屏蔽字 -- 有屏蔽字返回false, 没有返回true function checkMaskWord(str, maskCode) local regex = getBatchRegex(maskCode); local result = regex:search(str); if #result > 0 then print('在字符串 "' .. str .. '" 发现屏蔽字 ' .. result[1]); return false , result[1]; end -- 表情也屏蔽 local names32 = Utf8To32(str); local namesRet = ""; for i , v in ipairs(names32) do local emoji = emojiTable[v]; if emoji then if type(emoji) == "table" then local val = names32[i + 1]; if val then if emoji[val] then return false , "表情"; end end else return false , "表情"; end end end return true; end -- 替换字符串里可能有的屏蔽字, 效率低下, 求改成c++版本 -- @replaceChar 屏蔽字替换成的字符, 默认为"*"; function replaceMaskWord(str, replaceChar, maskCode) require("luaScript.Tools.Utf16"); replaceChar = replaceChar or "*"; print("replaceChar" , replaceChar); local regex = getBatchRegex(maskCode); print("regex" , regex); return regex:replace(str , replaceChar); end local XmlFilter = { [60] = "<"; [62] = ">"; [38] = "&"; [39] = "'"; [34] = """; } -- 表情库 function createEmojiHtml(emojiValue , value2) local emoji = emojiTable[emojiValue]; if emoji then if type(emoji) == "table" then if value2 then local emoji = emoji[value2]; if emoji then return ""; end end else return ""; end end return XmlFilter[emojiValue] or Utf32To8(emojiValue); end -- 把聊天中的表情文字替换掉 function replaceChatEmoji(str) local utf32 = Utf8To32(str); local text = ""; for i , v in ipairs(utf32) do text = text .. createEmojiHtml(v , utf32[i + 1]); end return text; end -- 在waitView关闭后执行 function runInWaitViewEnd(func , ...) if g_WaitViewInstance then local arg = {...}; local function onCloseWaitView() game:unregEvent("onCloseWaitView"); func(unpack(arg)); end game:regEvent("onCloseWaitView" , onCloseWaitView); else func(...); end end -- 显示平台相关的等待窗口 function showPlatformWaitView() local platformPlugin = PluginManager:getInstance():loadPlugin("AnalyticsPlatform"); if platformPlugin then platformPlugin:call("showWaitView"); end end -- 关闭平台相关的等待窗口 function closePlatformWaitView() local platformPlugin = PluginManager:getInstance():loadPlugin("AnalyticsPlatform"); if platformPlugin then platformPlugin:call("closeWaitView"); end end -- 显示滚动公告 function showFloatTooltip(msg) local homeView = cc.Director:getInstance():getRunningScene(); local html = cc.HtmlCtrl:create('' .. replaceXmlByFilter(msg) .. ''); if homeView.mFloatBar == nil then homeView.mFloatBar = cc.LayerColor:create(cc.c4b(0,0,0,76)); homeView:addChild(homeView.mFloatBar); end local winSize = cc.Director:getInstance():getWinSize(); homeView.mFloatBar:removeAllChildren(); homeView.mFloatBar:addChild(html); homeView.mFloatBar:setContentSize(cc.size(winSize.width , html:getHeight())); homeView.mFloatBar:setPosition(cc.p(0 , winSize.height - html:getContentSize().height)); html:setAnchorPoint(cc.p(0,0)); html:setPosition(cc.p(1280 , (homeView.mFloatBar:getContentSize().height - html:getContentSize().height) / 2)); local function onEnd() homeView.mFloatBar:removeFromParent(); homeView.mFloatBar = nil; end print("width " , html:getContentSize().width , html:getContentSize().height , winSize.width , html:getWidth() , html:getHeight()); -- 多少像素每秒 local speed = 30; html:runActions(cc.MoveTo:create(html:getContentSize().width / speed , cc.p( - html:getContentSize().width , 0)) , onEnd); end local function sortFunc(a,b) local numA = tonumber(a) local numB = tonumber(b) if numA ~= nil and numB ~= nil then return numA < numB else return a < b end end -- 根据Key排序 :从小到大 function pairsByKeys (t) local a = {} for n in pairs(t) do table.insert(a, n) end table.sort(a, sortFunc) local i = 0 -- iterator variable local iter = function () -- iterator function i = i + 1 if a[i] == nil then return nil else return a[i], t[a[i]] end end return iter end local function sortFuncEx(a,b) local numA = tonumber(a) local numB = tonumber(b) if numA ~= nil and numB ~= nil then return numA > numB else return a > b end end -- 根据Key排序(从大到小) function pairsByKeysEx (t) local a = {} for n in pairs(t) do table.insert(a, n) end table.sort(a, sortFuncEx) local i = 0 -- iterator variable local iter = function () -- iterator function i = i + 1 if a[i] == nil then return nil else return a[i], t[a[i]] end end return iter end function getNetTimeOutNum() if g_netTimeOut then return g_netTimeOut else return 10; end end function sortListByFunc(list, sortFunc) local a = {} for _, v in pairs(list) do table.insert(a, v) end if not sortFunc then return; end table.sort(a, sortFunc) return a; end local stQuery = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p" , "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O" , "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", [0] = "0" }; -- 10进制转62进制, 注意数值太大会导致溢出 function dec2ST(decNum) local ret = ""; local num = 0; if type(decNum) == "string" then num = tonumber(decNum); elseif type(decNum) == "number" then num = decNum; else return ret; end; local h; repeat h = math.floor(num / 62); local m = num % 62; ret = ret .. stQuery[m]; num = h; until h <= 0; return string.reverse(ret); end; -- 16进制转62进制, 注意数值太大会导致溢出 function hex2ST(str) local ret = ""; local num = 0; if type(str) == "string" then local len = string.len(str); for i = 1, len do local c = string.sub(str, i, i); local ascii = string.byte(c); if ascii >= 48 and ascii <= 57 then num = num * 16 + ascii - 48; elseif ascii >= 65 and ascii <= 70 then num = num * 16 + ascii - 65 + 10; elseif ascii >= 97 and ascii <= 102 then num = num * 16 + ascii - 97 + 10; end end else return ret; end; local h; repeat h = math.floor(num / 62); local m = num % 62; ret = ret .. stQuery[m]; num = h; until h <= 0; return string.reverse(ret); end; function alignString(str, alignSize, maxSize) local len = string.len(str); local alignStr = str; if maxSize then if len < maxSize then for i = 1, maxSize - len do alignStr = "0" .. alignStr; end end; else local m = len % alignSize; if m > 0 then for i = 1, (alignSize - m) do alignStr = "0" .. alignStr; end end; end return alignStr; end; -- 16进制转62进制 支持大数, 但只有压缩效果(4位16进制用3位62进制表示), 数值不相等 function hex2STEx(str) local ret =""; local len = string.len(str); local alignStr = str; local alignSize = 4; -- 对齐字符串 local m = len % alignSize; if m > 0 then for i = 1, (alignSize - m) do alignStr = "0" .. alignStr; end len = len + alignSize - m; end; local i = 1; while i < len do local s = string.sub(alignStr, i, i + alignSize - 1); ret = ret .. hex2ST(s); i = i + alignSize; end; return ret; end -- 播放UI的从上往下掉落的动画 function fallDownUI(ui) if not isUIAniEnabled() then return; end local oldPosY = ui:getPositionY(); ui:setPositionY( cc.Director:getInstance():getVisibleSize().height); ui:runActions(cc.EaseSineInOut:create(cc.MoveTo:create(0.3 , cc.p(ui:getPositionX() , oldPosY - 15))) , cc.EaseSineInOut:create(cc.MoveBy:create(0.2 , cc.p(0 , 45))) , cc.EaseSineIn:create(cc.MoveBy:create(0.2 , cc.p(0 , -30))) ); end -- 播放UI放大动画 function scaleUI(ui) if not isUIAniEnabled() then return; end local oldScaleX = ui:getScaleX(); local oldScaleY = ui:getScaleY(); ui:setScaleX(0); ui:setScaleY(0); ui:runActions(cc.EaseSineInOut:create(cc.ScaleTo:create(0.2 , oldScaleX + 0.05 , oldScaleY + 0.05)) , cc.EaseSineInOut:create(cc.ScaleTo:create(0.1 , oldScaleX - 0.05 , oldScaleY - 0.05)) , cc.EaseSineIn:create(cc.ScaleTo:create(0.1 , oldScaleX , oldScaleY)) ); end -- 播放UI的从上往下掉落的动画 function moveDownUI(ui) if not isUIAniEnabled() then return; end local oldPosY = ui:getPositionY(); ui:setPositionY( cc.Director:getInstance():getVisibleSize().height); ui:runActions(cc.EaseExponentialOut:create(cc.MoveTo:create(0.5 , cc.p(ui:getPositionX() , oldPosY))) ); end -- 播放UI的从下往上掉落的动画 function moveUpUI(ui) if not isUIAniEnabled() then return; end local oldPosY = ui:getPositionY(); ui:setPositionY(-ui.ContentSize.height); ui:runActions(cc.EaseExponentialOut:create(cc.MoveTo:create(0.5 , cc.p(ui:getPositionX() , oldPosY))) ); end -- 播放UI的从右往左掉落的动画 function moveLeftUI(ui) if not isUIAniEnabled() then return; end local oldPosX = ui:getPositionX(); ui:setPositionX(cc.Director:getInstance():getVisibleSize().width); ui:runActions(cc.EaseExponentialOut:create(cc.MoveTo:create(0.5 , cc.p(oldPosX , ui:getPositionY()))) ); end -- 播放UI的从左往右掉落的动画 function moveRightUI(ui) if not isUIAniEnabled() then return; end local oldPosX = ui:getPositionX(); ui:setPositionX(- cc.Director:getInstance():getVisibleSize().width); ui:runActions(cc.EaseExponentialOut:create(cc.MoveTo:create(0.5 , cc.p(oldPosX , ui:getPositionY()))) ); end -- 播放UI放大动画 function scaleAndMoveUI(ui , sourcePos) if not isUIAniEnabled() then return; end local oldScaleX = ui:getScaleX(); local oldScaleY = ui:getScaleY(); ui:setScaleX(0); ui:setScaleY(0); ui:runActions(cc.EaseExponentialOut:create(cc.ScaleTo:create(0.5 , oldScaleX , oldScaleY)) ); local oldX = ui:getPositionX(); local oldY = ui:getPositionY(); ui:setPosition(sourcePos); ui:runAction(cc.EaseExponentialOut:create(cc.MoveTo:create(0.5 , cc.p(oldX , oldY)))); end function beijingDate(formatStr, time) assert(formatStr and type(formatStr) == "string", "formatStr必须是字符串类型"); time = time or os.time(); local convert2Zone = 8; local timeZone = tonumber(os.date("%H", 0)); local d = tonumber(os.date("%d", 0)); if d ~= 1 then timeZone = timeZone - 24; end; if timeZone ~= convert2Zone then time = time - (timeZone - convert2Zone) * 3600; end return os.date(formatStr, time); end AutoCallback = { cbList = {}; id = nil; lastIndex = 1; }; -- 指定每天的某一时刻之前x秒运行一次回调 -- @func 回调函数 -- @hour 小时, 默认0点 -- @min 分, 默认0分 -- @sec 秒, 默认0秒 -- @beforeSec 提前秒数, 可为负数, 达到之后多少秒执行的效果, 默认0秒 -- @thresholdValue 误差阀值, 0为过期不候, 默认为0 -- @return 此回调的句柄, 用于反注册 function AutoCallback:runBeforeTime(func, hour, min, sec, beforeSec, thresholdValue) assert(func, "回调函数不能为空"); hour = hour or 0; min = min or 0; sec = sec or 0; beforeSec = beforeSec or 0; thresholdValue = thresholdValue or 0; local param = { func = func; hour = hour; min = min; sec = sec; thresholdValue = thresholdValue; beforeSec = beforeSec; bHasRun = false; } local handler = self.lastIndex; self.cbList[handler] = param; self.lastIndex = handler + 1; if not self.id then self.id = cc.Director:getInstance():getScheduler():scheduleScriptFunc(function() self:update();end , 1 , false); end return handler; end; -- 指定每天的某一时刻运行一次回调 -- @func 回调函数 -- @hour 小时, 默认0点 -- @min 分, 默认0分 -- @sec 秒, 默认0秒 -- @thresholdValue 误差阀值, 0为过期不候, 默认为0 -- @return 此回调的句柄, 用于反注册 function AutoCallback:runAtTime(func, hour, min, sec, thresholdValue) return self:runBeforeTime(func, hour, min, sec, 0, thresholdValue); end; function AutoCallback:update() local bHave = next(self.cbList); if not bHave then cc.Director:getInstance():getScheduler():unscheduleScriptEntry(self.id); self.id = nil; return; end local curServerTime = getCurServerTime(); --[[ local year = tonumber(os.date("%Y", curServerTime)); local month = tonumber(os.date("%m", curServerTime)); local day = tonumber(os.date("%d", curServerTime)); ]] local curServerTimeTable = BeijingTime.dateFunc(curServerTime); local year = curServerTimeTable.year; local month = curServerTimeTable.month; local day = curServerTimeTable.day; for k, v in pairs(self.cbList) do --local cbTime = os.time({year = year, month = month, day = day, hour = v.hour, min = v.min, sec = v.sec}); local cbTime = BeijingTime.timeFunc({year = year, month = month, day = day, hour = v.hour, min = v.min, sec = v.sec}); cbTime = cbTime - v.beforeSec; local interval = curServerTime - cbTime; if v.bHasRun then if interval < 0 or interval > v.thresholdValue then v.bHasRun = false; end else if interval >= 0 and interval <= v.thresholdValue then v.func(); v.bHasRun = true; end end; end end; function AutoCallback:removeCB(handler) assert(handler, "句柄不能为空"); self.cbList[handler] = nil; end; function logAppEvent(event) if plugins.analytics then plugins.analytics:logEvent(event , {}); end end -- 显示内嵌网页 function showWebView(url , width , height , posX , posY) print("showWebView" ,url , width , height , posX , posY); local platformPlugin = PluginManager:getInstance():loadPlugin("AnalyticsPlatform"); if platformPlugin then local visibleSize = cc.Director:getInstance():getVisibleSize(); local frameSize = cc.Director:getInstance():getOpenGLView():getFrameSize(); platformPlugin:call("openWebView" , url , width / visibleSize.width * frameSize.width , height / visibleSize.height * frameSize.height , posX / visibleSize.width * frameSize.width , posY / visibleSize.height * frameSize.height); end end -- 关闭内嵌网页 function closeWebView() local platformPlugin = PluginManager:getInstance():loadPlugin("AnalyticsPlatform"); if platformPlugin then platformPlugin:call("closeWebView"); end end -- 在目标窗口的旁边创建一个Tooltip,并返回创建好的tooltip节点 -- targetContainer 目标参考容器节点 -- targetNode 目标参考点的节点 -- tipsText tooltip的文本内容 -- tipsWidth 文本宽度,可选值 function createTooltip(targetContainer , targetNode , tipsText , tipsWidth, dir) local tips = loadUI("res/ui/ui_tongyong/tips.ui"); -- 定义一个函数给外部用 function tips:setText(text) tips.Items.HtmlCtrl_1:setText(text); end --tips.Items.HtmlCtrl_1:setTextAreaSize(cc.size(tipsWidth or 0 , 0)); tips:setText(tipsText); -- 强制计算一遍布局,以便让screenLayoutNode里的getContentSize能取到准确的位置 tips:requestDoLayout(); tips:doLayout(); -- targetContainer:addChild(tips) app.mainScene.showDialogNode:addChild(tips); -- 关注targetContainer退出的消息(兼容以前的版本) local function onNodeEvent(node , eventType) if eventType == cc.NodeEvent.OnExit then -- 移除悬浮框 if not tolua.isnull(tips) then tips:removeFromParent() end end end targetContainer:addNodeEventListener(onNodeEvent) -- 让他每帧执行布局计算 autoScreenLayoutNode(targetContainer , targetNode , tips, nil, dir); return tips; end -- 在目标窗口的旁边创建一个Tooltip,并返回创建好的tooltip节点 -- targetContainer 目标参考容器节点 -- targetNode 目标参考点的节点 -- tipsText tooltip的文本内容 -- tipsWidth 文本宽度,可选值 function createTooltip2(targetContainer , targetNode , tipsText , tipsWidth) local tips = loadUI("res/ui/ui_maoxian/mx_guanka_xinxi_texin_tips.ui"); -- 定义一个函数给外部用 function tips:setText(text) tips.Items.HtmlCtrl_3:setText(text); end --tips.Items.HtmlCtrl_1:setTextAreaSize(cc.size(tipsWidth or 0 , 0)); tips:setText(tipsText); -- 强制计算一遍布局,以便让screenLayoutNode里的getContentSize能取到准确的位置 tips:requestDoLayout(); tips:doLayout(); targetContainer:addChild(tips); -- 让他每帧执行布局计算 autoScreenLayoutNode(targetContainer , targetNode , tips); --[[ tips:playClip("fadeIn" , function() tips.FadeInEnd = true; if tips.DestroyRequest then tips:playClip("fadeOut" , function() tips:removeFromParent(); end ); end end ) local remove = tips.removeFromParent; function tips:removeFromParent() if self.FadeInEnd then self:playClip("fadeOut" , function()remove(tip);end ); else self.DestroyRequest = true; end end --]] return tips; end ---- 显示一个特别Tooltip,并自动销毁 function showDiffTooltip(tipsText,autoRemove) local tips = loadUI("res/ui/ui_tongyong/t_buzu_tishi.ui"); tips.Items.HtmlCtr_miaoshu:setText(tipsText); if(autoRemove)then local winSize = cc.Director:getInstance():getWinSize(); tips:setTranslation(winSize.width / 2 , winSize.height /3* 2 , 0); app.mainScene:addChild(tips , UIZOrder.TipsOrder); local t = app.config.GlobalClient.TASK_TOOL_TIPS_TIME.number/1000; local delayRemove = function() tips:runActions(cc.DelayTime:create(t) , function() tips:playClip("danchu",function() tips:removeFromParent(); end) end); end tips:playClip("danru",delayRemove); else app:showUI(tips,transparency or 204); tips:playClip("danru"); end return tips; end function showTooltipWithHtmlText(htmlText) local tips = loadUI("res/ui/ui_tongyong/texttips.ui"); tips.Items.Text:setText(htmlText); local director = cc.Director:getInstance(); app.mainScene:addChild(tips , UIZOrder.TipsOrder); local winSize = director:getWinSize(); tips:requestDoLayout(); tips:doLayout(); tips.Items.Layout_1:setAutoSize(false); tips.Items.Layout_1:setUpdateBackgroundWithChildren(false); local t = 1 local delayRemove = function() tips.Items.Layout_3:runActions(cc.DelayTime:create(t) , function() tips.Items.Layout_3:playClip("danchu",function() tips:removeFromParent(); end) end); end tips.Items.Layout_3:playClip("danru",delayRemove); return tips; end -- 显示一个Tooltip,并自动销毁 function showTooltip(tipsText) if (not tipsText) or tipsText == "" then return end if app.club_php.clubID and app.club_php.clubID ~= 0 then if app.club_php:getCestIsOpen(app.club_php.clubID) then tipsText = string.gsub(tipsText, "玩家", "选手") tipsText = string.gsub(tipsText, "成员", "选手") tipsText = string.gsub(tipsText, "亲友圈", "赛事") tipsText = string.gsub(tipsText, "茶馆", "赛事") end end logD("showTooltip() : " .. tipsText) --local model = '%s' local strTips = string.format("%s", tipsText) showTooltipWithHtmlText(strTips) end function showDropTip(tipsText) app:dropNotice(tipsText) end -- 显示对话框 -- btnCallbackOk : 点击确定时的回调 -- btnCallbackCancel : 点击取消时的回调,如果为空,则不显示取消按钮 -- fontSize : 显示内容字体大小 -- imgpath : 替换确定按钮图片路径 -- notice:备注 -- bShowKeFu: 显示联系客服的按钮 function showConfirmDialog(textContent, btnCallbackOk, btnCallbackCancel, fontSize, imgpath, notice, bShowKeFu) local view; local ui = loadUI("res/ui/ui_tongyong/ui_querenjiemian.ui"); ui:setAnchorPoint(cc.p(0.5, 0.5)); logD(textContent) -- 内容 ui.Items.Text_Content:setText(textContent or ""); if fontSize then ui.Items.Text_Content:setFontSize(fontSize) end -- 确认按钮 local function onButtonClickOk() playBtnEffect() if btnCallbackOk then btnCallbackOk(); end view:removeFromParent(); end ui.Items.Button_OK:registerClick(onButtonClickOk); if imgpath then ui.Items.Button_OK:loadTextureNormal(imgpath) end -- 取消按钮 local function onButtonClickCancel() playBtnEffect() if btnCallbackCancel then btnCallbackCancel(); end view:removeFromParent(); end if btnCallbackCancel then ui.Items.Button_Cancel:registerClick(onButtonClickCancel); else ui.Items.Button_Cancel:removeFromParent(); end if notice then ui.Items.Text_notice:setText(tostring(notice)) else ui.Items.Text_notice:setVisible(false) end if bShowKeFu then local function onButtonClickKeFu() playBtnEffect() local tt = { action = "collecturl.getDataByUid"; app_id = getAppId(); --应用id }; local phpUrl = getGlobalPhpUrl() httpPost(phpUrl, tt, function(status, response) if status == "successed" then local data = json.decode(response) if data.code == 200 and data.result ~= null then app.plugin:callUrl(data.result.kefuurl) end else local url = "http://cs.zxtycest.com/addons/kefu?gameid=47" app.plugin:callUrl(url) end end) end ui.Items.Button_KeFu:registerClick(onButtonClickKeFu); else ui.Items.Button_KeFu:setVisible(false) end view = app:showWaitUI(ui, nil, nil, 9999); return view; end -- 显示一个不可关闭的对话框 -- btnCallbackOk : 点击确定时的回调 -- btnCallbackCancel : 点击取消时的回调,如果为空,则不显示取消按钮 -- fontSize : 显示内容字体大小 -- imgpath : 替换确定按钮图片路径 -- notice:备注 -- bShowKeFu: 显示联系客服的按钮 function showConfirmDialogNotClose(textContent, btnCallbackOk, btnCallbackCancel, fontSize, imgpath, notice, bShowKeFu) local view; local ui = loadUI("res/ui/ui_tongyong/ui_querenjiemian.ui"); ui:setAnchorPoint(cc.p(0.5, 0.5)); logD(textContent) -- 内容 ui.Items.Text_Content:setText(textContent or ""); if fontSize then ui.Items.Text_Content:setFontSize(fontSize) end -- 确认按钮 local function onButtonClickOk() playBtnEffect() if btnCallbackOk then btnCallbackOk(); end end ui.Items.Button_OK:registerClick(onButtonClickOk); if imgpath then ui.Items.Button_OK:loadTextureNormal(imgpath) end ui.Items.Button_Cancel:removeFromParent(); if notice then ui.Items.Text_notice:setText(tostring(notice)) else ui.Items.Text_notice:setVisible(false) end if bShowKeFu then local function onButtonClickKeFu() playBtnEffect() local tt = { action = "collecturl.getDataByUid"; app_id = getAppId(); --应用id }; local phpUrl = getGlobalPhpUrl() httpPost(phpUrl, tt, function(status, response) if status == "successed" then local data = json.decode(response) if data.code == 200 and data.result ~= null then app.plugin:callUrl(data.result.kefuurl) end else local url = "http://cs.zxtycest.com/addons/kefu?gameid=47" app.plugin:callUrl(url) end end) end ui.Items.Button_KeFu:registerClick(onButtonClickKeFu); else ui.Items.Button_KeFu:setVisible(false) end view = app:showWaitUI(ui, nil, nil, 9999); ui:bindEvent(app, "showConfirmDialogNotClose",function () view:removeFromParent() end) return view; end -- 播放UI声音文件 -- soundFile 声音文件名 function playUISound(filePath , streamBuffer) if app.systemSetting.info.sound then --print("播放UI声音" , filePath , debug.traceback()); return cc.AudioController:getInstance():playSound(filePath , streamBuffer or false); end end -- 播放一个飞行ce -- @ceFile 技能文件 -- @targetPoints 飞行技能的源坐标点 -- @effectPoints 飞行技能的目标坐标点 -- @onHit 被击触发函数,默认可以不设置 function playFlyEffect(ceFile, parentNode, targetPoints, effectPoints, onHit, onEndFunc) --print("播放的技能资源名称:" .. ceFile) -- 载入技能 local ce = createCombineEffect(ceFile); if ce then --创建一个源模型、源点、目标模型、目标点 local params = createDefaultCombineEffectParams(); params.ParentNode = parentNode; params.CameraNode = app:getMainCameraNode(); params.SourcePos = effectPoints params.TargetPos = targetPoints local onStartEventFunc = nil if onHit then local hasOnHitEvent = false; for i , v in pairs(ce:getChildren()) do if v:getStartEventName() == "OnHit" then hasOnHitEvent = true; break; end end if not hasOnHitEvent then error("技能文件没配置OnHit事件:" , ceFile); end -- 绑定技能事件 local function onStartEvent(eventName , params) -- 播放被击效果 if eventName == "OnHit" then -- 只触发一次 if onHit then onHit(); end end end onStartEventFunc = onStartEvent; end -- 播放技能 local ceAction = cc.CombineEffectAction:createWithInstance(ce, params, onStartEventFunc, onEndFunc) parentNode:runAction(ceAction) return ceAction; -- 如果没配技能,则直接触发被击 else if onHit then onHit(); end end end function playModeCeEffect(ceFile, parentNode, sourceNode, targetPoints, effectPoints, onHit, onEndFunc) --print("播放的技能资源名称:" .. ceFile) -- 载入技能 local ce = createCombineEffect(ceFile); if ce then --创建一个源模型、源点、目标模型、目标点 local params = createDefaultCombineEffectParams(); params.ParentNode = parentNode; params.CameraNode = app:getMainCameraNode(); params.SourcePos = effectPoints params.TargetPos = targetPoints params.SourceNode = sourceNode; local onStartEventFunc = nil if onHit then local hasOnHitEvent = false; for i , v in pairs(ce:getChildren()) do if v:getStartEventName() == "OnHit" then hasOnHitEvent = true; break; end end if not hasOnHitEvent then error("技能文件没配置OnHit事件:" , ceFile); end -- 绑定技能事件 local function onStartEvent(eventName , params) -- 播放被击效果 if eventName == "OnHit" then -- 只触发一次 if onHit then onHit(); end end end onStartEventFunc = onStartEvent; end -- 播放技能 local ceAction = cc.CombineEffectAction:createWithInstance(ce, params, onStartEventFunc, onEndFunc) parentNode:runAction(ceAction) return ceAction; -- 如果没配技能,则直接触发被击 else if onHit then onHit(); end end end -- 回收所有资源 function collectMemory() print("start collectMemory"); -- 卸载光效池 clearEffectCache(); -- 卸载没有使用的纹理 cc.BundleCache:getInstance():removeUnusedScenes(); cc.MaterialCache:getInstance():removeUnusedMaterials(); cc.AudioCache:getInstance():removeUnusedAudioBuffers(); cc.SpriteFrameCache:getInstance():unloadUnusedSpriteFrames(10); cc.Director:getInstance():getTextureCache():removeUnusedTextures(); print("lua回收前的内存", collectgarbage("count")) collectgarbage("collect") print("lua回收后的内存", collectgarbage("count")) print("finish collectMemory"); end -- 是否安卓测试包 function isAndroidTestVersion() return isAndroidPlatform() and app.config.Setting.LanguageType == 1; end -- 自动创建xml文件 function newLocalXmlFile(fileName) local tempXmlPath = cc.FileUtils:getInstance():getWritablePath() .. fileName .. ".xml"; local file = io.open(tempXmlPath,"rb"); local tempXmlData; if file then local data = file:read("*a"); if data then tempXmlData = xml.eval(data); end file:close(); end if tempXmlData == nil then tempXmlData = xml.new(fileName); end return tempXmlData, tempXmlPath; end -- 刪除XML文件 function deleteXmlFile(fileName) local tempXmlPath = cc.FileUtils:getInstance():getWritablePath() .. fileName .. ".xml"; local removeOver = os.remove(tempXmlPath) return removeOver end local UserConfigFileName = "UserConfig" local xmlData, xmlPath = newLocalXmlFile(UserConfigFileName); local XmlFileTable = {} function initLocalXmlFile(fileName) if not XmlFileTable[fileName] then local xmlMsgData, xmlMsgPath = newLocalXmlFile(fileName); XmlFileTable[fileName] = {}; XmlFileTable[fileName].data = xmlMsgData; XmlFileTable[fileName].path = xmlMsgPath; end end -- xml 以xml格式保存玩家数据 function saveUserXml(eName, eValue, fileName) if not fileName then fileName = UserConfigFileName; end if not XmlFileTable[fileName] then initLocalXmlFile(fileName); end local fileData = XmlFileTable[fileName]; fileData.data[eName] = eValue; xml.save(fileData.data , fileData.path); end -- xml 载入玩家xml数据 function loadUserXml(eName, fileName) if not fileName then fileName = UserConfigFileName; end if not XmlFileTable[fileName] then initLocalXmlFile(fileName); end local fileData = XmlFileTable[fileName]; return fileData.data[eName] or ""; end function escape(s) return (string.gsub(s, "([^A-Za-z0-9_])", function(c) return string.format("%%%02x", string.byte(c)) end)) end function unescape(s) return (string.gsub(s, "%%(%x%x)", function(hex) return string.char(tonumber(hex, 16)) end)) end local PasswordKey = "dfdsfsdf#*%&^*&#$^(*&@#^%*(&@#%^sdfdsfdsfwezhangsandfsdf"; function saveUserInfo(key,value) saveUserXml(tostring(key) , escape(md5.crypt(tostring(value) , PasswordKey))); end function loadUserInfo(key) local value = loadUserXml(tostring(key)); if value ~= "" then return md5.decrypt(unescape(value) , PasswordKey); else return value; end end local function checkUserConfig() local key = "20190712" local value = loadUserInfo(key) if not value or value == "" then XmlFileTable[UserConfigFileName] = nil deleteXmlFile(UserConfigFileName) initLocalXmlFile(UserConfigFileName); saveUserInfo(key, 1); end end if not EditorMode then checkUserConfig(); end -- 是否正在加载场景 LoadingScene = false; -- 是否在解析xml过程中立即加载资源 function isImmediatelyLoad() return EditorMode or LoadingScene; end function tableToPostUrl(tb) local postUrl = ""; for k,v in pairs(tb) do postUrl = postUrl.."&"..k.."="..v; end postUrl = string.sub(postUrl,2,-1); print("post url is :",postUrl); return postUrl; end -- 显示内嵌网页 function showWebView(url , width , height , posX , posY) print("showWebView" ,url , width , height , posX , posY); if PluginDevice then local visibleSize = cc.Director:getInstance():getVisibleSize(); local frameSize = cc.Director:getInstance():getOpenGLView():getFrameSize(); PluginDevice:callVoid("openWebView" , url , width / visibleSize.width * frameSize.width , height / visibleSize.height * frameSize.height , posX / visibleSize.width * frameSize.width , posY / visibleSize.height * frameSize.height); end end -- 关闭内嵌网页 function closeWebView() if PluginDevice then PluginDevice:callVoid("closeWebView"); end end -- Node的每帧更新回调 -- stopCondition如果为值则代表最大时间,到达时间就取消定时器 -- stopCondition如果为函数则为停止定时器的回调函数 function scheduleUpdateScriptFunc(targetNode, stopCondition, callback) if not targetNode then return end local sum = 0 local function onFrameUpdate(t) sum = sum + t if type(stopCondition) == "function" then local result = stopCondition() if result then -- 停止定时器 targetNode:unscheduleUpdate() end else if sum >= stopCondition then -- 停止定时器 targetNode:unscheduleUpdate() end end callback(sum, t) end targetNode:scheduleUpdateWithPriorityLua(onFrameUpdate, 0) end -- 截取指定节点的字符串,中文算两个、英文算一个 function substr(str, endPos) local pos = 0; local size = #str; local nextIndex = 1; local len = 0; for i = 1, size do if i >= nextIndex then local curByte = string.byte(str, i) local byteCount = 1; if curByte>0 and curByte<=127 then byteCount = 1 elseif curByte>=192 and curByte<223 then byteCount = 2 elseif curByte>=224 and curByte<239 then byteCount = 3 elseif curByte>=240 and curByte<=247 then byteCount = 4 end nextIndex = i + byteCount; if len >= endPos then break; end if byteCount == 1 then len = len + 1; else len = len + 2; end pos = pos + byteCount; end end return string.sub(str, 1, pos); end -- 获取名字的长度,一个中文算2个长度单位,一个英文字符算一个长度单位 function getStringLen(str) assert(type(str) == "string") local len = 0; local size = #str; local nextIndex = 1; for i = 1, size do if i >= nextIndex then local curByte = string.byte(str, i) local byteCount = 1; if curByte>0 and curByte<=127 then byteCount = 1 elseif curByte>=192 and curByte<223 then byteCount = 2 elseif curByte>=224 and curByte<239 then byteCount = 3 elseif curByte>=240 and curByte<=247 then byteCount = 4 end nextIndex = i + byteCount; if byteCount == 1 then len = len + 1; else len = len + 2; end end end return len; end -- 系统自带的base64编码和解码好像有问题,在某些设备上可能出现缺少最后一个字符的情况 -- 所以自己写一个 function ToBase64(source_str) local b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' local s64 = '' local str = source_str while #str > 0 do local bytes_num = 0 local buf = 0 for byte_cnt=1,3 do buf = (buf * 256) if #str > 0 then buf = buf + string.byte(str, 1, 1) str = string.sub(str, 2) bytes_num = bytes_num + 1 end end for group_cnt=1,(bytes_num+1) do b64char = math.fmod(math.floor(buf/262144), 64) + 1 s64 = s64 .. string.sub(b64chars, b64char, b64char) buf = buf * 64 end for fill_cnt=1,(3-bytes_num) do s64 = s64 .. '=' end end return s64 end function FromBase64(str64) local b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' local temp={} for i=1,64 do temp[string.sub(b64chars,i,i)] = i end temp['=']=0 local str="" for i=1,#str64,4 do if i>#str64 then break end local data = 0 local str_count=0 for j=0,3 do local str1=string.sub(str64,i+j,i+j) if not temp[str1] then return end if temp[str1] < 1 then data = data * 64 else data = data * 64 + temp[str1]-1 str_count = str_count + 1 end end for j=16,0,-8 do if str_count > 0 then str=str..string.char(math.floor(data/math.pow(2,j))) data=math.mod(data,math.pow(2,j)) str_count = str_count - 1 end end end return str end local clubXmlData, clubXmlPath = newLocalXmlFile("ClubConfig"); local XmlFileClubTable = { ["ClubConfig"] = { data = clubXmlData, path = clubXmlPath }, } -- xml 以xml格式保存茶馆数据 function saveClubXml(eName, eValue, fileName) if not fileName then fileName = "ClubConfig"; else initLocalXmlFile(fileName); end local fileData = XmlFileClubTable[fileName]; fileData.data[eName] = eValue; xml.save(fileData.data , fileData.path); end -- xml 载入茶馆xml数据 function loadClubXml(eName, fileName) if not fileName then fileName = "ClubConfig"; else initLocalXmlFile(fileName); end local fileData = XmlFileClubTable[fileName]; return fileData.data[eName] or ""; end local clubPasswordKey = "dfdsfsdf#*%&^*&#$^(*&@#^%*(&@#%^sdfdsfdsfwefsdfsdf"; function saveClubInfo(key,value) saveClubXml(tostring(key) , escape(md5.crypt(tostring(value) , clubPasswordKey))); end function loadClubInfo(key) local value = loadClubXml(tostring(key)); if value ~= "" then return md5.decrypt(unescape(value) , clubPasswordKey); else return value; end end