UnityFS5.x.x2022.3.58f1$pA[CQ$@$CAB-3f1f8f4d805b080506d56e62e209bde0>$? 2022.3.58f11Hk]jd0X O7H11@QjH11@Qj_FZWȴ-OBIr:7H11@Qj 11@yj  .$ - 11  jH11@Qj9 CPy\ .$9bCPy\ .$nH 11!@"Qj#$11%&Hj'11(@)Qj*L+@,-.11/0j1H2113@4Qj5H 6117@8Qj9AssetBundlem_PreloadTablem_FileIDm_PathIDm_ContainerAssetInfopreloadIndexpreloadSizeassetm_MainAssetm_RuntimeCompatibilitym_AssetBundleNamem_Dependenciesm_IsStreamedSceneAssetBundlem_ExplicitDataLayoutm_PathFlagsm_SceneHashes7j"|$G/(0(ОX*85HA)=NCݰ}*u(Ld&`svL@^h{ Kbˊ|P ;8؇P e2Rǃ(N(aYVZptĦ9$ؚ@׹ T2q_б .BARDZ*N{Oq7AҌ!\+} O$Zvf0Up(:d< ^n|4Kkhq?4"&?ݟ 6}: 3KB@Քċ{YW0GE*ְ)h$ <ΞA J<yU+` X$t{D-IVWx2AB8884V6 M m_ GH9^u+q}X|M$HT.Tl h%) ҿBX Fvo`eznr/,[t| DZrϔH)<nߔ.K1Ǽx1D^З`H4͉:;IMۯp!5*XIFET8$ sa` 8xRim| PkW$L~:nOL0P MJ,;< pMlK]<єdKMAP$7ud~kHqSc &^=-]x*ƫv`/X 0œȝ:p9 @(> 3rHq _ύ͟~4/DiElsLxL ^J9 ȣ8黀V aNoL, ͤH(xa"b-vtxx\}Ck¥2|`(YBX ZlӜG\\b8vYyq(cBsM@g,3qUWg4,ih(9J"T(<J*%ζh`:ELǴC^(PCXE6x ی<M2ȸ wZOظ8Bnܸ/lg0#ȡܹw,5^k xYdμ2h%8D6 Hӵ}"˾7x"psXjtcp\8k%gлh!gDpyK H POrY%O $4"  iSiP(,|0Xt sT;_ 06hǀ# 5_? X ` [Hf | `](p [4Br x Ƕ nH| HtJzȋ C  T   o\Jƨ ĘQXnp3 $+oǘ4 $,J!XrE!` \t ~ i 1(m H +h*opv pQ~2v pNPy @V Hb\h 9p, _܍Oʀ  !Jּ t,Bˀ ZJ# @"2@' pET(̰B @C C kPD (@\'x] w͐a >O-`d xiu2ae Ui΀f )(u(j f8 (v y[D! SFxa& 6ysI ȣ $;g7 Cĭrd" 38% :' Rԝ; 06<]hT eSdxW 6 iHX 7Zn TC e % $" \첓.H T]נ P >bY^ VoRL-ِ hqt@=v8/h  %H l^ȀE@bZ~x2m/9̛xFXx^JH\0gpl/u$j V~}`ku }~v!zz  a΀`:J G80iΐh&cq)  Q#w(ma@%Ѣ} Xh/#l 0!f}s6& LRm(.0%~SXh7 %V~@HY߾Q!Yp TG?uPcTMIi~Fj 9hm|v<!,ԹD:1,?\]W$D!+ k ɇ7UXּ먦C c98EX8j]N<kqi@cnzvhcC}&)?z6`tu`H~3^6";z9 \,<؏oL?}5~.`4J<(D$Ki=rP 39#XH`P0Bh-QODͿ1Wo0^T ஐչHkL-=;fؘh! L@ >ojrg5 "|a=P@cF< *)gfE 0a  Hr @%ЁŖ (;Y'Q |#t  'sk 1* es-4TXO*=h!H"L7\'0\Y*??{gB|gPsWG kT$LXVh:0N\8Z `zkΓ3b?V#8d4Zpf$'*jKiyq0~W}Nv#8$( BE`K(HFP_t{P3v0.oXʮ/̃<<9 X:tNx|WĀ祵Y=ֈ \:/HR(/ @8TPP.H BRқhX R_+i6'fMeЙ1 ҅{p͎`n H,LS%N x9uE㹲 @tF'!WK`!`wԈ|"8לjt"0#PG60-$sq/$&q^qro$X.P"H{D$P|-\Tx$(~<5]QQ%hws'hѦ)O7_N+4 |+60şM6,7%B7KK-8]Xus .a9w'.pv2ֻd$.(0Ud.L _`K&/D#*֋r/HP:ca𙭕/zH|0xL90ȓTujH0 w\00\k08T ؔ05ЏK0xp!S0N2-\֔&2h=>K>x}7:B>(":~N>@%N fL? X>5T?W}x?x/@`0 (ϬQZ@\ TV(?-B  c%V%B&~n']C8 ccC*@Ó% 'mC2{S9wD:"+x\E\0 hGEg HqNEu=݁ExRvPaFh}*گ"GXDaGp(TMG@8O\HxhQI@5;Jwu4JLNKhf'KDK5L@t iJULK,aL$z,9mMQMNfֽ֙N( >DOoO83Tumio$Q40i+b3Q4PoR= PDkSJASK ݁$ShW|æSX{,vS]9En7T@`\|ЊҼTbLH)lUiҢ"Vx$8%Vt$DVT7ZxhV*,msW$? XXls@4Y (hk9Y`# ( _^0Z,  xZ? |[\ 4M[c ,>[e l( a\k Thdni\s ~/ \x 폌 ] %@&qG]!yH]*!:!aa].!+]H!,_`z^M!h t3?/^`Y!@v1[^Y!tZ@X^xZ!XoNE`_\!@rߜ#`x!0HOaz!4Ka|!@TfaP!lTvśb!\ bh!@蚂$d!2Qy~$>e@!|lkf@! 0r07gP!?7/5h!kjRh!h)8Lղh!/h!X3bti!&J7G9k`#"j#@k@$"*BY}\kO"&*=dkO"bq&khR"T[ SkR"lskS";atkT"l}{q"P[Qr8"0Ta'rh"|ALFs"cqs"{ȎsP"q% l(t"Sb_t" ^t"Iu@#48$wx##` ܬ ؟w,#a'GxX-#<uxj#<5-F6yxP#<KƁjx#$+y(#u0Q|y# Dy#%!aM"z#AߏFzP#\v9 {# 17S|@#Ddb S}#H =/~#KVMgr ---@class KVMgr:DBMgr local KVMgr = defClassStatic("KVMgr", DBMgr) function KVMgr:init(aes_key) KVMgr.super.init(self, false) self.aes_key = aes_key self.logTag = "KVMgr" self.userDBName = "user.txt" self.roleDBName = "role.txt" self.deviceDBName = "device.txt" self.dbCls = KVDatabase Msg.add(Msg.APP_INIT_FINISH, function (id) printVerbose(self.logTag, "onmsg APP_INIT_FINISH id:%s", id) self:openCurDeviceDB() end) end function KVMgr:getKey() return self.aes_key endCosLuaTemporaryCredential ( ---@class CosLuaTemporaryCredential:LuaStaticClass local CosLuaTemporaryCredential = defClassStatic("CosLuaTemporaryCredential") local LOGTAG = "CosLuaTemporaryCredential" local CosSDKAPI = CS.iHuman.UnitySz.Framework.COS.CosSDKAPI local function generateParameterKey(sourceKey) if sourceKey == nil or sourceKey == "0" or sourceKey == "-1" then return "" end local key = sourceKey while #key < 32 do key = key .. sourceKey end return string.sub(key, 1, 32) end ---@param appid string ---@param deviceid string ---@param buildEnv string ---@param oversea boolean ---@param getTimeFunc fun():integer ---@param onCredentialFetched fun(result:boolean, data:{errorCode:integer, errorMsg:string, service_type:CosLuaServiceType}) function CosLuaTemporaryCredential:init(appid, buildEnv, oversea, deviceid, getTimeFunc, onCredentialFetched) self.appid = appid self.key = generateParameterKey(appid) printVerbose(LOGTAG, "self.key:%s", self.key) self.buildEnv = buildEnv self.deviceid = deviceid self.overea = oversea self.getTimeFunc = getTimeFunc self.onCredentialFetched = onCredentialFetched or function () end self:clear() end function CosLuaTemporaryCredential:setUserInfo(uid, utoken, showUid) printInfo(LOGTAG, "setUserInfo uid:%s, utoken:%s, showUid:%s", uid, utoken, showUid) self.uid = uid self.utoken = utoken self.showUid = showUid end function CosLuaTemporaryCredential:clearUserInfo() printInfo(LOGTAG, "clearUserInfo") self:clear() end ---comment ---@param service_type CosLuaServiceType ---@return boolean function CosLuaTemporaryCredential:hasValidCredential(service_type) local cosCredential = self.cosCredentialDict[service_type] if cosCredential == nil then return false end local curTime = self.getTimeFunc() if curTime > cosCredential.expired_time then return false end return true end ---@param service_type CosLuaServiceType ---@return CosLuaCredentialBean|nil function CosLuaTemporaryCredential:getCredential(service_type) if not self:hasValidCredential(service_type) then return nil end return self.cosCredentialDict[service_type] end function CosLuaTemporaryCredential:_getApiUrl(service_type) local avatarConfig = { path = "avatar/open/v2/get_credential", host = { staging = "https://staging-api.ihumand.com/", production = "https://avatars.ihuman.com/", production_oversea = "https://avatars.bekids.com/" } } local normalConfig = { path = "cos/gateway/open/v5/get_credential", host = { staging = "https://staging-api.ihumand.com/", production = "https://cos.ihuman.com/", production_oversea = "https://cos.bekids.com/" } } local configs = { [CosLuaServiceType.UGC] = normalConfig, [CosLuaServiceType.AVATAR] = avatarConfig, } local config = service_type and configs[service_type] if config == nil then return end local key = (self.buildEnv ~= "production") and "staging" or (self.overea and "production_oversea" or "production") local host = config.host[key] return string.format("%s%s", host, config.path) end ---@param service_type CosLuaServiceType function CosLuaTemporaryCredential:fetchCredential(service_type) if not self.uid then self.onCredentialFetched(false, {errorCode = -1, errorMsg = "uid is empty", service_type = service_type}) return end local api = self:_getApiUrl(service_type) if not api then self.onCredentialFetched(false, {errorCode = -1, errorMsg = string.format("service_type:%s not right", tostring(service_type)), service_type = service_type}) return end local service_type_str = json.encode({tostring(service_type)}) local data = { appid = tostring(self.appid), service_type = service_type ~= CosLuaServiceType.AVATAR and service_type_str or nil, uid = tostring(self.uid), show_uid = self.showUid, timestamp = tostring(self.getTimeFunc()), deviceid = self.deviceid, utoken = self.utoken, } local function onFetchFinish(result, data, rawStr) if not result then self.onCredentialFetched(false, {errorCode = -2, errorMsg = string.format("fetchCredential code:%d, error:%s", data.errorCode, data.errorMsg)}) return end local cosCredential = CosLuaCredentialBean.new() cosCredential:initWithConfig(data) self.cosCredentialDict[service_type] = cosCredential -- 创建 CosLuaXmlServer local secretId = CS.AES256.Decrypt(cosCredential.left, self.key) local secretKey = CS.AES256.Decrypt(cosCredential.right, self.key) local cosLuaXmlServer = CosSDKAPI.GetCosXml(cosCredential.region, secretId, secretKey, cosCredential.session_token, cosCredential.start_time, cosCredential.expired_time, cosCredential.protocol == "https") self.cosXmlServerDict[service_type] = cosLuaXmlServer self.onCredentialFetched(true, {service_type = service_type}) end local mock = false local mock_data = { [CosLuaServiceType.AVATAR] = { allow_prefix = "31/U5809619974/", base_url = "https://avatar-1253822818.cos.ap-beijing.myqcloud.com", bucket_name = "avatar-1253822818", cos_appid = "1253822818", domain = "myqcloud.com", expired_time = "1705903790", left = "g2zCMncoaOJDuPOptuf9CaFBaQhJpxVRApsf95JqypA077MkXWEwzI4xHIKQqtiM4A8aOshIoqI0hkgl5P9IWoF5iVn6nu95kFGc+MaJ/+E=", max_file_size = "5", protocol = "https", region = "ap-beijing", right = "aOCwq5PFZBAPW130BgZkRnE/yzvInUFBqV+xioh7tsQy8Adfx0LuSJ3uMtK7gVxp", server_time_zone = "GMT+08:00", session_token = "msxTNe5pkeT5TeoiNI7g6UDFplgRkgba76413bacb69e093617eac421be173fb4CS9BSiBG_qcFL3OMXnzEWQKA1XfBjt4x743e3gcd0KJkKRXW8eZdKeS2BAEQWR_B16msAH4Nnz-dIBlQC5g8gG57hTvv5J1RHGNZlL3oUoAysw34T-8wWbkODwE7WWgysx-uhDj5g2PH3_sqV4a_Rr48_4rz2ILXb_mHvdd3cXkot44hVHwLUwPSo6fiNFWvHfUr82PXoq-kqLy7qTHP3Lg3YmYWPUeolWLbxdttDbdujlDp3CJi0clzEabuj3tR_iHib-y6ux5vCpLXvj0UrkoljN2veGtiP8Ia7rVzie_3cx-myU8g_MNQPKMDHsIdkoVRUo3w65Ru77g2mM6-Rlrbdj8U89LUZKwbUB8YrGrymHvoAcLsNsxPvcM8nAaaQk7V6joFM4RGE_7LCfXNGGWC5j5q1mNhMMswgKgRnlvn9JZiFrkNI_Y5GF2uBpOVm7eSShAd7ANLIBkDOLLKoHHml9WETJRIcbcVgGBbtpkbKbARV7o9bdnbTHv5RTDAa4sTdwjtftoCbr_cLnxnDaK5McFLwIPTaxUO00OTjT2_902D83wGe_tdAY20hZCNzI53OvEQ7TNMEYqJ9rnjja6Vm_yKUXIjmVMswXXntnOtKJhPWOMxFMhqyjW5aWg1Ic7yJE6hJnYLl0VNGWtvZmIUAZ2H_tlsnUXUnecnLfMlBx16Ll2tT-aTPAhxG79zm1ew_kdvbmcOmXn5LAzQorAzRclJMY2pz1M1_ORzf8CWNuaCZ6TxR0cy_jnSwQbz", start_time = "1705896590", storage_type = "0" }, [CosLuaServiceType.UGC] = { allow_prefix = "U5809619974/", base_url = "https://31-ugc-1253822818.cos.ap-beijing.myqcloud.com", bucket_name = "31-ugc-1253822818", cos_appid = "1253822818", domain = "myqcloud.com", expired_time = "1706001131", left = "S6sRa3cvJk6JBPLeN5L+W9wqNhSuClTeDFsUNw+bMDcH5xpLlKDxq4h9kKrJ0DFmt+0qdcNH8DACKPADcvOrHGFPvyKFhPETrMBKZnQfSJg=", max_file_size = "100", protocol = "https", region = "ap-beijing", right = "Nng4d/FBAjq71Xkh64xdHuK5f7s0rj5YNCbHWgkx0f+CgQ7z+aa1Bdca2uKznzxN", server_time_zone = "GMT+08:00", service_type = "2", session_token = "msxTNe5pkeT5TeoiNI7g6UDFplgRkgbaa1718188bec43e8c5adb351d9981db18CS9BSiBG_qcFL3OMXnzEWR1tzDu3LTIRcDQMBnNmEGKWDrUixsiVYkJaepGPnXKNB-nNfNvNBRsnaoqi-arqK7CKNGL2YDb1TDHmLQGcgzjaURDgNhKZuG8kNVqVKXyyEmSNlRojYbG7PmofWRdLIzWdZyCnALFAMucf-z0bhwhXr-a2kXidiYDGBkrPuAbWSiQLusZ5f1vLTz4yUlWRBpB3_KwF2kuSGwKJf0dPinHafmlF7RP2zWQU9AVHWQFM4U71lZYGxmb3LPgYWaGkHqHwhN5Bu9Z0DYs1eT-rgCZq9D-WoZUarQK6gwAxELNEZU48N8V73wdrBcM8rDkNT_7vFwVa_vaSW9SWrtJBsdr6wpFdMEcbrifT1u6a8jJpmRj_HuXGqVT8zUffSUJwb8ygH8s3d-4tT7bv66USZK-JE2jIfma0LHtAY_pwdWSp_cF0LNeC21uLlypJq89UwInkEecqwQSHWjoakF7pXaMJC0cufvZVVFqtrZAWt1CM3ZtuGtUIrfRrVP32x3cm3TZOCJmmUX6onXcklrZecNuxIheSumKzxJ31UI1jFg-Ci54DDKxvfI4mL_N8WG8wAgDD4h4E1QEcY6xRIwdaZ1vP-EQkYLPca7GLLFon_h940zZzdckBB7Qw2V254UnFISKFKE1tFcKF2qJdf2m3_fyMoRr_x4naxU2YcpQgnDF5BwLKJaHWzinM7AkEX_HEWgzIqbtWLgNR2TTroVFRXhC7Ukhl7zT73qBZGRKq792_GCE7C0sKeU1OTJdUt1yvWxEi4zXTnef7dgNh7LM8f3V9M7nXxftCuGbKXEH_Fsab2CoDTr7Xz_PMyNSJsG8bO7yjCXb5YHurNZc1CAUrKbI", start_time = "1705893131", storage_type = "0" } } if mock then onFetchFinish(true, mock_data[service_type]) return end HttpUtil:fetchApiWithSign(api, "post", data, function (result, data, rawStr) onFetchFinish(result, result and data[1] or data, rawStr) end, nil, "sdk") end function CosLuaTemporaryCredential:clear() self.uid = nil self.utoken = nil self.showUid = nil self.cosXmlServerDict = {} --key:service_type, value CosLuaXmlServer ---@type table self.cosCredentialDict = {} --key:service_type, value CosLuaCredentialBean end ---comment ---@param service_type CosLuaServiceType function CosLuaTemporaryCredential:getCosXmlServer(service_type) return self.cosXmlServerDict[service_type] end function CosLuaTemporaryCredential:getPrepareUrl(service_type) local cosCredential = self.cosCredentialDict[service_type] local base_url = cosCredential.base_url local prefix = cosCredential.allow_prefix return string.format("%s/%s", base_url, prefix), "/"..prefix end ---comment ---@param cosUrl string ---@param service_type any ---@return string|nil function CosLuaTemporaryCredential:getRelativePath(cosUrl, service_type) local cosCredential = self.cosCredentialDict[service_type] local base_url = cosCredential.base_url local base_url_with_slash = base_url .. "/" if cosUrl:sub(1, #base_url_with_slash) == base_url_with_slash then return cosUrl:sub(#base_url_with_slash + 1) else return nil end end function CosLuaTemporaryCredential:exit() self:clear() end FishermanInfolocal FishermanInfo = defClass("FishermanInfo") function FishermanInfo:ctor(cfgId) self.id = cfgId self:init() end function FishermanInfo:init() self:initData() self:initStatus() end function FishermanInfo:initData() end function FishermanInfo:initStatus() -- 当前等级 self.grade = 1 -- 等级配置 self.gradeConfig = FishingGradeCfgParse:getFishingGradeCfgByGrade(self.grade) -- 当前能量 self.energy = self.gradeConfig.maxEnergy / 2 -- 等级对应能量消耗 self.energyConsumption = self.gradeConfig.energyConsumption -- 最大能量 self.maxEnergy = self.gradeConfig.maxEnergy -- 等级对应速度 self.moveSpeed = self.gradeConfig.speed -- 当前无敌时间 self.invincibleTime = 0 -- 是否在无敌状态 self.isInvincible = false -- 是否有护盾 self.isShield = false -- 是否有双倍经验 self.isDoubleEnergy = false -- 双倍经验时间 self.doubleEnergyTime = 0 end -- 获取速度 function FishermanInfo:getMoveSpeed() return self.moveSpeed end -- 获取当前等级 function FishermanInfo:getGrade() return self.grade end -- 获取当前能量 function FishermanInfo:getEnergy() return self.energy end -- 获取最大能量 function FishermanInfo:getMaxEnergy() return self.maxEnergy end -- 获取当前无敌时间 function FishermanInfo:getInvincibleTime() return self.invincibleTime end -- 减少当前无敌时间 function FishermanInfo:reduceInvincibleTime(deltaTime) self.invincibleTime = self.invincibleTime - deltaTime if self.invincibleTime <= 0 then self.invincibleTime = 0 -- self.isInvincible = false end end -- 设置无敌状态 function FishermanInfo:setInvincible(isInvincible, timeLimit) self.isInvincible = isInvincible if isInvincible then self.invincibleTime = timeLimit else self.invincibleTime = 0 end end -- 获取无敌状态 function FishermanInfo:getInvincible() return self.isInvincible end -- 捕食 function FishermanInfo:eat(energy) if self.isDoubleEnergy then self.energy = self.energy + energy * 2 else self.energy = self.energy + energy end if (self.energy >= self.maxEnergy) then self.energy = self.maxEnergy end return self.energy end -- 受伤 function FishermanInfo:hurt() self.energy = self.energy - (self.maxEnergy / 3) end -- 减少能量 function FishermanInfo:reduceEnergy() --self.energyConsumption = 50 -- test if self.energyConsumption then self.energy = self.energy - self.energyConsumption if self.energy > self.maxEnergy then self.energy = self.maxEnergy end end return self.energy end function FishermanInfo:getGrade() return self.grade end -- 能否升级 function FishermanInfo:canUpgrade() return self.energy >= self.maxEnergy and FishingConst.MaxGrade > self.grade end -- 升级 function FishermanInfo:upgrade() if not self:canUpgrade() then return false end self.grade = self.grade + 1 -- 等级配置 self.gradeConfig = FishingGradeCfgParse:getFishingGradeCfgByGrade(self.grade) -- 当前能量 self.energy = self.gradeConfig.maxEnergy / 2 -- 等级对应能量消耗 self.energyConsumption = self.gradeConfig.energyConsumption -- 最大能量 self.maxEnergy = self.gradeConfig.maxEnergy -- 等级对应速度 self.moveSpeed = self.gradeConfig.speed -- 升级后增加生命值和无敌时间 --self.invincibleTime = math.min(self.invincibleTime + 1, self.invincibleTimeLimit) return true end -- 复活 function FishermanInfo:revive() self.energy = self.maxEnergy / 2 end -- 是否由护盾 function FishermanInfo:getShield() return self.isShield end -- 设置护盾状态 function FishermanInfo:setShield(isShield) self.isShield = isShield end -- 是否有双倍经验 function FishermanInfo:getDoubleEnergy() return self.isDoubleEnergy end -- 设置双倍经验状态 function FishermanInfo:setDoubleEnergy(isDouble, doubleTime) self.isDoubleEnergy = isDouble if isDouble then self.doubleEnergyTime = doubleTime else self.doubleEnergyTime = 0 end end -- 减少双倍经验时间 function FishermanInfo:reduceDoubleEnergyTime(deltaTime) self.doubleEnergyTime = self.doubleEnergyTime - deltaTime if self.doubleEnergyTime <= 0 then self.doubleEnergyTime = 0 --self.isDoubleEnergy = false end end -- 获取双倍经验时间 function FishermanInfo:getDoubleEnergyTime() return self.doubleEnergyTime end return FishermanInfo gachaRewardCfg--[[ from file:扭蛋奖励.xlsx --]] local gachaRewardCfg = { [1] = { id = 1, rewardItemType = 1, rewardItemId = -1, minCount = 1, maxCount = 3, weight = 10, }, [2] = { id = 2, rewardItemType = 1, rewardItemId = -1, minCount = 3, maxCount = 5, weight = 20, }, [3] = { id = 3, rewardItemType = 1, rewardItemId = -1, minCount = 5, maxCount = 10, weight = 10, }, [4] = { id = 4, rewardItemType = 2, rewardItemId = -1, minCount = 10, maxCount = 20, weight = 10, }, [5] = { id = 5, rewardItemType = 2, rewardItemId = -1, minCount = 20, maxCount = 30, weight = 10, }, [6] = { id = 6, rewardItemType = 2, rewardItemId = -1, minCount = 40, maxCount = 60, weight = 10, }, [7] = { id = 7, rewardItemType = 3, rewardItemId = 600001, minCount = 1, maxCount = 1, weight = 15, }, [8] = { id = 8, rewardItemType = 3, rewardItemId = 600002, minCount = 1, maxCount = 1, weight = 5, }, [9] = { id = 9, rewardItemType = 3, rewardItemId = 600003, minCount = 1, maxCount = 1, weight = 5, }, [10] = { id = 10, rewardItemType = 3, rewardItemId = 600004, minCount = 1, maxCount = 1, weight = 5, }, [11] = { id = 11, rewardItemType = 3, rewardItemId = 600005, minCount = 1, maxCount = 1, weight = 5, }, } return gachaRewardCfg FishingReviveUIy-- 捕鱼复活界面 local FishingReviveUI = defClass("FishingReviveUI", UILayer) local TMProUGUI = CS.TMPro.TextMeshProUGUI -- 构造函数 function FishingReviveUI:ctor(isEnergyDepletion) UILayer.ctor(self) self.R = ResLoader.loadResLink("modules/ui/fishingui/fishinguireslink") self.isEnergyDepletion = isEnergyDepletion or false -- 是否能量耗尽 end -- 当页面加载 function FishingReviveUI:onLoad() self.ui = GameObject.Instantiate(self.R.fishing_revive_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end -- 初始化界面 function FishingReviveUI:initUI() self.close_btn = self.ui:Seek("close_btn") self.title = self.ui:Seek("title") self.energy_die = self.ui:Seek("image_energy") self.die = self.ui:Seek("image_die") self.revive_video_btn = self.ui:Seek("video_btn") self.revive_share_btn = self.ui:Seek("share_btn") util.ugui.addClickEvent(self.close_btn, function() self:gameFailed() end) util.ugui.addClickEvent(self.revive_video_btn, function() self:gameReviveVideo() end) util.ugui.addClickEvent(self.revive_share_btn, function() self:gameReviveShare() end) end -- 展示界面 function FishingReviveUI:showUI() if self.isEnergyDepletion then self.title[TMProUGUI].text = "能量耗尽" else self.title[TMProUGUI].text = "复活" end self.energy_die:SetActive(self.isEnergyDepletion) self.die:SetActive(not self.isEnergyDepletion) self.revive_video_btn:SetActive(FishingGameMgr:getReviveIsVideo()) self.revive_share_btn:SetActive(FishingGameMgr:getReviveIsShare()) end -- 复活游戏 function FishingReviveUI:gameRevive() FishingGameMgr:revive() self:close() end -- 通过广告复活游戏 function FishingReviveUI:gameReviveVideo() FishingGameMgr:revive() self:close() end -- 通过分享复活游戏 function FishingReviveUI:gameReviveShare() FishingGameMgr:revive() self:close() end -- 退出游戏 function FishingReviveUI:gameFailed() FishingGameMgr:gameFailed() self:close() end return FishingReviveUI ScrollerTopDownJ&--[[ 上下滚动组件 author:{zhangpeng} time:2024-04-15 14:56:19 ]] local ScrollerTopDown, super = defClass("ScrollerTopDown", SceneComponent) local Time = CS.UnityEngine.Time local Vector3 = CS.UnityEngine.Vector3 local Screen = CS.UnityEngine.Screen local Color = CS.UnityEngine.Color local Debug = CS.UnityEngine.Debug local LOG_TAG = "ScrollerTopDown" local OUTDIR = { LEFT = 1, RIGHT = 2, IN = 3 } function ScrollerTopDown:ctor(scene) super.ctor(self,scene) self.scene = scene self.scrollEndCbs = {} self.clickEffects = {} self.layerBounds = {} end function ScrollerTopDown:onLoad() self:initMsg() end function ScrollerTopDown:initScreenBounds() -- 获取相机视口上的屏幕边界,并用一个3D边界对象表示该边界,以确保滚动的容器不超出屏幕边界 local cam = self.scene.cams.scene local bottom_left = cam:ViewportToWorldPoint(Vector3(0, 0, 0))-- 相机左下角在世界坐标中的位置 local top_right = cam:ViewportToWorldPoint(Vector3(1, 1, 0)) --相机右上角在世界坐标中的位置 bottom_left.z = -10 top_right.z = 10 -- 屏幕在世界坐标中的边界 self.screenBounds = CS.UnityEngine.Bounds(CS.UnityEngine.Vector3.zero, CS.UnityEngine.Vector3.zero) self.screenBounds:SetMinMax(bottom_left, top_right) -- self:drawScreenBounds() end function ScrollerTopDown:init(range, scrollContainer, touchNode) local touch_node = touchNode or self.scene.rootNode self:initTouch(touch_node) self.canScroll = true self:initScreenBounds() -- self:initTouch(touchNode) self.container = scrollContainer or touch_node:Seek("ScrollContainer") self.range = range self.rate = 0.6 self.speedFactor = 0.001 -- 容器滚动速度 self.containerScrollSpeed = 0 self.containerAutoScrollSpeed = 0.07 end function ScrollerTopDown:initTouch(touchNode) self.touchCom = TouchCom.new(touchNode, GameUtil:getCamera()) self.touchCom:addListener( touchNode, TouchCom.LISTENER_TYPE.CUSTOM, function(p) self:onTouchBegan(p) return true end, function(p) self:onTouchMoved(p) end, function(p) self:onTouchEnd(p) end ):setSwallow(false) end function ScrollerTopDown:initMsg() self:onmsg( { GameSceneMsg.InitScoller, GameSceneMsg.IndoorMapAutoScroll, GameSceneMsg.IndoorMapStopAutoScroll, GameSceneMsg.ScrollTopDownForced }, function (...) self:listener(...) end ) end function ScrollerTopDown:listener(msgId, arg) if not self:valid() then return end if msgId == GameSceneMsg.InitScoller then self:init(arg.range, arg.scrollContainer, arg.touchNode) elseif msgId == GameSceneMsg.IndoorMapAutoScroll then self:autoScroll(arg) elseif msgId == GameSceneMsg.IndoorMapStopAutoScroll then self:stopAutoScroll() elseif msgId == GameSceneMsg.ScrollTopDownForced then self:focusScrollToY(arg.y, arg.time) end end function ScrollerTopDown:addMoveEndCb(cb) table.insert(self.scrollEndCbs, cb) end -- 滚动到左边界回调 function ScrollerTopDown:addMoveRangeMinCb(cb) self.moveRangMinCb = cb end -- 滚动到右边界的回调 function ScrollerTopDown:addMoveRangeMaxCb(cb) self.moveRangMaxCb = cb end -- 执行边界回调 function ScrollerTopDown:doMoveRangeMinCb() if self.moveRangMinCb and self.enableMoveMinCb then self.enableMoveMinCb = false self.moveRangMinCb() end end function ScrollerTopDown:doMoveRangeMaxCb() if self.moveRangMaxCb and self.enableMoveMaxCb then self.enableMoveMaxCb = false self.moveRangMaxCb() end end function ScrollerTopDown:doMoveEndCb() for _, f in ipairs(self.scrollEndCbs) do f() end end function ScrollerTopDown:onTouchBegan(p) self:enableMoveRangeEdgeCb() self.downWorldY = p.y self.downContainerY = self.container:GetPositionY() if self.moveAction then self.container:StopAction(self.moveAction) self.moveAction = nil end self.containerScrollSpeed = 0 local prevY = self.container:GetPositionY() local prevT = Time.time self.calcVelAct = self.container:Step(function() if not self:valid() then return end local y = self.container:GetPositionY() local t = Time.time self.containerScrollSpeed = (y - prevY) / (t - prevT) * 1.1 prevY = y prevT = t end) end function ScrollerTopDown:onTouchMoved(p) local delta = p.y - self.downWorldY local dstY = self.downContainerY + delta dstY = self:checkRange(dstY) self:checkBounds() if not self.canScroll then return end Msg.send(GameSceneMsg.SceneMapScroll) self:updatePosY(dstY) end function ScrollerTopDown:onTouchEnd(p) if not self.touchCom then return end self.isTouchDown = false if math.abs(self.containerScrollSpeed) > 0 then local container = self.container local t = 0 local y0 = container:GetPositionY() local v = self.containerScrollSpeed local log = math.log local rate = self.rate -- 开始惯性滑动 self.moveAction = self.container:Step(function() if not self:valid() then return end t = t + Time.deltaTime local factor = rate ^ (10 * t) local dy = v * (factor - 1) * 0.03 / log(rate, 10) if factor > self.speedFactor then local y = self:checkRange(y0 + dy) self:checkBounds() self:updatePosY(y) else -- 结束惯性滚屏 self.moveAction = nil self:doMoveEndCb() self.containerScrollSpeed = 0 return true end end) else self:doMoveEndCb() end if self.calcVelAct then self.container:StopAction(self.calcVelAct) end end -- 强制设置到y(无滚动) function ScrollerTopDown:focusToY(y) local dstY = self:checkRange(-y) self:checkBounds() printInfo(LOG_TAG, string.format("focusToY: %s", y) ) self:updatePosY(dstY) end -- 强制滚动到y function ScrollerTopDown:focusScrollToY(y, time, cb) if self.moveAction then self.container:StopAction(self.moveAction) self.moveAction = nil end if self.focusAct then return end local _time = time or 0.5 self.touchCom:disable() local srcY = self.container:GetPositionY() local dstY = y self.focusAct = self.container:RunAction( ua.Sequence({ ua.Tween(_time,function(r) local _y = srcY * (1 - r) + dstY * r _y = self:checkRange(_y) self:checkBounds() self:updatePosY(_y) end), ua.cb(function() self.focusAct = nil dstY = self:checkRange(dstY) self:checkBounds() self:updatePosY(dstY) self.touchCom:enable() if cb then cb() end end), }) ) end function ScrollerTopDown:checkBounds() end -- 滚动范围控制在range范围内 function ScrollerTopDown:checkRange(dstY) local range = self.range if dstY > range.yMin then dstY = range.yMin self:doMoveRangeMinCb() elseif dstY < -range.yMax then self:doMoveRangeMaxCb() dstY = -range.yMax end return dstY end function ScrollerTopDown:updatePosY(y) local difPosY = y - self.container:GetPositionX() Msg.send(GameSceneMsg.SceneMapRefreshPos, { difPosX = difPosY }) self.container:SetPositionY(y) end -- 开始拖拽物品的时候,禁止滚动 function ScrollerTopDown:disableScroll() self.canScroll = false end function ScrollerTopDown:enableScroll() self.canScroll = true end -- 简介检测回调可用(滑动中启用一次,回调之后禁用,再次滑动重新启用) function ScrollerTopDown:enableMoveRangeEdgeCb() self.enableMoveMinCb = true self.enableMoveMaxCb = true end function ScrollerTopDown:autoScroll(dir) local posY = self:getPosY() if dir == OUTDIR.LEFT then posY = posY - self.containerAutoScrollSpeed elseif dir == OUTDIR.RIGHT then posY = posY + self.containerAutoScrollSpeed end posY = self:checkRange(posY) self.needAutoScroll = true self:updatePosY(posY) end function ScrollerTopDown:isScrolling(threshold) threshold = threshold or 0.01 if math.abs(self.containerScrollSpeed) > threshold then return true end return false end function ScrollerTopDown:getContainer() return self.container end function ScrollerTopDown:setContainer(container) printInfo(LOG_TAG, string.format("设置容器: %s", container.name)) self.container = container end function ScrollerTopDown:getPosY() return self.container:GetPositionY() end function ScrollerTopDown:stopAutoScroll() self.needAutoScroll = false end function ScrollerTopDown:setRate(rate) if rate > 1 or rate < 0 then self.rate = 0.6 printError(LOG_TAG, string.format("rate值设置应在0-1的范围内")) end self.rate = rate end function ScrollerTopDown:setSpeedFactor(factor) self.speedFactor = factor end function ScrollerTopDown:reset() self.canScroll = true end function ScrollerTopDown:onExit() if self.touchCom then self.touchCom:disable() self.touchCom = nil end super.onExit(self) end return ScrollerTopDownmainhrequire("modules/building/barbecue/bbq/BbqDesk") require("modules/building/barbecue/bbq/BbqDeskMgr") TaskBootConst local TaskBootConst = defClassStatic("TaskBootConst") function TaskBootConst:init() end TaskBootConst.TriggerType = { } TaskBootConst:init()EntityDetailUI--- ---@class EntityDetailUI : UILayer local EntityDetailUI = defClass("EntityDetailUI", UILayer) function EntityDetailUI:ctor(title, entityType, entityId) self.R = ResLoader.loadResLink("modules/ui/commonui/commonuireslink") self.title = title self.entityType = entityType self.entityIds = entityId self:initUI() self:showUI() end function EntityDetailUI:onLoad() self.ui = GameObject.Instantiate(self.R.entity_detail_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) end function EntityDetailUI:initUI() end function EntityDetailUI:showUI() self.entity_title:GetComponent("TextMeshProUGUI").text = self.title end return EntityDetailUITouchClickListener-- -- TouchClickListener.lua -- @author zhangxiaojun -- @description -- @created 2023-07-11T15:51:31.773Z+08:00 -- @last-modified 2023-07-12T09:25:36.564Z+08:00 -- ---@class TouchClickListener:TouchListener local TouchClickListener = defClass("TouchClickListener", TouchListener) function TouchClickListener:onInit() self._triggerOnce = false end function TouchClickListener:triggerOnce() self._triggerOnce = true end ---@param point CS.UnityEngine.Vector3 function TouchClickListener:onBegan(point) if TouchCom.intersect(self.gameObject, point) then self._actived = true self._downPt = point else self._actived = false end end ---@param point CS.UnityEngine.Vector3 function TouchClickListener:onEnd(point) local isIntersect = false if self._actived then if (self._downPt - point).magnitude < 0.1 then if TouchCom.intersect(self.gameObject, point) then self._endCb(point) if self._triggerOnce then self:destroy() end isIntersect = true end end self._actived = false end return isIntersect end main require("common/ui/UITypeEnums") require("common/ui/uibase/main") -- coms require("common/ui/uicoms/UIToast") require("common/ui/uicoms/UIDialog") require("common/ui/uicoms/UILoading") require("common/ui/uicoms/UIComsTool") require("common/ui/uicoms/UIDialogSimple") PlayerPrefsMgr5 local PlayerPrefsMgr = defClassStatic("PlayerPrefsMgr") local PlayerPrefs = UnityEngine.PlayerPrefs -- Unity的PlayerPrefs类,用于存储用户数据 local GetString = PlayerPrefs.GetString -- 获取指定key的值 fun(key, defValue):string local SetString = PlayerPrefs.SetString -- 设置指定key的值 fun(key, value):void local GetInt = PlayerPrefs.GetInt -- 获取指定key的值 fun(key, defValue):int local SetInt = PlayerPrefs.SetInt -- 设置指定key的值 fun(key, value):void local GetFloat = PlayerPrefs.GetFloat -- 获取指定key的值 fun(key, defValue):float local SetFloat = PlayerPrefs.SetFloat -- 设置指定key的值 fun(key, value):void local DeleteKey = PlayerPrefs.DeleteKey -- 删除指定key fun(key):void local DeleteAll = PlayerPrefs.DeleteAll -- 删除所有key fun():void local Save = PlayerPrefs.Save -- 保存所有修改 fun():void -- 初始化 function PlayerPrefsMgr:init() end -- 获取字符串值 function PlayerPrefsMgr:getString(key, defaultValue) if not defaultValue then defaultValue = "" -- 如果没有提供默认值,使用空字符串 end local value = GetString(key, defaultValue) if value == "null" then return defaultValue end return value end -- 设置字符串值 function PlayerPrefsMgr:setString(key, value) if value == nil or value == "" then return else SetString(key, value) end end -- 获取整数值 function PlayerPrefsMgr:getInt(key, defaultValue) if not defaultValue then defaultValue = 0 -- 如果没有提供默认值,使用0 end return GetInt(key, defaultValue) end -- 设置整数值 function PlayerPrefsMgr:setInt(key, value) if value == nil then return else SetInt(key, value) end end -- 获取浮点数值 function PlayerPrefsMgr:getFloat(key, defaultValue) if not defaultValue then defaultValue = 0.0 -- 如果没有提供默认值,使用0.0 end return GetFloat(key, defaultValue) end -- 设置浮点数值 function PlayerPrefsMgr:setFloat(key, value) if value == nil then return else SetFloat(key, value) end end -- 删除指定key function PlayerPrefsMgr:deleteKey(key) DeleteKey(key) end -- 删除所有key function PlayerPrefsMgr:deleteAll() DeleteAll() end -- 保存所有修改 function PlayerPrefsMgr:save() Save() end -- 将字典的键从数字转换为字符串 function PlayerPrefsMgr:idToString(dic) local put = {} for i, v in pairs(dic) do put[tostring(i)] = v end return put end -- 将字典的键从字符串转换为数字 function PlayerPrefsMgr:idToNumber(dic) local put = {} for i, v in pairs(dic) do put[tonumber(i)] = v end return put end PlayerPrefsMgr.init() BbqDeskMgr< --[[ 烤架建筑管理器 author:{zhangpeng} time:2025-01-01 00:00:00 ]] local BbqDeskMgr = defClassStatic("BbqDeskMgr") local LOGTAG = "BbqDeskMgr" -- init function BbqDeskMgr:init() printInfo(LOGTAG, "------ 烤架建筑管理 初始化 ------") self.bbqDesks = {} end -- 根据建筑信息创建一个烤架 function BbqDeskMgr:createBbqDesk(buildingInfo) local buildingType = buildingInfo.buildingType local parentNode = RestaurantMgr:getRestaurantScene().rootNode:Seek("building_pos_" .. buildingType) local args = { deskId = buildingInfo.buildingCfgId, parent = parentNode, scene = RestaurantMgr:getRestaurantScene(), } local bbqDesk = nil if not self.bbqDesks then self.bbqDesks = {} end if self:hasSameTypeDesk(buildingType) then self:replaceOldDesk(buildingType, buildingInfo.buildingCfgId) else bbqDesk = BbqDesk.new(args) table.insert(self.bbqDesks, bbqDesk) end return bbqDesk end -- 是否有同类不同id的烤架建筑 function BbqDeskMgr:hasSameTypeDesk(buildingType) for i = 1, table.nums(self.bbqDesks) do local bbqDesk = self.bbqDesks[i] if bbqDesk.buildingInfo.buildingType == buildingType then return true end end return false end -- 查找相同类型建筑的旧烤架建筑,替换旧的烤架建筑 -- @param buildingType 建筑类型 -- @param newDeskCfgId 新烤架建筑配置id function BbqDeskMgr:replaceOldDesk(buildingType, newDeskCfgId) for i = 1, #self.bbqDesks do local bbqDesk = self.bbqDesks[i] -- 大类相同,配置id不同,则是旧烤架建筑 if bbqDesk.buildingInfo.buildingType == buildingType and bbqDesk.buildingInfo.buildingCfgId ~= newDeskCfgId then -- 只修改一下prefab的名字为配置id bbqDesk.buildingNode:SetName(newDeskCfgId) break end end end -- 启动逻辑 function BbqDeskMgr:start() end -- 获取所有烤架建筑 function BbqDeskMgr:getAllBbqDesks() return self.bbqDesks end -- 获取指定ID的烤架建筑 function BbqDeskMgr:getBbqDeskById(deskId) for _, desk in ipairs(self.bbqDesks) do if desk.deskId == deskId then return desk end end return nil end return BbqDeskMgr mainrequire("modules/building/barbecue/cashier/CashierDeskBbq") require("modules/building/barbecue/cashier/CashierDeskBbqMgr") require("modules/building/barbecue/cashier/CashierDeskInfo")maint-- 通用组件 require("modules/common/component/Scroller") require("modules/common/component/ScrollerTopDown") UITweenAction\--[[ author:{zhangpeng} time:2023-09-17 12:26:17 ]] local UITweenAction,_ = defClassStatic("UITweenAction") function UITweenAction:init() end function UITweenAction:runTweenScale(layerObj, srcScale, dstScale, cb) if App.isPaused() then return end layerObj.__tweenOnOpenGo:SetScalef(srcScale) layerObj:disableAllUITouch() layerObj.__tweenOnOpenGo:RunAction(ua.Sequence({ua.ease.BackOut(ua.ScaleTo(0.3, dstScale)), ua.cb(function() layerObj:enableAllUITouch() if cb then cb() end end) })) end function UITweenAction:runTweenScaleUINode(node, srcScale, dstScale, cb) node:SetScalef(srcScale) node:RunAction(ua.Sequence({ua.ease.BackOut(ua.ScaleTo(0.3, dstScale)), ua.cb(function() if cb then cb() end end) })) end UITweenAction:init() CommonUtils--[[ luaide 模板位置位于 Template/FunTemplate/NewFileTemplate.lua 其中 Template 为配置路径 与luaide.luaTemplatesDir luaide.luaTemplatesDir 配置 https://www.showdoc.cc/web/#/luaide?page_id=713062580213505 author:{author} time:2024-01-31 15:13:59 ]] local CommonUtils = {} local Color = CS.UnityEngine.Color local inv255 = 1/255 function CommonUtils.hex2color(n) local r = ((n>>16)&0xff) local g = ((n>>8)&0xff) local b = ((n)&0xff) return Color(r*inv255,g*inv255,b*inv255) end function CommonUtils.hex2color4(n) local r = ((n>>24)&0xff) local g = ((n>>16)&0xff) local b = ((n>>8)&0xff) local a = ((n)&0xff) return Color(r*inv255,g*inv255,b*inv255,a*inv255) end function CommonUtils.color2hex(color) local floor = math.floor local r,g,b = floor(color.r*255),floor(color.g*255),floor(color.b*255) local n = 0 n = (r<<16)|n n = (g<<8)|n n = (b)|n return n end function CommonUtils.color2hex4(color) local floor = math.floor local r,g,b,a = floor(color.r*255),floor(color.g*255),floor(color.b*255),floor(color.a*255) local n = 0 n = (r<<24)|n n = (g<<16)|n n = (b<<8)|n n = (a)|n return n end return CommonUtilsVictoryAnimation(-- 捕鱼胜利动画 local VictoryAnimation = defClass("VictoryAnimation") function VictoryAnimation:ctor(map) self:init(map) end function VictoryAnimation:init(map) self.cagePoint = map:Seek("cage_point") self.cagePointSpine = self.cagePoint:Seek("spine") self.key = map:Seek("key") self.keySpine = self.key:Seek("spine") end function VictoryAnimation:play(fishPos) self.cagePos = Vector3(fishPos.x + 20, fishPos.y, fishPos.z) self.cagePoint:SetActive(true) self.cagePoint.transform.localPosition = self.cagePos FishingGameMgr.map.anims:SetActive(true) -- 游动时间 local swimTime = 3 -- 钥匙加载时间 local keyLoadingTime = 1 -- 钥匙展示时间 local keyDelayTime = 0.5 -- 钥匙移动时间 local keyMoveTime = 0.5 -- 钥匙开锁时间 local keyAnimTime = 1 -- 胜利动画时间 local winTime = 2 -- 水獭位置 self.capybaraPos = Vector3(self.cagePos.x - 10, self.cagePos.y + 0.7, self.cagePos.z) -- 相机位置 self.cameraPos = Vector3(self.cagePos.x - 5, self.cagePos.y, -10) -- 钥匙位置 self.keyPos = Vector3(self.cagePos.x - 0.05, self.cagePos.y + 0.2, self.cagePos.z) -- 播放水獺移动动画 self:playCapybaraMoveAnim(swimTime, keyLoadingTime, keyDelayTime + keyMoveTime + keyAnimTime, winTime) -- 播放钥匙移动动画 self:playKeyMoveAnim(swimTime + keyLoadingTime, keyDelayTime, keyMoveTime) -- 播放相机移动动画 self:playCameraMoveAnim(swimTime) end -- 播放水獺移动的动画 function VictoryAnimation:playCapybaraMoveAnim(swimTime, keyLoadingTime, keyAnimTime, winTime) local capybara = FishingGameMgr.character if not capybara then return end capybara.transform.localRotation = Quaternion.Euler(0, 0, 90) capybara.go:RunAction(ua.Sequence({ -- 单独移动到牢笼附近位置 ua.MoveTo(swimTime, self.capybaraPos), ua.cb(function() self:playCapybaraAnim() end), ua.Delay(keyLoadingTime + keyAnimTime), -- 播放胜利动画 ua.cb(function() self.keySpine:SetActive(false) self:playCapybaraAnim() self:playCageAnim() end), -- 等待动画播放完成 ua.Delay(winTime), ua.cb(function() self:reset() -- 游戏胜利 FishingGameMgr:gameVictory() end) })) end -- 播放相机移动动画 function VictoryAnimation:playCameraMoveAnim(swimTime) FishingGameMgr.cameraCtl.go:RunAction(ua.Sequence({ -- 单独移动到牢笼附近位置 ua.MoveTo(swimTime, self.cameraPos) })) end -- 播放钥匙移动动画 function VictoryAnimation:playKeyMoveAnim(delayTime, keyDelayTime, keyMoveTime) self.key.transform.localPosition = Vector3(self.capybaraPos.x, self.capybaraPos.y + 5, self.capybaraPos.z) self.key:RunAction(ua.Sequence({ -- 等待一段时间 ua.Delay(delayTime), ua.cb(function() -- 播放钥匙动画 self.keySpine:SetActive(true) self:playKeyStandAnim() end), -- 等待钥匙展示时间 ua.Delay(keyDelayTime), ua.cb(function() self:playCapybaraStandAnim() end), -- 单独移动到牢笼附近位置 ua.MoveTo(keyMoveTime, self.keyPos), -- 播放钥匙动画 ua.cb(function() self:playKeyOpenAnim() end), })) end function VictoryAnimation:reset() FishingGameMgr.map.anims:SetActive(false) if self.cagePoint then self.cagePoint:SetActive(false) end end -- 播放钥匙动画 function VictoryAnimation:playKeyStandAnim() if self.key then util.spine.play(self.keySpine, "stand", false) end end -- 播放钥匙动画 function VictoryAnimation:playKeyOpenAnim() if self.key then util.spine.play(self.keySpine, "open", false) end end function VictoryAnimation:playCapybaraAnim() local capybara = FishingGameMgr.character if not capybara then return end capybara:playAnimation("kapibala_win", false) end function VictoryAnimation:playCapybaraStandAnim() local capybara = FishingGameMgr.character if not capybara then return end capybara:playAnimation("kapibala_stand_swim", false) end function VictoryAnimation:playCageAnim() if self.cagePointSpine then self.cagePoint:SetActive(true) util.spine.play(self.cagePointSpine, "longzi_yangtuo_win", false) end end return VictoryAnimationEmployeeStoryCell--- 故事单元格 ---@class EmployeeStoryCell:LuaClass local EmployeeStoryCell = defClass("EmployeeStoryCell") function EmployeeStoryCell:ctor(go, storyId, isUnlock) local bg = go:Seek("bg") local story_value = go:Seek("story_value") local story_lock = go:Seek("story_lock") local story_btn = go:Seek("story_btn") self.isUnlock = isUnlock local config = StorydialogCfgParse:getDialogCfg(storyId) util.ugui.addClickEvent(bg, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:showStory(storyId) end) story_value:GetComponent("TextMeshProUGUI").text = config.dialogName story_lock:SetActive(not (isUnlock or false)) story_btn:SetActive(isUnlock or false) end function EmployeeStoryCell:showStory(storyId) if self.isUnlock then --printWarn("EmployeeCell", "EmployeeCell:showStory() id = " .. storyId) StoryDialogMgr:showPageStoryDialogUI(storyId, StorydialogConst.RoleType.Employee) else PopUpUI.new("员工雇佣后可查看剧情"):show():showMask():enableCloseWhenClickMask() end end return EmployeeStoryCell RestaurantMgr --[[ 餐厅管理 author:{zhangpeng} time:2025-05-15 20:41:14 ]] local RestaurantMgr = defClassStatic("RestaurantMgr") local LOGTAG = "RestaurantMgr" function RestaurantMgr:getRestaurantScene() return self.restaurantScene end function RestaurantMgr:setRestaurantScene(restaurantScene) self.restaurantScene = restaurantScene end -- 开始餐厅流程 function RestaurantMgr:start() printInfo(LOGTAG, "=== 开始餐厅流程 ===") -- 确保CustomerQueMgr已初始化 CustomerQueMgr:init() -- 开始处理顾客分配 self:startCustomerAssignment() end -- 获取餐厅地图 function RestaurantMgr:getRestaurantMapNode() local scene = self:getRestaurantScene() return scene.rootNode:Seek(MapsConst.mapNodeNames[MapsConst.MapType.restaurant]) end -- 获取餐厅入口 function RestaurantMgr:getRestaurantEnter() return MapsMgr:getMap(MapsConst.MapType.restaurant):getRestaurantEnter() end -- 获取餐厅出口 function RestaurantMgr:getRestaurantExit() return MapsMgr:getMap(MapsConst.MapType.restaurant):getRestaurantExit() end -- 获取员工出生点和带几点 function RestaurantMgr:getBirthAndIdlePoint(employeeType) return MapsMgr:getMap(MapsConst.MapType.restaurant):getBirthAndIdlePoint(employeeType) end -- 获取普通顾客根节点 function RestaurantMgr:getCustomerRoot() local scene = self:getRestaurantScene() return scene.rootNode:Seek("customer_root") end -- 获取餐厅去往音乐烤吧的入口 function RestaurantMgr:getRestaurantToMusicBbqEnter() return MapsMgr:getMap(MapsConst.MapType.restaurant):getToMusicBbqEnter() end -- 获取餐厅去往捕鱼岛的入口 function RestaurantMgr:getRestaurantToFishingEnter() return MapsMgr:getMap(MapsConst.MapType.restaurant):getToFishingEnter() end -- 开始处理顾客分配 function RestaurantMgr:startCustomerAssignment() printInfo(LOGTAG, "开始处理顾客分配") -- 先分配一次测试 CustomerQueMgr:tryAssignToDiningDesk() end -- 定时器逻辑,每隔一段时间尝试为顾客分配餐桌 function RestaurantMgr:update(dt) printInfo(LOGTAG, "尝试为顾客分配餐桌") -- 检查并分配顾客到餐桌 CustomerQueMgr:tryAssignToDiningDesk() end -- 普通顾客进入餐厅 放入餐厅并设置出生点 function RestaurantMgr:customerEnterRestaurant(customer) customer:setSceneType(MapsConst.MapType.restaurant) local customerRoot = self:getCustomerRoot() local mapNode = self:getRestaurantMapNode() local birthPoint = self:getRestaurantEnter() -- 获取餐厅入口点 local birthPos = birthPoint and birthPoint:GetPosition() or Vector3(0, 0, 0) -- 设置顾客父节点 customer:setCustomerParent(customerRoot) customer:setCurPosition(Vector3(birthPos.x, birthPos.y, birthPos.z)) customer:setMapRoot(mapNode) return customer end -- 开始普通顾客进入餐厅逻辑 function RestaurantMgr:customerEnterRestaurantLogic(customer) end return RestaurantMgr DollDetailUI--- 池塘 摊主详情界面 local DollDetailUI, super = defClass("DollDetailUI", UILayer) -- component local Image = UnityEngine.UI.Image local TMProUGUI = CS.TMPro.TextMeshProUGUI local Sprite = typeof(UnityEngine.Sprite) -- 构造函数 function DollDetailUI:ctor(dollId) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/dollui/dolluireslink") self.dollId = dollId end -- 当页面加载 function DollDetailUI:onLoad() self.ui = GameObject.Instantiate(self.R.doll_detail_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end -- 初始化UI function DollDetailUI:initUI() self.closeBtn = self.ui:Seek("close_btn") self.lockNode = self.ui:Seek("lock") self.infoNode = self.ui:Seek("info") self.reward_btn = self.ui:Seek("reward_btn") util.ugui.addClickEvent(self.closeBtn, function() self:close() end) util.ugui.addClickEvent(self.reward_btn, function() self:reward() end) self:initInfo() self:initWay() self:initNeedStar() self:initCollectQuantity() self:initReward() end -- 展示UI function DollDetailUI:showUI() local config = DollMgr:getConfig(self.dollId) local currProgress = DollMgr:getDollCount(self.dollId) local totalProgress = config.count local has = currProgress > 0 local isCollected = currProgress >= totalProgress local isReceived = DollMgr:getDollState(self.dollId) >= DollConst.State.Received self.lockNode:SetActive(not has) self.infoNode:SetActive(has) self.reward_btn:SetActive(isCollected and (not isReceived)) self:showInfo(config, has) self:showWay(config.way) self:showNeedStar(config.needStar) self:showCollectQuantity(config.count) self:showRewardList(config) end function DollDetailUI:initInfo() self.iconNode = self.infoNode:Seek("icon") self.spineNode = self.infoNode:Seek("spine_root") self.nameNode = self.infoNode:Seek("name") self.descNode = self.infoNode:Seek("desc") end function DollDetailUI:showInfo(config, has) if has then local image = self.iconNode[Image] local path = string.format("Assets/AssetsPackage/Res/modules/ui/doll/images/roles/%s.png", config.dollIcon) if ResLoader.hasAsset(path) then image.sprite = ResLoader.loadAsset(path, Sprite) else image.sprite = nil end image:SetNativeSize() self.nameNode[TMProUGUI].text = config.name self.descNode[TMProUGUI].text = config.desc end end function DollDetailUI:initWay() self.way = self.ui:Seek("way") end function DollDetailUI:showWay(wayId) local has = wayId and wayId > 0 self.way:SetActive(has) if has then self.way[TMProUGUI].text = string.format("获取途径:%s", DollConst.WayDesc[wayId]) end end function DollDetailUI:initNeedStar() self.need_star = self.ui:Seek("need_star") end function DollDetailUI:showNeedStar(value) local has = value and value > 0 self.need_star:SetActive(has) if has then self.need_star[TMProUGUI].text = string.format("解锁条件:人气需要达到 %d", value) end end function DollDetailUI:initCollectQuantity() self.count = self.ui:Seek("count") end function DollDetailUI:showCollectQuantity(value) local has = value and value > 0 self.count:SetActive(has) if has then self.count[TMProUGUI].text = string.format("任务条件:收集%s个玩偶可获得奖励,同时可以召唤隐藏客人", value) end end function DollDetailUI:initReward() self.rewards = self.ui:SearchPattern("reward_\\d", true) end function DollDetailUI:showRewardList(config) for i = 1, #self.rewards do self:showReward(self.rewards[i], config["rewardId_" .. i], config["param_" .. i]) end end function DollDetailUI:showReward(obj, type, value) local has = type and type ~= -1 and (type ~= DollConst.RewardType.Customer) obj:SetActive(has) if has then if type == DollConst.RewardType.Star then obj[TMProUGUI].text = string.format("任务奖励: +%d", value) elseif type == DollConst.RewardType.MusicalNotes then obj[TMProUGUI].text = string.format("任务奖励: +%d", value) end end end function DollDetailUI:reward() DollMgr:getRewardAll(self.dollId) self:close() end return DollDetailUI UIOrderDef--[[ UI层级定义 author:{zhangpeng} time:2022-05-25 17:34:07 ]] local UIOrderDef,_ = defClassStatic("UIOrderDef") UIOrderDef.SORTING_ORDER = { LOCAL_UI = 10000, GLOBAL_UI = 30000 --32767是允许的最大值 } --UI类型定义 UIOrderDef.UI_ORDER = { GAME = 1,--非UGUI的ui界面 PANEL = 10000, -- 默认panle,全屏 CURRENCY_BAR = 10000, -- 货币栏 GUIDE = 20001, -- 新手引导 DIALOG = 20002, --对话框 SYS_PANEL = 20005, -- 系统弹窗 层级高 LOADING = 20006, -- TOAST = 20007, -- 纯文字提示条 TOP_TOUCH_MASK = 30001-- 最顶层点击遮罩 } function UIOrderDef:init() end UIOrderDef:init() main-- mgr require("modules/role/vendor/VendorMgr") -- const require("modules/role/vendor/const/VendorConst") -- data require("modules/role/vendor/data/VendorInfo") require("modules/role/vendor/data/VendorUserData") BuildingInfo--[[ 建筑信息(纯数据) author:{zhangpeng} time:2025-05-19 17:53:44 ]] local BuildingInfo, super = defClass("BuildingInfo") local LOGTAG = "BuildingInfo" function BuildingInfo:ctor(buildingCfgId) self.id = buildingCfgId self:init() end function BuildingInfo:init() self:initData() self:initStatus() self:initDynamicData() end function BuildingInfo:initData() self.config = self:getBuildingCfg() local cfg = self:getBuildingCfg() -- 建筑配置id self.buildingCfgId = cfg.uid -- 建筑名字 self.buildingName = cfg.name -- 建筑类型(建筑大类) self.buildingType = cfg.buildingType -- 建筑加成效果id self.buildingEffectId = cfg.effectId -- 建筑购买条件id self.buildingBuyConditionId = cfg.buyConditionId -- 建筑解锁条件id self.buildingUnlockConditionId = cfg.unlockConditionId -- 建筑加成数据 self.buildingEffectData = self:getBuildingEffectData() end function BuildingInfo:initStatus() self.state = UserDataMgr.buildingUserData:getBuildingState(self.id) -- 初始化阻挡状态 - 默认为隐藏阻挡(false) self.blockHidden = false self:refreshTips() end -- 初始化动态数据 function BuildingInfo:initDynamicData(serverData) if serverData then -- 是否解锁 self.state = serverData.state -- 阻挡状态 self.blockHidden = serverData.blockHidden or false else self.state = UserDataMgr.buildingUserData:getBuildingState(self.id) self.blockHidden = false end end -- 获取建筑配置信息 function BuildingInfo:getBuildingCfg() return BuildingCfgParse:getBuildingCfg(self.id) end -- 获取建筑加成配置表 function BuildingInfo:getBuildingEffectCfg() return BuildingEffectCfgParse:getBuildingEffectCfg(self.buildingEffectId) end --- 获取建筑状态 function BuildingInfo:getState() return self.state end --- 设置建筑状态 function BuildingInfo:setState(state) self.state = state end function BuildingInfo:getMapIconName() return self.config.resid end function BuildingInfo:getUiIconName() return self.config.uiresid end function BuildingInfo:getMapId() return self.config.mapId end function BuildingInfo:getMapIconPath() return string.format("Assets/AssetsPackage/Res/modules/restaurant/map/images/buildings/map/%s.png", self:getMapIconName()) end function BuildingInfo:getUiIconPath() return string.format("Assets/AssetsPackage/Res/modules/restaurant/map/images/buildings/ui/%s.png", self:getUiIconName()) end --- 检查建筑是否解锁 function BuildingInfo:isUnlocked() return self:getState() >= BuildingConst.buildingState.unlocked end --- 是否满足解锁条件 function BuildingInfo:canUnlock() return UnlockMgr:isUnlock(self.id) end -- 解锁一个建筑 function BuildingInfo:unlock() self:setState(BuildingConst.buildingState.unlocked) end --- 检查建筑是否已购买 function BuildingInfo:isPurchased() return self:getState() >= BuildingConst.buildingState.purchased end -- 购买一个建筑 function BuildingInfo:buy() self:setState(BuildingConst.buildingState.purchased) end --- 检查建筑是否在使用中 function BuildingInfo:isInUse() return self:getState() >= BuildingConst.buildingState.used end -- 使用一个建筑 function BuildingInfo:use() self:setState(BuildingConst.buildingState.used) end -- 被替换 function BuildingInfo:replace() self:setState(BuildingConst.buildingState.purchased) end -- 获取建筑名称 function BuildingInfo:getName() return self.config.name end -- 获取建筑描述 function BuildingInfo:getDesc() return self.config.desc end -- 获取加成数据 function BuildingInfo:getBuildingEffectData() if self.buildingEffectId then if self.buildingEffectData then return self.buildingEffectData else self.buildingEffectData = CommonBonusCfgParse:getCommonBonusListCfgById(self.buildingEffectId) return self.buildingEffectData end end return nil end -- 获取购买条件数据 function BuildingInfo:getBuildingBuyConditionData() return BuyCommonCfgParse:getBuyCommonCfg(self.config.buyConditionId) end --- 获取购买价格 function BuildingInfo:getBuyPrice() local conditionData = self:getBuildingBuyConditionData() return conditionData.price end --- 获取当前使用中的建筑ID function BuildingInfo:getInUseId() return BuildingMgr:getInUseId(self.buildingType) end --- 获取建筑阻挡隐藏状态 function BuildingInfo:isBlockHidden() return self.blockHidden or false end --- 设置建筑阻挡隐藏状态 function BuildingInfo:setBlockHidden(hidden) self.blockHidden = hidden end -- 加成获取 -- 获取人气加成 function BuildingInfo:getStarBonus() return BonusMgr:getStarBonus() end -- 获取小费加成 function BuildingInfo:getTipBonus() return BonusMgr:getTipBonus() end -- 获取小费最大积攒时间 function BuildingInfo:getTipMaxTimeBonus() return BonusMgr:getTipMaxTimeBonus() end -- 获取顾客每次消费可获得收入加成 function BuildingInfo:getPerConsumeBonus() local config = self:getBuildingEffectCfg() for i = 1, 3 do local bonusId = config["BonusId_" .. i] if bonusId and (bonusId == BonusConst.BonusType.coin_income_per_consume) then return config["Value_" .. i] end end return config end -- 获取做菜效率加成 function BuildingInfo:getCookEfficiencyBonus() return BonusMgr:getCookEfficiencyBonus() end -- 获取每隔30秒可获得额外收入加成 function BuildingInfo:getCoinIncomePer30sBonus() return BonusMgr:getCoinIncomePer30sBonus() end -- 获取每小时自助收入加成 function BuildingInfo:getCoinIncomePerHourBonus() return BonusMgr:getCoinIncomePerHourBonus() end -- 小费 -- 放置小费建筑 function BuildingInfo:initTips() if self.tipsTime == -1 then -- 首次放置存档放置时间 -- 设置小费时间为当前时间 self.tipsTime = UserDataMgr.buildingUserData:updateBuildingTipsTime() end end -- 获取当前罐中积攒的小费 function BuildingInfo:getCurrTips() return self.currTips end -- 更新小费 function BuildingInfo:refreshTips() self.tipsTime = UserDataMgr.buildingUserData:getBuildingTipsTime() self:updateTip() end -- 小费增长 每分钟调用 function BuildingInfo:updateTip() if self.tipsTime == -1 then self.currTips = 0 else local span = os.time() - self.tipsTime local max = self:getTipMaxTimeBonus() self.currTips = self:getTipBonus() * math.floor(span / 60) if self.currTips > max then self.currTips = max end end end -- 获取小费 function BuildingInfo:collectTip() -- 更新存档 self.tipsTime = UserDataMgr.buildingUserData:updateBuildingTipsTime() -- 获取当前小费 local tips = self.currTips self.currTips = 0 --CurrencyMgr:changeCoin(tips) -- 由领取页面改动金币数值 return tips end -- 获取图标 function BuildingInfo:getIcon(path) local sprite = nil if ResLoader.hasAsset(path) then sprite = ResLoader.loadAsset(path, "Sprite") else printInfo(LOGTAG, "没有找到图片路径:%s", path) sprite = nil end return sprite end -- 获取建筑图标 function BuildingInfo:getUiIcon() return self:getIcon(self:getUiIconPath()) end -- 获取建筑图标 function BuildingInfo:getMapIcon() return self:getIcon(self:getUiIconPath()) end -- 获取建筑所属地图(餐厅,烤吧) function BuildingInfo:getBuildingInMap() local buildingTypesCfg = BuildingTypesCfgParse:getBuildingTypesCfg(self.buildingType) return buildingTypesCfg.scene end -- 定时 return BuildingInfo FishingGameMap\local FishingGameMap = defClass("FishingGameMap") local LOGTAG = "FishingGameMap" local Random = UnityEngine.Random local TMPro = CS.TMPro.TextMeshPro function FishingGameMap:ctor() self:init() end -- 基础配置 -- 初始化 function FishingGameMap:init() self:initConfig() self:initMapObj() self:initCamera() self:initAttribute() self:initGradient() self:initBg() self:initPoints() self:initDecorate() end -- 初始化配置 function FishingGameMap:initConfig() self.cellMaxCount = 6 -- 每块区域最大生成数量 self.nearIds = {} -- 近距离预制体数组 self.nearWeights = {} -- 近距离权重数组(长度与nearIds一致) self.nearCellDiameter = 3 -- 近距离区域直径,单位为块 self.distanceIds = {} -- 远距离预制体数组 self.distanceWeights = {} -- 远距离权重数组(长度与distanceIds一致) self.distanceCellDiameter = 5 -- 远距离区域直径,单位为块 self.fishes = {} -- 存储生成的鱼对象 self.cellSize = nil -- 每个区块的世界尺寸 self.mapSize = {} -- 整个地图的世界尺寸 self.mapOffset = {} -- 地图的世界偏移 self.posOffset = {} -- 物体位置的世界偏移 self.hideFishDic = {} -- 隐藏的鱼列表 self.swordfishList = {} -- 剑鱼列表 self.swordfishTime = 0 -- 剑鱼计时 self.swordSpawnInterval = 0 -- 剑鱼生成间隔 self.swordSpawnCount = 0 -- 剑鱼生成数量 self.gradeConfig = nil -- 当前等级配置 end -- 初始化地图对象 function FishingGameMap:initMapObj() self.go = GameObject.Instantiate(FishingGameMgr.resLink.fishing_game_map) self.transform = self.go.transform self.transform.position = Vector3(-100, -100, 0) self.normal_fishes_root = self.go:Seek("normal_fishes").transform self.special_fishes_root = self.go:Seek("special_fishes").transform self.character_root = self.go:Seek("character").transform self.cage_root = self.go:Seek("cage").transform self.mine_root = self.go:Seek("mine").transform self.bg = self.go:Seek("bg") self.anims = self.go:Seek("anims") self.challengers_value = self.go:Seek("challengers") -- 今日挑战人数 self.successes_value = self.go:Seek("successes") -- 今日成功人数 end -- 初始化摄像机 function FishingGameMap:initCamera() self.cameraCtl = FishingGameCameraCtl.new(self.go:Seek("Camera"), 10) end -- 初始化属性 function FishingGameMap:initAttribute() self.cellSize = self.cameraCtl:getCameraWorldSize() -- 获取每个区块的世界尺寸,与摄像机世界尺寸相同 self.mapSize.width = self.cellSize.width * self.distanceCellDiameter -- 地图宽度 self.mapSize.height = self.cellSize.height * self.distanceCellDiameter -- 地图高度 self.posOffset.x = -self.mapSize.width / 2 -- 水平偏移量 self.posOffset.y = 0 -- 垂直偏移量 self.width = self.cellSize.width * self.distanceCellDiameter -- 地图宽度 self.height = self.cellSize.height * self.distanceCellDiameter -- 地图高度 -- 更新相机可活动区域 self.cameraCtl:updateActiveArea(self:getWorldSize()) -- 初始化不可用区域 self:initUnusableArea() -- 设置相机扩展区域(展示装饰之类的方便) self.cameraCtl:setExpandArea({top = 3}) end -- 初始化不可随机区域,鱼不能在这个位置生成 function FishingGameMap:initUnusableArea() self.unusedArea = {} -- 不可用区域 -- 通过两个对角点,获取矩形区域 不能设置为整个区块大小 否则会陷入死循环 self.unusedArea.minX = -self.cellSize.width / 4 self.unusedArea.maxX = self.cellSize.width / 4 self.unusedArea.minY = -self.mapSize.height / 2 - self.cellSize.height / 4 self.unusedArea.maxY = -self.mapSize.height / 2 + self.cellSize.height / 4 end -- 底层背景初始化 function FishingGameMap:initBg() local waterBgNode = self.go:Seek("water_bg") self:setWorldSize(waterBgNode, self.mapSize.width, self.mapSize.height) waterBgNode.transform.localPosition = Vector3(0, -self.mapSize.height / 2, 0) end -- 渐变背景初始化 function FishingGameMap:initGradient() local gradientNode = self.go:Seek("gradient") self:setWorldSize(gradientNode, self.mapSize.width, self.mapSize.height) gradientNode.transform.localPosition = Vector3(0, -self.mapSize.height / 2, 0) -- 设置渐变背景位置 end -- 创建噪点 function FishingGameMap:initPoints() local map_point = self.go:Seek("map_point") local map_point_misplaced = self.go:Seek("map_point_misplaced") local size = self:setWorldSize(map_point, self.mapSize.width, self.mapSize.height) local size_m = self:setWorldSize(map_point_misplaced, self.mapSize.width, self.mapSize.height) self:setMaterialVector(map_point, "_Tiling", Vector4(size.width, size.height, 0, 0)) -- 设置材质属性 self:setMaterialVector(map_point_misplaced, "_Tiling", Vector4(size_m.width, size_m.height, 0, 0)) -- 设置材质属性 map_point.transform.localPosition = Vector3(0, -self.mapSize.height / 2, 0) -- 设置噪点位置 map_point_misplaced.transform.localPosition = Vector3(0.1, -self.mapSize.height / 2 - 0.1, 0) -- 设置噪点位置 end -- 初始化装饰 function FishingGameMap:initDecorate() self.decorate = self.go:Seek("decorate") self.poon_up = self.decorate:Seek("poon_up") self.poon_up_sprite = self.poon_up:Seek("sprite") self.poon_down = self.decorate:Seek("poon_down") self.poon_down_sprite = self.poon_down:Seek("sprite") self.poon_up.transform.localPosition = Vector3.zero self.poon_down.transform.localPosition = Vector3(0, -self.mapSize.height, 0) -- 将下方放在地图底部 local up_width = self:setWorldWidth(self.poon_up_sprite, self.mapSize.width) -- 设置上方的宽度 local down_width = self:setWorldWidth(self.poon_down_sprite, self.mapSize.width) -- 设置下方的宽度 self:setMaterialVector(self.poon_up_sprite, "_Tiling", Vector4(up_width, 1, 0, 0)) -- 设置上方的材质属性 self:setMaterialVector(self.poon_down_sprite, "_Tiling", Vector4(down_width, 1, 0, 0)) -- 设置上方的材质属性 end -- 获取地图的世界偏移 function FishingGameMap:getWorldOffset() return {x = -100, y = -100} end -- 获取地图的世界尺寸 function FishingGameMap:getWorldSize() return self.mapSize end -- 流程 -- 游戏开始 function FishingGameMap:start() -- 更新配置 self:updateGrade() -- 启动主角 self:runCharacter() self.bg:SetActive(false) if self.fishes and #self.fishes > 0 then self:showFishes() else self:createFishes() end --self:runCage() -- 显示牢笼 -- 展示装饰 self.decorate:SetActive(true) self:setCameraUpdate(true) end -- 时间更新 function FishingGameMap:timeUpdate(deltaTime) self.character:timeUpdate(deltaTime) if self.cameraUpdate then self.cameraCtl:updateCameraPosition(self.character.transform.localPosition) end -- 眩晕状态处理 if self.dizzy then self:reduceDizzyTime(deltaTime) end -- 更新地图上的鱼 for _, v in ipairs(self.fishes) do if v then v:timeUpdate(deltaTime) -- 更新鱼的状态 end end -- 更新隐藏的鱼 for id, info in pairs(self.hideFishDic) do if info and info.fish then if info.time <= 0 then self:removeHideFish(id) else info.time = info.time - deltaTime -- 减少隐藏时间 end else self.hideFishDic[id] = nil -- 清理无效的隐藏鱼信息 end end -- 更新剑鱼 if self.swordfishTime <= 0 then self.swordfishTime = self.swordSpawnInterval self:runSwordfish() else self.swordfishTime = self.swordfishTime - deltaTime end for _, swordfish in ipairs(self.swordfishList) do if swordfish then swordfish:timeUpdate(deltaTime) end end end -- 游戏结束 function FishingGameMap:over() self.bg:SetActive(true) self:stopCharacter() self:stopFishes() self:stopMine() self:stopSwordfish() --self:stopCage() self:clearHideFish() end -- 更改 -- 升级改动 function FishingGameMap:upgrade(grade) self:updateGrade(grade) -- 对应等级生成水雷 if FishingGameMgr:getCharacterGrade() >= FishingConst.MineSpawnGrade and (not self.mineList) then self:runMine() end -- 更新鱼的尺寸和速度 for _, fish in ipairs(self.fishes) do fish:updateGradeConfig(self.gradeConfig) end end -- 更新等级 function FishingGameMap:updateGrade(grade) if not grade then grade = 1 end self.gradeConfig = FishingGradeCfgParse:getFishingGradeCfgByGrade(grade) -- 生成权重 self:updateWeights() -- 相机尺寸 self:updateCamera() -- 更新剑鱼配置 self:updateSwordfishConfig(grade) end -- 更新权重 function FishingGameMap:updateWeights() self.nearIds = {} self.nearWeights = {} self.distanceIds = {} self.distanceWeights = {} local gradeConfig = self.gradeConfig for i, v in pairs(gradeConfig.spawnDistance) do table.insert(self.nearIds, i) table.insert(self.nearWeights, v) end for i, v in pairs(gradeConfig.spawnFarDistance) do table.insert(self.distanceIds, i) table.insert(self.distanceWeights, v) end end -- 更新相机 function FishingGameMap:updateCamera() local gradeConfig = self.gradeConfig self.cameraCtl:setCameraSize(gradeConfig.cameraZoom) -- 设置摄像机正交大小 self.cameraCtl:updateActiveArea(self:getWorldSize()) -- 更新摄像机活动区域 self.cameraCtl:updateGradientSize() -- 初始化渐变背景大小 end -- 更新剑鱼生成配置 function FishingGameMap:updateSwordfishConfig() local gradeConfig = self.gradeConfig self.swordfishTime = gradeConfig.swordfishInterval -- 初始化剑鱼生成时间 self.swordSpawnInterval = gradeConfig.swordfishInterval -- 初始化剑鱼生成间隔 self.swordSpawnCount = gradeConfig.swordfishCount -- 初始化剑鱼生成数量 end -- 添加隐藏的鱼 function FishingGameMap:addHideFish(fish) local count = table.count(self.hideFishDic) local info = { id = nil, fish = fish, pos = fish.transform.localPosition, area = fish:getFishArea(), time = FishingConst.FishRefreshTime, } for i = 1, count + 1 do if not self.hideFishDic[i] then info.id = i self.hideFishDic[i] = info break end end end -- 更新游戏人数 function FishingGameMap:updateGameNum(challengers, successes) self.challengers_value[TMPro].text = string.format("今日挑战人数:%d", challengers) self.successes_value[TMPro].text = string.format("通关人数:%d", successes) end -- 设置相机是否更新 function FishingGameMap:setCameraUpdate(isUpdate) self.cameraUpdate = isUpdate end -- 移除隐藏的鱼 function FishingGameMap:removeHideFish(id) local info = self.hideFishDic[id] -- 判断主角和鱼的距离,是否在同一区块内,是则延迟刷新 local characterPos = self.character.transform.localPosition if (math.abs(characterPos.x - info.pos.x) < self.cellSize.width / 2) then return end if (math.abs(characterPos.y - info.pos.y) < self.cellSize.height / 2) then return end self.hideFishDic[id] = nil -- 重新生成鱼 local idx = self:getRandomIndexByWeight(self.distanceWeights) local grade = self.distanceIds[idx] info.fish:reload(grade, self.normal_fishes_root, info.pos, self:getRandomPointListInRect(info.area.x, info.area.y, 5)) end -- 清除隐藏的鱼 function FishingGameMap:clearHideFish() self.hideFishDic = {} -- 清空隐藏鱼列表 end -- 道具 -- 设置眩晕状态 function FishingGameMap:setDizzy(isOn, time) self.dizzy = isOn for i = #self.fishes, 1, -1 do local fish = self.fishes[i] if fish then fish:setDizzy(isOn) end end if isOn then self.dizzyTime = time else self.dizzyTime = 0 end end -- 减少眩晕时间 function FishingGameMap:reduceDizzyTime(deltaTime) if self.dizzyTime > 0 then self.dizzyTime = self.dizzyTime - deltaTime if self.dizzyTime <= 0 then self:setDizzy(false) end end end -- 生成 -- 启动角色 function FishingGameMap:runCharacter() if self.character then self.character:reload(1, self.character_root, Vector3(0, -self.mapSize.height / 2, 0)) -- 将角色放在地图中心 else self.character = FishermanCharacter.new(1, self.character_root, Vector3(0, -self.mapSize.height / 2, 0)) -- 创建角色对象 end -- 更新角色 self.character:updateGrade() end -- 停止角色 function FishingGameMap:stopCharacter() if self.character then self.character:hide() end end -- 创建普通鱼 function FishingGameMap:createFishes() for i = 1, self.distanceCellDiameter do for j = 1, self.distanceCellDiameter do if i < self.nearCellDiameter and j < self.nearCellDiameter then -- 近距离区域 for _ = 1, self.cellMaxCount do table.insert(self.fishes, self:createNearWeights(i, j)) end else -- 远距离区域 for _ = 1, self.cellMaxCount do table.insert(self.fishes, self:createDistanceWeights(i, j)) end end end end end -- 显示普通鱼 function FishingGameMap:showFishes() local idx = 1 for i = 1, self.distanceCellDiameter do for j = 1, self.distanceCellDiameter do if i < self.nearCellDiameter and j < self.nearCellDiameter then -- 近距离区域 for _ = 1, self.cellMaxCount do self:setNearWeights(self.fishes[idx], i, j) end idx = idx + 1 else -- 远距离区域 for _ = 1, self.cellMaxCount do self:setDistanceWeights(self.fishes[idx], i, j) end idx = idx + 1 end end end end -- 隐藏普通鱼 function FishingGameMap:stopFishes() -- 清空当前鱼对象 for _, fish in ipairs(self.fishes) do if fish then fish:destroy() -- 销毁鱼对象 end end self.fishes = {} end -- 显示牢笼 function FishingGameMap:runCage() -- 如果没有创建牢笼,则创建一个新的 if self.cage then self.cage.go:SetActive(true) else self.cage = Cage.new(1, self.cage_root, Vector3(0, -self.mapSize.height + 3, 0)) -- 将牢笼放在底部 end end -- 隐藏牢笼 function FishingGameMap:stopCage() if self.cage then self.cage.go:SetActive(false) end end -- 启动水雷 function FishingGameMap:runMine() local count = math.random(FishingConst.MineSpawnCountMin, FishingConst.MineSpawnCountMax) if not self.mineList then self.mineList = {} end local minX = -self.mapSize.width / 2 local maxX = self.mapSize.width / 2 local minY = 0 local maxY = -self.mapSize.height for i = 1, count do local worldPos = Vector3(minX + math.random() * (maxX - minX), minY + math.random() * (maxY - minY), 0) -- 随机生成水雷位置 if self.mineList[i] then self.mineList[i]:reload(worldPos) else self.mineList[i] = Mine.new(worldPos, self.mine_root) end end for i = count + 1, #self.mineList do self.mineList[i]:hide() end end -- 停止水雷 function FishingGameMap:stopMine() if self.mineList and #self.mineList > 0 then for _, mine in ipairs(self.mineList) do mine:hide() end end end -- 启动剑鱼 function FishingGameMap:runSwordfish() if not self.swordfishList then self.swordfishList = {} end local count = self.swordSpawnCount local minX = self.mapSize.width / 2 local maxX = self.mapSize.width / 2 + 20 -- 剑鱼生成范围在地图右侧 local minY = 0 local maxY = -self.mapSize.height for i = 1, count do local worldPos = Vector3(minX + math.random() * (maxX - minX), minY + math.random() * (maxY - minY), 0) -- 随机生成剑鱼位置 if self.swordfishList[i] then self.swordfishList[i]:reload(worldPos) else self.swordfishList[i] = Swordfish.new(worldPos, self.special_fishes_root) end end for i = count + 1, #self.swordfishList do self.swordfishList[i]:hide() -- 隐藏多余的剑鱼 end end -- 停止剑鱼 function FishingGameMap:stopSwordfish() if self.swordfishList then for _, v in ipairs(self.swordfishList) do v:hide() -- 隐藏剑鱼 end end end -- 创建近距离权重的鱼 function FishingGameMap:createNearWeights(x, y) local prefabIndex = self:getRandomIndexByWeight(self.nearWeights) local grade = self.nearIds[prefabIndex] local worldPos = self:getRandomPointInRect(x, y, true) local fish = Fish.new(grade, self.normal_fishes_root, worldPos, self:getRandomPointListInRect(x, y, 5)) fish:setFishArea(x, y) -- 设置鱼所在区域 return fish end -- 设置近距离权重的鱼 function FishingGameMap:setNearWeights(fish, x, y) local prefabIndex = self:getRandomIndexByWeight(self.nearWeights) local grade = self.nearIds[prefabIndex] local worldPos = self:getRandomPointInRect(x, y, true) fish:reload(grade, self.normal_fishes_root, worldPos, self:getRandomPointListInRect(x, y, 5)) return fish end -- 创建远距离权重的鱼 function FishingGameMap:createDistanceWeights(x, y) local prefabIndex = self:getRandomIndexByWeight(self.distanceWeights) local grade = self.distanceIds[prefabIndex] local worldPos = self:getRandomPointInRect(x, y, true) local fish = Fish.new(grade, self.normal_fishes_root, worldPos, self:getRandomPointListInRect(x, y, 5)) fish:setFishArea(x, y) -- 设置鱼所在区域 return fish end -- 设置远距离权重的鱼 function FishingGameMap:setDistanceWeights(fish, x, y) local prefabIndex = self:getRandomIndexByWeight(self.distanceWeights) local grade = self.distanceIds[prefabIndex] local worldPos = self:getRandomPointInRect(x, y, true) fish:reload(grade, self.normal_fishes_root, worldPos, self:getRandomPointListInRect(x, y, 5)) return fish end -- 工具 -- 获取随机索引,根据权重数组 function FishingGameMap:getRandomIndexByWeight(ws) local total = 0 for _, w in ipairs(ws) do total = total + w end local r = math.random() * total local sum = 0 for i = 1, #ws do sum = sum + ws[i] if r < sum then return i end end return #ws end -- 获取随机点列表在矩形区域内 function FishingGameMap:getRandomPointListInRect(x, y, count) local points = {} for _ = 1, count do local point = self:getRandomPointInRect(x, y) table.insert(points, point) end return points end -- 获取随机点在矩形区域内 function FishingGameMap:getRandomPointInRect(x, y, isCreate) -- 通过两个对角点,获取矩形区域内的随机点 local minX = self.cellSize.width * (x - 1) + self.posOffset.x local maxX = self.cellSize.width * (x) + self.posOffset.x local minY = -self.cellSize.height * (y - 1) + self.posOffset.y local maxY = -self.cellSize.height * (y) + self.posOffset.y local posX = math.random() * (maxX - minX) + minX -- 在x范围内随机生成 local posY = math.random() * (maxY - minY) + minY -- 在y范围内随机生成 if isCreate then -- 确保生成的点不在不可用区域内 while posX >= self.unusedArea.minX and posX <= self.unusedArea.maxX do posX = math.random() * (maxX - minX) + minX -- 在x范围内随机生成 end while posY >= self.unusedArea.minY and posY <= self.unusedArea.maxY do posY = math.random() * (maxY - minY) + minY -- 在y范围内随机生成 end end -- ui尺寸转世界尺寸 --local screenPos = Vector3(Random.Range(minX, maxX), Random.Range(minY, maxY), 0) --local worldPos = FishingGameMgr.cameraCtl.camera:ScreenToWorldPoint(screenPos) --worldPos.z = 0 -- 确保z轴为0 -- 直接使用世界尺寸 return Vector3(posX, posY, 0) end -- 获取一个Renderer的世界尺寸 function FishingGameMap:getRendererWorldSize(targetObject) local renderer = targetObject:GetComponent("Renderer") if renderer == nil then printError(LOGTAG, "目标对象没有 Renderer 组件") return Vector3.zero end -- 获取物体的世界尺寸 return {width = renderer.bounds.size.x, height = renderer.bounds.size.y} end -- 设置物体世界宽度 function FishingGameMap:setWorldWidth(targetObject, width) local size = self:getRendererWorldSize(targetObject) local scaleWidth = width * (1 / size.width) targetObject.transform.localScale = Vector3(scaleWidth, targetObject.transform.localScale.y, 1) return scaleWidth end -- 设置物体世界高度 function FishingGameMap:setWorldHeight(targetObject, height) local size = self:getRendererWorldSize(targetObject) local scaleHeight = height * (1 / size.height) targetObject.transform.localScale = Vector3(targetObject.transform.localScale.x, scaleHeight, 1) return scaleHeight end -- 设置物体世界尺寸 function FishingGameMap:setWorldSize(targetObject, width, height) local size = self:getRendererWorldSize(targetObject) local scaleWidth = width * (1 / size.width) local scaleHeight = height * (1 / size.height) targetObject.transform.localScale = Vector3(scaleWidth, scaleHeight, 1) return { width = scaleWidth, height = scaleHeight } end -- 设置SpriteRenderer的Material参数 function FishingGameMap:setMaterialVector(targetObject, propertyName, vector4) local renderer = targetObject:GetComponent("Renderer") if renderer == nil then printError(LOGTAG, "目标对象没有 Renderer 组件") return Vector3.zero end if renderer and renderer.material then renderer.material:SetVector(propertyName, vector4) else printError(LOGTAG, "SpriteRenderer或其材质为空,无法设置属性") end end return FishingGameMap AdCustomerUI--- 广告窗口 ---@class AdCustomerUI : UILayer local AdCustomerUI, super = defClass("AdCustomerUI", UILayer) function AdCustomerUI:ctor(coinValue) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/customerui/customeruireslink") self.coinValue = coinValue self.isClear = false end function AdCustomerUI:onLoad() self.ui = GameObject.Instantiate(self.R.ad_customer_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() end function AdCustomerUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.video_btn = self.ui:Seek("video_btn") self.video_text = self.ui:Seek("video_text") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addButtonClickEvent(self.video_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:clickVideo() end) end function AdCustomerUI:showUI() self.video_text:GetComponent("TextMeshProUGUI").text = string.format("愿意观看一段视频获得%s金币吗?", self.coinValue) end function AdCustomerUI:clickVideo() printWarn("AdCustomerUI", "AdCustomerUI:clickVideo()") self:videoCallback() end function AdCustomerUI:videoCallback() self.isClear = true self:close() end function AdCustomerUI:badVideoCallback() self.isClear = false self:close() end return AdCustomerUI CuisineMgr--[[ desc: 菜品管理 纯数据管理,升级,交互其他模块等 author:{zhangpeng} time:2025-05-16 20:41:31 ]] --- 菜品管理 ---@class CuisineMgr local CuisineMgr = defClassStatic("CuisineMgr") local LOGTAG = "CuisineMgr" local IconPathBase = "Assets/AssetsPackage/Res/modules/cuisinen/images/%s.%s" -- 初始化 function CuisineMgr:init() printInfo(LOGTAG, "------ 菜品管理 初始化 ------") self:initEvent() self.infoDic = {} self:initUserData() end -- 初始化事件 function CuisineMgr:initEvent() self.cuisineLearnedEvent = SimpleEvent.new("CuisineLearnedEvent") end -- 初始化用户数据 function CuisineMgr:initUserData() self.userData = UserDataMgr.cuisineUserData end -- 设置事件 function CuisineMgr:setEvent() end -- 开始游戏 function CuisineMgr:startGame() self:setEvent() end -- 基础配置 -- 获取菜单信息 function CuisineMgr:getCuisineInfo(cuisineId) if self.infoDic[cuisineId] then return self.infoDic[cuisineId] end local info = CuisineInfo.new(cuisineId) self.infoDic[cuisineId] = info return info end -- 获取菜品配置 function CuisineMgr:getConfig(cuisineId) return CookBookCfgParse:getCookBookCfg(cuisineId) end -- 图标 -- 获取图标路径 function CuisineMgr:getIconPath(cuisineId) local info = self:getCuisineInfo(cuisineId) return CuisineMgr:getIconPathByName(info:getIconName()) end -- 获取图标路径 function CuisineMgr:getIconPathByName(iconName) return string.format(IconPathBase, iconName, "png") end -- 获取图标 function CuisineMgr:getIcon(cuisineId) local path = self:getIconPath(cuisineId) if ResLoader.hasAsset(path) then return ResLoader.loadAsset(path, typeof(CS.UnityEngine.Sprite)) end -- 如果没有找到对应的图标,返回默认图标 return ResLoader.loadAsset(self:getIconPathByName("default_cuisine"), typeof(CS.UnityEngine.Sprite)) end -- 交互 -- 解锁 function CuisineMgr:cuisineUnlock(cuisineId) local info = self:getCuisineInfo(cuisineId) info:unlock() UserDataMgr:addUnlockCuisineCount(1) end -- 学习 function CuisineMgr:cuisineLearn(cuisineId) local info = self:getCuisineInfo(cuisineId) if not BuyMgr:buy(info:getBuyCond()) then return end info:learn() -- 添加菜品加成 BonusMgr:addBonusIdByCuisineId(cuisineId) self.cuisineLearnedEvent:triggerEvent(cuisineId) -- 触发菜品学习事件 -- 任务进度 UserDataMgr:addUnlockCuisineCount(1) end -- 视频学习 function CuisineMgr:cuisineLearnByVideo(cuisineId, learnShortage) local info = self:getCuisineInfo(cuisineId) if not BuyMgr:buyByVideo(info:getBuyCond(), learnShortage) then return end info:learn() -- 添加菜品加成 BonusMgr:addBonusIdByCuisineId(cuisineId) self.cuisineLearnedEvent:triggerEvent(cuisineId) -- 触发菜品学习事件 -- 任务进度 UserDataMgr:addUnlockCuisineCount(1) end -- 升级 function CuisineMgr:cuisineUpgrade(cuisineId) local info = self:getCuisineInfo(cuisineId) if not info then return end info:upgrade() end -- 是否解锁 function CuisineMgr:isCuisineUnlocked(cuisineId) return self.userData:getCuisineState(cuisineId) >= CuisineConst.State.Unlocked end -- 是否学习 function CuisineMgr:isCuisineLearned(cuisineId) return self.userData:getCuisineState(cuisineId) >= CuisineConst.State.Purchased end -- 销量增长 function CuisineMgr:addCuisineSales(cuisineId, sales) local info = self:getCuisineInfo(cuisineId) if not info then return end -- 更新存档 local curr = self.userData:addCuisineSales(cuisineId, sales) -- 更新信息状态 info:setSales(curr) return curr end return CuisineMgr ReceptionMgr( --[[ 迎宾台管理器, 迎宾台只有一个 author:{zhangpeng} time:2025-05-16 14:55:41 ]] ---@class ReceptionMgr local ReceptionMgr = defClassStatic("ReceptionMgr") local LOGTAG = "ReceptionMgr" -- init function ReceptionMgr:init() printInfo(LOGTAG, "------ 接待台管理器 初始化 ------") end -- 根据建筑id创建一个迎宾台 function ReceptionMgr:createReception(buildingInfo) local buildingType = buildingInfo.buildingType local parentNode = RestaurantMgr:getRestaurantScene().rootNode:Seek("building_pos_" .. buildingType) local args = { deskId = buildingInfo.buildingCfgId, parent = parentNode, scene = RestaurantMgr:getRestaurantScene(), } local reception = nil if self:hasSameTypeReception(buildingType) then self:replaceOldReception(buildingType, buildingInfo.buildingCfgId) reception = self.receptionDesk -- 返回已替换的迎宾台 else reception = ReceptionDesk.new(args) self.receptionDesk = reception end return reception end -- 是否有同类不同id的迎宾台 function ReceptionMgr:hasSameTypeReception(buildingType) if self.receptionDesk and self.receptionDesk.buildingInfo then if self.receptionDesk.buildingInfo.buildingType == buildingType then return true end end return false end -- 查找相同类型建筑的旧迎宾台,替换旧的迎宾台 -- @param buildingType 建筑类型 -- @param newReceptionCfgId 新迎宾台配置id function ReceptionMgr:replaceOldReception(buildingType, newReceptionCfgId) if self.receptionDesk and self.receptionDesk.buildingInfo then local receptionDesk = self.receptionDesk -- 大类相同,配置id不同,则是旧迎宾台 if receptionDesk.buildingInfo.buildingType == buildingType and receptionDesk.buildingInfo.buildingCfgId ~= newReceptionCfgId then -- 只修改一下prefab的名字为配置id receptionDesk.buildingNode:SetName(newReceptionCfgId) end end end -- 初始化迎宾台 function ReceptionMgr:initReception() -- 迎宾台只有一个 local scene = RestaurantMgr:getRestaurantScene() local args = { deskId = ReceptionConst.receptionDeskId, parent = scene.rootNode:Seek("building_pos_" .. ReceptionConst.receptionDeskId), scene = scene, } -- 初始化迎宾台 self.receptionDesk = ReceptionDesk.new(args) end -- 获取迎宾台 function ReceptionMgr:getReception() return self.receptionDesk end return ReceptionMgr DeviceVibrationc--[[ 设备振动 author:{zhangpeng} time:2025-04-18 20:59:16 ]] local DeviceVibration, super = defClassStatic("DeviceVibration") local IOS_NATIVE_CLASS_NAME = "DeviceVibrationUtil" local JavaADClass = "com/fy/xgame/tilelink/util/VibrationUtil" function DeviceVibration:init() end function DeviceVibration:vibrate(milliseconds) if Device.isIOS() then luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "vibrate", {milliseconds}) elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "vibrate", {tostring(milliseconds)}) end end DeviceVibration:init() UGuiUtil?-- -- UGUI 的lua侧工具,给UI添加各种触摸事件 -- local UGuiUtil = {} local LOGTAG = "UGuiUtil" local EventTrigger = CS.UnityEngine.EventSystems.EventTrigger local EventTriggerType = CS.UnityEngine.EventSystems.EventTriggerType local Button = CS.UnityEngine.UI.Button local UnityEngine = CS.UnityEngine local UI = CS.UnityEngine.UI local Screen = CS.UnityEngine.Screen local RectTransform = CS.UnityEngine.RectTransform function UGuiUtil._addEvent(go,eventID,cb) local trigger = go:AddComponent(typeof(EventTrigger)) local entry = EventTrigger.Entry() entry.eventID = eventID entry.callback = EventTrigger.TriggerEvent() entry.callback:AddListener( function (data) -- print("click ugui:"..go.name) if cb then cb(data) end end) trigger.triggers:Add(entry) end function UGuiUtil.addClickEvent(go, cb) if not go then return end go._clickCallbackList = go._clickCallbackList or {} table.insert(go._clickCallbackList, cb) UGuiUtil._addEvent(go,EventTriggerType.PointerClick,cb) end function UGuiUtil.addBeginDragEvent(go,cb) if go then UGuiUtil._addEvent(go,EventTriggerType.BeginDrag,cb) end end function UGuiUtil.addDragEvent(go,cb) if go then UGuiUtil._addEvent(go,EventTriggerType.Drag,cb) end end function UGuiUtil.addEndDragEvent(go,cb) if go then UGuiUtil._addEvent(go,EventTriggerType.EndDrag,cb) end end function UGuiUtil.addPointerDownEvent(go,cb) if go then UGuiUtil._addEvent(go,EventTriggerType.PointerDown,cb) end end function UGuiUtil.addPointerUpEvent(go,cb) if go then UGuiUtil._addEvent(go,EventTriggerType.PointerUp,cb) end end function UGuiUtil.addPointerEnterEvent(go,cb) if go then UGuiUtil._addEvent(go,EventTriggerType.PointerEnter,cb) end end function UGuiUtil.addPointerExitEvent(go,cb) if go then UGuiUtil._addEvent(go,EventTriggerType.PointerExit,cb) end end function UGuiUtil.addMoveEvent(go,cb) if not go then return end UGuiUtil._addEvent(go,EventTriggerType.Move,cb) end function UGuiUtil.addButtonClickEvent(go,cb) if not go then return end local button = go:GetComponent(typeof(Button)) if not button then return end go._clickCallbackList = go._clickCallbackList or {} table.insert(go._clickCallbackList, cb) button.onClick:AddListener(cb) end -- 输入框内容变化事件 function UGuiUtil.addInputFieldValueChangeEvent(go, cb) if not go then return end local componet = go:GetComponent(typeof(UI.InputField)) if not componet then return end componet.onValueChanged:AddListener(cb) end -- 输入框输入结束 function UGuiUtil.addInputFieldEndEditEvent(go, cb) if not go then return end local componet = go:GetComponent(typeof(UI.InputField)) if not componet then return end componet.onEndEdit:AddListener(cb) end -- 复选框切换 function UGuiUtil.addToggleValueChangeEvent(go, cb) if not go then return end local componet = go:GetComponent(typeof(UI.Toggle)) if not componet then return end componet.onValueChanged:AddListener(cb) end function UGuiUtil.isLongScreen() local screenWidth = CS.UnityEngine.Screen.width local screenHeight = CS.UnityEngine.Screen.height local whRatio = screenWidth / screenHeight if whRatio >= 1.78 then --2436*1125 是iphoneX的分辨率 return true else return false end end function UGuiUtil.worldSpaceToLocalSpace(p3, rectTrans) p3 = rectTrans:InverseTransformPoint(p3) local p2 = CS.UnityEngine.Vector2(p3.x, p3.y) return p2 end ---@param p2 CS.UnityEngine.Vector2 ---@param rectTrans CS.UnityEngine.RectTransform ---@param cameraCom CS.UnityEngine.Camera ---@return CS.UnityEngine.Vector2 function UGuiUtil.screenSpaceToLocalSpace(p2, rectTrans, cameraCom) local p3 = CS.UnityEngine.Vector3(p2.x, p2.y, 0) p3 = cameraCom:ScreenToWorldPoint(p3) p2 = util.ugui.worldSpaceToLocalSpace(p3, rectTrans) return p2 end ---屏幕空间转canvas空间 ---@param p2 CS.UnityEngine.Vector2 ---@param canvas CS.UnityEngine.GameObject ---@param cameraCom CS.UnityEngine.Camera function UGuiUtil.screenSpaceToCanvasSpace(p2, canvas, cameraCom) p2 = util.ugui.screenSpaceToLocalSpace(p2, canvas[CS.UnityEngine.RectTransform], cameraCom) return p2 end function UGuiUtil.getCutoutAreaRectInCanvasSpace(isFourceSymmetry, padding) padding = padding or 0 local orientation = CS.UnityEngine.Screen.orientation local w, h = CS.UnityEngine.Screen.width, CS.UnityEngine.Screen.height local rect = CS.UnityEngine.Rect(0, 0, w, h) local isCutout = util.ugui.isLongScreen() --and Device.isIOS() if isCutout then if orientation == CS.UnityEngine.ScreenOrientation.LandscapeLeft then rect.xMin = rect.xMin + 90 if isFourceSymmetry then rect.xMax = rect.xMax - 90 end else rect.xMax = rect.xMax - 90 if isFourceSymmetry then rect.xMin = rect.xMin + 90 end end end local p1 = util.ugui.screenSpaceToCanvasSpace(rect.min) local p2 = util.ugui.screenSpaceToCanvasSpace(rect.max) if isCutout then if p1 and p2 then p1.x = p1.x + padding p2.x = p2.x - padding end end return CS.UnityEngine.Rect(p1, p2 - p1) end -- 获取屏幕尺寸 function UGuiUtil:getScreenSize() if self.screenSize then return self.screenSize end local canvas = CS.UnityEngine.GameObject.Find("UIRoot") local size = {x = 0, y = 0} if canvas then local rectTrans = canvas:GetComponent(typeof(RectTransform)) if rectTrans then local sizeDelta = rectTrans.sizeDelta size.width = sizeDelta.x size.height = sizeDelta.y self.screenSize = size return size end end self.screenSize = size return size end -- 某一个物体是否在屏幕内 function UGuiUtil:isInScreen(node) local cam = UnityEngine.GameObject.Find("UICamera") local screenSize = self:getScreenSize() local worldpos = UnityEngine.Vector3(node:GetWorldPosition().x, node:GetWorldPosition().y, 0) local screenpos = cam:GetComponent("Camera"):WorldToScreenPoint(worldpos) return screenpos.x >0 and screenpos.x < screenSize.width end -- 某一个物体是否在某个物体的右侧 function UGuiUtil:isWayPointInScreen(rightNode,node) local cam = UnityEngine.GameObject.Find("UICamera") local rightNode_worldpos = UnityEngine.Vector3(rightNode:GetWorldPosition().x, rightNode:GetWorldPosition().y, 0) local rightNodescreenpos = cam:GetComponent("Camera"):WorldToScreenPoint(rightNode_worldpos) local screenSize = self:getScreenSize() local worldpos = UnityEngine.Vector3(node:GetWorldPosition().x, node:GetWorldPosition().y, 0) local screenpos = cam:GetComponent("Camera"):WorldToScreenPoint(worldpos) return screenpos.x - rightNodescreenpos.x > 0 and screenpos.x < screenSize.width end ---获取真正的安全区,去掉留海等等 ---@param padding {left:number, bottom:number, right:number, top:number} ---@param canvas CS.UnityEngine.GameObject ---@param cameraCom CS.UnityEngine.Camera function UGuiUtil.getSafeAreaRectInCanvasSpace(padding, canvas, cameraCom) padding = padding or CS.UnityEngine.RectOffset(-25, -25, 0, -25) local safeArea = Screen.safeArea safeArea.xMin = math.max(safeArea.xMin + padding.left, 0) safeArea.yMin = math.max(safeArea.yMin + padding.bottom, 0) safeArea.xMax = math.min(safeArea.xMax - padding.right, Screen.width) safeArea.yMax = math.min(safeArea.yMax - padding.top, Screen.height) local p1 = util.ugui.screenSpaceToCanvasSpace(safeArea.min, canvas, cameraCom) local p2 = util.ugui.screenSpaceToCanvasSpace(safeArea.max, canvas, cameraCom) local screenMin = util.ugui.screenSpaceToCanvasSpace(Vector2(0, 0), canvas, cameraCom) local screenMax = util.ugui.screenSpaceToCanvasSpace(Vector2(Screen.width, Screen.height), canvas, cameraCom) return Rect(p1, p2 - p1),p1-screenMin,p2-screenMax end function UGuiUtil.isUIGameObject(go) -- local isui = go:GetComponent(typeof(RectTransform)) ~=nil local isui = go:SeekInParentHierarchy("UICanvas") return isui end function UGuiUtil.isGlobalUIGameObject(go) return go:SeekInParentHierarchy("UIGlobal.uiroot") end -- 世界空间转canvas空间 function UGuiUtil.worldSpaceToCanvasSpace(p3, canvas) local p2 = util.ugui.worldSpaceToLocalSpace(p3, canvas[CS.UnityEngine.RectTransform]) return p2 end function UGuiUtil.rmClickEvent(go) if not go then return end go._clickCallbackList = nil UGuiUtil._rmEvent(go, EventTriggerType.PointerClick) end function UGuiUtil._rmEvent(go, eventID) local triggerList = go:GetComponents(typeof(EventTrigger)) if not triggerList then return end for i = 0, triggerList.Length - 1 do local trigger = triggerList[i] local entryList = trigger.triggers for j = entryList.Count - 1, 0 do local entry = entryList[j] if entry.eventID == eventID then entryList:Remove(entry) end end end end function UGuiUtil.rebuildLayout(go) if not go then printWarn(LOGTAG, "rebuildLayout, go is nil") return end local rectTrans = go[RectTransform] if not rectTrans then printWarn(LOGTAG, "rebuildLayout, rectTrans is nil") return end CS.UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(rectTrans) end function UGuiUtil.disableAllTouches() CS.LuaGlobal.instance:SetTouchEnable(false) end function UGuiUtil.enableAllTouches() CS.LuaGlobal.instance:SetTouchEnable(true) end --#region 坐标转换 ---本地空间转世界空间 未测试 ---@param p2 CS.UnityEngine.Vector2 ---@param rectTrans CS.UnityEngine.RectTransform ---@return CS.UnityEngine.Vector3 function UGuiUtil.localSpaceToWorldSpace(p2, rectTrans) local p3 = CS.UnityEngine.Vector3(p2.x, p2.y, 0) p3 = rectTrans:TransformPoint(p3) return p3 end ---本地空间转屏幕空间 未测试 ---@param p2 CS.UnityEngine.Vector2 ---@param rectTrans CS.UnityEngine.RectTransform ---@param cameraCom CS.UnityEngine.Camera ---@return CS.UnityEngine.Vector2 function UGuiUtil.localSpaceToScreenSpace(p2, rectTrans, cameraCom) local p3 = UGuiUtil.localSpaceToWorldSpace(p2, rectTrans) p3 = cameraCom:WorldToScreenPoint(p3) local p2 = CS.UnityEngine.Vector2(p3.x, p3.y) return p2 end ---把 listener 移除,以防止 csharp 持有 lua 的方法,无法销毁 luaenv function UGuiUtil.removeAllListeners() printInfo(LOGTAG, "removeAllListeners") for index, value in ipairs(events) do value:RemoveAllListeners() end end --- 获取B物体世界坐标,讲B物体wp坐标转换到A物体父节点下的局部坐标 -- 让a物体(fromNode)飞到b物体(toNode),支持自定义飞行时间和动画结束回调。 -- @param fromNode 源UGUI节点 -- @param toNode 目标UGUI节点 -- @param duration 动画时长,单位秒 -- @param needClone 是否需要克隆 -- @param onComplete 回调函数,动画结束后调用 function UGuiUtil.flyUIToUI(fromNode, toNode, duration, needClone, onComplete) -- 1. 克隆fromNode到其父节点下 local parent = fromNode.transform.parent if not parent then printWarn(LOGTAG, "fromNode has no parent!") return end local parentRect = parent:GetComponent(typeof(CS.UnityEngine.RectTransform)) local actFromNode if needClone then actFromNode = GameObject.Instantiate(fromNode) actFromNode:SetName("fly_clone") actFromNode.transform:SetParent(parent, false) actFromNode.transform:SetAsLastSibling() else actFromNode = fromNode end -- 2. 获取toNode的世界坐标 local toWorldPos = toNode.transform:TransformPoint(Vector3.zero) -- 3. 获取fromNode父节点 local fromParent = fromNode:GetParent() -- 4. 将toNode的世界坐标转换到fromNode父节点下的局部坐标 local localTo = fromParent.transform:InverseTransformPoint(toWorldPos) actFromNode:RunAction(ua.Sequence({ ua.MoveTo(duration or 1.0, Vector3(localTo.x, localTo.y, 0)), ua.cb(function () if onComplete then onComplete(actFromNode) end end) })) end --- -- 让UI节点(fromNode)飞向世界物体(targetWorldObj),支持自定义飞行时间和动画结束回调。 -- @param fromNode 源UGUI节点(RectTransform) -- @param targetWorldObj 目标世界物体(如3D物体、场景物体等,需有GetWorldPosition方法或transform.position) -- @param duration 动画时长,单位秒 -- @param needClone 是否需要克隆UI节点 -- @param onComplete 回调函数,动画结束后调用 -- @param canvas UI画布对象(可选,默认查找"UIRoot") function UGuiUtil.flyUIToWorld(fromNode, targetWorldObj, duration, needClone, onComplete, canvas) canvas = canvas or CS.UnityEngine.GameObject.Find("UIRoot") if not canvas then printWarn(LOGTAG, "找不到UIRoot画布") return end local uiCamera = UILayerUtil:getCamera() local mainCamera = nil -- 兼容目标物体是3D物体或带有GetWorldPosition方法 local worldPos = nil if targetWorldObj.GetWorldPosition then worldPos = targetWorldObj:GetWorldPosition() elseif targetWorldObj.transform and targetWorldObj.transform.position then worldPos = targetWorldObj.transform.position else printWarn(LOGTAG, "目标物体没有世界坐标") return end -- 1. 获取目标世界物体的世界坐标转屏幕坐标 mainCamera = CS.UnityEngine.GameObject.Find("MainCamera") if not mainCamera then printWarn(LOGTAG, "找不到主摄像机 MainCamera") return end local screenPos = mainCamera:GetComponent("Camera"):WorldToScreenPoint(worldPos) -- 2. 屏幕坐标转UI画布下的局部坐标 local canvasRect = canvas:GetComponent(typeof(CS.UnityEngine.RectTransform)) local localTo = util.ugui.screenSpaceToLocalSpace(screenPos, canvasRect, uiCamera) -- 3. 获取fromNode在画布下的局部坐标 local fromParent = fromNode:GetParent() local actFromNode if needClone then actFromNode = GameObject.Instantiate(fromNode) actFromNode:SetName("fly_clone") actFromNode.transform:SetParent(fromParent, false) actFromNode.transform:SetAsLastSibling() else actFromNode = fromNode end -- 4. 执行动画 actFromNode:RunAction(ua.Sequence({ ua.MoveTo(duration or 1.0, Vector3(localTo.x, localTo.y, 0)), ua.cb(function () if onComplete then onComplete(actFromNode) end end) })) end -- 获取设备分辨率 function UGuiUtil.getDeviceResolution() local width = CS.UnityEngine.Screen.width local height = CS.UnityEngine.Screen.height printInfo(LOGTAG, string.format("设备分辨率: %dx%d", width, height)) return {width = width, height = height} end -- 按指定大小正方形框限制设置图片的平铺尺寸 function UGuiUtil:setImageTileSize(image, size) self:setImageTileSizeToRectangles(image, size, size) end -- 按指定大小长方形框限制设置图片的平铺尺寸 function UGuiUtil:setImageTileSizeToRectangles(image, maxWidth, maxHeight) if (not image) or (not image.sprite) then printWarn(LOGTAG, "图片平铺设置失败") return end local originalWidth = image.sprite.bounds.size.x local originalHeight = image.sprite.bounds.size.y local widthRatio = maxWidth / originalWidth; local heightRatio = maxHeight / originalHeight; -- 取最小比例,保证不会超出限制 local scaleRatio = Mathf.Min(widthRatio, heightRatio); -- 设置图片,保持图片的宽高比 image.rectTransform.sizeDelta = Vector2(originalWidth * scaleRatio, originalHeight * scaleRatio); end return UGuiUtilDiningDeskSeat&--[[ 餐桌作为 author:{zhangpeng} time:2025-05-16 12:00:51 ]] local DiningDeskSeat, super = defClass("DiningDeskSeat") local LOGTAG = "DiningDeskSeat" function DiningDeskSeat:ctor(seatNode) self.state = DiningConst.SeatState.idle self.cuisineId = nil self.customer = nil self.cuisine = nil self.isSeatDirty = false self.diningDesk = nil -- 所属餐桌 self.seatIndex = 0 -- 座位索引 self.buildingNode = seatNode -- 座位节点 self.foodBubble = nil -- 食物节点上的气泡 self.seatPoint = self.buildingNode:Seek("yz_point") end -- 设置座位状态 function DiningDeskSeat:setState(state) self.state = state -- 更新餐桌状态 if self.diningDesk then self.diningDesk:updateState() end end -- 获取座位状态 function DiningDeskSeat:getState() return self.state end -- 获取座位位置 function DiningDeskSeat:getSeatPosition() if self.seatPoint then -- 直接获取seatPoint的世界坐标 return self.seatPoint.transform.position end -- 后备方案:使用buildingNode的位置 return self.buildingNode.transform.position end -- 设置座位位置 function DiningDeskSeat:setSeatPoint(point) self.seatPoint = point end -- 是否可入座 function DiningDeskSeat:isCanSitDowm() -- 只有座位状态为idle(空闲)时才能入座 if self.state ~= DiningConst.SeatState.idle then return false end -- 确保没有顾客正在前往该座位 if self.customer ~= nil then return false end return true end --- 坐入顾客 function DiningDeskSeat:sitDown(target) -- 检查状态是否为"正在前往" if self.state ~= DiningConst.SeatState.approaching then printError(LOGTAG, string.format("座位[%d]状态错误,当前状态:%d,应为:%d", self.seatIndex, self.state, DiningConst.SeatState.approaching)) return false end -- 确保是同一个顾客 if self.customer ~= target then printError(LOGTAG, string.format("座位[%d]顾客不匹配,期望:%s,实际:%s", self.seatIndex, self.customer and self.customer:getRoleUniqueId() or "nil", target and target:getRoleUniqueId() or "nil")) return false end -- 设置座位状态 self:setState(DiningConst.SeatState.used) printInfo(LOGTAG, string.format("顾客[%s-%s]入座到座位[%d]", self.customer.customerCfgId, self.customer.roleUniqueId, self.seatIndex)) return true end -- 生成该座位的订单 function DiningDeskSeat:genOrder(orderArgs) printInfo(LOGTAG, string.format("[%s号桌号-%s号座位]生成订单,顾客ID:[%s-%s],菜品ID:[%s]", self.diningDesk:getDeskIndex(), self.seatIndex, orderArgs.customerId, orderArgs.customer.roleUniqueId, orderArgs.cuisineId)) -- 生成订单 local orderInfo = OrderInfo.new(orderArgs) -- 使用setSeat方法设置座位 orderInfo:setSeat(self) -- 保存订单到座位 self.cuisine = orderInfo -- 设置状态为等待上菜 self:setState(DiningConst.SeatState.waitingFood) return orderInfo end -- 上菜 function DiningDeskSeat:giveCuisine(orderInfo) printInfo(LOGTAG, string.format("座位[%d]上菜,顾客ID:[%s-%s],菜品ID:[%s]", self.seatIndex, orderInfo.customer.customerCfgId, orderInfo.customer.roleUniqueId, orderInfo.cuisineId)) -- 保存菜品信息 self.cuisineId = orderInfo.cuisineId self.cuisine = orderInfo -- 设置状态为进餐中 self:setState(DiningConst.SeatState.eating) -- 在餐桌食物节点上显示菜品 self:showFoodBubble() -- 通知顾客开始用餐 if self.customer then self.customer:startEating(function () -- 就餐结束 self:finishEating() end) end end -- 取消当前的延迟任务 function DiningDeskSeat:cancelDelayTasks() -- 尝试停止通过buildingNode设置的延迟任务 if self.buildingNode then if self.eatingActionId then self.buildingNode:StopAction(self.eatingActionId) self.eatingActionId = nil end end end -- 取消当前座位的订单(暂时不需要) function DiningDeskSeat:cancelOrder() -- 取消延迟任务 self:cancelDelayTasks() -- 清除食物气泡 self:clearFoodBubble() -- 清理订单信息 self.cuisineId = nil self.cuisine = nil -- 设置状态为idle self:setState(DiningConst.SeatState.idle) printInfo(LOGTAG, string.format("座位[%d]的订单已取消", self.seatIndex)) end -- 在餐桌食物节点上显示菜品气泡 function DiningDeskSeat:showFoodBubble() -- 确保有cuisineId if not self.cuisineId then printError(LOGTAG, "showFoodBubble失败:没有菜品ID") return end -- 获取food节点 local foodNode = self:getFoodNode() if not foodNode then printError(LOGTAG, string.format("座位[%d]找不到对应的food节点", self.seatIndex)) return end -- 创建菜品气泡 printInfo(LOGTAG, string.format("在餐桌食物节点上显示菜品[%s]", self.cuisineId)) self.foodBubble = CuisineBubbleMgr:createBubble(self.cuisineId, foodNode, CuisineConst.BubbleShowParentType.desk) end -- 清除餐桌食物节点上的菜品气泡 function DiningDeskSeat:clearFoodBubble() if self.foodBubble then printInfo(LOGTAG, "清除餐桌食物节点上的菜品气泡") -- 调用destroy方法销毁资源 self.foodBubble:destroy() self.foodBubble = nil end end -- 获取food节点 function DiningDeskSeat:getFoodNode() if not self.diningDesk then return nil end return self.diningDesk:getFoodNodeBySeatIndex(self.seatIndex) end -- 完成用餐 function DiningDeskSeat:finishEating() -- 取消可能存在的延迟任务,防止重复调用 self:cancelDelayTasks() printInfo(LOGTAG, string.format("座位[%d]完成用餐,顾客ID:[%s-%s]", self.seatIndex, self.customer.customerCfgId, self.customer.roleUniqueId)) -- 清除餐桌食物气泡 self:clearFoodBubble() -- 设置座位为脏 self:setSeatDirty() -- 在餐桌上创建金币 if self.diningDesk then -- 创建金币 local cuisineInfo = CuisineMgr:getCuisineInfo(self.cuisineId) local coin = CoinDeskMgr:createCoin(self.diningDesk, cuisineInfo:getCurrPrice(), MapsConst.MapType.restaurant) -- 延迟1秒后自动收集金币 self.buildingNode:Delay(1.0, function() CoinDeskMgr:clearCoinsOnDesk(self.diningDesk:getBuildingId()) end) end -- 通知顾客离开 if self.customer then -- 保存顾客引用 local customer = self.customer local customerId = customer.customerCfgId -- 清除当前顾客引用 self.customer = nil -- 清理用餐相关信息 self.cuisineId = nil self.cuisine = nil -- 恢复到idle状态,但标记为脏 self:setState(DiningConst.SeatState.idle) -- 通知顾客开始闲逛 customer:startWandering() -- 一次招待完成 -- 任务进度 UserDataMgr:addEntertainCustomerCount(1) UserDataMgr:addEntertainSpecificCustomerCount(customerId, 1) if CustomerMgr.customerFirstVisit:getCurrentNewCustomerId() == customerId then CustomerMgr.customerFirstVisit:firstVisitComplete(customerId) end end end -- 没有餐品 function DiningDeskSeat:noFood() -- 取消可能存在的延迟任务,防止重复调用 self:cancelDelayTasks() printInfo(LOGTAG, string.format("座位[%d]没有菜品,顾客ID:[%s-%s]", self.seatIndex, self.customer.customerCfgId, self.customer.roleUniqueId)) -- 清除餐桌食物气泡 self:clearFoodBubble() -- 通知顾客离开 if self.customer then -- 保存顾客引用 local customer = self.customer -- 清除当前顾客引用 self.customer = nil -- 清理用餐相关信息 self.cuisineId = nil self.cuisine = nil -- 恢复到idle状态,但标记为脏 self:setState(DiningConst.SeatState.idle) -- 强制拉回到坐下前的安全位置 customer:forceReturnToSafePosition() -- 来访失败 CustomerMgr.customerFirstVisit:firstVisitFail(customer.customerCfgId) -- 立即设置为离开状态并前往出口,跳过闲逛流程 customer.state = CustomerConst.State.leaving local exitPosition = customer:getRestaurantExitPosition() customer:rolePathFind(exitPosition, function() printInfo(LOGTAG, string.format("顾客[%s-%s]已到达出口,离开餐厅", customer.customerCfgId, customer.roleUniqueId)) -- 移除顾客对象 customer:onExit() end) end end -- 餐桌是否为脏 function DiningDeskSeat:checkSeatDirty() return self.isSeatDirty end -- 设置为脏 function DiningDeskSeat:setSeatDirty() self.isSeatDirty = true -- todo 展示餐余物 end -- 清理餐余物 function DiningDeskSeat:cleanSeatResidueCuisine() if not self.isSeatDirty then return end self.isSeatDirty = false -- todo::清理餐余物 end ---点击 function DiningDeskSeat:clickSeat() -- todo::点击座位 end -- 获取座位上的顾客 function DiningDeskSeat:getCustomer() return self.customer end -- 设置所属餐桌和索引 function DiningDeskSeat:setDiningDeskAndIndex(diningDesk, index) self.diningDesk = diningDesk self.seatIndex = index end return DiningDeskSeat TaskUIConst--[[ 任务ui常量 author:{zhangpeng} time:2025-05-26 12:05:11 ]] local TaskUIConst = defClassStatic("TaskUIConst") TaskUIConst.TaskType = { -- 每日任务 daily = 1, -- 成就任务 achievement = 2, -- 订单任务 order = 3, } -- 任务状态 TaskUIConst.TaskState = { -- 进行中 in_progress = 1, -- 可完成,未领取 success = 2, -- 已领取 claimed = 3, } function TaskUIConst:init() end TaskUIConst:init() uicircleloadingreslinkreturn { --BASIC --ASSET circle_loading_ui = {"Assets/AssetsPackage/Res/modules/common/ui/circle_loading/circle_loading_ui.prefab", 0, 0}, } TextCfg--[[ from file:TextCfg.xlsx --]] local TextCfg = { [1] = { textId = "buy_suc", cn = "购买成功", en = "Purchase successful!", jp = '', desc = '', }, } return TextCfgFish-- 鱼 local Fish = defClass("Fish") -- 构造函数 function Fish:ctor(grade, parent, worldPos, waypoints) self.grade = grade self:initData() self:initDisplay(parent) self:initEvent() self:start(worldPos, waypoints) end -- 初始化数据 function Fish:initData() local configList = FishCfgParse:getFishCfgByGrade(self.grade) local config = configList[math.random(1, #configList)] self.id = config.id self.speed = config.speed self.energy = config.energy end -- 初始化显示 function Fish:initDisplay(parent) local resId = "fish_" .. self.id self.go = GameObject.Instantiate(FishingGameMgr.resLink[resId], parent) self.transform = self.go.transform local gradeConfig = FishingGameMgr.map.gradeConfig self:setScale(gradeConfig.fishZoom[self.grade] or 1) self:setSpeed(self.speed * (gradeConfig.fishSpeed[self.grade] or 0)) end -- 初始化事件 function Fish:initEvent() self.collider = self.go:Seek("collider") --Event.add(self.collider, Event.OnCollisionEnter2D, function(other) Event.add(self.collider, Event.OnTriggerEnter2D, function(other) self:onTriggerEnter(other) end) end -- 开始 function Fish:start(worldPos, waypoints) self.transform.localPosition = worldPos self:setWaypoints(worldPos, waypoints) self:startSwimming() self.currPos = self.transform.localPosition self.active = true --self.grade = 1 -- test end -- 当玩家触发碰撞时 function Fish:onTriggerEnter(other) if (not FishingGameMgr.isPlaying) or FishingGameMgr.isPaused then return end if other.gameObject:CompareTag("Player") then if FishingGameMgr:getCharacterGrade() >= self.grade then FishingGameMgr:eat(self) else FishingGameMgr:hurt() -- 改为减少能量 end end end -- 设置鱼所在区域 function Fish:setFishArea(x, y) self.activeArea = {x = x, y = y} end -- 获取鱼所在区域 function Fish:getFishArea() return self.activeArea end -- 重新加载鱼对象 function Fish:reload(grade, parent, worldPos, waypoints) self.grade = grade self:initData() self:initDisplay(parent) self:initEvent() self:start(worldPos, waypoints) end -- 设置路径点 function Fish:setWaypoints(worldPos, waypoints) self.worldPos = worldPos self.waypoints = waypoints table.insert(self.waypoints, 1, worldPos) table.insert(self.waypoints, worldPos) end -- 隐藏 function Fish:hide() self.go:SetActive(false) self.active = false end -- 销毁鱼对象 function Fish:destroy() if not self.go then return end local go = self.go self.active = false GameObject.Destroy(go) --Event.remove(go, Event.OnTriggerEnter2D) end -- 设置鱼的能量 function Fish:getEnergy() return self.energy end -- 获取前点 function Fish:getPreviousWaypoint(idx) if idx == 1 then return self.waypoints[#self.waypoints] end return self.waypoints[idx - 1] end -- 获取后点 function Fish:getNextWaypoint(idx) if idx == #self.waypoints then return self.waypoints[1] end return self.waypoints[#self.waypoints + 1] end -- 设置眩晕 function Fish:setDizzy(dizzy) self.dizzy = dizzy if (not self.active) or (not self.go) then return end local anim = self.go:Seek("dizzy") if anim then anim:SetActive(dizzy) end --if dizzy then -- -- 设置眩晕动画 -- local anim = self:Seek("dizzy") -- if anim then -- anim:SetActive(true) -- end --end end -- 时间更新 function Fish:timeUpdate(deltaTime) if self.active and (not self.dizzy) then if self.bezierMover then self.bezierMover:update(deltaTime) --local dir = self:getMoveDirection(self.currPos, self.transform.localPosition) --self.transform.right = -dir --if dir.x < 0 then -- self.transform.localScale = Vector3(1, 1, 1) --else -- self.transform.localScale = Vector3(-1, 1, 1) --end end end end -- 开始游 function Fish:startSwimming() self.bezierMover = BezierMover.new(self.go, self.waypoints, self.speed / 100, true, false, true, 180) end -- 更新等级配置 function Fish:updateGradeConfig(gradeConfig) if (not self.go) or not self.active then return end self:setScale(gradeConfig.fishZoom[self.grade] or 1) self:setSpeed(self.speed * (gradeConfig.fishSpeed[self.grade] or 1)) end -- 设置新的移动速度 function Fish:setSpeed(speed) if self.bezierMover then self.bezierMover:setSpeed(speed / 100) -- 转换为单位速度 end end -- 设置新的缩放 function Fish:setScale(scale) if self.go then self.go:SetScalef(scale) end end -- 通过前后两个位置获取移动方向 function Fish:getMoveDirection(beforePos, currPos) local direction = currPos - beforePos return direction.normalized end return Fish GameConst~ local GameConst = defClassStatic("GameConst") function GameConst:init() end GameConst.Const = { } GameConst:init()main-- 顾客常量 require("modules/role/customer/CustomerConst") -- 顾客信息 require("modules/role/customer/data/main") -- 特殊顾客 require("modules/role/customer/special/main") -- 顾客基类 require("modules/role/customer/Customer") -- 顾客管理 require("modules/role/customer/CustomerMgr") -- 顾客生成 require("modules/role/customer/generated/main") Env--[[ author:{zhangpeng} time:2022-05-11 18:15:12 ]] ---@class Env:LuaClass local Env,super = defClass("Env") local LOGTAG = "Env" function Env:ctor(cfg) local env = cfg or {} env._G = env env.require = function(filename) return _require(self.env, filename) end env.import = function(filename) return _require(self.env, self.root .. filename) end env.defClass = function(name, super, _env) return defClass(name, super, _env or self.env) end env.defClassStatic = function(name, super, _env) return defClassStatic(name, super, _env or self.env) end env.defGlobal = function (name, _env) return defGlobal(name, _env or self.env) end env.setGlobal = function (t) for k, v in pairs(t) do printInfo(LOGTAG, "setGlobal k:%s, v:%s", k, v) rawset(self.env, k, v) end env.setGlobal = function () printError(LOGTAG, "setGlobal Cannot call twice") end end self.env = setmetatable(env, { __newindex = function(t, k, v) printError(LOGTAG, "Cannot define global variant in localenv") end, __index = function(t, k) local v = _ENV[k] if v == nil then v = _G[k] end rawset(t, k, v) return v end }) end function Env:require(filename, ...) local t = filename if type(filename) == "string" then t = {filename} end local class for _, f in ipairs(t) do local root, file = string.match(f, "(.*/)(.-)$") self.root = root or self.root class = self.env.require(f, ...) end return class end function Env:onExit() self.env = nil super.onExit(self) end fishingGradeCfg--[[ from file:捞鱼经验.xlsx --]] local fishingGradeCfg = { [1] = { id = 1, grade = 1, speed = 50, maxEnergy = 10, energyConsumption = 0.1, spawnDistance = {[1]=70,[2]=30}, spawnFarDistance = {[1]=50,[2]=20,[3]=20}, fishZoom = {[1] = 1, [2] = 0.8, [3] = 1.1}, fishSpeed = {[1] = 1, [2] = 0.8, [3] = 0.8}, characterZoom = 1.0, cameraZoom = 8.0, swordfishInterval = 30, swordfishCount = 3, }, [2] = { id = 2, grade = 2, speed = 70, maxEnergy = 32, energyConsumption = 0.32, spawnDistance = {[1]=30,[2]=50,[3]=20}, spawnFarDistance = {[1]=30,[2]=50,[3]=20,[4]=20}, fishZoom = {[1] = 1, [2] = 0.8, [3] = 1.3, [4] = 1.8}, fishSpeed = {[1] = 1, [2] = 1, [3] = 1, [4] = 0.8}, characterZoom = 1.8, cameraZoom = 9.0, swordfishInterval = 25, swordfishCount = 3, }, [3] = { id = 3, grade = 3, speed = 80, maxEnergy = 100, energyConsumption = 1.0, spawnDistance = {[1]=15,[2]=25,[3]=40,[4]=10}, spawnFarDistance = {[1]=20,[2]=20,[3]=20,[4]=10,[5]=20}, fishZoom = {[1] = 1, [2] = 0.8, [3] = 1, [4] = 1.5, [5] = 3 }, fishSpeed = {[1] = 1, [2] = 1, [3] = 1, [4] = 0.8, [5] = 0.5 }, characterZoom = 2.6, cameraZoom = 11.0, swordfishInterval = 22, swordfishCount = 4, }, [4] = { id = 4, grade = 4, speed = 100, maxEnergy = 200, energyConsumption = 2.0, spawnDistance = {[4]=70,[5]=30,[6]=10}, spawnFarDistance = {[4]=50,[5]=10,[6]=10}, fishZoom = {[1] = 0, [2] = 0, [3] = 0, [4] = 0.6, [5] = 1.8, [6] = 3 }, fishSpeed = {[1] = 0, [2] = 0, [3] = 0, [4] = 1, [5] = 0.8, [6] = 0.5 }, characterZoom = 2.0, cameraZoom = 11.0, swordfishInterval = 20, swordfishCount = 4, }, [5] = { id = 5, grade = 5, speed = 100, maxEnergy = 480, energyConsumption = 6.0, spawnDistance = {[4]=50,[5]=40,[6]=20,[7]=10}, spawnFarDistance = {[4]=40,[5]=30,[6]=20,[7]=20}, fishZoom = {[1] = 0, [2] = 0, [3] = 0, [4] = 0.6, [5] = 1.2, [6] = 2.2, [7] = 3.2 }, fishSpeed = {[1] = 0, [2] = 0, [3] = 0, [4] = 1, [5] = 1, [6] = 0.8, [7] = 0.5 }, characterZoom = 2.8, cameraZoom = 13.0, swordfishInterval = 18, swordfishCount = 4, }, [6] = { id = 6, grade = 6, speed = 100, maxEnergy = 1200, energyConsumption = 15.0, spawnDistance = {[4]=15,[5]=25,[6]=40,[7]=20}, spawnFarDistance = {[4]=20,[5]=20,[6]=20,[7]=20,[8]=20}, fishZoom = {[1] = 0, [2] = 0, [3] = 0, [4] = 0.6, [5] = 1, [6] = 1, [7] = 2.2, [8] = 3.2 }, fishSpeed = {[1] = 0, [2] = 0, [3] = 0, [4] = 1, [5] = 1, [6] = 1, [7] = 0.5, [8] = 0.5 }, characterZoom = 3.6, cameraZoom = 15.0, swordfishInterval = 16, swordfishCount = 5, }, [7] = { id = 7, grade = 7, speed = 100, maxEnergy = 3000, energyConsumption = 40.0, spawnDistance = {[4]=10,[5]=10,[6]=30,[7]=30,[8]=20}, spawnFarDistance = {[4]=30,[5]=20,[6]=20,[7]=20,[8]=10}, fishZoom = {[1] = 0, [2] = 0, [3] = 0, [4] = 0.4, [5] = 0.6, [6] = 1, [7] = 1.2, [8] = 2.2 }, fishSpeed = {[1] = 0, [2] = 0, [3] = 0, [4] = 1, [5] = 1, [6] = 1, [7] = 1, [8] = 0.5 }, characterZoom = 4.4, cameraZoom = 17.0, swordfishInterval = 14, swordfishCount = 5, }, [8] = { id = 8, grade = 8, speed = 100, maxEnergy = 6000, energyConsumption = 60.0, spawnDistance = {[6]=50,[7]=20,[8]=30}, spawnFarDistance = {[5]=20,[6]=20,[7]=30,[8]=30}, fishZoom = {[1] = 0, [2] = 0, [3] = 0, [4] = 0, [5] = 0, [6] = 0.6, [7] = 1, [8] = 1.2 }, fishSpeed = {[1] = 0, [2] = 0, [3] = 0, [4] = 0, [5] = 0, [6] = 1, [7] = 1, [8] = 1 }, characterZoom = 5.2, cameraZoom = 20.0, swordfishInterval = 12, swordfishCount = 5, }, } return fishingGradeCfg buildingslistreslinkreturn { --BASIC --ASSET } ShortForUnityu--[[ author:{zhangpeng} time:2022-08-14 13:09:31 ]] UnityEngine = CS.UnityEngine UGUI = CS.UnityEngine.UIcuisineUpgradeCfg9$--[[ from file:菜谱升级.xlsx --]] local cuisineUpgradeCfg = { [1] = { id = 290101, saleIncom = 100, limitCond = 1002, limitArgs = 101301, }, [2] = { id = 290102, saleIncom = 500, limitCond = 1001, limitArgs = 500, }, [3] = { id = 290103, saleIncom = 1000, limitCond = -1, limitArgs = -1, }, [4] = { id = 290201, saleIncom = 150, limitCond = 1001, limitArgs = 200, }, [5] = { id = 290202, saleIncom = 750, limitCond = 1001, limitArgs = 600, }, [6] = { id = 290203, saleIncom = 1250, limitCond = -1, limitArgs = -1, }, [7] = { id = 290301, saleIncom = 130, limitCond = 1007, limitArgs = 300007, }, [8] = { id = 290302, saleIncom = 650, limitCond = 1001, limitArgs = 700, }, [9] = { id = 290303, saleIncom = 1400, limitCond = -1, limitArgs = -1, }, [10] = { id = 290401, saleIncom = 160, limitCond = 1002, limitArgs = 101402, }, [11] = { id = 290402, saleIncom = 750, limitCond = 1001, limitArgs = 800, }, [12] = { id = 290403, saleIncom = 1500, limitCond = -1, limitArgs = -1, }, [13] = { id = 290501, saleIncom = 220, limitCond = 1001, limitArgs = 300, }, [14] = { id = 290502, saleIncom = 850, limitCond = 1001, limitArgs = 1000, }, [15] = { id = 290503, saleIncom = 1600, limitCond = -1, limitArgs = -1, }, [16] = { id = 290601, saleIncom = 260, limitCond = 1002, limitArgs = 101802, }, [17] = { id = 290602, saleIncom = 1000, limitCond = 1001, limitArgs = 1200, }, [18] = { id = 290603, saleIncom = 2000, limitCond = -1, limitArgs = -1, }, [19] = { id = 290701, saleIncom = 350, limitCond = 1001, limitArgs = 400, }, [20] = { id = 290702, saleIncom = 1200, limitCond = 1001, limitArgs = 1400, }, [21] = { id = 290703, saleIncom = 2200, limitCond = -1, limitArgs = -1, }, [22] = { id = 290801, saleIncom = 400, limitCond = 1002, limitArgs = 100203, }, [23] = { id = 290802, saleIncom = 1350, limitCond = 1001, limitArgs = 1500, }, [24] = { id = 290803, saleIncom = 2400, limitCond = -1, limitArgs = -1, }, [25] = { id = 290901, saleIncom = 500, limitCond = -1, limitArgs = -1, }, [26] = { id = 291001, saleIncom = 600, limitCond = 1002, limitArgs = 102303, }, [27] = { id = 291002, saleIncom = 1600, limitCond = 1001, limitArgs = 2000, }, [28] = { id = 291003, saleIncom = 3000, limitCond = -1, limitArgs = -1, }, [29] = { id = 291101, saleIncom = 800, limitCond = -1, limitArgs = -1, }, [30] = { id = 291201, saleIncom = 1000, limitCond = 1002, limitArgs = 101403, }, [31] = { id = 291202, saleIncom = 2000, limitCond = 1001, limitArgs = 2200, }, [32] = { id = 291203, saleIncom = 3800, limitCond = -1, limitArgs = -1, }, [33] = { id = 291301, saleIncom = 1100, limitCond = -1, limitArgs = -1, }, [34] = { id = 291401, saleIncom = 1200, limitCond = -1, limitArgs = -1, }, [35] = { id = 291501, saleIncom = 1300, limitCond = 1001, limitArgs = 500, }, [36] = { id = 291502, saleIncom = 2400, limitCond = 1001, limitArgs = 2400, }, [37] = { id = 291503, saleIncom = 5000, limitCond = -1, limitArgs = -1, }, [38] = { id = 291601, saleIncom = 1400, limitCond = -1, limitArgs = -1, }, [39] = { id = 291701, saleIncom = 1500, limitCond = 1002, limitArgs = 101903, }, [40] = { id = 291702, saleIncom = 2800, limitCond = 1001, limitArgs = 2600, }, [41] = { id = 291703, saleIncom = 5600, limitCond = -1, limitArgs = -1, }, [42] = { id = 291801, saleIncom = 1600, limitCond = -1, limitArgs = -1, }, [43] = { id = 291901, saleIncom = 1800, limitCond = 1001, limitArgs = 600, }, [44] = { id = 291902, saleIncom = 3200, limitCond = 1001, limitArgs = 2800, }, [45] = { id = 291903, saleIncom = 6500, limitCond = -1, limitArgs = -1, }, [46] = { id = 292001, saleIncom = 2000, limitCond = -1, limitArgs = -1, }, [47] = { id = 292101, saleIncom = 2200, limitCond = -1, limitArgs = -1, }, [48] = { id = 292201, saleIncom = 2250, limitCond = 1002, limitArgs = 102204, }, [49] = { id = 292202, saleIncom = 3800, limitCond = 1001, limitArgs = 3000, }, [50] = { id = 292203, saleIncom = 7500, limitCond = -1, limitArgs = -1, }, [51] = { id = 292301, saleIncom = 2450, limitCond = -1, limitArgs = -1, }, [52] = { id = 292401, saleIncom = 2550, limitCond = -1, limitArgs = -1, }, [53] = { id = 292501, saleIncom = 2750, limitCond = -1, limitArgs = -1, }, [54] = { id = 292601, saleIncom = 2900, limitCond = 1007, limitArgs = 300009, }, [55] = { id = 292602, saleIncom = 4900, limitCond = 1001, limitArgs = 4000, }, [56] = { id = 292603, saleIncom = 9200, limitCond = -1, limitArgs = -1, }, [57] = { id = 292701, saleIncom = 3000, limitCond = -1, limitArgs = -1, }, [58] = { id = 292801, saleIncom = 3200, limitCond = -1, limitArgs = -1, }, [59] = { id = 292901, saleIncom = 3400, limitCond = 1001, limitArgs = 800, }, [60] = { id = 292902, saleIncom = 5600, limitCond = 1001, limitArgs = 5000, }, [61] = { id = 292903, saleIncom = 10500, limitCond = -1, limitArgs = -1, }, [62] = { id = 293001, saleIncom = 3550, limitCond = -1, limitArgs = -1, }, [63] = { id = 293101, saleIncom = 3750, limitCond = -1, limitArgs = -1, }, [64] = { id = 293201, saleIncom = 3900, limitCond = 1001, limitArgs = 1000, }, [65] = { id = 293202, saleIncom = 6350, limitCond = 1001, limitArgs = 6000, }, [66] = { id = 293203, saleIncom = 1200, limitCond = -1, limitArgs = -1, }, [67] = { id = 293301, saleIncom = 4100, limitCond = -1, limitArgs = -1, }, [68] = { id = 293401, saleIncom = 4300, limitCond = -1, limitArgs = -1, }, [69] = { id = 293501, saleIncom = 4500, limitCond = 1001, limitArgs = 1200, }, [70] = { id = 293502, saleIncom = 7200, limitCond = 1001, limitArgs = 7000, }, [71] = { id = 293503, saleIncom = 13500, limitCond = -1, limitArgs = -1, }, [72] = { id = 293601, saleIncom = 4650, limitCond = -1, limitArgs = -1, }, [73] = { id = 293701, saleIncom = 4850, limitCond = -1, limitArgs = -1, }, [74] = { id = 293801, saleIncom = 5000, limitCond = -1, limitArgs = -1, }, [75] = { id = 293901, saleIncom = 5500, limitCond = -1, limitArgs = -1, }, [76] = { id = 294001, saleIncom = 6000, limitCond = -1, limitArgs = -1, }, [77] = { id = 294101, saleIncom = 6500, limitCond = -1, limitArgs = -1, }, [78] = { id = 294201, saleIncom = 7000, limitCond = -1, limitArgs = -1, }, [79] = { id = 294301, saleIncom = 7500, limitCond = -1, limitArgs = -1, }, [80] = { id = 294401, saleIncom = 8000, limitCond = -1, limitArgs = -1, }, [81] = { id = 294501, saleIncom = 8500, limitCond = -1, limitArgs = -1, }, [82] = { id = 294601, saleIncom = 9000, limitCond = -1, limitArgs = -1, }, [83] = { id = 294701, saleIncom = 9500, limitCond = -1, limitArgs = -1, }, [84] = { id = 294801, saleIncom = 10000, limitCond = -1, limitArgs = -1, }, [85] = { id = 294901, saleIncom = 10500, limitCond = -1, limitArgs = -1, }, [86] = { id = 295001, saleIncom = 11000, limitCond = -1, limitArgs = -1, }, [87] = { id = 295101, saleIncom = 11500, limitCond = -1, limitArgs = -1, }, [88] = { id = 295201, saleIncom = 12000, limitCond = -1, limitArgs = -1, }, [89] = { id = 295301, saleIncom = 12500, limitCond = -1, limitArgs = -1, }, [90] = { id = 295401, saleIncom = 13000, limitCond = -1, limitArgs = -1, }, [91] = { id = 295501, saleIncom = 13500, limitCond = -1, limitArgs = -1, }, [92] = { id = 295601, saleIncom = 14000, limitCond = -1, limitArgs = -1, }, [93] = { id = 295701, saleIncom = 14500, limitCond = -1, limitArgs = -1, }, [94] = { id = 295801, saleIncom = 15000, limitCond = -1, limitArgs = -1, }, [95] = { id = 295901, saleIncom = 15500, limitCond = -1, limitArgs = -1, }, [96] = { id = 296001, saleIncom = 16000, limitCond = -1, limitArgs = -1, }, } return cuisineUpgradeCfg HelpCategoryCell--- 帮助单元格 ---@class HelpCategoryCell : LuaClass local HelpCategoryCell = defClass("HelpCategoryCell") require("modules/ui/helpui/HelpListUI") function HelpCategoryCell:ctor(go, helpType) self.ui = go self.helpType = helpType self:initUI() self:showUI() end function HelpCategoryCell:initUI() self.bg = self.ui:Seek("bg") self.icon = self.ui:Seek("icon") self.title = self.ui:Seek("title") util.ugui.addButtonClickEvent(self.bg, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:click() end) end function HelpCategoryCell:showUI() local str = "Assets/AssetsPackage/Res/modules/ui/helpui/images/image%d.png" str = string.format(str, self.helpType % 100) local image = self.icon:GetComponent("Image") image.sprite = ResLoader.loadAsset(str,typeof(CS.UnityEngine.Sprite)) util.ugui:setImageTileSizeToRectangles(image, 150, 140) self.title:GetComponent("TextMeshProUGUI").text = HelpConst.TypeName[self.helpType] end function HelpCategoryCell:click() local view = HelpListUI.new(self.helpType):show():showMask():enableCloseWhenClickMask() end return HelpCategoryCell employeFunCfg--[[ from file:员工功能类型.xlsx --]] local employeFunCfg = { [1] = { id = 301, desc = "招揽顾客,持续%d秒,需要休息%d秒。", funType = -1, }, [2] = { id = 302, desc = "可以长按宣传按钮,%.2f秒招揽一个传单宣传", funType = -1, }, [3] = { id = 303, desc = "帮助客人自动点菜,持续%d秒,需要休息%d秒钟", funType = -1, }, [4] = { id = 304, desc = "驱赶捣乱的客人,每隔%d秒尝试驱赶%d次", funType = -1, }, [5] = { id = 305, desc = "增加%d的移动速度", funType = -1, }, [6] = { id = 306, desc = "疯狂状态:加快烹饪速度%d%%,持续%d秒,需要休息%d秒。", funType = -1, }, [7] = { id = 307, desc = "每分钟增加收入%d", funType = -1, }, [8] = { id = 308, desc = "到处收集金币,持续%d秒,需要休息%d秒", funType = -1, }, [9] = { id = 309, desc = "在自助餐厅唱歌,每4小时挣得%d个音符", funType = -1, }, [10] = { id = 310, desc = "可演唱歌曲%d首", funType = -1, }, [11] = { id = 311, desc = "每天可以自动挖出%d个鱼饵", funType = -1, }, [12] = { id = 312, desc = "钓鱼深度增加%d米", funType = -1, }, [13] = { id = 313, desc = "钓鱼最大数量达到%d", funType = -1, }, } return employeFunCfg SpecialCustomerInfo--[[ 特殊顾客数据信息 author:{zhangpeng} time:2025-05-30 12:12:30 ]] local SpecialCustomerInfo = defClass("SpecialCustomerInfo") local LOGTAG = "SpecialCustomerInfo" local Sprite = typeof(CS.UnityEngine.Sprite) function SpecialCustomerInfo:ctor(customerId) self.id = customerId self:init() end function SpecialCustomerInfo:init() self:initData() self:initStatus() end --- 初始化配置 function SpecialCustomerInfo:initData() self.config = self:getConfig() self.params = { self.config.param_1, self.config.param_2, self.config.param_3 } end --- 初始化状态 function SpecialCustomerInfo:initStatus() self.status = self:getStatus() end --- 获取顾客配置 function SpecialCustomerInfo:getConfig() if self.config then return self.config end return CustomerSpecialCfgParse:getCustomerSpecialCfg(self.id) end --- 获取顾客状态 function SpecialCustomerInfo:getStatus() if self.status then return self.status end return UserDataMgr.specialCustomerUserData end --- 获取名称 function SpecialCustomerInfo:getName() return self:getConfig().name end --- 获取性格 function SpecialCustomerInfo:getNature() return self:getConfig().nature end --- 获取描述 function SpecialCustomerInfo:getDesc() return self:getConfig().desc end --- 获取移动速度 function SpecialCustomerInfo:getMoveSpeed() return self:getConfig().moveSpeed end --- 获取招揽标签id function SpecialCustomerInfo:getSolicitTagId() return self:getConfig().solicitTag end --- 获取美术资源名称 function SpecialCustomerInfo:getRecName() return self:getConfig().artRes end --- 获取剧情id function SpecialCustomerInfo:getPlotId() return self:getConfig().plotId end --- 获取成功对话id function SpecialCustomerInfo:getSuccessDialogId() return self:getConfig().sucDialogId end --- 获取失败对话id function SpecialCustomerInfo:getFailDialogId() return self:getConfig().faileDialogId end --- 获取分享奖励金币 function SpecialCustomerInfo:getShareRewardCoin() return self:getConfig().sharRewardCoin end --- 获取每日来访次数上限 function SpecialCustomerInfo:getLimitTimes() return self:getConfig().limitTimes end --- 获取新顾客提示 function SpecialCustomerInfo:getNewTip() return self:getConfig().newTip == 1 end --- 获取权重 function SpecialCustomerInfo:getWeight() return self:getConfig().weight end --- 获取活跃时间 单位秒 function SpecialCustomerInfo:getActiveTime() return self:getConfig().activeTimeLen end --- 获取点击次数 function SpecialCustomerInfo:getClickTimes() return self:getConfig().clickTimes end --- 获取参数列表 function SpecialCustomerInfo:getParams() return self.params end --- 获取参数 function SpecialCustomerInfo:getParam(index) if index < 1 or index > #self.params then printError(LOGTAG, "索引超限") return nil end return self.params[index] end --- 是否为已来访状态 function SpecialCustomerInfo:isVisited() return self:getStatus():getSpecialCustomerState(self.id) >= CustomerConst.CustomerVisitState.Visited end --- 来访 function SpecialCustomerInfo:visit() return self:getStatus():setSpecialCustomerState(self.id, CustomerConst.CustomerVisitState.Visited) end --- 是否为已分享状态 function SpecialCustomerInfo:isShared() return self:getStatus():getSpecialCustomerState(self.id) >= CustomerConst.CustomerVisitState.Shared end --- 获取分享奖励金币 function SpecialCustomerInfo:getShareCoin() return self:getConfig().sharRewardCoin end --- 分享 function SpecialCustomerInfo:share() return self:getStatus():setSpecialCustomerState(self.id, CustomerConst.CustomerVisitState.Shared) end --- 是否为新顾客 function SpecialCustomerInfo:isNewCustomer() return false end --- 图片路径 function SpecialCustomerInfo:getImgPath() return "Assets/AssetsPackage/Res/modules/roles/customer/images/" .. self:getArtRes() .. ".png" end --- 获取半身图标路径 function SpecialCustomerInfo:getBustIconPath() return "Assets/AssetsPackage/Res/modules/roles/customer/images/bust/" .. self:getArtRes() .. ".png" end --- 获取SpineUI路径 function SpecialCustomerInfo:getSpineUIPath(id) return "Assets/AssetsPackage/Res/modules/roles/customer/prefabs/ui/customer_" .. id .. ".prefab" end function SpecialCustomerInfo:getImg(path) if ResLoader.hasAsset(path) then return ResLoader.loadAsset(path, Sprite) end return nil end --- 获取全身图标 function SpecialCustomerInfo:getIcon() return self:getImg(self:getImgPath()) end --- 获取半身图标 function SpecialCustomerInfo:getBustIcon() return self:getImg(self:getBustIconPath()) end --- 获取剪影图标 function SpecialCustomerInfo:getSilhouetteIcon() return self:getImg(self:getImgPath()) end --- 获取半身剪影图标 function SpecialCustomerInfo:getBustSilhouetteIcon() return self:getImg(self:getBustIconPath()) end --- 获取SpineUI预制体 function SpecialCustomerInfo:getSpineUI() local path = self:getSpineUIPath(self.id) if ResLoader.hasAsset(path) then return ResLoader.loadAsset(path) end return nil end return SpecialCustomerInfoCosLuaUploadTask---@class CosLuaUploadTask:LuaClass local CosLuaUploadTask = defClass("CosLuaUploadTask") local LOGTAG = "CosLuaUploadTask" local CosSDKAPI = CS.iHuman.UnitySz.Framework.COS.CosSDKAPI CosLuaUploadTask.STATUS = { NONE = 0, RUNNING = 1, SUC = 2, FAIL = 3, CANCEL = 4, } ---@param srcPath string 文件路径 ---@param dstPath string 自定义url的文件名,不能为 nil ---@param serviceType CosLuaServiceType 服务类型 ---@param progressCallback fun(progress:number, srcPath:string, uploadTask:CosLuaUploadTask) ---@param completeCallback fun(result:boolean, data:{errorCode:integer, errorMsg:string, srcPath:string}, uploadTask:CosLuaUploadTask) ---@param prepareUrlCallback fun(originFilePath:string, prepareFileUrl:string, prepareFileRelativeUrl:string) function CosLuaUploadTask:ctor(srcPath, dstPath, serviceType, progressCallback, completeCallback, prepareUrlCallback) self.srcPath = srcPath self.dstPath = dstPath self.progress = 0 self.serviceType = serviceType self.progressCallback = progressCallback self.completeCallback = completeCallback self.prepareUrlCallback = prepareUrlCallback self.status = self.STATUS.NONE self.sdkUploadTask = nil end function CosLuaUploadTask:start() printInfo(LOGTAG, "start srcPath:%s", self.srcPath) if self.status ~= self.STATUS.NONE then printWarn(LOGTAG, "CosLuaUploadTask start error. status(%s) is not none.", tostring(self.status)) return end self.status = self.STATUS.RUNNING local cosXmlServer = CosLuaTemporaryCredential:getCosXmlServer(self.serviceType) if not cosXmlServer then self:onComplete(false, {errorCode = -4, errorMsg = "cosXmlServer is nil"}) return end local cosLuaCredentialBean = CosLuaTemporaryCredential:getCredential(self.serviceType) if not cosLuaCredentialBean then self:onComplete(false, {errorCode = -5, errorMsg = "cosLuaCredentialBean is nil"}) return end local prepareUrl,prepareUrlPrefix = CosLuaTemporaryCredential:getPrepareUrl(self.serviceType) printInfo(LOGTAG, "srcPath:%s, prepareUrl:%s", self.srcPath, prepareUrl..self.dstPath) if self.prepareUrlCallback then self.prepareUrlCallback(self.srcPath, prepareUrl..self.dstPath, prepareUrlPrefix..self.dstPath) end self.sdkUploadTask = CosSDKAPI.TransferUploadFile(cosXmlServer, cosLuaCredentialBean.bucket_name, self.srcPath, cosLuaCredentialBean.allow_prefix..self.dstPath, function (progress) if self.sdkUploadTask == nil then return end self:onProgress(progress) end, function (result, code, message) if self.sdkUploadTask == nil then return end self:onComplete(result, {errorCode = code, errorMsg = message or ""}) end) end function CosLuaUploadTask:cancel() if self.status ~= self.STATUS.RUNNING then printWarn(LOGTAG, "CosLuaUploadTask cancel error. status(%s) is not RUNNING.", tostring(self.status)) return end self.status = self.STATUS.CANCEL local sdkUploadTask = self.sdkUploadTask self.sdkUploadTask = nil if sdkUploadTask then if sdkUploadTask.Status == 3 then local cosXMLUploadTask = sdkUploadTask.Result cosXMLUploadTask:Cancel() end end self:onComplete(false, {errorCode = -6, errorMsg = "user cancelled"}) end function CosLuaUploadTask:isFinished() return (self.status == self.STATUS.SUC or self.status == self.STATUS.FAIL or self.status == self.STATUS.CANCEL) end function CosLuaUploadTask:isSuc() return self.status == self.STATUS.SUC end function CosLuaUploadTask:canStart() return self.status == self.STATUS.NONE end ---获取失败,上层调用 ---@param service_type CosLuaServiceType function CosLuaUploadTask:failWithErrorCredential(service_type) if self.status == self.STATUS.NONE and self.service_type == service_type then self:onComplete(false, {errorCode = -3, errorMsg = "fetch credential error"}) end end function CosLuaUploadTask:getStatus() return self.status end ---@private ---@param result boolean ---@param data {errorCode:integer, errorMsg:string} function CosLuaUploadTask:onComplete(result, data) printInfo(LOGTAG, "onComplete result:%s, data:%s", result, table.toString(data)) self.status = result and self.STATUS.SUC or self.STATUS.FAIL if self.completeCallback then self.completeCallback(result, data, self) end end ---@private ---@param progress number function CosLuaUploadTask:onProgress(progress) self.progress = progress if self.progressCallback then self.progressCallback(progress, self.srcPath, self) end end return CosLuaUploadTask Log--[[ author:wanghuai time:2022-08-01 10:41:23 ]] Log = {} local json = raw_require("rapidjson") local isEditor = CS.UnityEngine.Application.isEditor local useColor = isEditor local raw_print = raw_print local LOG_LEVEL = { VERBOSE = 2, DEBUG = 3, INFO = 4, WARN = 5, ERROR = 6, ASSERT = 7 } local TRACEBACK_LEVEL = { NONE = 0, SIMPLE = 1, FULL = 2 } local LOG_LEVEL_NAME_DICT = { [LOG_LEVEL.VERBOSE] = "VERB", [LOG_LEVEL.DEBUG] = "DEBUG", [LOG_LEVEL.INFO] = "INFO", [LOG_LEVEL.WARN] = "WARN", [LOG_LEVEL.ERROR] = "ERROR", [LOG_LEVEL.ASSERT] = "FATAL" } local LOG_LEVEL_COLOR_DICT = { [LOG_LEVEL.VERBOSE] = "silver", [LOG_LEVEL.DEBUG] = "lightblue", [LOG_LEVEL.INFO] = "green", [LOG_LEVEL.WARN] = "orange", [LOG_LEVEL.ERROR] = "red", [LOG_LEVEL.ASSERT] = "red" } ---@type table local SENSITIVE_STR_DICT = {} local isEnableSensitive = false local function getLogLevelStr(logLevel) local str = LOG_LEVEL_NAME_DICT[logLevel] if not useColor then return str end local color = LOG_LEVEL_COLOR_DICT[logLevel] str = string.format("%s", color, str) return str end function Log.init() Log.LOG_LEVEL = LOG_LEVEL Log.TRACEBACK_LEVEL = TRACEBACK_LEVEL Log.logTagDict = {} Log.logLevelReverseMap = {} for k, v in pairs(LOG_LEVEL) do Log.logLevelReverseMap[v] = k end if isEditor then Log.curLogLevel = LOG_LEVEL.VERBOSE Log.curTracebackLevel = TRACEBACK_LEVEL.FULL else Log.curLogLevel = LOG_LEVEL.INFO Log.curTracebackLevel = TRACEBACK_LEVEL.NONE end end -- # todo next 需要在app内调用 function Log.setLogTagDict(initLogTagDict) Log.logTagDict = initLogTagDict or {} end function Log.log(logLevel, tag, fmt, ...) if logLevel < Log.curLogLevel then return end tag = tostring(tag or "") if Log.logTagDict[tag] == nil then Log.logTagDict[tag] = (logLevel >= Log.curLogLevel) end if Log.logTagDict[tag] == false then return end local logLevelStr = getLogLevelStr(logLevel or LOG_LEVEL.VERBOSE) fmt = "[%s][%s]" .. tostring(fmt) local status, str = pcall(string.format, fmt, logLevelStr, tag, ...) if not status then str = table.concat({...}, " ") end if Log.curTracebackLevel == TRACEBACK_LEVEL.SIMPLE then local info = debug.getinfo(3) or {} local source = info.source if not isEditor then local index = string.find(source, "/Assets/") if index then source = string.sub(source, index + 8) end end str = str .. string.format('\n["%s"]: %d: %s', source, info.currentline, info.name) elseif Log.curTracebackLevel == TRACEBACK_LEVEL.FULL then str = str .. "\n" .. debug.traceback(nil, 3) end -- 日期和 [Lua] 前缀 local timeStr = CS.System.DateTime.Now:ToString("G") str = timeStr .. " [LUA] "..str if isEnableSensitive then for sensitiveStr, value in pairs(SENSITIVE_STR_DICT) do str = string.gsub(str, sensitiveStr, string.rep("*", #sensitiveStr)) end end if logLevel >= LOG_LEVEL.ERROR then CS.UnityEngine.Debug.LogError(str) elseif logLevel == LOG_LEVEL.WARN then CS.UnityEngine.Debug.LogWarning(str) else CS.UnityEngine.Debug.Log(str) end return str end function Log.setLogLevel(level) Log.curLogLevel = level end function Log.setTracebackLevel(level) Log.curTracebackLevel = level end function Log.getLogLevel() return Log.curLogLevel end function Log.getTracebackLevel() return Log.curTracebackLevel end function Log.setSensetiveEnable(enable) isEnableSensitive = enable end -- # todo next 在app内调用 -- 屏蔽print方法,提示开发者用 printInfo ... function Log.disableRawPrint() function print(...) local t = {...} local str = "" for i, v in ipairs(t) do str = str .. tostring(v) .. " " end Log.log(LOG_LEVEL.WARN, "请替换为printXXXX", str) end raw_print = print end function printAssert(LOGTAG, fmt, ...) local tmp = Log.getTracebackLevel() Log.setTracebackLevel(TRACEBACK_LEVEL.FULL) Log.log(LOG_LEVEL.ASSERT, LOGTAG, fmt, ...) Log.setTracebackLevel(tmp) end function printError(LOGTAG, fmt, ...) local tmp = Log.getTracebackLevel() Log.setTracebackLevel(TRACEBACK_LEVEL.FULL) Log.log(LOG_LEVEL.ERROR, LOGTAG, fmt, ...) Log.setTracebackLevel(tmp) end function printWarn(LOGTAG, fmt, ...) Log.log(LOG_LEVEL.WARN, LOGTAG, fmt, ...) end function printInfo(LOGTAG, fmt, ...) Log.log(LOG_LEVEL.INFO, LOGTAG, fmt, ...) end function printDebug(LOGTAG, fmt, ...) Log.log(LOG_LEVEL.DEBUG, LOGTAG, fmt, ...) end function printVerbose(LOGTAG, fmt, ...) Log.log(LOG_LEVEL.VERBOSE, LOGTAG, fmt, ...) end function dump(t, LOGTAG, logLevel) LOGTAG = LOGTAG or "dump" Log.log(logLevel or LOG_LEVEL.DEBUG, LOGTAG, util.serpent.block(t)) end function setSensitiveStr(str, enable) if isEditor then return end if enable then SENSITIVE_STR_DICT[str] = true else SENSITIVE_STR_DICT[str] = nil end end Log.init() -- printAssert("test", "printAssert") -- printError("test", "printError") -- printWarn("test", "printWarn") -- printInfo("test", "printInfo") -- printDebug("test", "printDebug") -- printVerbose("test", "printVerbose") VendorMgr-- 摊主管理 local VendorMgr = defClassStatic("VendorMgr") -- log local LOGTAG = "VendorMgr" -- 初始化 function VendorMgr:init() printInfo(LOGTAG, "------ 摊主管理 初始化 ------") self:initEvent() self:initUserData() self:initInfo() end -- 开始 function VendorMgr:start() self:setEvent() end -- 初始化事件 function VendorMgr:initEvent() end -- 初始化用户数据 function VendorMgr:initUserData() self.userData = UserDataMgr.vendorUserData end -- 设置事件 function VendorMgr:setEvent() end -- 初始化信息 function VendorMgr:initInfo() self.infoDic = {} end -- 获取信息 function VendorMgr:getVendorInfo(vendorId) if self.infoDic[vendorId] then return self.infoDic[vendorId] end local info = VendorInfo.new(vendorId) self.infoDic[vendorId] = info return info end -- 获取摊主配置 function VendorMgr:getVendorConfig(vendorId) return VendorCfgParse:getVendorCfg(vendorId) end -- 获取摊主来访状态 function VendorMgr:getVendorVisitState(vendorId) local info = self:getVendorInfo(vendorId) return info:getVisitState(vendorId) end -- 设置摊主来访状态 function VendorMgr:setVendorVisitState(vendorId, state) local info = self:getVendorInfo(vendorId) return info:setVisitState(state) end -- 获取摊主来访次数 function VendorMgr:getVendorVisitCount(vendorId) local info = self:getVendorInfo(vendorId) return info:getVisitCount(vendorId) end -- 增加摊主来访次数 function VendorMgr:addVendorVisitCount(vendorId, count) local info = self:getVendorInfo(vendorId) return info:addVisitCount(count) end return VendorMgr AdCustomerw--- 广告顾客 ---@class AdCustomer:CustomerSpecial local AdCustomer, super = defClass("AdCustomer", CustomerSpecial) require("modules/ui/customerui/AdCustomerUI") local LOGTAG = "AdCustomer" -- 状态 AdCustomer.State = { Idle = 1, -- 空闲 Entrance = 2, -- 入场 Waiting = 3, -- 等待 Plot = 4, -- 剧情 Leaving = 5, -- 离开 } --- 构造函数 function AdCustomer:ctor(resLink) super.ctor(self, resLink, CustomerConst.CustomerSpecialType.AdCustomer) self:init() end function AdCustomer:init() self:initDisplay("ad_customer") self:initProperties() self:startAdLogic() end --- 初始化属性 function AdCustomer:initProperties() self.info = SpecialCustomerMgr:getSpecialCustomerInfo(self.specialCustomerTypeId) self.state = AdCustomer.State.Idle -- 初始状态 self.activeTime = self.info:getActiveTime() -- 活动时间 self.clickTimes = self.info:getClickTimes() -- 点击次数 self.currClickTimes = 0 -- 当前点击次数 end --- 开始广告顾客逻辑 function AdCustomer:startAdLogic() printInfo(LOGTAG, "广告顾客开始活动,活动时间:%d秒", self.info:getActiveTime()) -- 入场 self:enter() -- 添加点击事件 self:addClickEvent(function() self:click() end) -- 初始化点击进度 self:initClickProgress() end --- 入店 function AdCustomer:enter() printInfo(LOGTAG, "广告顾客入店") self.state = AdCustomer.State.Entrance self:walkToPos(self:getSpecialCustomerCommonStandbyPoint().transform.position, self.info:getMoveSpeed(), function() printInfo(LOGTAG, "广告顾客到达等待点") self.state = AdCustomer.State.Waiting self:showDong() self:startTimer() end ) end --- 离开 function AdCustomer:exit() printInfo(LOGTAG, "广告顾客离开") self.state = AdCustomer.State.Leaving self:stopTimer() self:walkToPos(self:getSpecialCustomerExitPoint().transform.position, self.info:getMoveSpeed(), function() -- 离开后销毁 self:destroy() -- 触发离开事件 end ) end --- 时间更新 ---@param seconds number 秒数 function AdCustomer:timeupdate(seconds) if self.state == AdCustomer.State.Plot then return end self.time = self.time + seconds if self.time >= self.activeTime then self:exit() end end --- 剧情回调 function AdCustomer:plotCallback() self.state = AdCustomer.State.Waiting if self.ui.isClear then self:videoCallback() else self:badVideoCallback() end end --- 剧情 function AdCustomer:plot() printInfo(LOGTAG, "广告顾客开始剧情") -- todo 定值 self.coin = 200000 self.state = AdCustomer.State.Plot self:hideDong() self:showPlot(self.info.getPlotId, function() self.ui = AdCustomerUI.new(self.coin):show():showMask():enableCloseWhenClickMask():addCloseCallback(function() self:plotCallback() end) end) end --- 交互 function AdCustomer:click() if self.state ~= AdCustomer.State.Waiting then return end if self.clickTimes <= 1 then self:plot() else if self.currClickTimes < self.clickTimes then self.currClickTimes = self.currClickTimes + 1 self:showClickProgress(self.currClickTimes / self.clickTimes) else self:plot() end end end --- 成功回调 function AdCustomer:videoCallback() CurrencyMgr:changeCoin(self.coin) self:showPlot(self.info:getSuccessDialogId(), function() self:exit() end) end --- 失败回调 function AdCustomer:badVideoCallback() self:showPlot(self.info:getFailDialogId(), function() self:exit() end) end return AdCustomer EmployeConst?--[[ todo:: 烤串师傅: 每个待机点随机待3-5秒,播放扇扇子动作 歌手:出来就自动播放唱歌动作 餐厅助理: 点1播放摇,点2播放待机 author:{zhangpeng} time:2025-05-21 10:27:12 ]] --- 员工常量类 ---@class EmployeConst local EmployeConst = defClassStatic("EmployeConst") local LOG_TAG = "EmployeConst" -- init function EmployeConst:init() end -- 员工状态 EmployeConst.State = { -- 空闲 Idle = 1, -- 忙碌 Busy = 2, -- 休息中 Resting = 3, } -- 员工雇佣状态 EmployeConst.HireState = { -- 未解锁 Locked = 1, -- 已解锁 Unlocked = 2, -- 已购买 Purchased = 3 } -- 员工功能类型 EmployeConst.Type = { -- 男主角 MaleLead = 300001, -- 男主角 -- 女主角 FemaleLead = 300002, -- 女主角 -- 服务员 Waiter = 300003, -- 帮助客人自动点菜,持续N秒,需要休息N秒 -- 大厨 Chef = 300004, -- 烹饪食物,烹饪N秒,需要休息N秒 -- 餐厅助理 Assistant = 300005, -- 每分钟增加X收入 -- 保安 Security = 300006,-- 驱赶捣乱的客人,每隔N秒尝试驱赶Y次 -- 渔夫 Fisherman = 300007, -- 每天可以自动挖出X个鱼饵 -- 歌手 Singer = 300008, -- 可演唱歌曲N首 -- 烤吧厨师 BarbecueChef = 300009, -- 只在音乐烤吧的的烧烤区域活动,每4小时挣得x个音符() } -- 对应父节点名字 EmployeConst.EmployeeRootNames = { -- 男主角 [EmployeConst.Type.MaleLead] = "male_lead", -- 女主角 [EmployeConst.Type.FemaleLead] = "female_lead", -- 服务员 [EmployeConst.Type.Waiter] = "waiter", -- 大厨 [EmployeConst.Type.Chef] = "chef", -- 餐厅助理 [EmployeConst.Type.Assistant] = "assistant", -- 保安 [EmployeConst.Type.Security] = "guard", -- 渔夫 [EmployeConst.Type.Fisherman] = "fisherman", -- 歌手 [EmployeConst.Type.Singer] = "singer", -- 烤吧厨师 [EmployeConst.Type.BarbecueChef] = "barbecue_chef", } -- 员工动作列表 EmployeConst.EmployeeAction = { --男主角 [EmployeConst.Type.MaleLead] = { idle = "nan_stand_1", --待机1 idle2 = "nan_stand_2", --待机2 walk = "nan_move", --行走 welcome = "nan_welcome", --欢迎 dance = "nan_dance", --跳舞 }, --女主角 [EmployeConst.Type.FemaleLead] = { idle = "nv_stand_1", --待机1 idle2 = "nv_stand_2", --待机2 walk = "nv_move", --行走 welcome = "nv_welcome", --欢迎 dance = "nv_dance", --跳舞 }, -- 服务员 [EmployeConst.Type.Waiter] = { idle = "tongyong_stand_1", -- 待机 walk = "tongyong_move", -- 行走 record = "fuwuyuan_record", -- 点餐动作 }, -- 大厨 [EmployeConst.Type.Chef] = { idle = "chushi_stand_1", -- 待机 walk = "chushi_move", -- 行走 cut = "chushi_cut", -- 切菜动作 fry = "chushi_fry", -- 炒菜动作 }, -- 餐厅助理(加快饮品的制作速度) [EmployeConst.Type.Assistant] = { idle = "cantingzhuli_shulan_stand", --待机 walk = "cantingzhuli_shulan_move", --行走 special = "cantingzhuli_shulan_special", --特殊动作,调酒 }, --保安 [EmployeConst.Type.Security] = { idle = "baoan_demu_stand", --待机 walk = "baoan_demu_move", --行走 special = "baoan_demu_mining", --特殊动作,赶人 mining = "baoan_demu_special", --特殊动作,挖矿 }, -- 渔夫 [EmployeConst.Type.Fisherman] = { idle = "kapibala_stand_1", -- 待机1 idle2 = "kapibala_stand_2", -- 待机2 standWater = "kapibala_stand_swim", --水中待机 swim = "kapibala_swim", --游泳 fish = "kapibala_attack", -- 钓鱼动作 dive = "kapibala_dive", --跳水 win = "kapibala_win", --胜利 die = "kapibala_die", --死亡 }, -- 烤吧歌手 [EmployeConst.Type.Singer] = { idle = "geshou_lingyang_stand", -- 待机 walk = "geshou_lingyang_move", -- 行走 sing = "geshou_lingyang_special", -- 唱歌动作 }, -- 烤吧厨师 [EmployeConst.Type.BarbecueChef] = { idle = "shaokaochushi_huli_stand", -- 待机 walk = "shaokaochushi_huli_move", -- 行走 special = "shaokaochushi_huli_special", -- 烤串动作 }, } -- 页签类型 EmployeConst.PageType = { -- 店长 Manager = 1, -- 员工 Employee = 2, } -- 角色职位类型 EmployeConst.Posts = { -- 店长 Manager = 1, -- 服务员 Service = 2, -- 保安 Security = 3, -- 大厨 Chef = 4, -- 收银员 Cashier = 5, -- 渔夫 Fisherman = 6, -- 歌手 Singer = 7, -- 烤串师傅 Kebab = 8, -- 餐厅助理 Assistant = 9, } -- 角色职位类型描述 EmployeConst.PostsDesc = { [EmployeConst.Posts.Manager] = "店长", [EmployeConst.Posts.Service] = "服务员", [EmployeConst.Posts.Security] = "保安", [EmployeConst.Posts.Chef] = "大厨", [EmployeConst.Posts.Cashier] = "收银员", [EmployeConst.Posts.Fisherman] = "渔夫", [EmployeConst.Posts.Singer] = "歌手", [EmployeConst.Posts.Kebab] = "烤串师傅", [EmployeConst.Posts.Assistant] = "餐厅助理", } -- 角色功能 EmployeConst.Features = { -- 招揽顾客 Solicitation = 301, -- 长按宣传按钮 LongPress = 302, -- 点菜 Order = 303, -- 驱赶 Drive = 304, -- 增加移动速度 MovementSpeed = 305, -- 增加烹饪速度 CookingSpeed = 306, -- 增加小费 Tips = 307, -- 收集金币 CollectCoins = 308, -- 产出音符 MusicalNotes = 309, -- 演唱歌曲 SingSongs = 310, -- 产出鱼饵 FishingBait = 311, -- 钓鱼深度 FishingDepth = 312, -- 钓鱼数量 FishingCount = 313, } -- 角色功能描述 EmployeConst.FeaturesDesc ={ [EmployeConst.Features.Solicitation] = "招揽顾客,持续%s秒,需要休息%s秒。", [EmployeConst.Features.LongPress] = "可以长按宣传按钮,%s秒招揽一个传单宣传", [EmployeConst.Features.Order] = "帮助客人自动点菜,持续%s秒,需要休息%s秒钟", [EmployeConst.Features.Drive] = "驱赶捣乱的客人,每隔%s秒尝试驱赶%d次", [EmployeConst.Features.MovementSpeed] = "增加%%的移动速度", [EmployeConst.Features.CookingSpeed] = "疯狂状态:加快烹饪速度%%,持续%s秒,需要休息%s秒", [EmployeConst.Features.Tips] = "增加小费收入+%d", [EmployeConst.Features.CollectCoins] = "到处收集金币,持续%s秒,需要休息%s秒", [EmployeConst.Features.MusicalNotes] = "在自助餐厅唱歌,每%s小时挣得%d个音符", [EmployeConst.Features.SingSongs] = "可演唱歌曲%d首", [EmployeConst.Features.FishingBait] = "每天可以自动挖出%d个鱼饵", [EmployeConst.Features.FishingDepth] = "钓鱼深度增加%d米", [EmployeConst.Features.FishingCount] = "钓鱼最大数量达到%d", } EmployeConst.SkinType = { -- 移动速度 MovementSpeed = 1, -- 普通皮肤 } EmployeConst.SkinTypeDesc = { -- 移动速度 [EmployeConst.SkinType.MovementSpeed] = "移动速度提升%%", } EmployeConst.ActionTimes = { [EmployeConst.Type.Chef] = { -- 走到wander_2播放待机动作,持续6秒 [1] = 6, -- 走到wander_1播放炒菜动作,持续6秒 [2] = 6, -- 走回wander_2,播放待机动作持续6秒 [3] = 6, -- 走到wander_3,播放切菜动作,持续4秒 [4] = 4, -- 走回wander_1,播放待机,然后循环这个过程 [5] = 2, } } EmployeConst:init() LuaUtilZ --[[ luaide 模板位置位于 Template/FunTemplate/NewFileTemplate.lua 其中 Template 为配置路径 与luaide.luaTemplatesDir luaide.luaTemplatesDir 配置 https://www.showdoc.cc/web/#/luaide?page_id=713062580213505 author:{author} time:2022-05-15 16:57:36 ]] local LuaUtil = {} function LuaUtil.try(func, onError) return xpcall( func, function(err, a, b, c) local traceback = debug.traceback() local errStr = tostring(err) .. "\n" .. traceback CS.UnityEngine.Debug.LogError(errStr) CS.LogHelper.invokeLuaError(errStr) if onError then onError(err) end end ) end function LuaUtil.forEachCall(funcs, ...) for _, func in ipairs(funcs) do func(...) end end function LuaUtil.getLoc(depth) depth = depth or 1 local i = debug.getinfo(depth + 1) -- return string.format("%s:%d",i.short_src,i.currentline) return string.format("%s:%d",i.source,i.currentline) end local unpack = unpack or table.unpack -- 解决原生pack的nil截断问题,SafePack与SafeUnpack要成对使用 function LuaUtil.SafePack(...) local params = {...} params.n = select('#', ...) return params end -- 解决原生unpack的nil截断问题,SafePack与SafeUnpack要成对使用 function LuaUtil.SafeUnpack(safe_pack_tb) return unpack(safe_pack_tb, 1, safe_pack_tb.n) end -- 对两个SafePack的表执行连接 function LuaUtil.ConcatSafePack(safe_pack_l, safe_pack_r) local concat = {} for i = 1,safe_pack_l.n do concat[i] = safe_pack_l[i] end for i = 1,safe_pack_r.n do concat[safe_pack_l.n + i] = safe_pack_r[i] end concat.n = safe_pack_l.n + safe_pack_r.n return concat end -- 将字符串转换为boolean值 function LuaUtil.ToBoolean(s) local transform_map = { ["true"] = true, ["false"] = false, } return transform_map[s] end -- 深拷贝对象 function LuaUtil.DeepCopy(object) local lookup_table = {} local function _copy(object) if type(object) ~= "table" then return object elseif lookup_table[object] then return lookup_table[object] end local new_table = {} lookup_table[object] = new_table for index, value in pairs(object) do new_table[_copy(index)] = _copy(value) end return setmetatable(new_table, getmetatable(object)) end return _copy(object) end return LuaUtilSqliteDatabase ---@class SqliteDatabase:LuaClass local SqliteDatabase = defClass("SqliteDatabase") local sqlite3 = raw_require("lsqlite") local Path = CS.System.IO.Path local Directory = CS.System.IO.Directory local File = CS.System.IO.File local LOGTAG = SqliteDatabase.__cls_name function SqliteDatabase:ctor(filePath) self.filePath = filePath self.db = nil self.key = nil self.transactionList = nil self.tableNameList = {} self.tableNameDict = {} end function SqliteDatabase:open(key) if not self.filePath then return false end local str = string.format(self.filePath) local path = Path.GetDirectoryName(str) .. "/" if Directory.Exists(path) == false then Directory.CreateDirectory(path) end self.db = sqlite3.open(str) self.db:busy_handler( function(...) printWarn(LOGTAG, "open, sqlite is locked, try again") return 1 end, 1 ) self.key = key if self.key then sqlite3.key(self.db, self.key) end self:_updateTableNameList() return true end function SqliteDatabase:close() if self.db == nil then return end printVerbose(LOGTAG, "db close. %s", self) self.db:close() self.db = nil self.key = nil self.transactionList = nil self.tableNameList = {} self.tableNameDict = {} return true end function SqliteDatabase:onExit() self:close() SqliteDatabase.super.onExit(self) end function SqliteDatabase:remove() if File.Exists(self.filePath) then File.Delete(self.filePath) end end function SqliteDatabase:_updateTableNameList() self.tableNameList = self:_getTableNameListFromSql() for i, name in ipairs(self.tableNameList) do self.tableNameDict[name] = name end end function SqliteDatabase:getTable(tableCls, tableName, mgrApis) tableName = tableName or tableCls.__cls_name local apis = { exec = function (...) return self:_exec(...) end, beginTransaction = function (...) return self:_beginTransaction(...) end, finishTransaction = function (...) return self:_finishTransaction(...) end, nrows = function (...) return self:_nrows(...) end, getTimeFunc = mgrApis.getTimeFunc, getVersionFunc = mgrApis.getVersionFunc, } local sqlTable = tableCls.new(apis, tableName) local list = {} for i, col in ipairs(sqlTable:getColumnList()) do local colName = col.name local typeName = col:getSqlType() local str = colName .. " " .. typeName table.insert(list, str) end local str = string.format("CREATE TABLE IF NOT EXISTS %s (%s)", sqlTable:getName(), table.concat(list, ",")) self:_exec(str) self:_alterTable(sqlTable) return sqlTable end function SqliteDatabase:_alterTable(sqlTable) local oldColList = sqlTable:getColumnListFromSql() local newColList = sqlTable:getColumnList() local oldColDict = {} local newColDict = {} for i, col in ipairs(oldColList) do oldColDict[col.name] = col end for i, col in ipairs(newColList) do newColDict[col.name] = col end local addColList = {} local delColList = {} for k, col in pairs(oldColDict) do if not newColDict[k] then table.insert(delColList, col) end end for k, col in pairs(newColDict) do if not oldColDict[k] then table.insert(addColList, col) end end for i, col in ipairs(addColList) do sqlTable:addColumn(col) end end function SqliteDatabase:_beginTransaction() if self.transactionList then printError(LOGTAG, "beginTransaction, 当前正在执行事务") return end self.transactionList = {} printInfo(LOGTAG, "beginTransaction <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") table.insert(self.transactionList, "begin") end function SqliteDatabase:_finishTransaction() if not self.transactionList then printError(LOGTAG, "finishTransaction, 当前未在执行事务") return end table.insert(self.transactionList, "commit") if #self.transactionList <= 2 then self.transactionList = nil printWarn(LOGTAG, "finishTransaction, 当前事务为空") return end local str = "" for i, transaction in ipairs(self.transactionList) do str = str .. transaction .. ";\n" end self.transactionList = nil local ret = self:_exec(str) if ret ~= 0 then printError(LOGTAG, "数据库事务执行失败,尝试回滚失败") ret = self:_exec("rollback;") end if ret ~= 0 then printError(LOGTAG, "数据库回滚执行失败,数据可能已丢失") end printInfo(LOGTAG, "finishTransaction >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") return ret end function SqliteDatabase:_exec(str, func) if (not self.db) then printError(LOGTAG, "_exec, db is nil") return end if self.transactionList then table.insert(self.transactionList, str) return end local bm if util.perf then bm = util.perf.BenchMark() end local ret = self.db:exec(str, func) if bm then bm:dump("sqlite exec", 5, str) end if ret ~= 0 then printInfo(LOGTAG, "exec, sql:" .. str .. ", ret:" .. ret) if ret == 13 then printError(LOGTAG, string.format("SQL ERROR SQLITE_CANTOPEN 13: %s", tostring(self.dataBasePath))) elseif ret == 14 then printError(LOGTAG, string.format("SQL ERROR SQLITE_CANTOPEN 14: %s", tostring(self.dataBasePath))) elseif ret == 10 then printError(LOGTAG, string.format("SQL ERROR SQLITE_IOERR 10: %s", tostring(self.dataBasePath))) end local msg = self.db:errmsg() printError(LOGTAG, string.format("SQL ERROR %s, error msg: %s, sql:%s", tostring(ret), tostring(msg), tostring(str))) end return ret end function SqliteDatabase:_nrows(str) if (not self.db) then printError(LOGTAG, "_nrows, db is nil") return end return self.db:nrows(str) end function SqliteDatabase:_getTableNameListFromSql() local str = "select name from sqlite_master WHERE type = 'table'" local tableNameList = {} for row in self.db:nrows(str) do table.insert(tableNameList, row.name) end return tableNameList end function SqliteDatabase:_changeTableName(oldName, newName) if not self.tableNameDict[oldName] then return end if self.tableNameDict[newName] then return end printInfo(LOGTAG, "_changeTableName, from %s to %s", oldName, newName) local str = "ALTER TABLE " .. oldName .. " RENAME TO " .. newName return self:_exec(str) end return SqliteDatabase BonusUserData= -- 用户加成数据 local BonusUserData = defClass("BonusUserData") -- 构造函数 function BonusUserData:ctor() printInfo("BonusUserData", "------ 加成用户数据 初始化 ------") self:init() end -- 初始化 function BonusUserData:init() -- 已获得加成列表 self.bonus_obtained_id_list = {} self.coin_income_per_hour_last_time = -1 -- 每小时收益上次收入时间 self.coin_income_per_day_last_time = -1 -- 每日收益上次收入时间 -- 如果是新玩家,重置数据 if UserDataMgr.isNewPlayer then self:resetData() end end -- 重置数据 function BonusUserData:resetData() end --- 加载数据 function BonusUserData:load() if UserDataMgr.isNewPlayer then return end self:loadFromLocal() end --- 从本地加载数据 function BonusUserData:loadFromLocal() self.bonus_obtained_id_list = self:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.bonus_obtained_id_list, "{}"))) self.coin_income_per_hour_last_time = PlayerPrefsMgr:getInt(StrogeKeyDef.coin_income_per_hour_last_time, -1) self.coin_income_per_day_last_time = PlayerPrefsMgr:getInt(StrogeKeyDef.coin_income_per_day_last_time, -1) end --- 保存数据 function BonusUserData:save() self:saveToLocal() end --- 数据保存到本地 function BonusUserData:saveToLocal() PlayerPrefsMgr:setString(StrogeKeyDef.bonus_obtained_id_list, json.encode(self:idToString(self.bonus_obtained_id_list))) PlayerPrefsMgr:setInt(StrogeKeyDef.coin_income_per_hour_last_time, self.coin_income_per_hour_last_time) PlayerPrefsMgr:setInt(StrogeKeyDef.coin_income_per_day_last_time, self.coin_income_per_day_last_time) PlayerPrefsMgr:save() end -- 获取建筑加成ID列表 function BonusUserData:getBonusObtainedIdList() return self.bonus_obtained_id_list end -- 增加建筑加成ID function BonusUserData:addBonusId(bonusId, refuseSave) table.insert(self.bonus_obtained_id_list, bonusId) if not refuseSave then self:save() end return bonusId end -- 获取每小时加成的上次收益时间 function BonusUserData:getPerHourLastIncomeTime() return self.coin_income_per_hour_last_time or -1 end -- 更新每小时加成的上次收益时间 function BonusUserData:updatePerHourLastIncomeTime(excessTime, refuseSave) self.coin_income_per_hour_last_time = os.time() - (excessTime or 0) if not refuseSave then self:save() end return self.coin_income_per_hour_last_time end -- 获取每日加成的上次收益时间 function BonusUserData:getPerDayLastIncomeTime() return self.coin_income_per_day_last_time or -1 end -- 更新每日加成的上次收益时间 function BonusUserData:updatePerDayLastIncomeTime(excessTime, refuseSave) self.coin_income_per_day_last_time = os.time() - (excessTime or 0) if not refuseSave then self:save() end return self.coin_income_per_day_last_time end function BonusUserData:idToString(dic) local put = {} for i, v in pairs(dic) do put[tostring(i)] = v end return put end function BonusUserData:idToNumber(dic) local put = {} for i, v in pairs(dic) do put[tonumber(i)] = v end return put end BonusUserData:init()ScreenRecordUtill--[[ 录屏 author:{zhangpeng} time:2024-11-25 10:32:42 ]] local ScreenRecordUtil, super = defClassStatic("ScreenRecordUtil") local LOG_TAG = "ScreenRecordUtil" local IOS_CLASS_NAME = "ScreenRecorder" local JavaClass = "com/fy/xgame/tilelink/screenRecord/ScreenRecorderUtil" local max_record_time = 120 -- 最大录屏时间120秒 function ScreenRecordUtil:init() self:registLuaCallback() end -- 录屏开始倒计时动画 function ScreenRecordUtil:showCountdown(cb) local spine_prefab = "Assets/AssetsPackage/Res/Modules/common_spines/countdown/prefabs/countdown.prefab" local anim = CS.UnityEngine.GameObject.Instantiate(Res.loadAsset(spine_prefab)) anim:SetParent(util.ScreenUtil:getRootNode()) local render = anim:GetComponent(typeof(CS.UnityEngine.Renderer)) render.sortingOrder = 30 util.spine.play(anim, "idle",false,function () if cb then cb() end end) end -- 开始录屏倒计时,最长2分钟 function ScreenRecordUtil:startTimerCount(updateCallback, finalCallback) if self._countDownAct then return end -- timer ui local prefab = "Assets/AssetsPackage/Res/common/ui/timerui/daojishi/daojishi.prefab" if not self.timer_ui then self.timer_ui = CS.UnityEngine.GameObject.Instantiate(Res.loadAsset(prefab)) self.timer_ui:SetParent(util.ScreenUtil:getRootNode()) util.ScreenUtil:adaptScreen(self.timer_ui,util.ScreenUtil.layout_type.top_center, CS.UnityEngine.Vector3(0,0,0)) end self._countDownTime = max_record_time self._curTime = 0 self._countDownPause = false self._isCountDownOver = false self._finalCallback = function () self:stopTimerCount() if finalCallback then finalCallback() end end self._updateCallback = function (curTime, totalTime) if updateCallback then updateCallback() end local time = self._countDownTime - curTime if self.timer_ui then self.timer_ui:Seek("text")[CS.TMPro.TextMeshPro].text = math.floor(time) end end self._countDownAct = true self.timerId = Timer:add(function (dt) if self._countDownPause or self._isCountDownOver then return end self._curTime = self._curTime + dt self._updateCallback(self._curTime, self._countDownTime) if self._curTime >= self._countDownTime then self._isCountDownOver = true self._finalCallback() end end, nil, 0) end function ScreenRecordUtil:stopTimerCount() if self.timerId then printInfo(LOG_TAG, "停掉录屏计时器") Timer:rem(self.timerId) self.timer_ui:SetActive(false) end end function ScreenRecordUtil:registLuaCallback() local onScreenRecordStartSuc = function(message) printInfo(LOG_TAG,"ios启动录屏成功,开始录屏倒计时") self:startTimerCount() end local onScreenRecordStartError = function(error) printInfo(LOG_TAG,"ios启动录屏失败") end local onSaveVideoSuc = function () printInfo("保存录屏成功") end local onCancleSaveVideo = function () end local param = { onScreenRecordStartSuc = onScreenRecordStartSuc, onScreenRecordStartError = onScreenRecordStartError, onSaveVideoSuc = onSaveVideoSuc, onCancleSaveVideo = onCancleSaveVideo } if Device.isIOS() then luaoc.callStaticMethod(IOS_CLASS_NAME, "registLuaCallback", param) elseif Device.isAndroid() then luaj.callStaticMethod(JavaClass, "registLuaCallback", {onScreenRecordStartSuc, onScreenRecordStartError}) end end function ScreenRecordUtil:startScreenRecord() self:showCountdown(function () if Device.isIOS() then luaoc.callStaticMethod(IOS_CLASS_NAME, "StartRecording") elseif Device.isAndroid() then luaj.callStaticMethod(JavaClass, "startScreenCapture", {}) -- luaj.callStaticMethod(JavaClass, "startRecording", {}) end end) end function ScreenRecordUtil:pauseScreenRecord() if Device.isIOS() then luaoc.callStaticMethod(IOS_CLASS_NAME, "pauseRecordingWithCompletion",{}) elseif Device.isAndroid() then luaj.callStaticMethod(JavaClass, "pauseRecording", {}) end end function ScreenRecordUtil:stopScreenRecord() self:stopTimerCount() if Device.isIOS() then luaoc.callStaticMethod(IOS_CLASS_NAME, "StopRecording") elseif Device.isAndroid() then luaj.callStaticMethod(JavaClass, "stopRecording", {}) end end ScreenRecordUtil:init() RestaurantMap/--[[ 餐厅地图类 author:{zhangpeng} time:2025-05-20 15:30:00 ]] ---@class RestaurantMap:LuaClass local RestaurantMap, super = defClass("RestaurantMap", MapBase) local LOGTAG = "RestaurantMap" function RestaurantMap:ctor(scene, mapRoot) super.ctor(self, scene, mapRoot) -- 餐厅入口 self.restaurantEnterNode = self.rootNode:Seek("enter_point") -- 餐厅出口 self.restaurantExitNode = self.rootNode:Seek("out_point") -- 音乐烤吧入口 self.musicBbqEnterNode = self.rootNode:Seek("to_bbq_enter_point") -- 捕鱼入口 self.fishingEnterNode = self.rootNode:Seek("to_fishing_enter_point") -- 烤吧指示牌 self:initToMusicBbqSign() -- 池塘指示牌 self:initToFishingSign() -- 寻找闲逛点 self.customerWanderNodes = {} self.employeeNodes = {} self:initCustomerWanderNodes() self:initEmployeeNodes() end -- 重写父类方法:获取触摸组件 function RestaurantMap:getTouchComponents() local restaurantScene = RestaurantMgr:getRestaurantScene() if not restaurantScene then printError(LOGTAG, "找不到餐厅场景") return nil, nil end local touchCom = restaurantScene.touchCom local touchNode = restaurantScene.rootNode:Seek("bgroot") return touchCom, touchNode end -- 重写父类方法:触摸开始扩展处理 function RestaurantMap:onTouchBeganExtended(point) -- printInfo(LOGTAG, "点击到了: x:%s, y:%s, z:%s", point.x, point.y, point.z) -- 测试顾客走到点击的位置 local customer = CustomerMgr:getTestCustomer() if customer then -- 只有测试角色才支持手动寻路打断 if customer.isTestCustomer then -- 检查是否正在寻路,如果是则会自动打断 if customer.isPathFinding then printInfo(LOGTAG, string.format("测试顾客[%s]正在寻路中,将打断并开始新的寻路", customer.customerCfgId)) end -- 使用寻路走到点击的位置(会自动处理打断逻辑) customer:rolePathFind(point, function() printInfo(LOGTAG, string.format("测试顾客[%s]到达点击位置: x:%.2f, y:%.2f, z:%.2f", customer.customerCfgId, point.x, point.y, point.z)) end) else printWarn(LOGTAG, "当前顾客不是测试角色,不支持手动寻路控制") end else printWarn(LOGTAG, "没有找到测试顾客,请先创建测试顾客") end end -- 重写父类方法:触摸结束扩展处理 function RestaurantMap:onTouchEndExtended(point) -- printInfo(LOGTAG, "点击到了: x:%s, y:%s, z:%s", point.x, point.y, point.z) end -- 地图点击事件处理 function RestaurantMap:onMapClick(point) local tarPos = point -- printInfo(LOGTAG, "点击到了: x:%s, y:%s, z:%s", tarPos.x, tarPos.y, tarPos.z) end -- 重写父类方法:退出清理 function RestaurantMap:onExit() -- 清理餐厅地图特有资源 super.onExit(self) end -- 获取地图上餐厅入口 function RestaurantMap:getRestaurantEnter() return self.restaurantEnterNode end -- 获取地图上餐厅出口 function RestaurantMap:getRestaurantExit() return self.restaurantExitNode end -- 获取餐厅的音乐烤吧入口 function RestaurantMap:getToMusicBbqEnter() return self.musicBbqEnterNode end -- 获取餐厅的捕鱼入口 function RestaurantMap:getToFishingEnter() return self.fishingEnterNode end -- 初始化顾客闲逛点 function RestaurantMap:initCustomerWanderNodes() -- 找到闲逛节点父节点 local customer_root = self.rootNode:Seek("customer_root") local wanderParent = customer_root:Seek("wander_points") -- 获取所有子节点作为闲逛点 local childCount = wanderParent.transform.childCount for i = 0, childCount - 1 do local childNode = wanderParent.transform:GetChild(i).gameObject table.insert(self.customerWanderNodes, childNode) printInfo(LOGTAG, string.format("添加闲逛点: %s", childNode.name)) end printInfo(LOGTAG, string.format("找到%d个闲逛点", #self.customerWanderNodes)) end -- 获取顾客闲逛点 function RestaurantMap:getCustomerWanderNodes() return self.customerWanderNodes end -- 获取员工根节点 function RestaurantMap:getEmployeeRoot() return self.rootNode:Seek("employees_root") end -- 获取排队点 function RestaurantMap:getQueuePointRoot() return self.rootNode:Seek("queue_point") end -- 获取指定索引的排队点 function RestaurantMap:getQueuePointByIdx(idx) local queue = self:getQueuePointRoot() return queue:Seek(("queue_point_" .. idx)) end -- 初始化员工相关节点 function RestaurantMap:initEmployeeNodes() self.employeeNodes = {} -- 所有员工根节点 local employees_root = self:getEmployeeRoot() -- 遍历所有员工类型 for _, employeeType in ipairs(MapCfg.employeeRootNames.rootNames) do -- 获取当前类型员工的根节点 local root = employees_root:Seek(employeeType) if root then -- 初始化该类型员工的节点存储 self.employeeNodes[employeeType] = { birthPoint = nil, wanderPoints = {} } -- 获取出生点 local birthPoint = root:Seek(MapCfg.employeeRootNames.borthNodeName) if birthPoint then self.employeeNodes[employeeType].birthPoint = birthPoint end -- 获取闲逛点 local wanderParent = root:Seek(MapCfg.employeeRootNames.wanderRootNodeName) if wanderParent then -- 获取所有子节点作为闲逛点 local childCount = wanderParent.transform.childCount for i = 0, childCount - 1 do local childNode = wanderParent.transform:GetChild(i).gameObject table.insert(self.employeeNodes[employeeType].wanderPoints, childNode) end end end end end -- 获取员工的出生点和闲逛点 function RestaurantMap:getEmployeeNodes(employeeType) if not self.employeeNodes or not self.employeeNodes[employeeType] then printWarn(LOGTAG, string.format("找不到员工类型 '%s' 的节点信息", employeeType)) end return self.employeeNodes[employeeType] end -- 隐藏地图上所有建筑的遮挡信息 function RestaurantMap:hideAllBuildingBlock() for k,v in pairs(BuildingConst.buildingType) do self:updateBuildingBlock(v, false) end end -- 打开/关闭一个建筑的阻挡区域,并动态刷新Astar阻挡层 function RestaurantMap:updateBuildingBlock(buildingType, openBlock) -- 获取建筑信息 local buildingInfo = self:getBuildingInfoByType(buildingType) if not buildingInfo then printWarn(LOGTAG, string.format("找不到建筑类型对应的BuildingInfo: %s", buildingType)) return end local targetBlockHidden = openBlock local obRoot = self.rootNode:Seek("Obstacles") local obNode = obRoot:Seek("ob_building_" .. buildingType) if obNode then -- 设置阻挡物激活状态 obNode:SetActive(openBlock) -- 更新BuildingInfo中的状态 buildingInfo:setBlockHidden(targetBlockHidden) -- 动态刷新Astar寻路网格 self:refreshAstarGraphForBuilding(obNode, buildingType, openBlock) -- printInfo(LOGTAG, string.format("建筑阻挡更新成功: buildingType=%s, isOpen=%s, blockHidden=%s", buildingType, isOpen, targetBlockHidden)) else printWarn(LOGTAG, string.format("找不到建筑阻挡节点: ob_building_%s", buildingType)) end end -- 根据建筑类型获取对应的BuildingInfo function RestaurantMap:getBuildingInfoByType(buildingType) -- 遍历所有BuildingInfo,找到对应建筑类型的 local allBuildingInfos = BuildingMgr:getAllBuildingInfoList() for _, buildingInfo in pairs(allBuildingInfos) do if buildingInfo.buildingType == buildingType and buildingInfo:isInUse() then return buildingInfo end end -- 如果没有找到正在使用的,返回该类型的第一个 for _, buildingInfo in pairs(allBuildingInfos) do if buildingInfo.buildingType == buildingType then return buildingInfo end end return nil end -- 动态刷新建筑相关的Astar寻路网格 function RestaurantMap:refreshAstarGraphForBuilding(obNode, buildingType, isOpen) -- 检查AstarPath是否已初始化 local astar = CS.AstarPath.active if not astar then printError(LOGTAG, "AstarPath未初始化,无法刷新寻路网格") return end -- 获取阻挡物的边界范围 local bounds = self:getObstacleNodeBounds(obNode) if not bounds then printError(LOGTAG, string.format("无法获取建筑阻挡节点边界: %s", buildingType)) return end -- 创建图形更新对象 local guo = CS.Pathfinding.GraphUpdateObject(bounds) -- 设置更新参数 guo.updatePhysics = true -- 重新计算物理碰撞检测 guo.resetPenaltyOnPhysics = false -- 保持现有的penalty设置 -- 如果建筑被移除(isOpen=false),需要重新扫描该区域 -- 如果建筑被放置(isOpen=true),也需要重新扫描以识别新的阻挡 if isOpen then printInfo(LOGTAG, string.format("添加建筑阻挡,刷新寻路网格区域: %s", buildingType)) else -- printInfo(LOGTAG, string.format("移除建筑阻挡,刷新寻路网格区域: %s", buildingType)) end -- 异步更新图形,避免阻塞主线程 astar:UpdateGraphs(guo) -- editor的Scene窗口也执行一下scan astar:Scan() end -- 获取阻挡物节点的边界范围 function RestaurantMap:getObstacleNodeBounds(obNode) if not obNode then return nil end -- 尝试从Collider获取边界 local collider = obNode:GetComponent("Collider") if collider then return collider.bounds end -- 尝试从Collider2D获取边界 local collider2D = obNode:GetComponent("Collider2D") if collider2D then return collider2D.bounds end -- 如果没有碰撞器,使用Renderer边界 local renderer = obNode:GetComponent("Renderer") if renderer then return renderer.bounds end -- 最后尝试,基于Transform创建一个默认边界 local transform = obNode.transform if transform then local position = transform.position local scale = transform.localScale -- 创建一个基于scale的边界 local size = CS.UnityEngine.Vector3( math.max(scale.x, 1.0), math.max(scale.y, 1.0), math.max(scale.z, 1.0) ) return CS.UnityEngine.Bounds(position, size) end printWarn(LOGTAG, "无法确定阻挡物节点的边界范围") return nil end -- 通往烤吧的指示牌 function RestaurantMap:initToMusicBbqSign() local bbqsign = self.rootNode:Seek("bd01_zhishi_shaokao") local restaurantScene = RestaurantMgr:getRestaurantScene() local touchCom = restaurantScene.touchCom touchCom:addListener(bbqsign, TouchCom.LISTENER_TYPE.CLICK, function(p) -- 切换到烤吧地图 MapsMgr:switchMap(MapsConst.MapType.music_bar) end ) end -- 通往池塘的指示牌 function RestaurantMap:initToFishingSign() local fishingsign = self.rootNode:Seek("bd01_zhishi_yutang") local restaurantScene = RestaurantMgr:getRestaurantScene() local touchCom = restaurantScene.touchCom touchCom:addListener(fishingsign, TouchCom.LISTENER_TYPE.CLICK, function(p) -- 切换到烤吧地图 MapsMgr:switchMap(MapsConst.MapType.fishing_island) end ) end -- 通往捕鱼的指示牌 function RestaurantMap:getToFishingSign() return self.rootNode:Seek("to_fishing_sign") end return RestaurantMap EmployeCfgParseb --[[ 员工表解析 author:{zhangpeng} time:2025-05-21 09:57:45 ]] local EmployeCfg = require("data/config/employeCfg") local EmployeCfgParse = defClassStatic("EmployeCfgParse") local LOG_TAG = "EmployeCfgParse" local employe_list = {} local function initAllIds() for k, v in pairs(EmployeCfg) do table.insert(employe_list, v.employeId) end end -- init function EmployeCfgParse:init() initAllIds() end -- 根据员工配置id取一个员工的数据 function EmployeCfgParse:getEmployeCfg(employeId) for k, v in pairs(EmployeCfg) do if v.id == employeId then return v end end return nil end function EmployeCfgParse:getData() return EmployeCfg end function EmployeCfgParse:getDataByType(type) local temp = {} for _, v in pairs(EmployeCfg) do if v.tabType == type then table.insert(temp, v) end end return temp end function EmployeCfgParse:getDataByNotType(type) local temp = {} for _, v in pairs(EmployeCfg) do if v.tabType ~= type then table.insert(temp, v) end end return temp end function EmployeCfgParse:getEmployeCfgList() return employe_list end -- 返回首个剧情id function EmployeCfgParse:getFirstStoryDialogId(employeId) for k, v in pairs(EmployeCfg) do -- 找到这个员工 if v.id == employeId then -- 找到这个员工的剧情数量 local storyNum = v.storyNum for i = 1, storyNum do local dialogId = v["storyid_" .. i] if dialogId then return dialogId end end end end return nil end -- 返回员工剧情ids,员工会有多个剧情id,返回一个table function EmployeCfgParse:getStoryDialogIds(employeId) for k, v in pairs(EmployeCfg) do -- 找到这个员工 if v.id == employeId then -- 找到这个员工的剧情数量 local storyNum = v.storyNum local story_dialods = {} for i = 1, storyNum do local dialogId = v["storyid_" .. i] if dialogId then table.insert(story_dialods, dialogId) end end return story_dialods end end return nil end EmployeCfgParse:init() FishSalesCfgParse-- 鱼贩卖机配置解析 local FishSalesCfgParse = defClassStatic("FishSalesCfgParse") local FishSalesCfgData = require("data/config/fishSalesCfg") local LOGTAG = "FishSalesCfgParse" -- 初始化 function FishSalesCfgParse:init() self.idDic = {} for _, v in pairs(FishSalesCfgData) do self.idDic[v.id] = v end end -- 通过id获取鱼贩卖机配置 function FishSalesCfgParse:getFishSalesCfgById(id) return self.idDic[id] end -- 获取所有鱼贩卖机配置 function FishSalesCfgParse:getAllFishSalesCfg() return FishSalesCfgData end -- 随机获取一个长度为3不重复的鱼贩卖机配置列表(使用while实现) function FishSalesCfgParse:getFishSalesCfgListByRandom() local list = {} local i = 0 while i < 3 do local idx = math.random(1, #FishSalesCfgData) local t = false for _, v in ipairs(list) do if v == FishSalesCfgData[idx] then t = true break end end if not t then table.insert(list, FishSalesCfgData[idx]) i = i + 1 end end return list end FishSalesCfgParse:init() DollRowCell-- 玩偶行格子 local DollRowCell = defClass("DollRowCell") require("modules/ui/dollui/DollCell") -- 构造函数 function DollRowCell:ctor(ids, resLink, parent) self.resLink = resLink self.ids = ids self.go = GameObject.Instantiate(self.resLink.doll_row_cell, parent) self:initUI() self:showUI() end -- 设置ids function DollRowCell:setIds(ids) self.ids = ids end -- 初始化UI function DollRowCell:initUI() self.cell_roots = self.go:SearchPattern("doll_root_\\d", true) end -- 展示UI function DollRowCell:showUI() self.cells = {} for i, v in ipairs(self.ids) do self.cells[i] = DollCell.new(v, self.resLink, self.cell_roots[i].transform) end end return DollRowCell EmployeeCell.--- ---@class EmployeeCell local EmployeeCell = defClass("EmployeeCell") -- -- view -- require("modules/ui/employeeui/EmployeeDetailUI") -- UIComponent local UIImage = CS.UnityEngine.UI.Image local TMProUGUI = CS.TMPro.TextMeshProUGUI function EmployeeCell:ctor(employeeId, resLink, parent) self.ui = GameObject.Instantiate(resLink, parent) self.employeeId = employeeId self:initUI() self:showUI() end --- 重新加载 function EmployeeCell:reload(employeeId, parent) self.ui.transform:SetParent(parent) self.ui:SetActive(true) self.employeeId = employeeId self:showUI() end --- 初始化 function EmployeeCell:initUI() -- 初始化UI元素 self.bg_lock = self.ui:Seek("bg_lock") self.bg_unlock = self.ui:Seek("bg_unlock") self.employee_img = self.ui:Seek("employee_img") self.employee_posts = self.ui:Seek("employee_posts") self.employee_name = self.ui:Seek("employee_name") self.employee_desc = self.ui:Seek("employee_desc") self.employee_price = self.ui:Seek("employee_price") self.employee_training = self.ui:Seek("employee_training") self.employee_training_img = self.ui:Seek("employee_training_img") self.employee_training_value = self.ui:Seek("employee_training_value") self.employee_upgrade = self.ui:Seek("employee_upgrade") self.employee_no_upgrade_desc = self.ui:Seek("employee_no_upgrade_desc") self.tag_resting = self.ui:Seek("tag_resting") self.tag_working = self.ui:Seek("tag_working") --self.tag_dong = self.ui:Seek("tag_dong") self.rest_progress = self.ui:Seek("rest_progress") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.ui, function() self:click() end) end function EmployeeCell:showUI() local info = EmployeMgr:getEmployeeInfo(self.employeeId) info:refreshProficiency() local isUnlocked = info:isUnlocked() local isPurchased = info:isPurchased() local isMaxGrade = info:isMaxGrade() local isMaxProficiency = info:isMaxProficiency() local isMaster = info:getPosts() == EmployeConst.Posts.Manager local isCapybara = info.id == 300007 local needPurchase = isUnlocked and (not isPurchased) and (not isMaster) and (not isCapybara) local needUpgrade = (isPurchased or isCapybara) and (not isMaxGrade) self.bg_lock:SetActive(not isUnlocked) self.bg_unlock:SetActive(isUnlocked) self.employee_img:SetActive(isUnlocked) self.employee_posts[TMProUGUI].text = info:getPostsName() self.employee_name[TMProUGUI].text = info:getName() self.employee_desc[TMProUGUI].text = info:getDesc() self.tag_resting:SetActive(isPurchased and (info:getWorkState() == EmployeConst.State.Resting)) self.tag_working:SetActive(isPurchased and (info:getWorkState() == EmployeConst.State.Busy)) if isPurchased and (info:getWorkState() == EmployeConst.State.Resting) then local limit = info:getRestTimeLimit() local curr = info:getRestTime() if limit <= 0 then self.rest_progress[UIImage].fillAmount = 1 else self.rest_progress[UIImage].fillAmount = curr / limit end end if isUnlocked then self.employee_img[UIImage].sprite = info:getBustIcon() self.employee_img[UIImage]:SetNativeSize() end self.employee_price:SetActive(needPurchase) if needPurchase then self.employee_price[TMProUGUI].text = info:getHirePrice() end self.employee_training:SetActive(needUpgrade and (not isMaxProficiency)) self.employee_upgrade:SetActive(needUpgrade and isMaxProficiency) self.employee_no_upgrade_desc:SetActive(isPurchased and isMaxGrade) if needUpgrade and (not isMaxProficiency) then local proficiency = info:getProficiency() local maxProficiency = info:getMaxProficiency() -- 剩余时间 local remaining = maxProficiency - proficiency self.employee_training_img[UIImage].fillAmount = proficiency / maxProficiency self.employee_training_value[TMProUGUI].text = info:getTimeLeftDesc() end --self.tag_dong:SetActive(isPurchased and info:getCanUpgrade()) end function EmployeeCell:hide() self.ui:SetActive(false) end function EmployeeCell:click() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) local info = EmployeMgr:getEmployeeInfo(self.employeeId) local isUnlocked = info:isUnlocked() if not isUnlocked then PopUpUI.new(info:getUnlockDesc()):show():showMask():enableCloseWhenClickMask() return end EmployeeDetailUI.new(self.employeeId):show():showMask():enableCloseWhenClickMask():addCloseCallback(function() self:showUI() end) end --- 释放所有通过Seek获得的物体索引 function EmployeeCell:releaseUI() -- 遍历所有通过Seek获得的物体并将其置为空 self.bg_lock = nil self.bg_unlock = nil self.employee_img = nil self.employee_posts = nil self.employee_name = nil self.employee_desc = nil self.employee_price = nil self.employee_training = nil self.employee_training_img = nil self.employee_training_value = nil self.employee_upgrade = nil self.employee_no_upgrade_desc = nil self.tag_resting = nil self.tag_working = nil self.tag_up = nil -- 最后,将self.ui置为空 if self.ui then self.ui = nil end end return EmployeeCell ExtendBoundslocal Bounds = HackCSharpClass(CS.UnityEngine.Bounds) local Vector3 = CS.UnityEngine.Vector3 function Bounds:IntersectsOrContainsX(other) return not((self.min.x > other.max.x) or (self.max.x < other.min.x)) -- return (self.min.x <= other.max.x) and (self.max.x >= other.min.x) end CustomerMgrM--[[ author:{zhangpeng} time:2025-05-15 10:59:15 ]] --- 顾客管理 ---@class CustomerMgr:LuaStaticClass ---@field customerReslink string 顾客资源链接 ---@field pause boolean 是否暂停 ---@field time number 当天在线时间 ---@field firstCut number 第一个三分之一切割点 ---@field secondCut number 第二个三分之一切割点 ---@field customerUnlockList number[] 顾客解锁列表 ---@field customerFirstVisit CustomerFirstVisit 普通顾客首次来访 ---@field customerNormalVisit CustomerNormalVisit 普通顾客来访 ---@field customerSolicitation CustomerSolicitation 普通顾客招揽 ---@field customerSpecialVisit CustomerSpecialVisit 特殊顾客来访 local CustomerMgr = defClassStatic("CustomerMgr") local LOGTAG = "CustomerMgr" function CustomerMgr:init() printInfo(LOGTAG, "------ 顾客管理 初始化 ------") self:initUserData() self:initEvent() self:initVisitType() self:initCustomerInfo() self.pause = false local scene = RestaurantMgr:getRestaurantScene() CustomerMgr:setCustomerReslink(scene.customerR) CustomerQueMgr:init() end -- 开始执行逻辑 function CustomerMgr:start() self:setEvent() self:loadSave() -- 暂时不执行这些逻辑,直接添加一批测试顾客 self.tmr = TimerMgr:add(function (t) self:timeUpdate(t) end , 0.5, 0) end -- 初始化用户数据 function CustomerMgr:initUserData() self.userData = UserDataMgr.customerUserData -- todo 需要获取当日总时间 self.time = 0 self.customerUnlockList = UserDataMgr.customerUserData:getVisitCustomerIdList() self.customerUnlockList, self.firstCut, self.secondCut = self:sortAndGetCutPoints(self.customerUnlockList) end -- 初始化来访方式 function CustomerMgr:initVisitType() self.customerFirstVisit = CustomerFirstVisit.new() self.customerNormalVisit = CustomerNormalVisit.new() self.customerSolicitation = CustomerSolicitation.new() self.customerSpecialVisit = CustomerSpecialVisit.new() self.musicGrillBarVisit = MusicGrillBarVisit.new() self.fishingVisits = FishingVisits.new() end -- 初始化事件 function CustomerMgr:initEvent() end -- 设置事件 function CustomerMgr:setEvent() CurrencyMgr.currencyChangeEvent:addEvent(function (eventId, currencyType, value) if currencyType == CurrencyConst.CurrencyType.Star then self:onStarCountChangedEvent(eventId, value) end end) CuisineMgr.cuisineLearnedEvent:addEvent(function (eventId, cuisineId) self:onCuisineLearnedEvent(eventId, cuisineId) end) BuildingMgr.buildBuyEvent:addEvent(function (eventId, buildingId) self:onBuildingPurchasedEvent(eventId, buildingId) end) end -- 添加一些顾客到等待队列 function CustomerMgr:addTestCustomers(scene) -- CustomerMgr:setCustomerReslink(scene.customerR) -- 顾客父节点 local customerRoot = scene.rootNode:Seek("customer_root") if not customerRoot then printError(LOGTAG, "顾客父节点未找到!") return end -- 出生点 local birthPoint = scene.rootNode:Seek("test_customer") local birthPos = birthPoint and birthPoint:GetPosition() or Vector3(0, 0, 0) -- 创建几个顾客并添加到等待队列 local ids = {400001} self.testCustomer = nil -- 从customer_ids中随机取custom_count个 local customer_ids = {} for i = 1, #ids do local index = math.random(1, #ids) table.insert(customer_ids, ids[index]) end for i = 1, #customer_ids do -- 创建顾客 local customer = CustomerMgr:createCustomer(customer_ids[i]) -- 设置为测试角色,支持手动寻路打断 customer:setAsTestCustomer(true) -- 设置顾客父节点 customer:setCustomerParent(customerRoot) -- 设置顾客位置 (每个顾客位置稍微错开) local offset = (i - 1) * 0.5 customer:setCurPosition(Vector3(birthPos.x + offset, birthPos.y, birthPos.z)) -- 设置顾客的地图根节点 local mapNode = scene.rootNode:Seek(MapsConst.mapNodeNames[MapsConst.MapType.restaurant]) if mapNode then customer:setMapRoot(mapNode) end self.testCustomer = customer -- 设置寻路区域 customer:setSeekerAreaTag(MapsConst.getMusicBarTag()) end end function CustomerMgr:enterTheRestaurant(id) local scene = RestaurantMgr:getRestaurantScene() local customerRoot = scene.rootNode:Seek("customer_root") local birthPoint = scene.rootNode:Seek("enter_point") local birthPos = birthPoint and birthPoint:GetPosition() or Vector3(0, 0, 0) -- 创建顾客 local customer = CustomerMgr:createCustomer(id) -- 设置顾客父节点 customer:setCustomerParent(customerRoot) customer:setCurPosition(Vector3(birthPos.x, birthPos.y, birthPos.z)) -- 设置顾客的地图根节点 local mapNode = scene.rootNode:Seek(MapsConst.mapNodeNames[MapsConst.MapType.restaurant]) if mapNode then customer:setMapRoot(mapNode) end -- 任务进度 UserDataMgr:addRecruitCustomerCount(1) -- 招揽顾客计数 UserDataMgr:addRecruitSpecificCustomerCount(id, 1) -- 招揽特定顾客计数 self.userData:addCustomerVisits(id, 1) -- 顾客来访次数 -- 添加到等待队列 CustomerQueMgr:addCustomerToWaitingQueue(customer) return customer end -- 普通顾客入店 音乐烤吧 function CustomerMgr:enterTheMusicBbq(customerId) local customer = CustomerMgr:createCustomer(customerId) MusicbbqMgr:customerEnterTheMusicBbq(customer) MusicbbqMgr:startCustomerEnterTheMusicBbqLogic(customer) end -- 特殊顾客入店 音乐烤吧 function CustomerMgr:specialEnterTheMusicBbq(customerTeamId) local config = MusicBbqCustomerCfgParse:getMusicBbqCustomerCfgById(customerTeamId) local customerId = config.leadId local customer = CustomerMgr:createCustomer(customerId) -- 设置顾客的特殊行为标签 customer:setBehaviorTag(CustomerConst.BehaviorTag.musicBbqTeam) -- 从餐厅进入 RestaurantMgr:customerEnterRestaurant(customer) -- 展示气泡 customer:showDongBubble() -- 寻路 customer:rolePathFind(RestaurantMgr:getRestaurantToMusicBbqEnter().transform.position, function() printInfo(LOGTAG, "特殊顾客[%s]进入音乐烤吧", customerId) -- 入店 MusicbbqMgr:customerEnterTheMusicBbq(customer) -- 开始特殊顾客入店逻辑 MusicbbqMgr:startSpecialEnterTheMusicBbqLogic(customer, customerTeamId) end) end -- 普通顾客入店 池塘 function CustomerMgr:enterTheFishing(boothId, vendorId, customerId) local customer = CustomerMgr:createCustomer(customerId) FishingMgr:customerEnterTheFishing(customer) FishingMgr:startCustomerEnterTheFishingLogic(customer, boothId, vendorId) end -- 顾客进入餐厅 function CustomerMgr:normalCustomerEnterTheRestaurant(customer) RestaurantMgr:customerEnterRestaurant(customer) end --- 添加顾客到餐厅等待队列 function CustomerMgr:addRestaurantWaitingQueue(customer) CustomerQueMgr:addCustomerToWaitingQueue(customer) end --- 加载存档 function CustomerMgr:loadSave() -- todo 读取存档 self.isFirstLoginToday = UserDataMgr.customerUserData.isFirstLoginToday -- 是否当日首次登录 self.customerSpecialVisit:loadSave() end function CustomerMgr:setCustomerReslink(reslink) self.customerReslink = reslink end -- 创建一个测试顾客不参与任何逻辑,用来测试层级 function CustomerMgr:createTestCustomer() self:addTestCustomers(RestaurantMgr:getRestaurantScene()) end -- 获取测试顾客 function CustomerMgr:getTestCustomer() return self.testCustomer end -- 创建一个顾客 ---@param customerCfgId number 顾客配置表id function CustomerMgr:createCustomer(customerCfgId) local customer = Customer.new(self.customerReslink, customerCfgId) return customer end ----------主要流程---------- --- 计时 ---@param seconds number 秒 function CustomerMgr:timeUpdate(seconds) -- printInfo(LOGTAG, "顾客管理计时:%s",self.pause) if self.pause then return end -- 队列上限为15 if CustomerQueMgr:isToLimit() then return end -- printInfo(LOGTAG, "计时" .. seconds) -- printInfo(LOGTAG, "计时" .. self.time) self.time = self.time + seconds -- 计时 self.customerNormalVisit:timeUpdate(self.time) self.customerSolicitation:timeUpdate(self.time) self.customerSpecialVisit:timeUpdate(self.time) self.customerFirstVisit:timeUpdate(self.time) self.musicGrillBarVisit:timeUpdate(self.time) self.fishingVisits:timeUpdate(self.time) if #self.customerNormalVisit.naturalFlowQueue > 0 then -- 每秒自动从队列中生成一个顾客 local customerCfgId = self.customerNormalVisit:dequeue() -- 获取队列第一个顾客配置表id -- 更新顾客今日来访次数 self.userData:addCustomerNormalVisitsToday(customerCfgId, 1) self.customerNormalVisit:updateVisitsToday() printInfo(LOGTAG, "自然流顾客入店" .. customerCfgId) self:enterTheRestaurant(customerCfgId) end if #self.customerSolicitation.solicitationFlowQueue > 0 then -- 同上 local customerCfgId = self.customerSolicitation:dequeue() self.userData:addCustomerNormalVisitsToday(customerCfgId, 1) printInfo(LOGTAG, "招揽顾客入店" .. customerCfgId) self:enterTheRestaurant(customerCfgId) end if #self.customerSpecialVisit.customerSpecialQueue > 0 then -- 同上 --local customerSpecialType = self.customerSpecialVisit:dequeue() --printInfo(LOGTAG, "特殊顾客入店" .. customerSpecialType) ---- end end --- 决定会生成的顾客id 所有方式的普通顾客来访 ---@return number 顾客id function CustomerMgr:judgeCustomerId() math.randomseed(os.time()) local value = math.random() --printError("CC", "随机值: " .. value) --printError("CC", "总量: " .. #self.customerUnlockList) if value <= 0.2 then return self.customerUnlockList[math.random(1, self.firstCut)] elseif value >= 0.7 then return self.customerUnlockList[math.random(self.firstCut, #self.customerUnlockList)] else return self.customerUnlockList[math.random(self.firstCut, self.secondCut)] end end ----------顾客解锁---------- -- 当星星数量变化时触发 function CustomerMgr:onStarCountChangedEvent(eventId, starCount) local list = CustomerCfgParse:getStarVisitList(starCount) for _, v in ipairs(list) do if self:checkCustomerUnlock(v) then self.customerFirstVisit:addFirstVisitQueue(v.id) end end end -- 当菜品被学习时触发 function CustomerMgr:onCuisineLearnedEvent(eventId, cuisineId) local list = CustomerCfgParse:getCuisineVisitList(cuisineId) for _, v in ipairs(list) do if self:checkCustomerUnlock(v) then self.customerFirstVisit:addFirstVisitQueue(v.id) end end end -- 当建筑被购买时触发 function CustomerMgr:onBuildingPurchasedEvent(eventId, buildingId) local list = CustomerCfgParse:getBuildingVisitList(buildingId) for _, v in ipairs(list) do if self:checkCustomerUnlock(v) then self.customerFirstVisit:addFirstVisitQueue(v.id) end end end --- 顾客解锁 ---@param customerCfgId number 顾客配置表id function CustomerMgr:unlock(customerCfgId) table.insert(self.customerUnlockList, customerCfgId) local info = self:getCustomerInfo(customerCfgId) info:setState(CustomerConst.CustomerVisitState.Visited) --self.userData:setCustomerState(customerCfgId, CustomerConst.CustomerVisitState.Visited) self.customerUnlockList, self.firstCut, self.secondCut = self:sortAndGetCutPoints(self.customerUnlockList) end --- 对数组排序并获取三分之一切割点索引 ---@param arr number[] 输入数组 ---@return number[] 排序后的数组 ---@return number, number 两个三分之一切割点的索引值 function CustomerMgr:sortAndGetCutPoints(arr) -- 对数组从小到大排序 table.sort(arr, function(a, b) return a < b end) -- 计算三分之一切割点索引 local len = #arr local firstCut = math.floor(len / 3) local secondCut = math.floor(2 * len / 3) return arr, firstCut, secondCut end --- 检查顾客是否可以解锁 function CustomerMgr:checkCustomerUnlock(targetData) if (not targetData) then return false end -- 星数判断 if targetData.visitNeedStar > CurrencyMgr:getStar() then return false end -- 来访状态判断 if self.userData:getCustomerState(targetData.id) >= CustomerConst.CustomerVisitState.Visited then return false end -- 是否已在队列中 for _, v in ipairs(self.customerFirstVisit.customerFirstVisitQueue) do if v == targetData.id then return false end end -- 判断暂缓来访数组 for i, v in pairs(self.customerFirstVisit.customerNextVisit) do if v == targetData.id then return false end end for k, v in pairs(targetData.visitItemId) do if k == CustomerConst.VisitItemType.b then if type(v) == "number" then if not BuildingMgr:isBuildingPurchased(v) then return false end elseif type(v) == "table" then -- 如果是表则遍历 for _, id in ipairs(v) do if not BuildingMgr:isBuildingPurchased(id) then return false end end end elseif k == CustomerConst.VisitItemType.c then if type(v) == "number" then if not CuisineMgr:isCuisineLearned(v) then return false end elseif type(v) == "table" then for _, id in ipairs(v) do if not CuisineMgr:isCuisineLearned(id) then return false end end end elseif k == CustomerConst.VisitItemType.t then if type(v) == "number" then if not TaskMgr:isTaskCompleted(v) then return false end elseif type(v) == "table" then for _, id in ipairs(v) do if not TaskMgr:isTaskCompleted(id) then return false end end end elseif k == CustomerConst.VisitItemType.s then if type(v) == "number" then if UserDataMgr:getStarCount() < v then return false end elseif type(v) == "table" then for _, starCount in ipairs(v) do if UserDataMgr:getStarCount() < starCount then return false end end end else printError(LOGTAG, "未知的来访物品类型: %s", k) return false end end return true end --- 获取指定数组中在多个其他数组中没有的元素 ---@param targetArr table 指定数组 ---@param otherArrList table[] 其他数组的集合 ---@return table 不在其他数组中的元素数组 function CustomerMgr:getUniqueElements(targetArr, otherArrList) local elementSet = {} for _, arr in ipairs(otherArrList) do for _, value in ipairs(arr) do elementSet[value] = true end end local result = {} for _, value in ipairs(targetArr) do if not elementSet[value] then table.insert(result, value) end end return result end function CustomerMgr:initCustomerInfo() self.infos = {} end function CustomerMgr:getCustomerInfo(customerId) if self.infos[customerId] then return self.infos[customerId] end local info = CustomerInfo.new(customerId) self.infos[customerId] = info return info end ----------Img---------- --- 图片路径 function CustomerMgr:getImgPath(name) return "Assets/AssetsPackage/Res/modules/roles/customer/images/" .. name .. ".png" end --- 通用图片路径 function CustomerMgr:getCommonImgPath(name) return "Assets/AssetsPackage/Res/modules/ui/common/images/" .. name .. ".png" end --- 获取图片资源 function CustomerMgr:GetImgRes(name) return ResLoader.loadAsset(self:getImgPath(name), typeof(CS.UnityEngine.Sprite)) end --- 获取图片资源 function CustomerMgr:GetCommonImgRes(name) return ResLoader.loadAsset(self:getCommonImgPath(name), typeof(CS.UnityEngine.Sprite)) end --- 获取全身图标 function CustomerMgr:getIcon(customerId) local icon = self:GetImgRes("" .. customerId) if icon then return icon end -- 如果没有找到对应的全身图标,返回默认全身图标 return self:GetImgRes("default_role") end --- 获取半身图标 function CustomerMgr:getBustIcon(customerId) local icon = self:GetImgRes("" .. customerId) if icon then return icon end -- 如果没有找到对应的半身图标,返回默认半身图标 return self:GetImgRes("default_role_cell") end --- 获取全身剪影图标 function CustomerMgr:getSilhouetteIcon(customerId) local icon = self:GetImgRes("" .. customerId) if icon then return icon end -- 如果没有找到对应的剪影图标,返回默认剪影图标 return self:GetImgRes("default_role_lock") end --- 获取半身剪影图标 function CustomerMgr:getBustSilhouetteIcon(customerId) local icon = self:GetImgRes("" .. customerId) if icon then return icon end -- 如果没有找到对应的剪影图标,返回默认剪影图标 return self:GetImgRes("default_role_cell_lock") end --- 获取惊叹号图标 function CustomerMgr:getDongIcon() return self:GetCommonImgRes("dong") end --- 获取问号图标 function CustomerMgr:getUnknownIcon() return self:GetCommonImgRes("tab_unknown") end --- 获取升级图标 function CustomerMgr:getUpIcon() return self:GetCommonImgRes("upgrade") end --- 获取熊爪图标 function CustomerMgr:getClawIcon() return self:GetCommonImgRes("common_att_icon") end ----------Save---------- --- 是否已来访 function CustomerMgr:isVisited(customerId) return UserDataMgr.customerUserData:getCustomerState(customerId) >= CustomerConst.CustomerVisitState.Visited end ----------功能---------- --- 功能id function CustomerMgr:getFunTagId(customerId) local info = self:getCustomerInfo(customerId) if info then return info:getFunTagId(1) end return -1 end --- 功能描述 function CustomerMgr:getFunTagDesc(customerId) local info = self:getCustomerInfo(customerId) if info then return info:getFunTagDesc(1) end return "" end ----------时间---------- -- 检查视频招揽时间 function CustomerMgr:checkVideoSolicitationTime() if self.videoSolicitationTime then local now = os.time() if now - self.videoSolicitationTime >= 45 then return true else return false end end return true end -- 设置视频招揽时间 function CustomerMgr:updateVideoSolicitationTime() self.videoSolicitationTime = os.time() end return CustomerMgr helpCfg--[[ from file:帮助.xlsx --]] local helpCfg = { [1] = { id = 700001, helpType = 701, question = "如何购买建筑", answer = "在建造界面花费金币可以购买建筑", }, [2] = { id = 700002, helpType = 701, question = "如何提高人气?", answer = "您可以通过解锁设施、升级伙伴、扭蛋机、完成订单、完成每日任务、完成成就来提高人气", }, [3] = { id = 700003, helpType = 701, question = "如何邀请更多顾客", answer = "通过宣传可以获得更顾客", }, [4] = { id = 700004, helpType = 701, question = "升级菜品有什么作用", answer = "升级菜品后可以提升顾客吃饭时付费金额", }, [5] = { id = 700005, helpType = 701, question = "为什么流浪歌手/富二代/魔法师/礼物员不来", answer = "特殊顾客是随机到来的,不会留下吃饭,但是都自带特殊技能,您可以点击与他们互动,没准就有意外惊喜,可以前往游戏中等待留意哦~", }, [6] = { id = 700006, helpType = 701, question = "如何赶走捣乱的臭鼬小花和土拨鼠兄弟呢?", answer = "连续点击捣蛋的NPC就能进行驱赶了,当前您也可以雇佣保安旺财进行帮忙,会一定程度上帮助到您驱赶捣乱的客人的。", }, [7] = { id = 700007, helpType = 701, question = "如何完成订单任务", answer = "订单接取后您只需要在订单倒计时结束后准时上线就可以完成订单任何获得奖励了。", }, [8] = { id = 700008, helpType = 701, question = "订单任务过期了怎么办", answer = "订单任务在完成后的24小时之内是不会过期的,当您有订单的时候要多多上线查看订单的时间哦~", }, [9] = { id = 700009, helpType = 702, question = "我如何从音乐烤吧中赚取金币", answer = "音乐烤吧的收益按小时计算,离线或闲置时也能获得金币。进入游戏时,请留意离线收益通知!", }, [10] = { id = 700010, helpType = 702, question = "我如何获得音符?", answer = "帮助你的朋友们兼职赚取音符、歌手唱歌会自动兼职赚取音符,或者解锁音乐烤吧的扭蛋机,每天掉落一定数量的音符。升级你的扭蛋机,增加音符掉落数量!另外,让我告诉你一个秘密——奖励音符的任务可能会出现在你的每日任务中,所以请密切关注!", }, [11] = { id = 700011, helpType = 702, question = "扭蛋机在哪儿?为什么我没有扭蛋机?", answer = "扭蛋机将出现在音乐烤吧中,只需在音乐烤吧设施中解锁即可使用。立即解锁吧!", }, [12] = { id = 700012, helpType = 703, question = "我怎样才能捕鱼?", answer = "你需要工作人员渔夫来捕鱼!雇佣它后,点击鱼塘边的渔夫,即可开始体验捕鱼功能。", }, [13] = { id = 700013, helpType = 703, question = "我如何解锁“出租”标志?", answer = "在鱼塘边敲一块石头,派维修队把石头凿掉,它就会变成一个出租摊位。快去试试吧!", }, [14] = { id = 700014, helpType = 703, question = "鱼为什么消失了?", answer = "将鱼放入出租点在摊主随机招揽中起着很大的作用,所以如果你没有成功招揽,它会在一段时间后变黑并消失。", }, [15] = { id = 700015, helpType = 703, question = "我如何获得小鱼?", answer = "在捕鱼游戏中你可以通过攻击比你小的鱼获得等级提升并直至通关,每次挑战结束时系统会根据你的挑战进度发放小鱼奖励。", }, [16] = { id = 700016, helpType = 703, question = "如何解锁商贩?", answer = "要解锁商贩,你首先需要了解他们喜欢什么鱼。然后,把鱼带到“出租”告示牌上,随机邀请相应的摊位主人来搭建摊位。一定要多多尝试——如果他们搭建了摊位,你就能获得他们部分收入!", }, [17] = { id = 700017, helpType = 705, question = "为什么我满足了某些客户的要求却无法解锁?", answer = "只有满足解锁条件后,顾客才会出现在餐厅,用餐后才算解锁!顾客出现的时间没有固定!多多宣传,希望他们能光顾你的餐厅。", }, [18] = { id = 700018, helpType = 705, question = "为什么顾客身上会有一个小标签?", answer = "顾客身上的小标签意味着顾客是因为你促销活动而来的。它们并没有什么特殊效果。", }, [19] = { id = 700019, helpType = 705, question = "为什么有些顾客不吃就走了", answer = "可能是因为你还没解锁他们最爱的食物,所以他们生气地离开了。不过别担心,等你解锁了他们最爱的食物,他们下次就会留下来吃的。", }, [20] = { id = 700020, helpType = 706, question = "怎么增加每日扭蛋次数?", answer = "通过购买扭蛋机增加扭蛋次数,购买的越多,扭蛋次数越多", }, [21] = { id = 700021, helpType = 706, question = "扭蛋产出的玩偶有什么作用?", answer = "集齐玩偶可以召唤顾客、获得奖励", }, [22] = { id = 700022, helpType = 706, question = "扭蛋产出的音符怎么这么少?", answer = "扭蛋产生的音符数量是随机的", }, } return helpCfg ReceptionUI --[[ 迎宾台弹窗 author:{zhangpeng} time:2025-05-23 15:57:29 ]] local ReceptionUI, super = defClass("ReceptionUI",UILayer) local LOGTAG = "ReceptionUI" function ReceptionUI:ctor(buildingId) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/reception/receptionreslink") self.buildingId = buildingId end function ReceptionUI:onLoad() self.ui = GameObject.Instantiate(self.R.reception_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end function ReceptionUI:initUI() printInfo(LOGTAG, "initUI") -- close btn local closeBtn = self.ui:Seek("close") local get = self.ui:Seek("get") local get_double = self.ui:Seek("get_double") self.coin_img = self.ui:Seek("coin_img") self.coin_count = self.ui:Seek("coin_count") util.ugui.addButtonClickEvent(closeBtn, function() self:close() end) util.ugui.addButtonClickEvent(get, function() self:getReward() end) util.ugui.addButtonClickEvent(get_double, function() self:getVideoReward() end) end function ReceptionUI:showUI() local info = BuildingMgr:getBuildingInfo(self.buildingId) info:initTips() info:refreshTips() self:refreshTips(info:getCurrTips()) end function ReceptionUI:refreshTips(currValue) self.coin_count_tips = currValue self.coin_count:GetComponent("TextMeshProUGUI").text = "+" .. self.coin_count_tips end function ReceptionUI:getReward() -- local info = BuildingMgr:getBuildingInfo(self.buildingId) -- 检查当前小费是否大于0 local currTips = info:getCurrTips() if currTips <= 0 then PopUpUI.new("没有可领取的金币"):show():showMask():enableCloseWhenClickMask() return end -- 领取小费 CurrencyMgr:changeCoin(currTips, true) -- 播放金币飞到货币栏的动画 CoinDeskMgr:flyToCurrencyBar(self.coin_img, function() -- 刷新货币栏显示 CurrencyMgr:refreshCoin(true) end) info:collectTip() -- 关闭界面 self:close() end function ReceptionUI:getVideoReward() -- local info = BuildingMgr:getBuildingInfo(self.buildingId) -- 检查当前小费是否大于0 local currTips = info:getCurrTips() if currTips <= 0 then PopUpUI.new("没有可领取的金币"):show():showMask():enableCloseWhenClickMask() return end self:adCallback() end function ReceptionUI:adCallback() -- local info = BuildingMgr:getBuildingInfo(self.buildingId) -- 双倍领取小费 CurrencyMgr:changeCoin(info:getCurrTips() * 2, true) -- 播放金币飞到货币栏的动画 CoinDeskMgr:flyToCurrencyBar(self.coin_img, function() -- 刷新货币栏显示 CurrencyMgr:refreshCoin(true) end) info:collectTip() -- 关闭界面 self:close() end function ReceptionUI:badAdCallback() -- local info = BuildingMgr:getBuildingInfo(self.buildingId) -- 领取小费 CurrencyMgr:changeCoin(info:getCurrTips(), true) -- 播放金币飞到货币栏的动画 CoinDeskMgr:flyToCurrencyBar(self.coin_img, function() -- 刷新货币栏显示 CurrencyMgr:refreshCoin(true) end) info:collectTip() -- 关闭界面 self:close() end return ReceptionUI CuisineCell --- ---@class CuisineCell local CuisineCell = defClass("CuisineCell") -- UIComponent local UIImage = CS.UnityEngine.UI.Image local TMProUGUI = CS.TMPro.TextMeshProUGUI function CuisineCell:ctor(cuisineId, resLink, parent) self.ui = GameObject.Instantiate(resLink, parent) self.cuisineId = cuisineId self:initUI() self:showUI() end function CuisineCell:reload(cuisineId, parent) self.ui.transform:SetParent(parent) self.ui:SetActive(true) self.cuisineId = cuisineId self:showUI() end --- 初始化 function CuisineCell:initUI() -- 初始化UI元素 self.bg_lock = self.ui:Seek("bg_lock") self.bg_unlock = self.ui:Seek("bg_unlock") self.cuisine_img = self.bg_unlock:Seek("cuisine_img") self.cuisine_name = self.bg_unlock:Seek("cuisine_name") self.cuisine_price = self.ui:Seek("cuisine_price") self.cuisine_sales = self.ui:Seek("cuisine_sales") self.tag_up = self.ui:Seek("tag_up") self.tag_max = self.ui:Seek("tag_max") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.ui, function() self:click() end) end --- 展示ui function CuisineCell:showUI() local info = CuisineMgr:getCuisineInfo(self.cuisineId) local isUnlocked = info:isUnlocked() local isPurchased = info:isPurchased() local isMaxGrade = info:isMaxGrade() self.bg_lock:SetActive(not isUnlocked) self.bg_unlock:SetActive(isUnlocked) self.cuisine_img:SetActive(isUnlocked) if isUnlocked then self.cuisine_name[TMProUGUI].text = info:getName() self.cuisine_img[UIImage].sprite = info:getIcon() util.ugui:setImageTileSize(self.cuisine_img[UIImage], 220) else self.cuisine_name[TMProUGUI].text = "?" end self.cuisine_price:SetActive(isUnlocked and (not isPurchased)) self.cuisine_sales:SetActive(isUnlocked and isPurchased) if isUnlocked then if not isPurchased then self.cuisine_price[TMProUGUI].text = info:getLearnPriceDesc() else self.cuisine_sales[TMProUGUI].text = "销量 " .. info:getSales() end end self.tag_up:SetActive(isPurchased and (not isMaxGrade)) self.tag_max:SetActive(isPurchased and isMaxGrade) end function CuisineCell:hide() self.ui:SetActive(false) end function CuisineCell:click() local info = CuisineMgr:getCuisineInfo(self.cuisineId) local isUnlocked = info:isUnlocked() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) if isUnlocked then --CuisineMgr:showCuisineDetailUI(self.cuisineId, function() -- self:showUI() --end) CuisineDetailUI.new(self.cuisineId):show():showMask():enableCloseWhenClickMask():addCloseCallback(function() self:showUI() end) else PopUpUI.new(UnlockMgr:getUnlockDesc(info.unlockCondId)):show():showMask():enableCloseWhenClickMask() end end --- 释放所有通过Seek获得的物体索引 function CuisineCell:releaseUI() -- 遍历所有通过Seek获得的物体并将其置为空 self.bg_lock = nil self.bg_unlock = nil self.cuisine_img = nil self.cuisine_name = nil self.cuisine_price = nil self.cuisine_sales = nil self.tag_up = nil -- 最后,将self.ui置为空 if self.ui then self.ui = nil end end return CuisineCell HttpCmdMgr--[[ 使用http发送和接收json协议 author:{zhangpeng} time:2023-08-17 11:57:50 ]] local HttpCmdMgr = defClassStatic("HttpCmdMgr") local HttpGlobalUtil = CS.HttpGlobalUtil local GameObject = CS.UnityEngine.GameObject local LOG_TAG = "HttpCmdMgr" local CMD = HttpCmdDef.CMD function HttpCmdMgr:init() end function HttpCmdMgr:getJsonHeader() -- 请求头 self.header = { ['x-deviceid'] = "", ['x-os'] = Device.getPlatformStr(), ['x-version'] = "1.0", ['x-channel'] = "appstore", ['x-language'] = "en", } if self.tokenEnable then self.header['x-token'] = self.token end return self.header end function HttpCmdMgr:setHeaderToken(token) if not token then printWarn(LOG_TAG,"token is nil ! ! !") return end self.tokenEnable = true self.token = token CS.AESCTR.SetToken(token) end function HttpCmdMgr:getHeaderToken() return self.token end function HttpCmdMgr:setUserId(userid) self.userId = userid end function HttpCmdMgr:getUserId() return self.userId end function HttpCmdMgr:setHeaderDeviceId(deviceId) self.deviceId = deviceId end function HttpCmdMgr:postCmdSync(cmd,data,callback) callback = callback or function() end if not self.httpGo then self.httpGo = CS.HttpGlobalUtil.GetHttpGoInScene() end local start_time = os.time() if not NetworkStateUtil.isReachable() then printInfo(LOG_TAG,"--close loading anim-- net not reachable") UIComsTool:showDailog("网络不可用,请检查网络连接", "ok", function (ui) ui:close() return true end ) callback(false, nil) return end self.curCmd = cmd -- 组装url local url = HttpCmdDef.getUrlByCmd(cmd) -- 加密post body self.deviceId = self.deviceId or User:getDeviceId() printInfo(LOG_TAG, "协议名: %s", cmd) local base64Str = CS.AESCTR.EnctryHttpCmd(self.deviceId,data) -- 加密后重新组装json local body = {["s"] = base64Str} local jsonStr = json.encode(body) local header = HttpCmdMgr:getJsonHeader() header['x-deviceid'] = self.deviceId local ret = CS.HttpGlobalUtil.httpPostJsonSyncInScene(url,jsonStr,header, function(errorCode, ret) ret = tostring(ret) if errorCode ~= 200 then local net_error = self:rpsNetErrorCode(errorCode) if not net_error then -- 处理逻辑错误信息 callback(false, {isHttpError = true, errorCode = errorCode, errorMsg = ret}) end return end local resultData = json.decode(ret) if self:rpsCode(resultData) then -- 解密 if resultData.data then local decryData = CS.AESCTR.DecryptHttpCmd(resultData.data) printInfo(LOG_TAG,"decryData:%s", decryData) local rspData = json.decode(decryData) callback(true, rspData) else callback(true, resultData) end end end ) end function HttpCmdMgr:rpsNetErrorCode(code) local is_net_error = false for k,v in pairs(HttpCmdDef.ErrorCodeNet) do if code == v.code then UIComsTool:showToast(v.text,2) is_net_error = true end end return is_net_error end function HttpCmdMgr:rpsCode(resultData) local code = resultData.code local msg = resultData.message or "" if code == 0 then -- no error code return true end printInfo(LOG_TAG, "处理错误信息 code:%s msg:%s", code, msg) if code == HttpCmdDef.ErrorCode.TOKEN_ERROR.ID then UIComsTool:showToast(HttpCmdDef.ErrorCode.TOKEN_ERROR.TEXT) -- todo::处理token错误的逻辑 -- User:clearInvalidToken() elseif code == HttpCmdDef.ErrorCode.EMAIL_BINDED.ID then UIComsTool:showToast(HttpCmdDef.ErrorCode.EMAIL_BINDED.TEXT) elseif code == HttpCmdDef.ErrorCode.EMAIL_PW_ERROR.ID then UIComsTool:showToast(HttpCmdDef.ErrorCode.EMAIL_PW_ERROR.TEXT) elseif code == HttpCmdDef.ErrorCode.EMAIL_BE_USED.ID then UIComsTool:showToast(HttpCmdDef.ErrorCode.EMAIL_BE_USED.TEXT) elseif code == HttpCmdDef.ErrorCode.COIN_NOT_ENOUGH.ID then UIComsTool:showToast(HttpCmdDef.ErrorCode.COIN_NOT_ENOUGH.TEXT) elseif code == HttpCmdDef.ErrorCode.GEM_NOT_ENOUGH.ID then UIComsTool:showToast(HttpCmdDef.ErrorCode.GEM_NOT_ENOUGH.TEXT) elseif code == HttpCmdDef.ErrorCode.APPLE_ACCOUNT_BIND_EXIST.ID then UIComsTool:showToast(HttpCmdDef.ErrorCode.APPLE_ACCOUNT_BIND_EXIST.TEXT, 2) else UIComsTool:showToast(string.format("post cmd %s 未知错误代码:%s\n message:%s",self.curCmd,code,msg),3) end return false end function HttpCmdMgr:getToken() return self.token end HttpCmdMgr:init()mainrequire("modules/ui/commonui/EntityCell") require("modules/ui/commonui/EntityDetailUI") require("modules/ui/commonui/EntityListUI") require("modules/ui/commonui/PopUpUI") -- 其他区域 -- 建筑 require("modules/ui/buildinglist/BuildingInfoUI") -- 菜品 require("modules/ui/cuisineui/CuisineDetailUI") require("modules/ui/cuisineui/MusicBbqCuisineDetailUI") -- 顾客 require("modules/ui/customerui/CustomerDetailUI") -- 员工 require("modules/ui/employeeui/EmployeeDetailUI") -- 订单 require("modules/ui/taskui/order/TaskOrderDetailUI") -- 摊主 require("modules/ui/customerui/VendorDetailUI") -- 玩偶 require("modules/ui/dollui/DollDetailUI")mainC-- restaurant const require("modules/restaurant/RestaurantConst") -- map require("modules/restaurant/map/MapCfg") require("modules/restaurant/map/RestaurantMap") -- mgr require("modules/restaurant/mgr/RestaurantMgr") require("modules/restaurant/mgr/CustomerQueMgr") require("modules/restaurant/mgr/OrderDishesMgr") taskorderuireslinkreturn { --BASIC --ASSET task_order_cell = {"Assets/AssetsPackage/Res/modules/ui/task/prefabs/task_order_cell.prefab", 0, 0}, task_order_detail_ui = {"Assets/AssetsPackage/Res/modules/ui/task/prefabs/task_order_detail_ui.prefab", 0, 0}, } MusicbbqMgr; --[[ 音乐烤吧管理 author:{zhangpeng} time:2025-06-03 14:47:30 ]] local MusicbbqMgr = defClassStatic("MusicbbqMgr") local LOGTAG = "MusicbbqMgr" function MusicbbqMgr:getMusicbbqScene() return self.musicBbqScene end function MusicbbqMgr:setMusicbbqScene(musicBbqScene) self.musicBbqScene = musicBbqScene end -- 获取餐厅地图 function MusicbbqMgr:getMusicBbqMapNode() local scene = self:getMusicbbqScene() return scene.rootNode:Seek(MapsConst.mapNodeNames[MapsConst.MapType.restaurant]) -- todo --return scene.rootNode:Seek(MapsConst.mapNodeNames[MapsConst.MapType.music_bar]) end function MusicbbqMgr:start() printInfo(LOGTAG, "=== 开始音乐烤吧流程 ===") end function MusicbbqMgr:getMusicbbqEnter() return self.musicBbqScene.rootNode:Seek("bbq_enter_point") end function MusicbbqMgr:getMusicbbqExit() -- todo return self.musicBbqScene.rootNode:Seek("out_point") end -- 获取普通顾客根节点 function MusicbbqMgr:getCustomerRoot() local scene = self:getMusicbbqScene() return scene.rootNode:Seek("customer_root") end function MusicbbqMgr:getBirthAndIdlePoint(employeType) return self.musicBbqScene:getBirthAndIdlePoint(employeType) end function MusicbbqMgr:getMusicBbqSpecialWaitPoint() return self.musicBbqScene.rootNode:Seek("bbq_special_wait_point") end -- 普通顾客入店 音乐烤吧 function MusicbbqMgr:customerEnterTheMusicBbq(customer) customer:setSceneType(MapsConst.MapType.music_bar) local customerRoot = self:getCustomerRoot() local mapNode = self:getMusicBbqMapNode() local birthPoint = self:getMusicbbqEnter() local birthPos = birthPoint and birthPoint:GetPosition() or Vector3(0, 0, 0) -- 设置顾客父节点 customer:setCustomerParent(customerRoot) customer:setCurPosition(Vector3(birthPos.x, birthPos.y, birthPos.z)) customer:setMapRoot(mapNode) -- 设置顾客的寻路网格 customer:setRolePathGrid(MapsConst.GridGraphsName[MapsConst.MapType.music_bar]) return customer end -- 开始普通顾客入店逻辑 function MusicbbqMgr:startCustomerEnterTheMusicBbqLogic(customer) -- 进入烤吧后,开始烤吧行为 if customer.wanderInMusicbbqMap then customer:wanderInMusicbbqMap() end end -- 开始特殊顾客入店逻辑 function MusicbbqMgr:startSpecialEnterTheMusicBbqLogic(customer, customerTeamId) local config = MusicBbqCustomerCfgParse:getMusicBbqCustomerCfgById(customerTeamId) local waitPos = self:getMusicBbqSpecialWaitPoint() customer:rolePathFind(waitPos:GetPosition(), function () customer:playAnimation(CustomerConst.CustomerAction.stand_1, true) end) --customer:showDongBubble() local touchCom = self:getMusicbbqScene().touchCom touchCom:addListener(customer:getBubble(), TouchCom.LISTENER_TYPE.CLICK, function () self:summon(customer, config) end ) end -- 召唤顾客进入音乐烤吧 function MusicbbqMgr:summon(customer, config) customer:hideDongBubble() local ids = config.teamIds if type(ids) == "table" then for _, v in pairs(ids) do CustomerMgr:enterTheMusicBbq(v) end elseif type(ids) == "number" then CustomerMgr:enterTheMusicBbq(ids) end self:startCustomerEnterTheMusicBbqLogic(customer) end return MusicbbqMgr StallDeskMgr--[[ 钓鱼中心摊位桌子管理器 author:{zhangpeng} time:2025-07-04 19:53:09 ]] local StallDeskMgr = defClassStatic("StallDeskMgr") local LOGTAG = "StallDeskMgr" -- init function StallDeskMgr:init() self.stallDesks = {} end -- 根据建筑id创建一个摊位 function StallDeskMgr:createStallDesk(buildingInfo) local parentNode = RestaurantMgr:getRestaurantScene().rootNode:Seek("building_pos_" .. buildingInfo.id) local args = { deskId = buildingInfo.id, state = buildingInfo.state, parent = parentNode, scene = RestaurantMgr:getRestaurantScene(), } local stallDesk = StallDesk.new(args) table.insert(self.stallDesks, stallDesk) return stallDesk end -- 获取指定id摊位 function StallDeskMgr:getStallDeskById(id) for _, desk in ipairs(self.stallDesks) do if desk.id == id then return desk end end return nil end -- 获取解锁摊位数量 function StallDeskMgr:getStallDeskCount() local stall_list = { BuildingConst.buildingType.booth1, BuildingConst.buildingType.booth2, BuildingConst.buildingType.booth3, BuildingConst.buildingType.booth4, BuildingConst.buildingType.booth5, BuildingConst.buildingType.booth6, } local count = 0 for _, v in pairs(stall_list) do if UserDataMgr.buildingUserData:getBuildingState(v) >= StallConst.State.idle then count = count + 1 end end return count end return StallDeskMgrCmdDef--[[ 定义指令的tag author:{author} time:2023-08-23 18:02:59 ]] local CmdDef,_ = defClassStatic("CmdDef") -- 消息类型 CmdDef.MsgType = { REQ = 0x0, --客户端到服务器的握手请求/服务器到客户端的握手响应 ACK = 0x1, --客户端到服务器的握手应答/服务器到客户端的握手应答 SERVER_DIS_CONN = 0x2,--服务器主动断连通知 HEART = 0x3,--心跳包 DATA = 0x4,-- 指令数据包 } -- 业务指令编号 CmdDef.SendDef = { SingleChat = 0x1 } -- 接收的指令 CmdDef.ReceiveDef = { SingleChat = 0x2 } function CmdDef:init() end CmdDef:init()main.require("modules/common/event/SimpleEvent") StoryDialogUI$--[[ 剧情对话框 todo::根据立绘设置角色真实形象 author:{zhangpeng} time:2025-05-28 10:19:02 ]] local StoryDialogUI, super = defClass("StoryDialogUI", UILayer) local TMPUGUI = CS.TMPro.TextMeshProUGUI local LOGTAG = "StoryDialogUI" function StoryDialogUI:ctor(roleType, storyDialogId) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/storydialogui/storydialoguireslink") -- 角色类型 self.roleType = roleType -- 剧情对话配置id self.storyDialogId = storyDialogId -- 剧情对话配置数据 self.storyDialogData = nil -- 当前段落序号 self.currentPieceIndex = 0 -- 文本播放状态 self.textPlayState = StorydialogConst.TextState.Init -- init data self:initStoryDialogData() end -- 根据剧情id找剧情数据 function StoryDialogUI:initStoryDialogData() if not self.storyDialogId then return nil end local _data = StorydialogCfgParse:getDialogCfg(self.storyDialogId) if not _data then return nil end -- 组装成table self.storyDialogData = {} for i = 1, _data.times do local roleId = _data["roleId_" .. i] local roleType = StoryDialogMgr:getRoleTypeById(roleId) if roleId ~= -1 then local roleSide = _data["roleSide_" .. i] local dialogType = _data["dialogType_" .. i] local content = _data["content_" .. i] local name = "" if roleType then if roleType == StorydialogConst.RoleType.Customer then name = CustomerCfgParse:getCustomerCfg(roleId).name elseif roleType == StorydialogConst.RoleType.Employee then name = EmployeCfgParse:getEmployeCfg(roleId).name end end printInfo(LOGTAG, "对话段数:%s, roleId:%s, roleName:%s, roleSide:%s, dialogType:%s, content:%s", i, roleId, name, roleSide, dialogType, content) table.insert(self.storyDialogData, { roleId = roleId, roleSide = roleSide, dialogType = dialogType, content = content, -- 根据角色id查找一下名字 name = name }) end end printInfo(LOGTAG, "对话段数:%s", #self.storyDialogData) end function StoryDialogUI:onLoad() self.ui = GameObject.Instantiate(self.R.story_dialog_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() -- 开始播放 self:startPlay() end function StoryDialogUI:initUI() if not self.storyDialogData then printError(LOGTAG, "剧情Id:%s 数据是nil", self.storyDialogId) return end -- left role self.leftRole = self.ui:Seek("role_left") self.rightRole = self.ui:Seek("role_right") -- name self.roleName = self.ui:Seek("name") -- text self.text = self.ui:Seek("content") -- 跳过 local skipBtn = self.ui:Seek("skip_btn") util.ugui.addButtonClickEvent(skipBtn, function() printInfo(LOGTAG, "跳过当前对话,关闭对话框") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) -- 如果正在播放打字机效果,先停止 if self.textPlayState == StorydialogConst.TextState.Playing then self:breakTypewriter() end -- 直接关闭对话框 self:close() end) -- bg click local bg = self.ui:Seek("bg") util.ugui.addButtonClickEvent(bg, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) if self.textPlayState == StorydialogConst.TextState.Playing then self:breakTypewriter() else self:playNextPiece(nil, function() self:close() end) end end) end -- 开始执行对话 function StoryDialogUI:startPlay() self:playNextPiece() end -- 播放下一段 function StoryDialogUI:playNextPiece(onePiceOver, allOver) if self.currentPieceIndex >= #self.storyDialogData then if allOver then allOver() end return end self:setTextPlayState(StorydialogConst.TextState.Playing) self:clearText() self.currentPieceIndex = self.currentPieceIndex + 1 local currentDialog = self.storyDialogData[self.currentPieceIndex] self:showOnePiece(currentDialog.content, currentDialog.roleId, function() if onePiceOver then onePiceOver() end end) end -- 设置立绘所在边 function StoryDialogUI:setRoleSide(roleId) if self.leftRole and self.rightRole then -- 默认隐藏两侧角色 self.leftRole:SetActive(false) self.rightRole:SetActive(false) -- 根据角色类型获取对应的立绘 local sprite = nil local roleType = StoryDialogMgr:getRoleTypeById(roleId) if roleType then if roleType == StorydialogConst.RoleType.Customer then sprite = CustomerMgr:getCustomerInfo(roleId):getBustIcon() elseif roleType == StorydialogConst.RoleType.Employee then sprite = EmployeMgr:getEmployeeInfo(roleId):getBustIcon() end end -- 根据roleSide显示对应位置的角色 if self.storyDialogData[self.currentPieceIndex].roleSide == StorydialogConst.RoleSide.Left then self.leftRole:SetActive(true) -- 这里可以根据roleId设置角色头像等信息 local image = self.leftRole[UGUI.Image] image.sprite = sprite util.ugui:setImageTileSizeToRectangles(image, 220, 370) printInfo(LOGTAG, "显示左侧角色, roleId: %s", self.storyDialogData[self.currentPieceIndex].roleId) elseif self.storyDialogData[self.currentPieceIndex].roleSide == StorydialogConst.RoleSide.Right then self.rightRole:SetActive(true) -- 这里可以根据roleId设置角色头像等信息 local image = self.rightRole[UGUI.Image] image.sprite = sprite util.ugui:setImageTileSizeToRectangles(image, 220, 370) printInfo(LOGTAG, "显示右侧角色, roleId: %s", self.storyDialogData[self.currentPieceIndex].roleId) end end end -- 清除文本 function StoryDialogUI:clearText() self.text[TMPUGUI].text = "" self.roleName[TMPUGUI].text = "" end -- 显示一段对话 function StoryDialogUI:showOnePiece(text, roleId, finishcb) printInfo(LOGTAG, "开始播放第%s段对话", self.currentPieceIndex) self:setRoleSide(roleId) self:setRoleName(self.storyDialogData[self.currentPieceIndex].name) self.breakFlag = false self.fullText = text self.onePieceOvercb = finishcb self:setTextPlayState(StorydialogConst.TextState.Init) self:splitTextStr(text) local content = "" -- 设置状态为播放中 self:setTextPlayState(StorydialogConst.TextState.Playing) self.showOnePieceAction = util.async.foreach( self.textList, function (word,resolve,i) self.resolve = resolve -- printInfo(LOG_TAG, "word:%s", word) content = string.format("%s%s", content, self.textList[i]) self.ui:Delay( StorydialogConst.WordPlayTime, function() if not self.breakFlag then self:setTextContent(content) self.curShowIndex = i end resolve() end ) end, function () if not self.breakFlag then -- 只有在正常播放完成时才设置状态为播放完毕 self:setTextPlayState(StorydialogConst.TextState.PlayOver) end printInfo(LOGTAG, "第%s段对话播放完毕", self.currentPieceIndex) if self.onePieceOvercb then self.onePieceOvercb() end end ) end -- 打断打字机效果,显示完整对话内容 function StoryDialogUI:breakTypewriter() if self.breakFlag then return end printInfo(LOGTAG, "打断打字机效果,直接显示完整内容") self.breakFlag = true -- 结束showOnePieceAction if self.showOnePieceAction and type(self.showOnePieceAction) == "function" then self.showOnePieceAction() end self:setTextContent(self.fullText) self.curShowIndex = self.fullTextLen self:setTextPlayState(StorydialogConst.TextState.PlayOver) end -- 设置文本播放状态 function StoryDialogUI:setTextPlayState(state) self.textPlayState = state end -- 分割文本为单个字符 function StoryDialogUI:splitTextStr(text) local chars = string.splitUTF8(text) self.textList = chars self.fullTextLen = table.nums(self.textList) end -- 设置文本内容 function StoryDialogUI:setTextContent(content) if self.text and self.text[TMPUGUI] then self.text[TMPUGUI].text = content end end -- 设置角色名字 function StoryDialogUI:setRoleName(name) if self.roleName and self.roleName[TMPUGUI] then self.roleName[TMPUGUI].text = name end end -- 文字是否显示完了 function StoryDialogUI:isShowFull() return self.curShowIndex == self.fullTextLen end return StoryDialogUI main-- data require("modules/cuisine/data/CuisineConst") require("modules/cuisine/data/CuisineInfo") require("modules/cuisine/data/CuisineUserData") -- view require("modules/cuisine/view/CuisineBubble") -- mgr require("modules/cuisine/mgr/CuisineMgr") -- view mgr require("modules/cuisine/mgr/viewmgr/CuisineBubbleMgr") defGlobal("CusineCommonReslink") local reslink = ResLoader.loadResLink("modules/cuisine/cuisinereslink") local reslinkLoader = ReslinkLoad.new() reslinkLoader:addResLink(reslink) reslinkLoader:load( function() print("加载完成") CusineCommonReslink = reslink end, function(progress) print("加载进度:" .. progress) end, true ) local a = 1 DollCell-- 玩偶格子 local DollCell = defClass("DollCell") local Image = UnityEngine.UI.Image local TMProUGUI = CS.TMPro.TextMeshProUGUI local Sprite = typeof(UnityEngine.Sprite) -- 构造函数 function DollCell:ctor(dollId, resLink, parent) self.dollId = dollId self.go = GameObject.Instantiate(resLink.doll_cell, parent) self:initUI() self:showUI() end -- 初始化UI function DollCell:initUI() self.icon = self.go:Seek("icon") self.dong = self.go:Seek("dong") self.progress = self.go:Seek("progress") self.progress_bar = self.go:Seek("progress_bar") self.progress_value = self.go:Seek("progress_value") util.ugui.addClickEvent(self.icon, function() self:click() end) end -- 展示UI function DollCell:showUI() local config = DollMgr:getConfig(self.dollId) local currProgress = DollMgr:getDollCount(self.dollId) local totalProgress = config.count local isCollected = currProgress >= totalProgress local isReceived = DollMgr:getDollState(self.dollId) >= DollConst.State.Received local image = self.icon[Image] local path = string.format("Assets/AssetsPackage/Res/modules/ui/doll/images/roles/%s.png", config.dollIcon) if ResLoader.hasAsset(path) then image.sprite = ResLoader.loadAsset(path, Sprite) else image.sprite = nil end image:SetNativeSize() self.dong:SetActive(isCollected and (not isReceived)) self.progress:SetActive((not isCollected) or (not isReceived)) if (not isCollected) or (not isReceived) then self.progress_bar[Image].fillAmount = currProgress / totalProgress self.progress_value[TMProUGUI].text = string.format("%d/%d", currProgress, totalProgress) end end -- 点击 function DollCell:click() DollDetailUI.new(self.dollId):show():showMask():enableCloseWhenClickMask():addCloseCallback(function() -- 点击关闭回调 self:showUI() -- 重新展示UI以更新状态 end) end return DollCellmainrequire("common/core/base/Log") require("common/core/base/Enum") require("common/core/base/ExtendLua") require("common/core/base/Util") require("common/core/base/utils/main") require("common/core/base/Def") EnableGlobalCheck() require("common/core/base/Env") require("common/core/base/Msg") require("common/core/base/resmgr/main") require("common/core/base/ReslinkLoad") require("common/core/base/Event") require("common/core/base/TimerMgr") require("common/core/base/extend/main") require("common/core/base/touch/main") Msg.def("PAUSE") Msg.def("RESUME") Msg.def("EXIT") Msg.init() TimerMgr:init() Msg.add(Msg.PAUSE, function() TimerMgr:onAppPause() end) Msg.add(Msg.RESUME, function() TimerMgr:onAppResume() end) Msg.add(Msg.EXIT, function() -- Msg.send(Msg.LUA_EXIT) Event.exit() TimerMgr:exit() ResLoader.exit() Msg.exit() util.ugui.removeAllListeners() end) MusicGrillBarVisit-- 音乐烤吧来访 local MusicGrillBarVisit = defClass("MusicGrillBarVisit") -- 构造函数 function MusicGrillBarVisit:ctor() self:init() end -- 初始化 function MusicGrillBarVisit:init() self:initUserData() end -- 初始化用户数据 function MusicGrillBarVisit:initUserData() self.isFirstLoginToday = UserDataMgr.isFirstLoginToday self.time = CustomerMgr.time self:updateInterval() end -- 更新来访间隔时间 function MusicGrillBarVisit:updateInterval() self.visitInterval = math.random(120*60, 180*60) -- 随机来访间隔时间,2-3小时 end -- 时间更新 function MusicGrillBarVisit:timeUpdate(time) if self.isFirstLoginToday and time >= 60 then self:visit() self.isFirstLoginToday = false return end if time - self.time < self.visitInterval then return end self.time = time self:visit() self:updateInterval() end -- 来访 function MusicGrillBarVisit:visit() local ids = MusicBbqCustomerCfgParse:getData() local idx = math.random(1, #ids) -- 生成特殊顾客 CustomerMgr:specialEnterTheMusicBbq(ids[idx].id) end return MusicGrillBarVisit BuildingCellD --[[ 单个建筑格子 author:zheng time:2025-06-04 11:20:32 ]] local BuildingCell = defClass("BuildingCell") local TMPUGUI = CS.TMPro.TextMeshProUGUI local LOGTAG = "BuildingCell" function BuildingCell:ctor(cellNode, data) self.cellNode = cellNode self.data = data self:initUI() -- click self:clickCell() end function BuildingCell:initUI() local info = BuildingMgr:getBuildingInfo(self.data.uid) local isUnlocked = info:isUnlocked() local isPurchased = info:isPurchased() local isInUse = info:isInUse() --local isUnlocked = UnlockMgr:isUnlock(self.data.unlockConditionId) self.cellNode:Seek("bg_lock"):SetActive(not isUnlocked) self.cellNode:Seek("bg_unlock"):SetActive(isUnlocked) local mask_lock = self.cellNode:Seek("mask_lock") mask_lock:SetActive(not isUnlocked) local building_name = info:getName() -- 名字 local nameNode = self.cellNode:Seek("name") if isUnlocked then nameNode[TMPUGUI].text = building_name else nameNode[TMPUGUI].text = "?" end -- 购买价格 local priceNode = self.cellNode:Seek("price_num") priceNode:SetActive(isUnlocked and (not isPurchased)) if isUnlocked and (not isPurchased) then local price = BuyCommonCfgParse:getPrice(self.data.buyConditionId) priceNode[TMPUGUI].text = price end -- 状态 local stateNode = self.cellNode:Seek("state_str") local stateNode2 = self.cellNode:Seek("state_str2") stateNode:SetActive(isPurchased and isInUse) stateNode2:SetActive(isPurchased and (not isInUse)) -- 图片 local iconNode = self.cellNode:Seek("building_img") local iconImage = iconNode[UGUI.Image] -- 换图 iconImage.enabled = true iconImage.sprite = info:getUiIcon() util.ugui:setImageTileSize(iconImage, 260) end -- 设置坐标 function BuildingCell:setCellPositionX(x) local rectTransform = self.cellNode:GetComponent(typeof(CS.UnityEngine.RectTransform)) rectTransform.anchoredPosition = Vector2(x, rectTransform.anchoredPosition.y) end -- 点击建筑 function BuildingCell:clickCell() local building_img = self.cellNode:Seek("building_img") util.ugui.addButtonClickEvent(building_img,function () printInfo(LOGTAG, "点击了建筑:%s name:%s", self.data.buildingType, self.data.name) AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) BuildingInfoUI.new(self.data):show():showMask():enableCloseWhenClickMask() end) local mask = self.cellNode:Seek("mask_lock") util.ugui.addButtonClickEvent(mask, function () printInfo(LOGTAG, "点击了未解锁建筑:%s name:%s", self.data.buildingType, self.data.name) PopUpUI.new(UnlockMgr:getUnlockDesc(self.data.unlockConditionId)):show():showMask():enableCloseWhenClickMask() end) end return BuildingCell BuyConst^local BuyConst = defClassStatic("BuyConst") function BuyConst:init() end BuyConst.BuyCurrencyType = { Coin = 1, -- 金币 MusicalNotes = 2, -- 音符 } -- 购买限制类型 BuyConst.BuyLimitType = { Star = 1001, -- 星级限制 Build = 1002, -- 建筑限制 Tag = 1003, -- 标签限制 Cuisine = 1004, -- 菜品限制 Event = 1005, -- 活动限定 } -- 购买限制类型描述 BuyConst.BuyLimitTypeDesc = { [BuyConst.BuyLimitType.Star] = "人气必须达到%s", [BuyConst.BuyLimitType.Event] = "活动限定:%s活动期间可购买", [BuyConst.BuyLimitType.Build] = "必须已购买%s建筑", [BuyConst.BuyLimitType.Tag] = "必须拥有%s标签", [BuyConst.BuyLimitType.Cuisine] = "必须习得%s菜品" } BuyConst:init()TaskUI3--[[ 任务界面 author:{zhangpeng} time:2025-05-26 11:42:29 ]] local TaskUI, super = defClass("TaskUI", UILayer) require("modules/ui/taskui/TaskUIConst") require("modules/ui/taskui/daily/TaskDailyCell") require("modules/ui/taskui/achievement/TaskAchCell") require("modules/ui/taskui/order/TaskOrderCell") local LOGTAG = "TaskUI" local TMProUGUI = CS.TMPro.TextMeshProUGUI local UIImage = CS.UnityEngine.UI.Image function TaskUI:ctor(taskType) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/taskui/taskuireslink") self.taskType = taskType or TaskUIConst.TaskType.daily self.daily_task_cells = {} self.order_task_cells = {} TaskOrderMgr:addRefreshCallback(function () if self and self.ui then self:refreshTaskOrderList() end end) end function TaskUI:onLoad() self.ui = GameObject.Instantiate(self.R.task_ui_main) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initData() self:initUI() self:initToggle() self:timer(function() local scrollView = self.ui:Seek("ScrollView"):GetComponent("UnityEngine.UI.ScrollRect") scrollView.verticalNormalizedPosition = 1 end, 0.2) end function TaskUI:initUI() printInfo(LOGTAG, "initUI") -- scroll view self.scroll_view = self.ui:Seek("ScrollView") -- close btn local closeBtn = self.ui:Seek("close_btn") util.ugui.addButtonClickEvent(closeBtn, function() self:close() end) end -- init data function TaskUI:initData() -- 成就类任务数据列表 self.achievement_task_list = {} -- 每日类任务数据列表 self.daily_task_list = {} -- 订单类任务数据列表 self.order_task_list = {} self.achievement_task_list = TaskCfgParse:getTaskListByType(TaskConst.TaskType.achievement) self.daily_task_list = TaskCfgParse:getTaskListByType(TaskConst.TaskType.daily) self.order_task_list = TaskCfgParse:getTaskListByType(TaskConst.TaskType.order) end function TaskUI:initToggle() self.content = self.ui:Seek("Content") self.toggle_achievement = self.ui:Seek("cj") self.toggle_daily = self.ui:Seek("daily") self.toggle_order = self.ui:Seek("order") self.toggle_achievement_mask = self.ui:Seek("select_cj") self.toggle_daily_mask = self.ui:Seek("select_daily") self.toggle_order_mask = self.ui:Seek("select_order") self.order_none = self.ui:Seek("order_none") self.content_achievement = self.ui:Seek("content_achievement") self.content_daily = self.ui:Seek("content_daily") self.content_order = self.ui:Seek("content_order") self.daily_progress = self.ui:Seek("daily_progress") self.viewport = self.scroll_view:Seek("Viewport") util.ugui.addToggleValueChangeEvent(self.toggle_achievement, function(isOn) self.content_achievement:SetActive(isOn) self.toggle_achievement_mask:SetActive(isOn) if isOn then self.taskType = TaskConst.TaskType.achievement self:showAchTaskView() end end) util.ugui.addToggleValueChangeEvent(self.toggle_daily, function(isOn) self.content_daily:SetActive(isOn) self.daily_progress:SetActive(isOn) self.toggle_daily_mask:SetActive(isOn) local layoutGroup = self.content:GetComponent(typeof(VerticalLayoutGroup)) if isOn then --layoutGroup.padding = RectOffset(0, 0, 240, 0) self.viewport:GetComponent(typeof(UnityEngine.RectTransform)).offsetMax = Vector2(0, -240) self.taskType = TaskConst.TaskType.daily self:showDailyTaskView() else --layoutGroup.padding = RectOffset(0, 0, 0, 0) self.viewport:GetComponent(typeof(UnityEngine.RectTransform)).offsetMax = Vector2(0, 0) end end) util.ugui.addToggleValueChangeEvent(self.toggle_order, function(isOn) self.content_order:SetActive(isOn) self.toggle_order_mask:SetActive(isOn) if isOn then self.taskType = TaskConst.TaskType.order self:showOrderView() else self.order_none:SetActive(false) end end) self:changeToggle(self.taskType) end -- 切换任务栏 function TaskUI:changeToggle(taskType) self.taskType = taskType if self.taskType == TaskConst.TaskType.achievement then self.toggle_achievement:GetComponent("Toggle").isOn = true elseif self.taskType == TaskConst.TaskType.daily then self.toggle_daily:GetComponent("Toggle").isOn = true elseif self.taskType == TaskConst.TaskType.order then self.toggle_order:GetComponent("Toggle").isOn = true end end -- 显示成就任务列表 function TaskUI:showAchTaskView() if self:haveAchTaskData() then self:updateAchTaskView() else self:initAchTask() end end -- 初始化成就任务 function TaskUI:initAchTask() -- 创建成就任务格子 self.achievement_task_cells = {} self:updateStateAndAchTackView() end -- 是否已经有成就任务数据 function TaskUI:haveAchTaskData() return self.achievement_task_cells and #self.achievement_task_cells > 0 end -- 已有数据,刷新界面 function TaskUI:updateAchTaskView() -- 获取成就任务的滚动视图 local achievement_content = self.ui:Seek("content_achievement") if not achievement_content then printError(LOGTAG, "achievement_content not found") return end -- 已经有任务格子,只需要更新状态 for i, taskCell in ipairs(self.achievement_task_cells) do if taskCell then taskCell:updateUI() end end end -- 更新数据并刷新界面 function TaskUI:updateStateAndAchTackView() -- 获取可用的成就任务 local availableTasks = TaskAchMgr:genAvailableAchTasks() local achievement_content = self.ui:Seek("content_achievement") local sequence = {} for i, taskData in ipairs(availableTasks) do -- 未领取任务 if TaskAchMgr:getTaskState(taskData.id) ~= TaskConst.TaskState.claimed then table.insert(sequence, taskData) end end table.sort(sequence, function(a, b) return (TaskMgr:getTaskProgress(a.id) / a.count) > (TaskMgr:getTaskProgress(b.id) / b.count) end) for i, v in ipairs(sequence) do self:createAchTaskCell(v, i, achievement_content.transform) end printInfo(LOGTAG, "initialized achievement tasks, count: " .. #self.achievement_task_cells) end -- init daily task function TaskUI:initDailyTask() local availableTasks = TaskDailyMgr:genAvailableDailyTasks() -- 随机选取5个新任务 local taskCount = math.min(5, #availableTasks) local selectedTasks = {} local taskIndices = {} local selectedTaskIds = {} -- 创建任务索引表 for i = 1, #self.daily_task_list do table.insert(taskIndices, i) end -- 随机选择5个任务 for i = 1, taskCount do local randomIndex = math.random(1, #taskIndices) local taskIndex = taskIndices[randomIndex] local taskData = self.daily_task_list[taskIndex] table.insert(selectedTasks, taskData) table.insert(selectedTaskIds, taskData.id) table.remove(taskIndices, randomIndex) end -- 保存任务ID列表 TaskDailyMgr:setDailyTaskIds(selectedTaskIds) printInfo(LOGTAG, "refreshed daily tasks, ids: " .. table.concat(selectedTaskIds, ", ")) -- 创建任务格子 for i, taskData in ipairs(selectedTasks) do self:createDailyTaskCell(taskData) end self:updateDailyProgress() --util.ugui.addButtonClickEvent(self.ui:Seek("daily_progress"), function() -- -- 520001 -- TaskMgr:getTaskRewardById(TaskMgr:getDailyFinalMissionsId()) --end) end -- 是否已经有每日任务数据 function TaskUI:haveDailyTaskData() local dailyTaskIds = TaskDailyMgr:getDailyTaskIds() return #dailyTaskIds > 0 end -- 已有数据,刷新界面 function TaskUI:updateDailyView() -- 获取每日任务的滚动视图 local daily_content = self.ui:Seek("content_daily") if not daily_content then printError(LOGTAG, "daily_content not found") return end local dailyTaskIds = TaskDailyMgr:getDailyTaskIds() -- 是否已经有每日任务数据 local haveDailyTaskData = self:haveDailyTaskData() -- 加载内存或者存档里的每日任务数据 if haveDailyTaskData then -- 关闭界面后再进入,daily_task_cells需要重新创建 if #self.daily_task_cells <=0 then for i = 1, #dailyTaskIds do self:createDailyTaskCell(TaskCfgParse:getTaskCfg(dailyTaskIds[i])) end end -- 重新获取一下任务状态,刷新一下cell for i, taskId in ipairs(dailyTaskIds) do local taskData = TaskCfgParse:getTaskCfg(taskId) if taskData then local taskCell = self.daily_task_cells[i] if taskCell then taskCell:updateUI() end end end self:updateDailyProgress() end end function TaskUI:updateDailyProgress() -- 获取每日任务的滚动视图 local daily_progress_front = self.daily_progress:Seek("daily_progress_front") local daily_progress_value = self.daily_progress:Seek("daily_progress_value") local noneObj = self.daily_progress:Seek("noneObj") local clearObj = self.daily_progress:Seek("clearObj") local reward_value = self.daily_progress:Seek("reward_value") local id = TaskMgr:getDailyFinalMissionsId() local dailyTaskIds = TaskDailyMgr:getDailyTaskIds() -- 计算进度 local completedCount = 0 for _, taskId in ipairs(dailyTaskIds) do if TaskDailyMgr:getTaskState(taskId) >= TaskConst.TaskState.claimed then completedCount = completedCount + 1 end end local rewardValue = TaskCfgParse:getTaskCfg(id).params_1 -- 更新奖励文本 reward_value[TMProUGUI].text = "+" .. rewardValue -- 更新进度条和文本 daily_progress_front[UIImage].fillAmount = completedCount / #dailyTaskIds daily_progress_value[TMProUGUI].text = completedCount .. "/" .. #dailyTaskIds noneObj:SetActive(completedCount < #dailyTaskIds) clearObj:SetActive(completedCount >= #dailyTaskIds) -- 奖励 if (not TaskMgr:isTaskClaimed(id)) and completedCount >= #dailyTaskIds then TaskMgr:getTaskRewardById(id) TaskMgr:setTaskClaimed(id) end end -- 首次创建活过了24小时之后重新初始化每日任务数据 function TaskUI:showDailyTaskView() if self:haveDailyTaskData() then self:updateDailyView() else self:initDailyTask() end end -- 创建一个每日任务的cell function TaskUI:createDailyTaskCell(taskData) local daily_content = self.ui:Seek("content_daily") local cellNode = GameObject.Instantiate(self.R.cell_daily, daily_content.transform) cellNode:SetName("task_cell_" .. #self.daily_task_cells) local taskCell = TaskDailyCell.new(cellNode, taskData) table.insert(self.daily_task_cells, taskCell) return taskCell end -- 创建一个成就任务的cell function TaskUI:createAchTaskCell(taskData, idx, parent) local taskCell = self.achievement_task_cells[idx] if taskCell then taskCell:reload(taskData) return taskCell end local cellNode = GameObject.Instantiate(self.R.cell_ach, parent) cellNode:SetName("task_cell_" .. #self.achievement_task_cells) taskCell = TaskAchCell.new(cellNode,taskData) table.insert(self.achievement_task_cells, taskCell) return taskCell end -- 显示订单任务列表 function TaskUI:showOrderView() local ids = TaskOrderMgr:getOrderIds() -- 获取订单任务的滚动视图 local order_content = self.ui:Seek("content_order") if not order_content then printError(LOGTAG, "order_content not found") return end -- 提示无订单 self.order_none:SetActive(#ids <= 0) -- 创建订单任务格子 self:createOrderAll(ids, order_content.transform) end -- 刷新订单任务列表 function TaskUI:refreshTaskOrderList() if self.taskType == TaskConst.TaskType.order then self:showOrderView() else self:changeToggle(TaskConst.TaskType.order) end end -- 创建所有订单任务格子 function TaskUI:createOrderAll(ids, parent) local idx = 0 for i, v in ipairs(self.order_task_cells) do if i <= #ids then v:reload(ids[i]) idx = i else v:hide() end end for i = idx + 1, #ids do local cellNode = GameObject.Instantiate(self.R.cell_order, parent) cellNode:SetName("task_order_cell_" .. i) table.insert(self.order_task_cells, TaskOrderCell.new(cellNode, ids[i])) end end return TaskUI UIComsToolw --[[ UI组件工具类 author:{zhangpeng} time:2023-08-01 17:37:41 ]] local UIComsTool = defClassStatic("UIComsTool") local LOG_TAG = "UIComsTool" local TMPUGUI = CS.TMPro.TextMeshProUGUI function UIComsTool:init() self:clearLoadingState() end --@region loading show and hide function UIComsTool:clearLoadingState() self.loadingCount = 0 self.loadingPanel = nil end -- mask alpha value:0-1 function UIComsTool:showLoading(maskalpha) maskalpha = maskalpha or 0.5 self.loadingCount = self.loadingCount + 1 if self.loadingCount <= 0 then return end if self.loadingPanel then return end self.loadingPanel = UILoading.new(true):show():showMask(maskalpha):addCloseCallback( function() self:clearLoadingState() end ) end function UIComsTool:hideLoading(isAll) if isAll then self.loadingCount = 0 else self.loadingCount = self.loadingCount - 1 end printInfo(LOG_TAG, "loading count: %s", self.loadingCount) if self.loadingCount > 0 then return end if self.loadingPanel then self.loadingPanel:close() end self:clearLoadingState() end --@endregion --#region show toast function UIComsTool:showToast(text, showtime, closecb, isGlobal) showtime = showtime or 1 if isGlobal == nil then isGlobal = true end return UIToast.new():show(isGlobal):setShowTime(showtime):setContentText(text):setPriority(UILayer.UI_ORDER.TOP_DEFAULT) end --#endregion function UIComsTool:showSimplyDialogBox(text) return UIDialogSimple.new():show():showMask(0.5):setContentText(text):setPriority(UILayer.UI_ORDER.TOP_DEFAULT) end -- 显示特殊外观的弹框 function UIComsTool:showDailogByStyle(style, content_text, btn_left_text, cb_left, price) content_text = content_text or "no content!" local dialog = self:createDialog(style) -- dialog:enableCloseWhenClickMask() dialog:setContentText(content_text) dialog.ui:Seek("price")[TMPUGUI].text = string.format("x%s", price) if btn_left_text then local btn_text = btn_left_text dialog:showSingleBtn(btn_text, cb_left) end return dialog end function UIComsTool:createDialog(style) local dialog = UIDialog.new(style) dialog:show(false) return dialog end --#region show dialog function UIComsTool:showDailog(content_text, btn_left_text, cb_left, btn_right_text, cb_right,tag) content_text = content_text or "no content!" local dialog = self:createDialog(UITypeEnums.DialogType.Common) dialog:setContentText(content_text) dialog:hideCloseBtn() dialog:setTagName(tag or "uidialog") if btn_left_text and btn_right_text then dialog:showDoubleBtns(btn_left_text, btn_right_text, function (ui) if cb_left then cb_left(ui) end end, function (ui) if cb_right then cb_right(ui) end end ) elseif btn_left_text or btn_right_text then local btn_text = btn_left_text or btn_right_text dialog:showSingleBtn(btn_text, function (ui) local cb = cb_left or cb_right if cb then cb(ui) end end ) end return dialog end --#endregion UIComsTool:init() DiningDesk--[[ 餐桌 author:{zhangpeng} time:2025-05-16 12:00:25 ]] local DiningDesk,super = defClass("DiningDesk",BuildingBase) local LOGTAG = "DiningDesk" function DiningDesk:ctor(args) super.ctor(self,args) self.deskCfgId = args.deskId self.resId = "building_"..self.buildingInfo.buildingType self.state = DiningConst.State.idle self.seats = {} self:initDeskView() end -- 餐桌序号(1号桌,2号桌..) function DiningDesk:getDeskIndex() return DiningConst.deskIndex[self.deskCfgId] end function DiningDesk:initDeskView() self.buildingNode = GameObject.Instantiate(self.scene.R[self.resId]) self.sortBaseNode = self.buildingNode:Seek("base") self:setBuildingParent(self.buildingParent) self:setBuildingNodeName(self.deskCfgId) self.sortBaseNode = self.buildingNode:Seek("base") -- 金币放置点 self.coinPosNode = self.buildingNode:Seek("coin_pos") -- 金币收集点击区域 self.coinCollectNode = self.coinPosNode:Seek("touch_mask") if self.coinCollectNode then self:addCoinPosClickEvent() end -- 桌子椅子分别设置层级 local node_desk = self.buildingNode:Seek("img") local yz_list = {} -- 存储food节点 self.foodNodes = {} local yz_count = 4 for i = 1, yz_count do -- 打印餐桌名字和座位名字 printInfo(LOGTAG, string.format("餐桌名字: %s, 座位名字: %s", self.resId, "yz_"..i)) local yz = self.buildingNode:Seek("yz_"..i) local sprite_render = yz:GetComponent("Renderer") -- sprite_render.sortingOrder = 9 table.insert(yz_list, yz) -- 查找对应的food节点 local foodNodeName = "food_" .. i local foodNode = self.buildingNode:Seek(foodNodeName) if foodNode then self.foodNodes[i] = foodNode end -- 创建座位对象 local seat = DiningDeskSeat.new(yz) -- 设置座位索引和餐桌引用 seat:setDiningDeskAndIndex(self, i) -- 添加到座位列表 table.insert(self.seats, seat) end -- 桌子压在椅子上面 local sprite_render = node_desk:GetComponent("Renderer") -- sprite_render.sortingOrder = 11 end -- 获取桌子状态 function DiningDesk:getState() return self.state end -- 设置桌子状态 function DiningDesk:setState(state) self.state = state end -- 获取空座位 function DiningDesk:findIdleSeat() local seats = self.seats for i = 1, #seats do local temp = seats[i] if temp:isCanSitDowm() then return temp end end return nil end -- 检查桌子是否有顾客 function DiningDesk:hasCustomer() local seats = self.seats for i = 1, #seats do if seats[i]:getState() == DiningConst.SeatState.used then return true end end return false end -- 检查是否还有空位 function DiningDesk:hasIdleSeat() local seats = self.seats for i = 1, #seats do if seats[i]:isCanSitDowm() then return true end end return false end -- 更新桌子状态 function DiningDesk:updateState() if not self:hasIdleSeat() then self:setState(DiningConst.State.used) else self:setState(DiningConst.State.idle) end -- 初始化餐桌后尝试分配顾客 CustomerQueMgr:checkAndAssignAllWaitingCustomers() end -- 桌子是否为脏 function DiningDesk:isDeskDirty() local seats = self.seats for i = 1, #seats do if seats[i]:checkSeatDirty() then return true end end return false end -- 清理餐桌餐余物 function DiningDesk:cleanDeskResidueCuisine() local seats = self.seats for i = 1, #seats do seats[i]:cleanSeatResidueCuisine() end end -- 获取座位位置 function DiningDesk:getSeatPosByIndex(seatIndex) local seats = self.seats return seats[seatIndex]:getSeatPosition() end -- 获取座位对应的食物节点 function DiningDesk:getFoodNodeBySeatIndex(seatIndex) return self.foodNodes[seatIndex] end -- 获取所有座位 function DiningDesk:getAllSeats() return self.seats end -- 获取金币放置点 function DiningDesk:getCoinPosNode() return self.coinPosNode end -- 给桌子上的金币放置点添加点击事件,点击后收集桌子上的所有金币 function DiningDesk:addCoinPosClickEvent() local restaurantScene = RestaurantMgr:getRestaurantScene() if not restaurantScene then printError(LOGTAG, "找不到餐厅场景") return end local touchCom = restaurantScene.touchCom touchCom:addListener( self.coinCollectNode, TouchCom.LISTENER_TYPE.CLICK, function(p) local coinCount = CoinDeskMgr:getCoinCountOnDesk(self:getBuildingId()) printInfo(LOGTAG, string.format("桌子%s上的金币放置点被点击,金币数量%d", self:getBuildingId(), coinCount)) if coinCount > 0 then -- 收集当前桌子上的所有金币 CoinDeskMgr:clearCoinsOnDesk(self:getBuildingId()) -- 播放金币收集动画和音效 if coinCount > 0 then printInfo(LOGTAG, string.format("收集了桌子%s上的%d个金币", self:getBuildingId(), coinCount)) -- todo: 播放金币收集动画和音效,增加玩家金币数量 end end end ) end -- 销毁餐桌 function DiningDesk:destroy() self.buildingNode:Destroy() end return DiningDeskHelpCategoryListUIm--- ---@class HelpCategoryListUI : UILayer local HelpCategoryListUI, super = defClass("HelpCategoryListUI", UILayer) require("modules/ui/helpui/HelpCategoryCell") function HelpCategoryListUI:ctor() super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/helpui/helpuireslink") end function HelpCategoryListUI:onLoad() self.ui = GameObject.Instantiate(self.R.help_category_list_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end function HelpCategoryListUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.content = self.ui:Seek("Content") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) end function HelpCategoryListUI:showUI() local list = HelpConst.Type for i, v in pairs(list) do local go = GameObject.Instantiate(self.R.help_category_cell, self.content.transform) HelpCategoryCell.new(go, v) end end return HelpCategoryListUI FishingPropUI3 -- 捕鱼道具界面 local FishingPropUI, super = defClass("FishingPropUI", UILayer) -- component local TMProUGUI = CS.TMPro.TextMeshProUGUI -- 构造函数 function FishingPropUI:ctor(propType) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/fishingui/fishinguireslink") self.propType = propType -- 道具类型 FishingConst.PropType end -- 当页面加载 function FishingPropUI:onLoad() self.ui = GameObject.Instantiate(self.R.fishing_prop_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end -- 初始化UI元素 function FishingPropUI:initUI() self.close_btn = self.ui:Seek("close_btn") self.image_dizzy = self.ui:Seek("image_dizzy") self.image_energy = self.ui:Seek("image_energy") self.image_shield = self.ui:Seek("image_shield") self.prop_name = self.ui:Seek("prop_name")[TMProUGUI] self.prop_desc = self.ui:Seek("prop_desc")[TMProUGUI] self.video_btn = self.ui:Seek("video_btn") self.share_btn = self.ui:Seek("share_btn") -- 绑定按钮事件 util.ugui.addClickEvent(self.close_btn, function() self:close() end) util.ugui.addClickEvent(self.video_btn, function() self:video() end) util.ugui.addClickEvent(self.share_btn, function() self:share() end) end -- 展示UI function FishingPropUI:showUI() self.image_dizzy:SetActive(self.propType == FishingConst.PropType.Dizzy) self.image_energy:SetActive(self.propType == FishingConst.PropType.Energy) self.image_shield:SetActive(self.propType == FishingConst.PropType.Shield) if self.propType == FishingConst.PropType.Dizzy then self.prop_name.text = "范围晕眩" self.prop_desc.text = "眩晕周围所有鱼类,持续3秒。" elseif self.propType == FishingConst.PropType.Energy then self.prop_name.text = "双倍经验" self.prop_desc.text = "短时间内捕鱼获得双倍能量值。" elseif self.propType == FishingConst.PropType.Shield then self.prop_name.text = "护盾" self.prop_desc.text = "获得免疫一次碰撞伤害护盾。" end self.video_btn:SetActive(FishingGameMgr:getPropIsVideo()) self.share_btn:SetActive(FishingGameMgr:getPropIsShare()) end -- video function FishingPropUI:video() FishingGameMgr:addFishingProp(self.propType, 1) FishingGameMgr:useFishingProp(self.propType) self:close() end -- share function FishingPropUI:share() FishingGameMgr:addFishingProp(self.propType, 1) FishingGameMgr:useFishingProp(self.propType) self:close() end return FishingPropUIFileUtil' local FileUtil = {} local Path = CS.System.IO.Path local File = CS.System.IO.File local Directory = CS.System.IO.Directory -- 拷贝文件 function FileUtil.copyFolder(sourceFolder,destFolder) -- 如果目标路径不存在,则创建目标路径 if not Directory.Exists(destFolder) then Directory.CreateDirectory(destFolder) end -- 得到原文件根目录下的所有文件 local files = Directory.GetFiles(sourceFolder); for i = 0,files.Length-1 do local file = files[i] local name = Path.GetFileName(file); local dest = Path.Combine(destFolder, name); File.Copy(file, dest); --复制文件 end --得到原文件根目录下的所有文件夹 local folders = Directory.GetDirectories(sourceFolder); for i=0,folders.Length-1 do local folder = folders[i] local name = Path.GetFileName(folder); local dest = Path.Combine(destFolder, name); FileUtil.copyFolder(folder, dest); --构建目标路径,递归复制文件 end end -- 路径是否存在 function FileUtil:isDirExist(destFolder) if Directory.Exists(destFolder) then return true end return false end -- 文件是否存在 function FileUtil:isFileExist(filePath) return File.Exists(filePath) end -- 递归获取指定目录下指定文件名后缀的文件路径 function FileUtil.getAllFilesWithExtension(directory, extension) local allFiles = {} local function ends(str, ending) return ending == "" or str:sub(-#ending) == ending end local function getFilesRecursively(dir) local files = Directory.GetFiles(dir) for i = 0, files.Length - 1 do local filePath = files[i] if ends(filePath, extension) then table.insert(allFiles, filePath) end end local subDirs = Directory.GetDirectories(dir) for i = 0, subDirs.Length - 1 do getFilesRecursively(subDirs[i]) end end getFilesRecursively(directory) return allFiles end return FileUtilGooglePaymentMgr --[[ Google支付 ]] local GooglePaymentMgr, super = defClassStatic("GooglePaymentMgr") local LOG_TAG = "GooglePaymentMgr" local IOC_FB_UTIL_CLASS_NAME = "PaymentUtil" local JavaClass = "com/fy/xgame/tilelink/billing/BillingManager" function GooglePaymentMgr:init() self:registIAPLuaCallback() self:registMsgListener() end function GooglePaymentMgr:registMsgListener() Msg.add( { Msg.SHOP_GOOGLE_PURCHASE_SUC, Msg.SHOP_GOOGLE_PURCHASE_FAILED }, function(...) self:listener(...) end ) end function GooglePaymentMgr:listener(msgId, data) if msgId == Msg.SHOP_GOOGLE_PURCHASE_SUC then printInfo(LOG_TAG, "google 支付成功") self:parseProductInfo(data) elseif msgId == Msg.SHOP_GOOGLE_PURCHASE_FAILED then printInfo(LOG_TAG, "google 支付失败 error code:%s", data.errorcode) self:handleErrorCode(data.errorcode) end end -- 根据id购买 function GooglePaymentMgr:payByProductId(productId) luaj.callStaticMethod(JavaClass, "buyProduct", { productId,productId }) end function GooglePaymentMgr:parseProductInfo(rspData) printInfo(LOG_TAG, "解析购买信息") for k,v in pairs(rspData) do printInfo(LOG_TAG, "parseProductInfo key:%s, value:%s", k, v) end UIComsTool:showToast(TextCfgParse:getTextStr("payment_parse_item"),2) Msg.send(Msg.GEM_UPDATE_COUNT, {}) UIComsTool:hideLoading() end function GooglePaymentMgr:registIAPLuaCallback() local purchaseSucCallback = function(param) printInfo(LOG_TAG, "购买成功") Msg.send(Msg.SHOP_GOOGLE_PURCHASE_SUC, {param = param}) end local purchaseFaileCallback = function (errorcode) printInfo(LOG_TAG,"购买失败回调到lua %s",errorcode) Msg.send(Msg.SHOP_GOOGLE_PURCHASE_FAILED, {errorcode = errorcode}) end local purchaseVerifyCallback = function (param) -- 交易验证(把票据信息receipt发给服务器,验签成功后,赋予用户购买的商品) printInfo(LOG_TAG, "购买成功服务器发送服务器 " .. param) luaj.callStaticMethod(JavaClass, "verifyOver", {""}) end local printLog = function(log) printInfo(LOG_TAG, log) end luaj.callStaticMethod(JavaClass, "registSucLuaCallback", { purchaseSucCallback }) luaj.callStaticMethod(JavaClass, "registFailLuaCallback", { purchaseFaileCallback }) luaj.callStaticMethod(JavaClass, "registVerifyLuaCallback", { purchaseVerifyCallback }) luaj.callStaticMethod(JavaClass, "registLuaLogCallback", { printLog }) end function GooglePaymentMgr:handleErrorCode(code) if code == PaymentErrorCode.GP.USER_CANCELED then UIComsTool:showToast(TextCfgParse:getTextStr("payment_cancle"), 1) -- gp 购买取消 else UIComsTool:showToast(TextCfgParse:getTextStr("payment_failed"), 1) -- gp购买失败 end UIComsTool:hideLoading() end fishSalesCfg--[[ from file:鱼塘贩卖机.xlsx --]] local fishSalesCfg = { [1] = { id = 1, fishId = 600001, price = 4, count = 3, }, [2] = { id = 2, fishId = 600002, price = 4, count = 3, }, [3] = { id = 3, fishId = 600003, price = 5, count = 3, }, [4] = { id = 4, fishId = 600004, price = 7, count = 3, }, [5] = { id = 5, fishId = 600005, price = 7, count = 3, }, [6] = { id = 6, fishId = 600006, price = 2, count = 3, }, [7] = { id = 7, fishId = 600007, price = 7, count = 3, }, [8] = { id = 8, fishId = 600008, price = 8, count = 3, }, [9] = { id = 9, fishId = 600009, price = 8, count = 3, }, } return fishSalesCfg MusicBbqCuisineDetailUI(--- 菜品详情界面 ---@class MusicBbqCuisineDetailUI : UILayer local MusicBbqCuisineDetailUI, super = defClass("MusicBbqCuisineDetailUI", UILayer) -- component local UIImage = CS.UnityEngine.UI.Image local TMProUGUI = CS.TMPro.TextMeshProUGUI function MusicBbqCuisineDetailUI:ctor(cuisineId) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/cuisineui/cuisineuireslink") self.cuisineId = cuisineId end function MusicBbqCuisineDetailUI:onLoad() self.ui = GameObject.Instantiate(self.R.music_bbq_cuisine_detail_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end --- 初始化 function MusicBbqCuisineDetailUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.cuisine_img = self.ui:Seek("cuisine_img") self.cuisine_name = self.ui:Seek("cuisine_name") self.cuisine_desc = self.ui:Seek("cuisine_desc") self.cuisine_bonus = self.ui:Seek("cuisine_bonus") self.cuisine_drop = self.ui:Seek("cuisine_drop") self.purchase_btn = self.ui:Seek("purchase_btn") self.purchase_btn_text = self.ui:Seek("purchase_btn_text") self.not_enough = self.ui:Seek("not_enough") self.accomplish = self.ui:Seek("accomplish") self.video_btn = self.ui:Seek("video_btn") self:initLearnLimits() self:initTags() -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addButtonClickEvent(self.purchase_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:purchase() end) util.ugui.addButtonClickEvent(self.video_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:videoPurchase() end) end --- 展示UI function MusicBbqCuisineDetailUI:showUI() local info = CuisineMgr:getCuisineInfo(self.cuisineId) local isUnlocked = info:isUnlocked() local isPurchased = info:isPurchased() local learnShortage = info:learnShortage() local price = info:getLearnPrice() local needPurchased = isUnlocked and (not isPurchased) -- 图片 self.cuisine_img[UIImage].sprite = info:getIcon() util.ugui:setImageTileSize(self.cuisine_img[UIImage], 220) -- 基础信息 self.cuisine_name[TMProUGUI].text = info:getName() self.cuisine_desc[TMProUGUI].text = info:getDesc() self.cuisine_bonus[TMProUGUI].text = info:getBonusDesc() self.cuisine_drop:SetActive(info:getDropType() ~= -1) if info:getDropType() ~= -1 then self.cuisine_drop[TMProUGUI].text = info:getDropDesc() end -- 购买按钮 self.purchase_btn:SetActive(needPurchased) self.not_enough:SetActive(needPurchased and (learnShortage > 0)) if needPurchased then if price <= 0 then self.purchase_btn_text[TMProUGUI].text = "免费" else self.purchase_btn_text[TMProUGUI].text = price .. "学习" end if learnShortage > 0 then self.not_enough[TMProUGUI].text = "音符不够了,还差" .. learnShortage .. "音符" end end -- 视频购买按钮 self.video_btn:SetActive(needPurchased and (learnShortage > 0) and (learnShortage < price * 0.2)) self:showLearnLimits(info, needPurchased) -- 标签 self:showTags(info) self.accomplish:SetActive(isPurchased) end function MusicBbqCuisineDetailUI:initLearnLimits() self.learnLimits = {} for i = 1, 3 do self.learnLimits[i] = self.ui:Seek("cuisine_buy_limit_" .. i) end end function MusicBbqCuisineDetailUI:showLearnLimits(info, canShow) for i = 1, 3 do local has = (not (info:getLearnLimitId(i) == -1)) and canShow self.learnLimits[i]:SetActive(has) if has then self.learnLimits[i][TMProUGUI].text = info:getLearnLimitDesc(i) end end end --- 初始化标签 function MusicBbqCuisineDetailUI:initTags() self.tags = {} for i = 1, 3 do self.tags[i] = self.ui:Seek("cuisine_tag_" .. i) end end --- 展示标签 function MusicBbqCuisineDetailUI:showTags(info) for i = 1, 3 do local has = info:getTagId(i) ~= -1 self.tags[i]:SetActive(has) if has then self.tags[i][TMProUGUI].text = info:getTagDesc(i) end end end --- 购买菜品 function MusicBbqCuisineDetailUI:purchase() local info = CuisineMgr:getCuisineInfo(self.cuisineId) local learnShortage = info:learnShortage() if learnShortage > 0 then PopUpUI.new("音符不够了,还差" .. learnShortage .. "音符"):show():showMask():enableCloseWhenClickMask() return end if not info:isPushLearnLimit() then PopUpUI.new(info:getLearnDesc()):show():showMask():enableCloseWhenClickMask() return end CuisineMgr:cuisineLearn(self.cuisineId) --self:showUI() self:close() end --- 视频购买菜品 function MusicBbqCuisineDetailUI:videoPurchase() local info = CuisineMgr:getCuisineInfo(self.cuisineId) local learnShortage = info:learnShortage() CuisineMgr:cuisineLearnByVideo(self.cuisineId, learnShortage) --self:showUI() self:close() end return MusicBbqCuisineDetailUI TaskOrderMgra--- 订单任务管理类 ---@class TaskOrderMgr : LuaStaticClass local TaskOrderMgr = defClassStatic("TaskOrderMgr") --- 初始化 function TaskOrderMgr:init() printInfo("TaskOrderMgr", "------ 订单任务管理 初始化 ------") self:initUserData() self.activeOrders = self:getOrderCreateTimeList() self.infos = {} self.refreshCallback = nil end --- 初始化用户数据 function TaskOrderMgr:initUserData() -- 初始化用户数据 self.userData = UserDataMgr.taskUserData.order end --- 启动订单任务管理 function TaskOrderMgr:start() self:checkOrderList() --self:startTimeOrder() end --- 获取订单Info ---@return TaskOrderInfo 订单任务信息 function TaskOrderMgr:getOrderInfo(taskOrderId) if self.infos[taskOrderId] then return self.infos[taskOrderId] end local info = TaskOrderInfo.new(taskOrderId) self.infos[taskOrderId] = info return info end --- 获取订单Info ---@return TaskOrderInfo[] 订单任务信息 function TaskOrderMgr:getOrderInfos() local ids = self:getOrderIds() for _, v in pairs(ids) do self:getOrderInfo(v) end return self.infos end --- 设置订单ID function TaskOrderMgr:setOrderId(taskOrderId) if not taskOrderId or taskOrderId == "" then return end if TaskOrderCfgParse:getTaskOrderCfg(taskOrderId) then self.userData:setTaskOrderId(taskOrderId) self.activeOrders = self:getOrderCreateTimeList() end end --- 重置订单id function TaskOrderMgr:resetOrderId(taskOrderId) if not taskOrderId or taskOrderId == "" then return end if TaskOrderCfgParse:getTaskOrderCfg(taskOrderId) then self.userData:resetTaskOrderId(taskOrderId) self.activeOrders = self:getOrderCreateTimeList() end end --- 设置订单ID function TaskOrderMgr:setOrderPrompt(taskOrderId, needPrompt) if not taskOrderId or taskOrderId == "" then return end if TaskOrderCfgParse:getTaskOrderCfg(taskOrderId) then self.userData:setTaskOrderPrompt(taskOrderId, needPrompt) end end --- 获取订单ID function TaskOrderMgr:getOrderIds() local times = self:getOrderCreateTimeList() local taskOrderIds = {} for i, _ in pairs(times) do table.insert(taskOrderIds, i) end return taskOrderIds end --- 获取订单存档 function TaskOrderMgr:getOrderCreateTimeList() return self.userData:getTaskOrderCreateTimes() end --- 获取订单存档 function TaskOrderMgr:getOrderCreateTime(taskOrderId) return self.userData:getTaskOrderCreateTime(taskOrderId) end --- 清理订单 function TaskOrderMgr:cleanTaskOrderId(taskOrderId) if not taskOrderId or taskOrderId == "" then return end self.userData:cleanTaskOrderId(taskOrderId) self.activeOrders = self:getOrderCreateTimeList() if self.refreshCallback then self.refreshCallback() end self.refreshCallback = nil end --- 设置订单刷新回调 function TaskOrderMgr:addRefreshCallback(callback) if type(callback) == "function" then self.refreshCallback = callback end end --- 清除订单刷新回调 function TaskOrderMgr:deleteRefreshCallback() self.refreshCallback = nil end --- 订单任务完成 function TaskOrderMgr:taskOrderComplete(taskOrderId) local config = TaskOrderCfgParse:getTaskOrderCfg(taskOrderId) StoryDialogMgr:showOrderStoryDialogUI(config.plot_3, function() TaskOrderDetailUI.new(config.id, true):show():showMask():enableCloseWhenClickMask() self:setOrderId(config.id) end) end --- 订单任务失败 function TaskOrderMgr:taskOrderFailed(taskOrderId) local config = TaskOrderCfgParse:getTaskOrderCfg(taskOrderId) StoryDialogMgr:showOrderStoryDialogUI(config.plot_2, function() TaskOrderDetailUI.new(config.id, true):show():showMask():enableCloseWhenClickMask() self:setOrderId(config.id) end) end ----------存档处理---------- function TaskOrderMgr:checkOrderList() local orders = self:getOrderCreateTimeList() self.currOrders = {} for i, v in pairs(orders) do local info = self:getOrderInfo(i) if info:isTimeArrived() then table.insert(self.currOrders, i) end end if #self.currOrders > 0 then local delay = math.random(15, 20) -- 开始游戏后延时15-20秒弹出订单存档页面 self.showOrder = TimerMgr:add(function(dt) self:timeArrived() end, delay, 1) end end -- 处理时间到达订单 function TaskOrderMgr:timeArrived() if #self.currOrders <= 0 then return end local curr = self.currOrders[1] TaskOrderDetailUI.new(curr):show():showMask():enableCloseWhenClickMask():addCloseCallback(function() table.remove(self.currOrders, 1) --self:timeArrived() -- 由剧情结束调用 end) end ----------时间触发---------- -- 启动时间订单 function TaskOrderMgr:startTimeOrder() self.onlineDays = UserDataMgr.online_days self.onlineSeconds = UserDataMgr.today_online_time self.orderDic = {} local configs = TaskOrderCfgParse:getTimeOrder() for i, v in pairs(configs) do if v.time then end end end function TaskOrderMgr:timeStart(orderId, startTime, save) if not startTime then startTime = os.time() end local config = TaskOrderCfgParse:getTaskOrderCfg(orderId) local createTime = self.userData:getTaskOrderCreateTime(orderId) self.orderDic[orderId] = TimerMgr:add(function(dt) self:triggerTimeOrder(config.id, v.customerId) end, config.time, 1) end function TaskOrderMgr:timeStop() self:clear(true, self.tmr) end --- 触发时间订单 function TaskOrderMgr:triggerTimeOrder(taskOrderId) -- 创建一个顾客 StoryDialogMgr:showOrderStoryDialogUI(taskOrderId) local view = TaskOrderDetailUI.new(taskOrderId, true):show():showMask():enableCloseWhenClickMask() end ----------角色触发---------- --- 触发指定角色订单 function TaskOrderMgr:triggerTargetOrder(customerId) local config = TaskOrderCfgParse:checkCustomerId(customerId) if config then self:setOrderId(config.id) StoryDialogMgr:showOrderStoryDialogUI(config.plot_1, function() TaskOrderDetailUI.new(config.id, true):show():showMask():enableCloseWhenClickMask() self:setOrderId(config.id) end) end end return TaskOrderMgr main_editor--[[ 用于editor模式下的boot文件 author:zhangpeng time:2025-07-20 19:24:04 ]] local _ENV = _G --FORCE CLEAN ENV local LOGTAG = "[boot/main_editor]" print(LOGTAG.."start editor mode 777") local json = require("rapidjson") print(LOGTAG.."launch luaengine from here") local luaengine = require("luaengine") local UnityEngine = CS.UnityEngine local AET = CS.AET local isEditor = CS.UnityEngine.Application.isEditor local YooAssetLoader = CS.YooAssetLoader.Instance _G.BOOT_MAIN_FILE = "boot/main" _G.GAME_MAIN_FILE = "main/main" local debug_flag = true if CS.LocalDataStorage.Get("PRINT_EVERY_LUA_CALL") == "true" then debug.sethook(function(event,line) local info = debug.getinfo(2) if info.currentline > 0 then print(string.format("%s:%s:%s:%s:%s",info.short_src,tostring(info.currentline),tostring(info.linedefined),tostring(info.name),tostring(info.namewhat))) end end, "c" ) end local cached_lua_ret_map = {} local CLEAR_ALL_LUA_CACHES = function() print("[boot.main] 清空lua缓存") for k,_ in pairs(cached_lua_ret_map) do cached_lua_ret_map[k] = nil end end local _loadlua = function (bytes, file, opts, env) if bytes == nil or bytes == "" then error("lua文件不存在->"..file..":"..tostring(bytes).. "\n" .. debug.traceback()) end if opts == "b" then print(LOGTAG .. "loadlua:bytes file") bytes = AET.Dec(bytes) end local f,err = load(bytes, file, opts, env) if f then local ok,ret = xpcall(f,function(err) CS.UnityEngine.Debug.LogError(string.format("加载lua失败[%s]%s\n%s",file,tostring(err),debug.traceback())) end) if not string.lower(file):find("reslink") and not isEditor then cached_lua_ret_map[file] = {ret = ret} end return ret, env else CS.UnityEngine.Debug.LogError("加载lua失败" .. file) error(tostring(err) .. "\n" .. debug.traceback()) end end -- 热更结束后加载lua文件(编辑器专用版本) -- @ filename:要加载的lua文件名 -- @ env:lua环境,用于加载 Lua 文件的执行环境 -- _require函数会根据传入的参数直接从文件系统加载指定的 Lua 文件,然后执行它,最终返回加载结果 print(LOGTAG .. "使用编辑器本地文件加载模式") local dataPath = CS.UnityEngine.Application.dataPath local _require = function(env, filename) local ret = cached_lua_ret_map[filename] if ret then return ret.ret end -- print("[require local]", filename) local filepath = dataPath.."/LuaScripts/"..filename..".lua" local src = CS.LuaHelper.ReadFileText(filepath) return _loadlua(src, filename, "bt", env) end local _newenv = function() -- local _G = _G local _E = _G local rawset = _G.rawset local env = { _G = _G, _print = print, CLEAR_ALL_LUA_CACHES = CLEAR_ALL_LUA_CACHES, ENV_REQUIRE = _require } _G.setmetatable( env, { __index = function(t, k) local v = _E[k] rawset(t, k, v) return v end } ) return env end --Run Main Code do print(LOGTAG .. "start run main code") local _ENV = _newenv() _ENV.CLEAR_ENV = function() for k, _ in pairs(_ENV) do _ENV[k] = nil end end _ENV.raw_require = raw_require or require _ENV._require = _require _ENV.require = function(filename, _env) _env = _env or _ENV return _require(_env, filename) end require("boot/build_config") -- 执行main/main.lua print(LOGTAG.." -------- RUN GAME_MAIN_FILE -------- ") require(_G.GAME_MAIN_FILE) end EnumMEnum = { ENV_DEVELOPMENT = "dev", ENV_PRODUCTION = "production", }mainrequire("modules/building/barbecue/stage/StageDeskConst") require("modules/building/barbecue/stage/StageDeskSeat") require("modules/building/barbecue/stage/StageDesk") require("modules/building/barbecue/stage/StageDeskMgr") GachaRewardUId--[[ 扭蛋结果弹窗 author:{zhangpeng} time:2025-07-09 16:08:09 ]] local GachaRewardUI, super = defClass("GachaRewardUI", UILayer) local TMPUGUI = CS.TMPro.TextMeshProUGUI local Image = UnityEngine.UI.Image local Sprite = typeof(UnityEngine.Sprite) local LOGTAG = "GachaRewardUI" function GachaRewardUI:ctor(rewardList, gachaType) super.ctor(self) self.gachaType = gachaType self.R = ResLoader.loadResLink("modules/ui/gacha/gacharewardreslink") self.rewardList = rewardList end function GachaRewardUI:onLoad() self.ui = GameObject.Instantiate(self.R.reward_ui) self:addChild(self.ui) self:initUI() self:showRewards() end function GachaRewardUI:initUI() self.once_view = self.ui:Seek("one_reward") self.ten_view = self.ui:Seek("ten_reward") self.once_view:SetActive(self.gachaType == GachaConst.GachaType.single) self.ten_view:SetActive(self.gachaType == GachaConst.GachaType.ten) if self.gachaType == GachaConst.GachaType.single then -- 获取所有奖励图标容器 self.rewardIconList = self.once_view:SearchPattern("icon_\\d", true) else -- 获取所有奖励图标容器 self.rewardIconList = self.ten_view:SearchPattern("icon_\\d", true) end printInfo(LOGTAG, string.format("找到 %d 个奖励图标槽位", #self.rewardIconList)) end -- 显示所有奖励 function GachaRewardUI:showRewards() local usedSlots = 0 -- 遍历所有奖励 for i, reward in ipairs(self.rewardList) do if usedSlots < #self.rewardIconList then usedSlots = usedSlots + 1 self:showRewardIcon(reward, usedSlots) else printError(LOGTAG, "奖励数量超过可用槽位数量") break end end -- 隐藏未使用的槽位 for i = usedSlots + 1, #self.rewardIconList do self.rewardIconList[i]:SetActive(false) end printInfo(LOGTAG, string.format("显示了 %d 个奖励", usedSlots)) end -- 根据奖励设置图标 function GachaRewardUI:showRewardIcon(reward, slotIndex) local slot = self.rewardIconList[slotIndex] if not slot then printError(LOGTAG, string.format("槽位 %d 不存在", slotIndex)) return end slot:SetActive(true) -- 获取槽位内的UI元素 local icon_music = slot:Seek("icon_music") local icon_star = slot:Seek("icon_star") local icon_doll = slot:Seek("icon_doll") icon_music:SetActive(reward.type == GachaConst.RewardType.music) icon_star:SetActive(reward.type == GachaConst.RewardType.star) icon_doll:SetActive(reward.type == GachaConst.RewardType.doll) local countText = slot:Seek("count") local nameText = slot:Seek("name") -- 设置数量文本 if countText then countText[TMPUGUI].text = "x" .. reward.count end -- 设置名称文本 if nameText then local displayName = self:getRewardDisplayName(reward) nameText[TMPUGUI].text = displayName end -- 设置图标 if reward.type == GachaConst.RewardType.doll then local config = DollMgr:getConfig(reward.itemId) local image = icon_doll[Image] local path = string.format("Assets/AssetsPackage/Res/modules/ui/doll/images/roles/%s.png", config.dollIcon) if ResLoader.hasAsset(path) then image.sprite = ResLoader.loadAsset(path, Sprite) else image.sprite = nil end image:SetNativeSize() end printInfo(LOGTAG, string.format("设置槽位 %d: %s x%d", slotIndex, reward.typeName, reward.count)) end -- 获取奖励显示名称 function GachaRewardUI:getRewardDisplayName(reward) if reward.type == 3 and reward.itemId > 0 then -- 玩偶显示具体名称 local dollCfg = DollCfgParse:getDollCfg(reward.itemId) if dollCfg and dollCfg.name then return dollCfg.name end end -- 其他类型显示类型名称 return reward.typeName or "未知奖励" end -- 获取可用槽位数量 function GachaRewardUI:getAvailableSlots() return #self.rewardIconList end return GachaRewardUI RichTextUtil--[[ 富文本工具 author:{zhangpeng} time:2024-03-27 23:36:07 ]] local RichTextUtil = {} --[[ @desc: time:2024-03-27 23:38:24 --@text: 原始文本 --@borderColor:描边颜色 --@borderWidth: 描边粗细(单位:像素) @return: ]] function RichTextUtil:addBorder(text, borderColor, borderWidth) local richText = string.format("%s", borderColor, text) local borderText = string.format("%s", text) for i = 1, borderWidth do richText = string.format("%s", richText) borderText = string.format("%s", borderText) richText = borderText .. richText end return richText end return RichTextUtil PlatformUtil --[[ author:{zhangpeng} time:2024-08-22 11:44:58 ]] local PlatformUtil, super = defClassStatic("PlatformUtil") local LOG_TAG = "PlatformUtil" local IOC_CLASS_NAME = "IHappy" local JavaUtilClass = "com/fy/xgame/tilelink/util/Util" function PlatformUtil:init() end -- 获取版本号 function PlatformUtil:getAppVersion() local version if Device.isIOS() then printInfo(LOG_TAG,"get ios version") local ok, _version = luaoc.callStaticMethod(IOC_CLASS_NAME, "getAppVersion") printInfo(LOG_TAG,"ios version:%s",version) version = _version elseif Device.isAndroid() then -- todo:: end version = version or CS.UnityEngine.Application.version printInfo(LOG_TAG,"version:%s",version) return version end -- 跳转到应用商店 function PlatformUtil:openAppStore() if Device.isIOS() then luaoc.callStaticMethod(IOC_CLASS_NAME, "openAppStore") elseif Device.isAndroid() then luaj.callStaticMethod(JavaUtilClass, "goToMarket", { "com.fy.xgame.tilelink", "com.android.vending"} ) end end -- 打开评分弹窗 function PlatformUtil:requestAppReview() if Device.isIOS() then luaoc.callStaticMethod(IOC_CLASS_NAME, "requestAppReview") end end PlatformUtil:init()CashierDeskInfof--[[ 烤吧收银台数据信息单独管理 author:{zhangpeng} time:2025-07-09 10:45:23 ]] local CashierDeskInfo, super = defClass("CashierDeskInfo") function CashierDeskInfo:ctor(cashierId) self.id = cashierId self.buildingType = BuildingConst.buildingType.cashier self:init() end function CashierDeskInfo:init() self:initDynamicData() end function CashierDeskInfo:initDynamicData() self.state = BuildingConst.buildingState.used end -- 是否使用中 function CashierDeskInfo:isInUse() return self.state >= BuildingConst.buildingState.used end return CashierDeskInfoCustomerUserData~--- 用户数据 顾客 ---@class CustomerUserData : LuaClass local CustomerUserData = defClass("CustomerUserData") local LOGTAG = "CustomerUserData" -- 构造函数 function CustomerUserData:ctor() self:init() end -- 初始化数据 function CustomerUserData:init() -- 指定顾客状态 self.customer_state = {} -- {[customerId] = state} 1.未来访 2.已来访 3.已分享 -- 指定顾客来访次数 self.customer_visits = {} -- 顾客首次来访队列 self.firstVisitQueue = {} -- {id} -- 当日顾客来访次数 self.customer_normal_visits_today = {} -- {customerId = count} end -- 重置数据 function CustomerUserData:resetData() self:setCustomerState(400001, 2, true) self:setCustomerState(400002, 2, true) self:setCustomerState(400003, 2, true) end -- 加载存档 function CustomerUserData:load() -- 如果是新手玩家,重置数据 if UserDataMgr.isNewPlayer then self:resetData() else self:loadFromLocal() end self:loadComplete() end -- 从本地存档加载数据 function CustomerUserData:loadFromLocal() self.customer_state = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.CUSTOMER_STATE, "{}"))) self.customer_visits = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.CUSTOMER_VISITS, "{}"))) self.firstVisitQueue = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.CUSTOMER_FIRST_VISIT_QUEUE, "{}"))) self.customer_normal_visits_today = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.CUSTOMER_NORMAL_VISITS_TODAY, "{}"))) end -- 加载完成 function CustomerUserData:loadComplete() -- 初始顾客 local data = CustomerCfgParse:getData() for _, v in ipairs(data) do if (v.visitNeedStar <= 0) and (v.visitItemId == {}) and (self:getCustomerState(v.id) < 1) then self:addCustomerVisits(v.id, 1, true) end end -- 每日更新来访可来访次数 if UserDataMgr.isFirstLoginToday then self.customer_normal_visits_today = {} end self:save() end -- 保存用户数据 function CustomerUserData:save() self:saveToLocal() end -- 保存用户数据到本地存档 function CustomerUserData:saveToLocal() PlayerPrefsMgr:setString(StrogeKeyDef.CUSTOMER_STATE, json.encode(PlayerPrefsMgr:idToString(self.customer_state))) PlayerPrefsMgr:setString(StrogeKeyDef.CUSTOMER_VISITS, json.encode(PlayerPrefsMgr:idToString(self.customer_visits))) PlayerPrefsMgr:setString(StrogeKeyDef.CUSTOMER_FIRST_VISIT_QUEUE, json.encode(PlayerPrefsMgr:idToString(self.firstVisitQueue))) PlayerPrefsMgr:setString(StrogeKeyDef.CUSTOMER_NORMAL_VISITS_TODAY, json.encode(PlayerPrefsMgr:idToString(self.customer_normal_visits_today))) PlayerPrefsMgr:save() end -- 获取指定顾客的状态 function CustomerUserData:getCustomerState(customerId) return self.customer_state[customerId] or 1 -- 默认未来访 end -- 设置指定顾客的状态 function CustomerUserData:setCustomerState(customerId, state, refuseSave) self.customer_state[customerId] = state if not refuseSave then self:save() end return state end -- 获取指定顾客的来访次数 function CustomerUserData:getCustomerVisits(customerId) return self.customer_visits[customerId] or 0 end -- 设置指定顾客的来访次数 function CustomerUserData:setCustomerVisits(customerId, value, refuseSave) self.customer_visits[customerId] = value if not refuseSave then self:save() end return value end -- 增加指定顾客的来访次数 function CustomerUserData:addCustomerVisits(customerId, value, refuseSave) local curr = self:getCustomerVisits(customerId) + value self:setCustomerVisits(customerId, curr, refuseSave) return curr end -- 获取所有已来访顾客的ID列表 function CustomerUserData:getVisitCustomerIdList() local list = {} for id, state in pairs(self.customer_state) do if state >= 2 then table.insert(list, id) end end return list end -- 获取新客队列 function CustomerUserData:getFirstVisitQueue() return self.firstVisitQueue or {} end -- 入队列 新客 function CustomerUserData:enqueueFirstVisit(customerId, refuseSave) for _, v in ipairs(self.firstVisitQueue) do if v == customerId then printError(LOGTAG, "新客队列中已存在此顾客ID: " .. customerId) return self.firstVisitQueue end end table.insert(self.firstVisitQueue, customerId) if not refuseSave then self:save() end return self.firstVisitQueue end -- 出队列 新客 function CustomerUserData:dequeueFirstVisit(customerId, refuseSave) local idx = -1 for i, v in ipairs(self.firstVisitQueue) do if v == customerId then idx = i break end end if idx == -1 then return self.firstVisitQueue end table.remove(self.firstVisitQueue, idx) if not refuseSave then self:save() end return self.firstVisitQueue end -- 获取指定顾客的来访次数 function CustomerUserData:getCustomerNormalVisitsToday(customerId) return self.customer_normal_visits_today[customerId] or 0 end -- 设置指定顾客的来访次数 function CustomerUserData:setCustomerNormalVisitsToday(customerId, value, refuseSave) self.customer_normal_visits_today[customerId] = value if not refuseSave then self:save() end return value end -- 增加指定顾客的来访次数 function CustomerUserData:addCustomerNormalVisitsToday(customerId, value, refuseSave) local curr = self:getCustomerNormalVisitsToday(customerId) + value self:setCustomerNormalVisitsToday(customerId, curr, refuseSave) return curr end -- 获取总的顾客来访次数 function CustomerUserData:getTotalCustomerVisitsToday() local total = 0 for _, count in pairs(self.customer_normal_visits_today) do total = total + count end return total end --function CustomerUserData:customerIsUnlocked(customerId) -- if self.customer_visited_ids[customerId] then -- return true -- else -- return false -- end --end return CustomerUserData CuisineBubbleF--[[ 角色头顶气泡里的菜图标 author:{zhangpeng} time:2025-05-17 11:53:21 ]] local CuisineBubble = defClass("CuisineBubble") local LOGTAG = "CuisineBubble" function CuisineBubble:ctor(cuisineCfgId, parent, parentType) self.cuisineCfgId = cuisineCfgId self.parent = parent -- 设置显示类型,默认为角色头顶 self.parentType = parentType self:init() end function CuisineBubble:init() self:initInfo() self:initView() end function CuisineBubble:initInfo() self.cuisineInfo = CuisineInfo.new(self.cuisineCfgId) end function CuisineBubble:initView() local resname = "cuisinen_" .. self.cuisineCfgId if CusineCommonReslink[resname] then self.cuisineGo = GameObject.Instantiate(CusineCommonReslink[resname]) self.gameObject = self.cuisineGo end self.angryGo = self.parent:Seek("angry") -- 设置气泡节点名字 self:setCuisineBubbleName(resname) -- 设置父节点 self:setCuisineParent(self.parent) -- 设置图标位置 self:setCuisinePosition(Vector3.zero) -- 设置图标缩放 self:setCuisineScaleByParentType() -- 设置层级 if self.parent and self.parent:GetComponent("Renderer") then local parent_orderinlayer = self.parent:GetComponent("Renderer").sortingOrder self:setCuisineOrder(parent_orderinlayer + 2) end if self.parentType == CuisineConst.BubbleShowParentType.desk then self:setCuisineOrder(9) end end function CuisineBubble:setParentType(parentType) self.parentType = parentType end function CuisineBubble:getParentType() return self.parentType end -- 如果是灶台上,执行cd效果 function CuisineBubble:doCookingEffect() if self.parentType == CuisineConst.BubbleShowParentType.stove then local effectNode = self.cuisineGo:Seek("cooking_effect") if effectNode then effectNode:SetActive(true) end end end -- 设置遮罩进度(默认需要是1,1是全灰开始制作,0是全亮制作完成) function CuisineBubble:setCookingMaskProgress(progress) if self.parentType == CuisineConst.BubbleShowParentType.stove then local maskNode = self.cuisineGo:Seek("mask") local sprite_fill_material = maskNode:GetComponent(typeof(CS.UnityEngine.SpriteRenderer)).material -- 设置fillAmount sprite_fill_material:SetFloat("_FillAmount", progress) end end -- 重置遮罩进度 function CuisineBubble:resetCookingMaskProgress() if self.parentType == CuisineConst.BubbleShowParentType.stove then local maskNode = self.cuisineGo:Seek("mask") local sprite_fill_material = maskNode:GetComponent(typeof(CS.UnityEngine.SpriteRenderer)).material -- 设置fillAmount sprite_fill_material:SetFloat("_FillAmount", 1) end end -- 如果是角色头顶,添加点击事件 function CuisineBubble:addClickEvent(customer) if self.parentType == CuisineConst.BubbleShowParentType.customer then -- 保存顾客引用 self.customer = customer local restaurantScene = RestaurantMgr:getRestaurantScene() if not restaurantScene then printError(LOGTAG, "找不到餐厅场景") return end local touchCom = restaurantScene.touchCom touchCom:addListener( self.cuisineGo:Seek("sprite"), TouchCom.LISTENER_TYPE.CLICK, function(p) printInfo(LOGTAG, string.format("点击头顶菜品图标[%s]", self.cuisineCfgId)) -- 处理点击事件 self:onBubbleClick() end ) end end -- 气泡闪烁 function CuisineBubble:doBubbleBlink() local _initScale = 1 ---- 保存动画引用,以便在需要时停止动画 --local seq = ua.Sequence{ -- ua.ScaleTo(0.5, _initScale * 1.5), -- ua.ScaleTo(0.5, _initScale) --} self.blinkAction = self.parent:RunAction(ua.RepeatForever( ua.Sequence({ ua.ScaleTo(0.1, _initScale * 1.1), ua.ScaleTo(0.2, _initScale), ua.ScaleTo(0.1, _initScale * 1.1), ua.ScaleTo(0.2, _initScale), ua.Delay(2) -- 闪烁间隔 }) )) printInfo(LOGTAG, "气泡闪烁效果已启动") end -- 停止闪烁效果 function CuisineBubble:stopBubbleBlink() if self.blinkAction then self.parent:StopAction(self.blinkAction) self.blinkAction = nil end end -- 处理气泡点击事件 function CuisineBubble:onBubbleClick() if not self.customer then printError(LOGTAG, "找不到关联的顾客") return end local currentSeat = self.customer.currentSeat if not currentSeat then printError(LOGTAG, "顾客没有关联的座位") return end -- 获取当前座位上的订单 local orderInfo = currentSeat.cuisine if not orderInfo then printWarn(LOGTAG, "座位上没有订单信息") return end ---- 判断菜品是否已购买 --local cuisineInfo = CuisineMgr:getCuisineInfo(self.cuisineCfgId) --if not cuisineInfo:isPurchased() then -- printInfo(LOGTAG, string.format("玩家点击气泡, 菜品未购买,ID:[%s]", self.cuisineCfgId)) -- self.cuisineGo:SetActive(false) -- self.angryGo:SetActive(true) -- currentSeat:noFood() -- self:stopBubbleBlink() -- return --end -- 停止闪烁效果 self:stopBubbleBlink() -- 确认处理订单 printInfo(LOGTAG, string.format("玩家点击气泡, 开始处理订单ID:[%s]", orderInfo:getOrderId())) OrderDishesMgr:confirmProcessOrder(orderInfo:getOrderId()) end function CuisineBubble:hideCuisineGo() self.cuisineGo:SetActive(false) end -- 设置父节点 function CuisineBubble:setCuisineParent(parent) self.cuisineGo:SetParent(parent) end -- 设置图标层级 function CuisineBubble:setCuisineOrder(order) local sprite = self.cuisineGo:Seek("sprite") local sprite_render = sprite:GetComponent("Renderer") sprite_render.sortingOrder = order end -- 根据父节点类型设置图标缩放 function CuisineBubble:setCuisineScaleByParentType() if self.parentType and CuisineConst.BubbleShowParentTypeScale and CuisineConst.BubbleShowParentTypeScale[self.parentType] then self:setCuisineScale(CuisineConst.BubbleShowParentTypeScale[self.parentType]) else printError(LOGTAG, string.format("头顶气泡缩放比例不存在,请检查CuisineConst.BubbleShowParentTypeScale[%s]", self.parentType)) end self.initScale = CuisineConst.BubbleShowParentTypeScale[self.parentType] end -- 设置图标缩放 function CuisineBubble:setCuisineScale(scale) self.cuisineGo:SetScalef(scale) end -- 获取气泡闪烁缩放 function CuisineBubble:getBubbleBlinkScale() return self.cuisineGo:GetScalef() end -- 设置图标位置 function CuisineBubble:setCuisinePosition(position) self.cuisineGo:SetPosition(position) end -- 设置菜品气泡节点的名字 function CuisineBubble:setCuisineBubbleName(name) self.cuisineGo:SetName(name) end -- 销毁气泡及资源 function CuisineBubble:destroy() printInfo(LOGTAG, "销毁菜品气泡,类型[%s]", CuisineConst.BubbleShowParentTypeName[self.parentType]) -- 停止闪烁效果 self:stopBubbleBlink() -- 销毁游戏对象 if self.cuisineGo then GameObject.Destroy(self.cuisineGo) self.cuisineGo = nil end -- 清除引用 self.parent = nil self.cuisineInfo = nil self.customer = nil self.blinkAction = nil self.parentType = nil end return CuisineBubble TaskMgr,--- ---@class TaskMgr : LuaStaticClass local TaskMgr = defClassStatic("TaskMgr") function TaskMgr:init() printInfo("TaskMgr", "------ 任务管理 初始化 ------") TaskDailyMgr:init() TaskAchMgr:init() TaskOrderMgr:init() TaskBootMgr:init() end function TaskMgr:start() printInfo("TaskMgr", "------ 启动任务管理 ------") TaskOrderMgr:start() end -- 获取某一任务的当前进度 function TaskMgr:getTaskProgress(taskId) local taskCfg = TaskCfgParse:getTaskCfg(taskId) if not taskCfg then return 0, 0 end local completeCond = taskCfg.completeCond local targetValue = taskCfg.count or 1 local currentValue = self:getTaskCurrentValue(completeCond, taskCfg.refId) -- 确保当前值不超过目标值 currentValue = math.min(currentValue, targetValue) return currentValue, targetValue end -- 根据任务完成条件获取当前值 function TaskMgr:getTaskCurrentValue(completeCond, refId) local currentValue = 0 -- 根据完成条件类型获取当前进度 if completeCond == TaskCfgParse.TaskReachType.get_star then -- 获得星星 currentValue = UserDataMgr:getStarCount() elseif completeCond == TaskCfgParse.TaskReachType.entertain_customer_count then -- 招待顾客 currentValue = UserDataMgr:getEntertainCustomerCount() elseif completeCond == TaskCfgParse.TaskReachType.income_coin then -- 收入金币 currentValue = UserDataMgr:getIncomeCoinCount() elseif completeCond == TaskCfgParse.TaskReachType.unlock_cuisine then -- 解锁菜品 currentValue = UserDataMgr:getUnlockCuisineCount() elseif completeCond == TaskCfgParse.TaskReachType.unlock_building then -- 解锁设施 currentValue = UserDataMgr:getUnlockBuildingCount() elseif completeCond == TaskCfgParse.TaskReachType.unlock_customer then -- 解锁顾客 currentValue = UserDataMgr:getUnlockCustomerCount() elseif completeCond == TaskCfgParse.TaskReachType.promote_count then -- 宣传次数 currentValue = UserDataMgr:getPromoteCount() elseif completeCond == TaskCfgParse.TaskReachType.promote_video_count then -- 视频宣传 currentValue = UserDataMgr:getPromoteVideoCount() elseif completeCond == TaskCfgParse.TaskReachType.watch_ad_count then -- 观看广告 currentValue = UserDataMgr:getWatchAdCount() elseif completeCond == TaskCfgParse.TaskReachType.unlock_cuisine_count then -- 解锁烧烤菜 currentValue = UserDataMgr:getUnlockCuisineTotalCount() elseif completeCond == TaskCfgParse.TaskReachType.use_eggs_count then -- 扭蛋次数 currentValue = UserDataMgr:getUseEggsCount() elseif completeCond == TaskCfgParse.TaskReachType.interact_special_customer_count then -- 特殊顾客互动 currentValue = UserDataMgr:getInteractSpecialCustomerCount() elseif completeCond == TaskCfgParse.TaskReachType.fish_count then -- 钓鱼次数 currentValue = UserDataMgr:getFishCount() elseif completeCond == TaskCfgParse.TaskReachType.get_note_count then -- 获得音符 currentValue = UserDataMgr:getNoteCount() elseif completeCond == TaskCfgParse.TaskReachType.unlock_employee_count then -- 解锁员工 currentValue = UserDataMgr:getUnlockEmployeeCount() elseif completeCond == TaskCfgParse.TaskReachType.visit_friend_count then -- 访问好友 currentValue = UserDataMgr:getVisitFriendCount() elseif completeCond == TaskCfgParse.TaskReachType.recruit_count then -- 招揽摊主 currentValue = UserDataMgr:getRecruitCount() elseif completeCond == TaskCfgParse.TaskReachType.recruit_customer_count then -- 招揽顾客 currentValue = UserDataMgr:getRecruitCustomerCount() elseif completeCond == TaskCfgParse.TaskReachType.unlock_scene_count then -- 解锁场景 currentValue = UserDataMgr:getUnlockSceneCount() elseif completeCond == TaskCfgParse.TaskReachType.task_progress_count then -- 任务进度 currentValue = UserDataMgr:getTaskProgressCount() elseif completeCond == TaskCfgParse.TaskReachType.recruit_specific_customer_count then -- 招揽特定顾客 currentValue = UserDataMgr:getRecruitSpecificCustomerCount(refId) elseif completeCond == TaskCfgParse.TaskReachType.entertain_specific_customer_count then -- 招待特定顾客 currentValue = UserDataMgr:getEntertainSpecificCustomerCount(refId) end return currentValue end -- 获取任务状态 -- @param taskId 任务ID -- @return TaskConst.TaskState 任务状态(进行中/可完成/已领取) -- 检查任务是否已领取 function TaskMgr:isTaskClaimed(taskId) local taskCfg = TaskCfgParse:getTaskCfg(taskId) if not taskCfg then return false end -- 根据任务类型调用不同的管理器 if taskCfg.taskType == TaskConst.TaskType.daily then return TaskDailyMgr:isTaskClaimed(taskId) elseif taskCfg.taskType == TaskConst.TaskType.achievement then return TaskAchMgr:isTaskClaimed(taskId) end return false end -- 标记任务为已领取 function TaskMgr:setTaskClaimed(taskId) local taskCfg = TaskCfgParse:getTaskCfg(taskId) if not taskCfg then return end -- 根据任务类型调用不同的管理器 if taskCfg.taskType == TaskConst.TaskType.daily then TaskDailyMgr:setTaskClaimed(taskId) elseif taskCfg.taskType == TaskConst.TaskType.achievement then TaskAchMgr:setTaskClaimed(taskId) end end -- 更新所有任务状态 function TaskMgr:updateAllTaskStates() -- 更新每日任务状态 if TaskDailyMgr:shouldRefreshDailyTasks() then -- 如果需要刷新每日任务,则重新生成每日任务 local dailyTasks = TaskCfgParse:getRandomDailyTasks(5) -- 生成 5 个每日任务 local dailyTaskIds = {} for _, task in ipairs(dailyTasks) do table.insert(dailyTaskIds, task.id) end TaskDailyMgr:setDailyTaskIds(dailyTaskIds) end -- 更新成就任务状态 TaskAchMgr:updateAllTaskStates() end -- 获取任务状态 -- @param taskId 任务ID -- @return TaskConst.TaskState 任务状态(进行中/可完成/已领取) function TaskMgr:getTaskState(taskId) -- 获取任务配置,判断任务类型 local taskCfg = TaskCfgParse:getTaskCfg(taskId) if not taskCfg then return TaskConst.TaskState.in_progress end local state = TaskConst.TaskState.in_progress -- 根据任务类型调用不同的管理器 if taskCfg.taskType == TaskConst.TaskType.daily then -- 每日任务 state = TaskDailyMgr:getTaskState(taskId) elseif taskCfg.taskType == TaskConst.TaskType.achievement then -- 成就任务 state = TaskAchMgr:getTaskState(taskId) elseif taskCfg.taskType == TaskConst.TaskType.order then -- 订单任务 -- 暂时使用进度判断 local currentValue, targetValue = self:getTaskProgress(taskId) if currentValue >= targetValue then state = TaskConst.TaskState.success else state = TaskConst.TaskState.in_progress end end -- 如果任务已经有状态(已领取),直接返回 if state == TaskConst.TaskState.claimed then return state end -- 判断任务是否可完成 local currentValue, targetValue = self:getTaskProgress(taskId) if currentValue >= targetValue then -- 更新任务状态为可完成 if state ~= TaskConst.TaskState.success then -- 根据任务类型调用不同的管理器更新状态 if taskCfg.taskType == TaskConst.TaskType.daily then TaskDailyMgr:setTaskState(taskId, TaskConst.TaskState.success) elseif taskCfg.taskType == TaskConst.TaskType.achievement then TaskAchMgr:setTaskState(taskId, TaskConst.TaskState.success) end end return TaskConst.TaskState.success end -- 其他情况为进行中 return TaskConst.TaskState.in_progress end --- 获取任务所有奖励 function TaskMgr:getTaskRewardById(taskId, startNode) -- 获取任务配置 local taskCfg = TaskCfgParse:getTaskCfg(taskId) if not taskCfg then printError("TaskMgr:getTaskReward - 未找到任务配置: %s", taskId) return end for i = 1, 2 do local rewardType = taskCfg["rewardType_" .. i] local rewardValue = taskCfg["params_" .. i] if rewardType and rewardValue and rewardValue > 0 then self:getTaskReward(rewardType, rewardValue, startNode) end end end --- 按类型获取任务奖励 function TaskMgr:getTaskReward(rewardType, rewardValue, startNode) -- 根据奖励类型调用对应的奖励处理函数 if rewardType == TaskConst.RewardType.Coin then CurrencyMgr:changeCoin(rewardValue, true) CoinDeskMgr:flyToCurrencyBar(startNode, function() CurrencyMgr:refreshCoin(true) end) elseif rewardType == TaskConst.RewardType.Diamond then CurrencyMgr:changeDiamond(rewardValue) elseif rewardType == TaskConst.RewardType.MusicalNotes then CurrencyMgr:changeMusicalNotes(rewardValue) elseif rewardType == TaskConst.RewardType.FishingBait then CurrencyMgr:changeFishingBait(rewardValue) elseif rewardType == TaskConst.RewardType.Progress then return elseif rewardType == TaskConst.RewardType.Star then CurrencyMgr:changeStar(rewardValue) elseif rewardType == TaskConst.RewardType.AdCoupons then CurrencyMgr:changeAdCoupons(rewardValue) else printError("TaskMgr:getTaskReward - 未知奖励类型: %s", rewardType) return end end --- 通过奖励序号获取任务奖励图标 function TaskMgr:getTaskRewardIconByIndex(taskId, index) -- 获取任务配置 local taskCfg = TaskCfgParse:getTaskCfg(taskId) if not taskCfg then printError("TaskMgr:getTaskReward - 未找到任务配置: %s", taskId) return end return self:getTaskRewardIconByType(taskCfg["rewardType_" .. index]) end --- 获取任务奖励图标 function TaskMgr:getTaskRewardIconByType(rewardType) if rewardType == TaskConst.RewardType.Coin then return CurrencyMgr:getCoinUIIcon() elseif rewardType == TaskConst.RewardType.Diamond then return CurrencyMgr:getDiamondUIIcon() elseif rewardType == TaskConst.RewardType.MusicalNotes then return CurrencyMgr:getMusicalNotesUIIcon() elseif rewardType == TaskConst.RewardType.FishingBait then return CurrencyMgr:getFishingBaitUIIcon() elseif rewardType == TaskConst.RewardType.Progress then return nil -- todo 进度图标 elseif rewardType == TaskConst.RewardType.Star then return CurrencyMgr:getStarUIIcon() elseif rewardType == TaskConst.RewardType.AdCoupons then return CurrencyMgr:getAdCouponsUIIcon() else printError("TaskMgr:getTaskRewardIcon - 未知奖励类型: %s", rewardType) return nil end end -- 获取每日任务最终任务ID function TaskMgr:getDailyFinalMissionsId() return 520001 end return TaskMgr EmployeeSkinu--- --- Generated by EmmyLua(https://github.com/EmmyLua) --- Created by ljy. --- DateTime: 2025/5/21 18:00 --- ResLoadereH--[[ 功能: 1. 加载资源 2. 缓存资源 3. 释放资源 4. 清理缓存 5. 卸载资源 6. 统计和调试 author: zhangheng time:2025-07-19 15:50:00 ]] local ResLoader = defClassStatic("ResLoader") local LOGTAG = "ResLoader" ResLoader._assetfileCache = {} -- 资源文件缓存 {assetName -> asset} ResLoader._reslinkCache = {} -- ResLink配置缓存 {path -> ResLink} -- ============================================================================ -- ResLink配置层 -- ============================================================================ -- 资源类型映射(保持与原版本兼容) local _load_func_map_by_type = { [0] = {"Prefab", function(c) return ResLoader.loadAsset(c[1]) end}, [1] = {"Scene", function(c) return c[1] end}, [2] = {"ResLink", function(c) return ResLoader.loadResLink(c[1]) end}, [3] = {"RawTxt", function(c) local f,err = load("return " .. c[1]) return f and f() or c[1] end}, [4] = {"LuaFile", function(c) return c[1] end}, [5] = {"ResPath", function(c) return c[1] end}, [6] = {"Sprite", function(c) return ResLoader.loadAsset(c[1], "Sprite") end}, [7] = {"AudioClip", function(c) return c[1] -- 音频通常是路径引用 end}, [8] = {"VideoClip", function(c) return ResLoader.loadAsset(c[1]) end}, [9] = {"TextAsset", function(c) local asset = ResLoader.loadAsset(c[1], "TextAsset") return asset and asset.bytes or nil end}, [10] = {"AnimationClip", function(c) return ResLoader.loadAsset(c[1], "AnimationClip") end}, [11] = {"RuntimeAnimatorController", function(c) return ResLoader.loadAsset(c[1], "RuntimeAnimatorController") end}, [12] = {"JsonFile", function(c) local asset = ResLoader.loadAsset(c[1], "TextAsset") if asset and asset.text then return json.decode(asset.text) end return nil end}, [13] = {"SkeletonDataAsset", function(c) return ResLoader.loadAsset(c[1], "SkeletonDataAsset") end}, } -- ResLink元表 local _reslink_meta = { __newindex = function(t, k, v) printError(LOGTAG, "[ResLink]只读,不能修改:%s", k) end, __index = function(t, k) local c = t.__asset[k] if c then return c[4] and c[4](c) else printWarn(LOGTAG,"[ResLink]缺失资源:%s", k) end end } -- ResLink实例数据 local _reslink_data = { __cls_inst = false, ---获取所有资源的键名 ---@param self ResLink ---@return string[] getAssetNames = function (self) local t = {} for k,_ in pairs(self.__asset) do table.insert(t, k) end return t end, ---获取所有资源配置 ---@param self ResLink ---@return AssetCfg[] getAssetList = function(self) local t = {} for k,v in pairs(self.__asset) do table.insert(t, v) end return t end, ---获取指定资源配置 ---@param self ResLink ---@param k string ---@return AssetCfg getAssetInfo = function(self, k) return self.__asset[k] end, ---获取指定资源路径 ---@param self ResLink ---@param k string ---@return string|nil getAssetPath = function(self, k) local info = self.__asset[k] return info and info[1] end, } -- 初始化 function ResLoader.init() -- 清理缓存 collectgarbage() CS.UnityEngine.Resources.UnloadUnusedAssets() printInfo(LOGTAG, "ResLoader系统初始化完成") return true end -- 检查资源是否存在 function ResLoader.hasAsset(path) if not path then return false end -- 直接使用YooAssetAdapter检查(已经内部处理了本地资源判断) return YooAssetAdapter.hasAsset(path) end -- 同步加载资源 ---@param path string 资源路径 ---@param assetType string|System.Type|nil 资源类型(可选) ---@return any|nil 加载的资源对象 function ResLoader.loadAsset(path, assetType) if not path then printError(LOGTAG, "资源路径为空") return nil end -- 生成缓存键名,支持字符串和System.Type local assetName = nil if assetType then if type(assetType) == "string" then assetName = string.format("%s:%s", path, assetType) else -- 对于System.Type对象,使用其名称作为缓存键 local typeName = assetType.Name or tostring(assetType) assetName = string.format("%s:%s", path, typeName) end else assetName = path end printInfo(LOGTAG, "[loadAsset]同步加载资源:%s", assetName) -- 检查缓存 local asset = ResLoader._assetfileCache[assetName] if asset then printDebug(LOGTAG, "使用缓存资源:%s", assetName) return asset end -- 使用YooAssetAdapter加载(已经内部处理了本地资源判断) asset = YooAssetAdapter.loadAssetSync(path, assetType) if asset then -- 缓存资源 ResLoader._assetfileCache[assetName] = asset printDebug(LOGTAG, "资源加载成功并缓存:%s", assetName) else printError(LOGTAG, "资源加载失败:%s", assetName) end return asset end -- 异步加载资源 ---@param path string 资源路径 ---@param assetType string|System.Type|nil 资源类型(可选) ---@param progressCallback function|nil 进度回调 ---@param finishCallback function|nil 完成回调 function ResLoader.loadAssetAsync(path, assetType, progressCallback, finishCallback) if not path then printError(LOGTAG, "资源路径为空") if finishCallback then finishCallback(nil) end return end -- 生成缓存键名,支持字符串和System.Type local assetName = nil if assetType then if type(assetType) == "string" then assetName = string.format("%s:%s", path, assetType) else -- 对于System.Type对象,使用其名称作为缓存键 local typeName = assetType.Name or tostring(assetType) assetName = string.format("%s:%s", path, typeName) end else assetName = path end printInfo(LOGTAG, "[loadAssetAsync]异步加载资源:%s", assetName) -- 检查缓存 local asset = ResLoader._assetfileCache[assetName] if asset then printDebug(LOGTAG, "使用缓存资源:%s", assetName) if progressCallback then progressCallback(1) end if finishCallback then finishCallback(asset) end return end -- 使用YooAssetAdapter异步加载(已经内部处理了本地资源判断) YooAssetAdapter.loadAssetAsync(path, assetType, progressCallback, function(asset) if asset then -- 缓存资源 ResLoader._assetfileCache[assetName] = asset printDebug(LOGTAG, "异步资源加载成功并缓存:%s", assetName) else printError(LOGTAG, "异步资源加载失败:%s", assetName) end if finishCallback then finishCallback(asset) end end) end -- 加载ResLink配置 ---@param path string ResLink配置路径 ---@param cache table|nil 缓存表(用于避免循环引用) ---@return ResLink|nil ResLink实例 function ResLoader.loadResLink(path, cache) if not path then printError(LOGTAG, "ResLink路径为空") return nil end printInfo(LOGTAG, "[loadResLink]加载配置:%s", path) -- 检查缓存 if ResLoader._reslinkCache[path] then printDebug(LOGTAG, "使用缓存ResLink:%s", path) return ResLoader._reslinkCache[path] end -- 加载配置文件 local success, assetData if CS.LuaHelper.UseLocalSrc() then -- 编辑器本地模式:直接从文件系统加载 printDebug(LOGTAG, "编辑器本地模式加载ResLink配置:%s", path) local dataPath = CS.UnityEngine.Application.dataPath local filepath = dataPath.."/LuaScripts/"..path..".lua" printDebug(LOGTAG, "加载ResLink文件路径:%s", filepath) local src = CS.LuaHelper.ReadFileText(filepath) if src then local func, err = load(src, path, "bt") if func then success, assetData = pcall(func) else printError(LOGTAG, "ResLink配置编译失败:%s, 错误:%s", path, tostring(err)) return nil end else printError(LOGTAG, "ResLink配置文件读取失败:%s", filepath) return nil end else -- 标准模式:使用require success, assetData = pcall(require, path) end if not success or not assetData then printError(LOGTAG, "ResLink配置加载失败:%s, 错误:%s", path, tostring(assetData)) return nil end -- 处理资源类型映射 for k, v in pairs(assetData) do if type(v) == "table" and #v >= 3 then local typeInfo = _load_func_map_by_type[v[3]] or {} v[3], v[4] = typeInfo[1], typeInfo[2] end end -- 处理继承机制 local baseLink = assetData.BASE if baseLink then assetData.BASE = nil cache = cache or {} cache[path] = true if cache[baseLink[1]] ~= true then local parentLink = ResLoader.loadResLink(baseLink[1], cache) if parentLink then -- 合并父配置 for k, v in pairs(parentLink.__asset) do if assetData[k] == nil then assetData[k] = v end end end end end -- 创建ResLink实例 local resLink = {__asset = assetData} for k, v in pairs(_reslink_data) do resLink[k] = v end local instance = setmetatable(resLink, _reslink_meta) -- 缓存ResLink ResLoader._reslinkCache[path] = instance printDebug(LOGTAG, "ResLink加载成功并缓存:%s", path) return instance end -- 预加载资源列表 ---@param assetList string[] 资源路径列表 ---@param callback function|nil 完成回调 function ResLoader.preloadAssets(assetList, callback) if not assetList or #assetList == 0 then if callback then callback(true) end return end printInfo(LOGTAG, "[preloadAssets]预加载资源列表,数量:%d", #assetList) -- 委托给YooAssetAdapter处理 YooAssetAdapter.preloadAssets(assetList, callback) end -- ============================================================================ -- 资源管理 -- ============================================================================ -- 释放指定资源 function ResLoader.releaseAsset(path, assetType) local assetName = assetType and string.format("%s:%s", path, assetType) or path -- 从缓存中移除 if ResLoader._assetfileCache[assetName] then ResLoader._assetfileCache[assetName] = nil printDebug(LOGTAG, "从缓存中移除资源:%s", assetName) end -- 释放YooAssetAdapter中的资源 YooAssetAdapter.releaseAsset(path) end -- 清理所有缓存 function ResLoader.clearCache() printInfo(LOGTAG, "清理所有资源缓存") -- 清理资源缓存 for k, v in pairs(ResLoader._assetfileCache) do if type(v) == "userdata" then xlua.release(v) end end ResLoader._assetfileCache = {} -- 清理ResLink缓存 ResLoader._reslinkCache = {} -- 释放YooAssetAdapter资源 YooAssetAdapter.clearAll() -- Unity资源清理 CS.UnityEngine.Resources.UnloadUnusedAssets() collectgarbage() printInfo(LOGTAG, "资源缓存清理完成") end -- 卸载资源(兼容性接口) function ResLoader.unloadAssets() ResLoader.clearCache() end -- 退出清理 function ResLoader.exit() ResLoader.clearCache() printInfo(LOGTAG, "ResLoader系统已退出") end -- 场景加载后的垃圾回收 function ResLoader.gcAfterLoadScene() printInfo(LOGTAG, "[gcAfterLoadScene]场景加载后执行垃圾回收") -- 执行Lua垃圾回收 collectgarbage("collect") -- 卸载未使用的Unity资源 CS.UnityEngine.Resources.UnloadUnusedAssets() printDebug(LOGTAG, "场景加载后垃圾回收完成") end -- ============================================================================ -- 便捷API -- ============================================================================ -- 加载预制体 function ResLoader.loadPrefab(path) return ResLoader.loadAsset(path, "GameObject") end -- 异步加载预制体 function ResLoader.loadPrefabAsync(path, callback) ResLoader.loadAssetAsync(path, "GameObject", nil, callback) end -- 加载贴图 function ResLoader.loadSprite(path) return ResLoader.loadAsset(path, "Sprite") end -- 异步加载贴图 function ResLoader.loadSpriteAsync(path, callback) ResLoader.loadAssetAsync(path, "Sprite", nil, callback) end -- 加载音频 ---@param path string 音频路径 ---@return AudioClip|nil 音频对象 function ResLoader.loadAudioClip(path) return ResLoader.loadAsset(path, "AudioClip") end function ResLoader.loadAudioClipAsync(path, callback) ResLoader.loadAssetAsync(path, "AudioClip", nil, callback) end -- 加载文本资源 ---@param path string 文本资源路径 ---@return TextAsset|nil 文本资源对象 function ResLoader.loadTextAsset(path) return ResLoader.loadAsset(path, "TextAsset") end function ResLoader.loadTextAssetAsync(path, callback) ResLoader.loadAssetAsync(path, "TextAsset", nil, callback) end -- 场景加载 ---@param path string 场景路径 ---@param callback function|nil 完成回调 function ResLoader.loadSceneSync(path, callback) printInfo(LOGTAG, "[loadSceneSync]同步加载场景:%s", path) -- 注意:Unity没有真正的同步场景加载,这里使用异步但立即等待 ResLoader.loadSceneAsync(path, nil, callback) end ---@param path string 场景路径 ---@param progressCallback function|nil 进度回调 ---@param finishCallback function|nil 完成回调 function ResLoader.loadSceneAsync(path, progressCallback, finishCallback) printInfo(LOGTAG, "[loadSceneAsync]异步加载场景:%s", path) if not path then printError(LOGTAG, "场景路径为空") if finishCallback then finishCallback(nil) end return end -- 从路径中提取场景名称,处理各种路径格式 local sceneName = path -- 去除.unity扩展名 sceneName = string.gsub(sceneName, "%.unity$", "") -- 提取最后一个路径分量作为场景名 sceneName = string.match(sceneName, "([^/\\]+)$") or sceneName printDebug(LOGTAG, "提取场景名称:%s", sceneName) -- 直接使用YooAssetLoader加载场景 local loader = CS.YooAssetLoader.Instance if loader then loader:LoadSceneAsyncLua(path, function(success) if finishCallback then if success then -- 通过遍历所有场景来找到匹配的场景对象 local sceneManager = CS.UnityEngine.SceneManagement.SceneManager local sceneCount = sceneManager.sceneCount local targetScene = nil for i = 0, sceneCount - 1 do local loadedScene = sceneManager:GetSceneAt(i) if loadedScene and loadedScene.isLoaded and loadedScene.name == sceneName then targetScene = loadedScene break end end if targetScene then printDebug(LOGTAG, "场景加载成功,场景名称:%s", sceneName) finishCallback(targetScene) else printError(LOGTAG, "场景加载失败,无法找到匹配的场景:%s", sceneName) finishCallback(nil) end else printError(LOGTAG, "场景加载失败:%s", path) finishCallback(nil) end end end) else printError(LOGTAG, "YooAssetLoader未初始化") if finishCallback then finishCallback(nil) end end end -- ============================================================================ -- 统计和调试 -- ============================================================================ -- 获取缓存统计 function ResLoader.getCacheStats() local assetCount = 0 local reslinkCount = 0 for _ in pairs(ResLoader._assetfileCache) do assetCount = assetCount + 1 end for _ in pairs(ResLoader._reslinkCache) do reslinkCount = reslinkCount + 1 end local yooStats = YooAssetAdapter.getStats() return { assetCount = assetCount, reslinkCount = reslinkCount, yooAssetCount = yooStats.assetCount, yooSceneCount = yooStats.sceneCount, yooReady = yooStats.isReady } end -- 打印缓存统计 function ResLoader.printCacheStats() local stats = ResLoader.getCacheStats() printInfo(LOGTAG, "=== ResLoader缓存统计 ===") printInfo(LOGTAG, "资源缓存数量: %d", stats.assetCount) printInfo(LOGTAG, "ResLink缓存数量: %d", stats.reslinkCount) printInfo(LOGTAG, "YooAsset资源数量: %d", stats.yooAssetCount) printInfo(LOGTAG, "YooAsset场景数量: %d", stats.yooSceneCount) printInfo(LOGTAG, "YooAsset就绪状态: %s", tostring(stats.yooReady)) printInfo(LOGTAG, "====================") end -- 打印系统状态 function ResLoader.printStatus() printInfo(LOGTAG, "=== ResLoader系统状态 ===") ResLoader.printCacheStats() print("") YooAssetAdapter.printStatus() end -- 获取YooAssetAdapter实例(用于高级操作) function ResLoader.getYooAssetAdapter() return YooAssetAdapter end return ResLoader PiranhaB-- 食人鱼 local Piranha = defClass("Piranha") -- 构造函数 function Piranha:ctor(targetId) self.id = targetId self:init() self:initEvent() end -- 初始化 function Piranha:init() self.go = GameObject.Instantiate(FishingGameMgr.resLink.piranha) self.transform = self.go.transform self.speed = 0 end -- 开始 function Piranha:start(direction, distance) self.direction = direction self.distance = distance self.time = 2 end function Piranha:timeUpdate(deltaTime) if (not self.direction) or (not self.distance) then local pos = FishingGameMgr.character.transform.position local direction = (pos - self.transform.position).normalized local distance = Vector3.Distance(self.transform.position, pos) if distance <= 5 then self:start(direction, distance) end return end -- 蓄力 if self.time > 0 then return end -- 计算移动距离 local moveDistance = math.min(self.speed * deltaTime, self.distance) -- 计算目标位置 local currentPosition = self.go.transform.position local targetPosition = currentPosition + self.direction * moveDistance -- todo: -- 更新位置 self.go.transform.position = targetPosition end -- 初始化事件 function Piranha:initEvent() self.collider = self.go:Seek("collider") Event.add(self.collider, Event.OnTriggerEnter2D, function(other) self:onTriggerEnter(other) end) end -- 当玩家触发碰撞时 function Piranha:onTriggerEnter(other) if (not FishingGameMgr.isPlaying) or FishingGameMgr.isPaused then return end if other.gameObject:CompareTag("Player") then FishingGameMgr:hurt() -- 改为减少能量 end end return PiranhaemployeLevelUpCfg]--[[ from file:员工升级.xlsx --]] local employeLevelUpCfg = { [1] = { id = 310101, levelUpPrice = 1000, cdTime = 1200, upStarNum = 30, typeId_1 = 301, arg_1_1 = 60, arg_1_2 = 90, arg_1_3 = -1, typeId_2 = 302, arg_2_1 = 6.4, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [2] = { id = 310102, levelUpPrice = 2000, cdTime = 2400, upStarNum = 35, typeId_1 = 301, arg_1_1 = 70, arg_1_2 = 90, arg_1_3 = -1, typeId_2 = 302, arg_2_1 = 6.3, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [3] = { id = 310103, levelUpPrice = 3000, cdTime = 3600, upStarNum = 40, typeId_1 = 301, arg_1_1 = 80, arg_1_2 = 90, arg_1_3 = -1, typeId_2 = 302, arg_2_1 = 6.3, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [4] = { id = 310104, levelUpPrice = 4000, cdTime = 4800, upStarNum = 45, typeId_1 = 301, arg_1_1 = 90, arg_1_2 = 90, arg_1_3 = -1, typeId_2 = 302, arg_2_1 = 6.1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [5] = { id = 310105, levelUpPrice = 5000, cdTime = -1, upStarNum = 50, typeId_1 = 301, arg_1_1 = 100, arg_1_2 = 90, arg_1_3 = -1, typeId_2 = 302, arg_2_1 = 6.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [6] = { id = 310201, levelUpPrice = 1000, cdTime = 1200, upStarNum = 30, typeId_1 = 301, arg_1_1 = 60, arg_1_2 = 90, arg_1_3 = -1, typeId_2 = 302, arg_2_1 = 6.4, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [7] = { id = 310202, levelUpPrice = 2000, cdTime = 2400, upStarNum = 35, typeId_1 = 301, arg_1_1 = 70, arg_1_2 = 90, arg_1_3 = -1, typeId_2 = 302, arg_2_1 = 6.3, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [8] = { id = 310203, levelUpPrice = 3000, cdTime = 3600, upStarNum = 40, typeId_1 = 301, arg_1_1 = 80, arg_1_2 = 90, arg_1_3 = -1, typeId_2 = 302, arg_2_1 = 6.3, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [9] = { id = 310204, levelUpPrice = 4000, cdTime = 4800, upStarNum = 45, typeId_1 = 301, arg_1_1 = 90, arg_1_2 = 90, arg_1_3 = -1, typeId_2 = 302, arg_2_1 = 6.1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [10] = { id = 310205, levelUpPrice = 5000, cdTime = -1, upStarNum = 50, typeId_1 = 301, arg_1_1 = 100, arg_1_2 = 90, arg_1_3 = -1, typeId_2 = 302, arg_2_1 = 6.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [11] = { id = 310301, levelUpPrice = 5000, cdTime = 900, upStarNum = 15, typeId_1 = 303, arg_1_1 = 120, arg_1_2 = 240, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [12] = { id = 310302, levelUpPrice = 10000, cdTime = 1800, upStarNum = 20, typeId_1 = 303, arg_1_1 = 140, arg_1_2 = 230, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [13] = { id = 310303, levelUpPrice = 15000, cdTime = 2700, upStarNum = 25, typeId_1 = 303, arg_1_1 = 160, arg_1_2 = 220, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [14] = { id = 310304, levelUpPrice = 20000, cdTime = 3600, upStarNum = 30, typeId_1 = 303, arg_1_1 = 180, arg_1_2 = 210, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [15] = { id = 310305, levelUpPrice = 25000, cdTime = 4500, upStarNum = 35, typeId_1 = 303, arg_1_1 = 200, arg_1_2 = 200, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [16] = { id = 310306, levelUpPrice = 30000, cdTime = 5400, upStarNum = 40, typeId_1 = 303, arg_1_1 = 220, arg_1_2 = 190, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [17] = { id = 310307, levelUpPrice = 40000, cdTime = 7200, upStarNum = 45, typeId_1 = 303, arg_1_1 = 240, arg_1_2 = 180, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [18] = { id = 310308, levelUpPrice = 50000, cdTime = 9000, upStarNum = 50, typeId_1 = 303, arg_1_1 = 260, arg_1_2 = 170, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [19] = { id = 310309, levelUpPrice = 60000, cdTime = 10800, upStarNum = 55, typeId_1 = 303, arg_1_1 = 280, arg_1_2 = 160, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [20] = { id = 310310, levelUpPrice = 70000, cdTime = 14400, upStarNum = 60, typeId_1 = 303, arg_1_1 = 300, arg_1_2 = 150, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [21] = { id = 310311, levelUpPrice = 80000, cdTime = 18000, upStarNum = 65, typeId_1 = 303, arg_1_1 = 320, arg_1_2 = 140, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [22] = { id = 310312, levelUpPrice = 90000, cdTime = 21600, upStarNum = 70, typeId_1 = 303, arg_1_1 = 340, arg_1_2 = 130, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [23] = { id = 310313, levelUpPrice = 100000, cdTime = 25200, upStarNum = 75, typeId_1 = 303, arg_1_1 = 360, arg_1_2 = 120, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [24] = { id = 310314, levelUpPrice = 150000, cdTime = 28800, upStarNum = 80, typeId_1 = 303, arg_1_1 = 380, arg_1_2 = 110, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [25] = { id = 310315, levelUpPrice = 200000, cdTime = -1, upStarNum = 85, typeId_1 = 303, arg_1_1 = 400, arg_1_2 = 100, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [26] = { id = 310401, levelUpPrice = 1000, cdTime = 1200, upStarNum = 30, typeId_1 = 304, arg_1_1 = 2, arg_1_2 = 1, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 5.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [27] = { id = 310402, levelUpPrice = 2000, cdTime = 2400, upStarNum = 35, typeId_1 = 304, arg_1_1 = 2, arg_1_2 = 1, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 7.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [28] = { id = 310403, levelUpPrice = 3000, cdTime = 3600, upStarNum = 40, typeId_1 = 304, arg_1_1 = 2, arg_1_2 = 1, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 9.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [29] = { id = 310404, levelUpPrice = 4000, cdTime = 4800, upStarNum = 45, typeId_1 = 304, arg_1_1 = 2, arg_1_2 = 1, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 11.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [30] = { id = 310405, levelUpPrice = 5000, cdTime = -1, upStarNum = 50, typeId_1 = 304, arg_1_1 = 2, arg_1_2 = 1, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 13.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [31] = { id = 310501, levelUpPrice = 1000, cdTime = 2700, upStarNum = 20, typeId_1 = 306, arg_1_1 = 50, arg_1_2 = 60, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [32] = { id = 310502, levelUpPrice = 2000, cdTime = 3600, upStarNum = 30, typeId_1 = 306, arg_1_1 = 70, arg_1_2 = 65, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [33] = { id = 310503, levelUpPrice = 3000, cdTime = 4500, upStarNum = 40, typeId_1 = 306, arg_1_1 = 90, arg_1_2 = 70, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [34] = { id = 310504, levelUpPrice = 4000, cdTime = 5400, upStarNum = 50, typeId_1 = 306, arg_1_1 = 110, arg_1_2 = 75, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [35] = { id = 310505, levelUpPrice = 5000, cdTime = 6300, upStarNum = 60, typeId_1 = 306, arg_1_1 = 130, arg_1_2 = 80, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [36] = { id = 310506, levelUpPrice = -1, cdTime = 9000, upStarNum = 70, typeId_1 = 306, arg_1_1 = 150, arg_1_2 = 85, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [37] = { id = 310507, levelUpPrice = -1, cdTime = 11700, upStarNum = 80, typeId_1 = 306, arg_1_1 = 170, arg_1_2 = 90, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [38] = { id = 310508, levelUpPrice = -1, cdTime = 14400, upStarNum = 90, typeId_1 = 306, arg_1_1 = 190, arg_1_2 = 95, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [39] = { id = 310509, levelUpPrice = -1, cdTime = 17100, upStarNum = 100, typeId_1 = 306, arg_1_1 = 200, arg_1_2 = 100, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [40] = { id = 310510, levelUpPrice = -1, cdTime = 21000, upStarNum = 110, typeId_1 = 306, arg_1_1 = 200, arg_1_2 = 105, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [41] = { id = 310511, levelUpPrice = -1, cdTime = 24900, upStarNum = 120, typeId_1 = 306, arg_1_1 = 200, arg_1_2 = 110, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [42] = { id = 310512, levelUpPrice = -1, cdTime = 28800, upStarNum = 130, typeId_1 = 306, arg_1_1 = 200, arg_1_2 = 115, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [43] = { id = 310513, levelUpPrice = -1, cdTime = 32700, upStarNum = 140, typeId_1 = 306, arg_1_1 = 200, arg_1_2 = 120, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [44] = { id = 310514, levelUpPrice = -1, cdTime = 36600, upStarNum = 150, typeId_1 = 306, arg_1_1 = 200, arg_1_2 = 125, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [45] = { id = 310515, levelUpPrice = -1, cdTime = -1, upStarNum = 160, typeId_1 = 306, arg_1_1 = 200, arg_1_2 = 130, arg_1_3 = 240, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [46] = { id = 310601, levelUpPrice = 1000, cdTime = 3600, upStarNum = 30, typeId_1 = 308, arg_1_1 = 120, arg_1_2 = 240, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 5.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [47] = { id = 310602, levelUpPrice = 2000, cdTime = 10800, upStarNum = 40, typeId_1 = 308, arg_1_1 = 130, arg_1_2 = 230, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 7.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [48] = { id = 310603, levelUpPrice = 3000, cdTime = 18000, upStarNum = 50, typeId_1 = 308, arg_1_1 = 140, arg_1_2 = 220, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 9.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [49] = { id = 310604, levelUpPrice = 4000, cdTime = 25200, upStarNum = 60, typeId_1 = 308, arg_1_1 = 150, arg_1_2 = 210, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 11.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [50] = { id = 310605, levelUpPrice = 5000, cdTime = 32400, upStarNum = 70, typeId_1 = 308, arg_1_1 = 160, arg_1_2 = 200, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 13.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [51] = { id = 310606, levelUpPrice = -1, cdTime = 43200, upStarNum = 80, typeId_1 = 308, arg_1_1 = 170, arg_1_2 = 190, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 15.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [52] = { id = 310607, levelUpPrice = -1, cdTime = 54000, upStarNum = 90, typeId_1 = 308, arg_1_1 = 180, arg_1_2 = 180, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 17.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [53] = { id = 310608, levelUpPrice = -1, cdTime = 64800, upStarNum = 100, typeId_1 = 308, arg_1_1 = 190, arg_1_2 = 170, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 19.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [54] = { id = 310609, levelUpPrice = -1, cdTime = 75600, upStarNum = 110, typeId_1 = 308, arg_1_1 = 200, arg_1_2 = 160, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 21.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [55] = { id = 310610, levelUpPrice = -1, cdTime = 86400, upStarNum = 120, typeId_1 = 308, arg_1_1 = 210, arg_1_2 = 150, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 23.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [56] = { id = 310611, levelUpPrice = -1, cdTime = 97200, upStarNum = 130, typeId_1 = 308, arg_1_1 = 220, arg_1_2 = 140, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 25.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [57] = { id = 310612, levelUpPrice = -1, cdTime = 108000, upStarNum = 140, typeId_1 = 308, arg_1_1 = 230, arg_1_2 = 130, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 27.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [58] = { id = 310613, levelUpPrice = -1, cdTime = 118800, upStarNum = 150, typeId_1 = 308, arg_1_1 = 240, arg_1_2 = 120, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 29.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [59] = { id = 310614, levelUpPrice = -1, cdTime = 129600, upStarNum = 160, typeId_1 = 308, arg_1_1 = 250, arg_1_2 = 110, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 31.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [60] = { id = 310615, levelUpPrice = -1, cdTime = -1, upStarNum = 170, typeId_1 = 308, arg_1_1 = 260, arg_1_2 = 100, arg_1_3 = -1, typeId_2 = 305, arg_2_1 = 33.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [61] = { id = 310701, levelUpPrice = 1000, cdTime = 1200, upStarNum = 30, typeId_1 = 311, arg_1_1 = 3, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = 312, arg_2_1 = 20.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = 313, arg_3_1 = 2, arg_3_2 = -1, arg_3_3 = -1, }, [62] = { id = 310702, levelUpPrice = 2000, cdTime = 2400, upStarNum = 35, typeId_1 = 311, arg_1_1 = 4, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = 312, arg_2_1 = 20.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = 313, arg_3_1 = 3, arg_3_2 = -1, arg_3_3 = -1, }, [63] = { id = 310703, levelUpPrice = 3000, cdTime = 3600, upStarNum = 40, typeId_1 = 311, arg_1_1 = 5, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = 312, arg_2_1 = 20.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = 313, arg_3_1 = 3, arg_3_2 = -1, arg_3_3 = -1, }, [64] = { id = 310704, levelUpPrice = 4000, cdTime = 4800, upStarNum = 45, typeId_1 = 311, arg_1_1 = 6, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = 312, arg_2_1 = 20.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = 313, arg_3_1 = 4, arg_3_2 = -1, arg_3_3 = -1, }, [65] = { id = 310705, levelUpPrice = 5000, cdTime = -1, upStarNum = 50, typeId_1 = 311, arg_1_1 = 7, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = 312, arg_2_1 = 20.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = 313, arg_3_1 = 4, arg_3_2 = -1, arg_3_3 = -1, }, [66] = { id = 310801, levelUpPrice = 1000, cdTime = 1200, upStarNum = 30, typeId_1 = 309, arg_1_1 = 20, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = 310, arg_2_1 = 3.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [67] = { id = 310802, levelUpPrice = 2000, cdTime = 2400, upStarNum = 35, typeId_1 = 309, arg_1_1 = 25, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = 310, arg_2_1 = 4.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [68] = { id = 310803, levelUpPrice = 3000, cdTime = 3600, upStarNum = 40, typeId_1 = 309, arg_1_1 = 30, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = 310, arg_2_1 = 5.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [69] = { id = 310804, levelUpPrice = 4000, cdTime = 4800, upStarNum = 45, typeId_1 = 309, arg_1_1 = 35, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = 310, arg_2_1 = 6.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [70] = { id = 310805, levelUpPrice = 5000, cdTime = -1, upStarNum = 50, typeId_1 = 309, arg_1_1 = 40, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = 310, arg_2_1 = 7.0, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [71] = { id = 310901, levelUpPrice = 1000, cdTime = 1200, upStarNum = 30, typeId_1 = 307, arg_1_1 = 100, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [72] = { id = 310902, levelUpPrice = 2000, cdTime = 2400, upStarNum = 35, typeId_1 = 307, arg_1_1 = 200, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [73] = { id = 310903, levelUpPrice = 3000, cdTime = 3600, upStarNum = 40, typeId_1 = 307, arg_1_1 = 300, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [74] = { id = 310904, levelUpPrice = 4000, cdTime = 4800, upStarNum = 45, typeId_1 = 307, arg_1_1 = 400, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, [75] = { id = 310905, levelUpPrice = 5000, cdTime = -1, upStarNum = 50, typeId_1 = 307, arg_1_1 = 500, arg_1_2 = -1, arg_1_3 = -1, typeId_2 = -1, arg_2_1 = -1, arg_2_2 = -1, arg_2_3 = -1, typeId_3 = -1, arg_3_1 = -1, arg_3_2 = -1, arg_3_3 = -1, }, } return employeLevelUpCfg BoundUtil  local BoundUtil = {} function BoundUtil:getObjBounds(obj, onlySelf) local p_max = Vector3.zero local p_min = Vector3.zero local center = Vector3.zero local p = obj.transform.position local mesh = obj:GetComponent(typeof(CS.UnityEngine.Renderer)) if mesh ~= nil then local b = mesh.bounds p_max = b.max p_min = b.min center = b.center end if not onlySelf then p_min, p_max = BoundUtil:_recursionCalculateBounds(obj, p_min, p_max) end if mesh == nil then center = BoundUtil:_calculateCenter(p_max, p_min) end local size = Vector3(p_max.x - p_min.x, p_max.y - p_min.y, p_max.z - p_min.z) local bound = UnityEngine.Bounds(center, size) bound.size = size bound.extents = size / 2 obj.offset_min = Vector2(p_min.x - p.x, p_min.y - p.y) obj.offset_max = Vector2(p_max.x - p.x, p_max.y - p.y) return bound end -- 计算包围盒顶点 function BoundUtil:_recursionCalculateBounds(node, p_min, p_max) local calculateF calculateF = function (obj) if obj.transform.childCount <= 0 then return end for i = 1, obj.transform.childCount do local item = obj.transform:GetChild(i - 1).gameObject local m = item:GetComponent(typeof(CS.UnityEngine.Renderer)) if m ~= nil and item.activeSelf then local b = m.bounds if p_max:Equals(Vector3.zero) and p_min:Equals(Vector3.zero) then p_max = b.max p_min = b.min end if b.max.x > p_max.x then p_max.x = b.max.x end if b.max.y > p_max.y then p_max.y = b.max.y end if b.max.z > p_max.z then p_max.z = b.max.z end if b.min.x < p_min.x then p_min.x = b.min.x end if b.min.y < p_min.y then p_min.y = b.min.y end if b.min.z < p_min.z then p_min.z = b.min.z end end if item.activeSelf then calculateF(item) end end end calculateF(node) return p_min, p_max end function BoundUtil:_calculateCenter(p_max, p_min) local xc = (p_max.x + p_min.x) / 2 local yc = (p_max.y + p_min.y) / 2 local zc = (p_max.z + p_min.z) / 2 return Vector3(xc, yc, zc) end function BoundUtil:isObjIntersect(obj1, obj2) local b1 = obj1.__boundutil_bounds if not b1 then b1 = BoundUtil:getObjBounds(obj1) obj1.__boundutil_bounds = b1 end local p1 = obj1.transform.position local b2 = obj2.__boundutil_bounds if not b2 then b2 = BoundUtil:getObjBounds(obj2) obj2.__boundutil_bounds = b2 end local p2 = obj2.transform.position local minx1, miny1 = p1.x - b1.size.x / 2, p1.y - b1.size.y / 2 local maxx1, maxy1 = p1.x + b1.size.x / 2, p1.y + b1.size.y / 2 local minx2, miny2 = p2.x - b2.size.x / 2, p2.y - b2.size.y / 2 local maxx2, maxy2 = p2.x + b2.size.x / 2, p2.y + b2.size.y / 2 local minx = math.max(minx1, minx2) local miny = math.max(miny1, miny2) local maxx = math.min(maxx1, maxx2) local maxy = math.min(maxy1, maxy2) if minx > maxx or miny > maxy then return false end return (maxx - minx) * (maxy - miny) end return BoundUtil DiningDeskMgr--[[ 所有餐桌管理,餐厅场景独有 author:{zhangpeng} time:2025-05-16 14:31:25 ]] local DiningDeskMgr = defClassStatic("DiningDeskMgr") local LOGTAG = "DiningDeskMgr" function DiningDeskMgr:init() printInfo(LOGTAG, "------ 餐桌管理 初始化 ------") self.diningDesks = {} end -- 根据建筑id创建一个餐桌 function DiningDeskMgr:createDesk(buildingInfo) local buildingType = buildingInfo.buildingType local parentNode = RestaurantMgr:getRestaurantScene().rootNode:Seek("building_pos_" .. buildingType) local args = { deskId = buildingInfo.buildingCfgId, parent = parentNode, scene = RestaurantMgr:getRestaurantScene(), } local diningDesk = nil if not self.diningDesks then self.diningDesks = {} end if self:hasSameTypeDesk(buildingType) then self:replaceOldDesk(buildingType, buildingInfo.buildingCfgId) else diningDesk = DiningDesk.new(args) table.insert(self.diningDesks, diningDesk) end -- 初始化餐桌后尝试分配顾客 CustomerQueMgr:checkAndAssignAllWaitingCustomers() return diningDesk end -- 是否有同类不同id的餐桌 function DiningDeskMgr:hasSameTypeDesk(buildingType) for i = 1, table.nums(self.diningDesks) do local diningDesk = self.diningDesks[i] if diningDesk.buildingInfo.buildingType == buildingType then return true end end return false end -- 查找相同类型建筑的旧餐桌,删除旧的餐桌 -- @param buildingType 建筑类型 -- @param newDeskCfgId 新餐桌配置id function DiningDeskMgr:replaceOldDesk(buildingType, newDeskCfgId) local oldDesk = nil for i = 1, #self.diningDesks do local diningDesk = self.diningDesks[i] -- 大类相同,配置id不同,则是旧餐桌 if diningDesk.buildingInfo.buildingType == buildingType and diningDesk.buildingInfo.buildingCfgId ~= newDeskCfgId then -- 只修改一下prefab的名字为配置id diningDesk.buildingNode:SetName(newDeskCfgId) break end end end -- 初始化餐桌 function DiningDeskMgr:initDesk() for k,v in pairs(DiningConst.diningDeskIds) do local parentNode = RestaurantMgr:getRestaurantScene().rootNode:Seek("building_pos_" .. v) local args = { deskId = v, parent = parentNode, scene = RestaurantMgr:getRestaurantScene(), } local diningDesk = DiningDesk.new(args) table.insert(self.diningDesks, diningDesk) end end -- 根据配置id初始化餐桌 function DiningDeskMgr:initDeskByCfgId(cfgId) local parentNode = RestaurantMgr:getRestaurantScene().rootNode:Seek("building_pos_" .. cfgId) local args = { deskId = cfgId, parent = parentNode, scene = RestaurantMgr:getRestaurantScene(), } local diningDesk = DiningDesk.new(args) table.insert(self.diningDesks, diningDesk) end -- 查找空闲餐桌 function DiningDeskMgr:findIdleDesk() for i = 1, #self.diningDesks do local diningDesk = self.diningDesks[i] if diningDesk:getState() == DiningConst.State.idle then return diningDesk end end return nil end -- 查找空闲座位 function DiningDeskMgr:findIdleSeat() local diningDesk = self:findIdleDesk() if diningDesk then return diningDesk:findIdleSeat() end return nil end -- 激活一个餐桌 function DiningDeskMgr:activeOneDesk(diningDesk) table.insert(self.diningDesks, diningDesk) end -- 获取所有餐桌 function DiningDeskMgr:getAllDiningDesks() return self.diningDesks end return DiningDeskMgr Msg--Msg.lua--------------------------- --@tianye112197 --@2016-09-22 ------------------------------------ ------------------------------------ ---@class Msg:LuaStaticClass local Msg = defClassStatic("Msg") Msg._msgmap = {} Msg._idMap = {} Msg.init = function() -- _MSG.init(Msg) end local i = 0 Msg.def = function(k) if Msg.isDef(k) then error("MsgDefine k redefined!" .. k) end i = i + 1 Msg._idMap[i] = k rawset(Msg, k, i) return i end Msg.getMsgName = function(i) return Msg._idMap[i] end Msg.isDef = function(k) if rawget(Msg, k) then return true else return false end end --[[ luaobj可以是BaseScene,BaseComponent,UILayer ]] ---@param idOrList string|string[] ---@param func fun(msgid:string, ...)|fun(object:LuaClass, msgid:string, ...) ---@param luaobj LuaClass ---@param priority? integer 默认是0, 优先级越大越先执行 Msg.add = function(idOrList, func, luaobj, priority) priority = priority or 0 if type(idOrList) == "table" and idOrList.__cls_type == nil then for i, v in ipairs(idOrList) do Msg._add(v, func, luaobj, priority) end else Msg._add(idOrList, func, luaobj, priority) end end Msg._add = function(msgid, func, luaobj, priority) if luaobj then local isValid = false local t = type(luaobj) if t == "table" then if luaobj.__cls_inst then isValid = true else printError("Msg.add只能用于luaoobject,不能用于" .. tostring(luaobj.__cls_name)) end else printError("Msg.add只能用于luaoobject,不能用于" .. t) end end local list = Msg._msgmap[msgid] if not list then list = {} Msg._msgmap[msgid] = list end local f,_luaobj for i, v in ipairs(list) do f = v[1] _luaobj = v[2] if f == func and _luaobj == luaobj then return end end table.insert(list, {func, luaobj, priority}) end Msg.del = function(idOrList, func, luaobj) if type(idOrList) == "table" and idOrList.__cls_type == nil then for i, v in ipairs(idOrList) do Msg._del(v, func, luaobj) end else Msg._del(idOrList, func, luaobj) end end Msg._del = function(msgid, func, luaobj) if msgid then local list = Msg._msgmap[msgid] if list then local f,_luaobj,v for i = #list, 1, -1 do v = list[i] f = v[1] _luaobj = v[2] if (func == nil or func == f) and (luaobj == nil or luaobj == _luaobj) then table.remove(list, i) end end end else for msgid, v in pairs(Msg._msgmap) do Msg._del(msgid, func, luaobj) end end end Msg.send = function(msgid, ...) local list = Msg._msgmap[msgid] if list then local temp = {} for i = #list, 1, -1 do temp[i] = list[i] end table.stableSort(temp, function (a, b) return a[3] > b[3] end) local luaobj,f for i, v in ipairs(temp) do luaobj = v[2] f = v[1] if luaobj then f(luaobj, msgid, ...) else f(msgid, ...) end end end end Msg.cast = Msg.send --for compat Msg.call = function(msgid, ...) local list = Msg._msgmap[msgid] if list then local temp = {} for i = #list, 1, -1 do temp[i] = list[i] end table.stableSort(temp, function (a, b) return a[3] > b[3] end) for i,v in ipairs(temp) do local ret = nil if v[2] then ret = {v[1](v[2], msgid, ...)} else ret = {v[1](msgid, ...)} end if ret[1] ~= nil then return table.unpack(ret) end end end end Msg.exit = function() Msg._msgmap = {} end --protect Msg Var setmetatable( Msg, { __newindex = function(t, k, v) error(string.format("MsgDefine readonly %s,%s,%s", tostring(t), tostring(k), tostring(v))) end, __index = function(t, k) error(string.format("MsgDefine notfound %s,%s", tostring(t), tostring(k))) end } ) UserModeln local UserModel, super = defClass("UserModel", SqliteModel) function UserModel:init() end -- id function UserModel:setUserId(userId) self.userId = userId end function UserModel:getUserId() return self.userId end -- 金币数量 function UserModel:setCoinNum(coinNum) self.coinNum = coinNum end function UserModel:getCoinNum() return self.coinNum end -- 交换道具 function UserModel:setItemChangeNum(itemChangeNum) self.itemChangeNum = itemChangeNum end function UserModel:getItemChangeNum() return self.itemChangeNum end -- 炸弹 function UserModel:setItemBombNum(itemBombNum) self.itemBombNum = itemBombNum end function UserModel:getItemBombNum() return self.itemBombNum end -- 刷新道具 function UserModel:setItemRefreshNum(itemRefreshNum) self.itemRefreshNum = itemRefreshNum end function UserModel:getItemRefreshNum() return self.itemRefreshNum end -- 刷新时间道具 function UserModel:setItemAddTimeNum(itemAddTimeNum) self.itemAddTimeNum = itemAddTimeNum end function UserModel:getItemAddTimeNum() return self.itemAddTimeNum end return UserModelmainrequire("modules/guide/GuideConst") require("modules/guide/GuideMgr") require("modules/guide/GuideCom") require("modules/guide/GuideUserData")main:require("common/data/static_config/parse/TextCfgParse")mainrequire("modules/building/restaurant/passiveincome/PassivityIncomeConst") require("modules/building/restaurant/passiveincome/PassivityIncomeDesk") require("modules/building/restaurant/passiveincome/PassivityIncomeDeskMgr") UILayer8 local UILayer, super = defClass("UILayer") local UnityEngine = CS.UnityEngine local UGUI = CS.UnityEngine.UI -- UI层级 UILayer.UI_ORDER = { GAME = 1, -- 例如视频ui,属于游戏内的,在其他ui之下 DEFAULT = 10000, -- 静态UI 全屏ui TOP_DEFAULT = 10001, -- 静态ui最上层 GUIDE = 20001, -- 新手引导 PANEL = 20002, -- 普通弹窗 默认值 TRANS = 20004, -- 系统弹窗 层级高 SYS_PANEL = 20005, -- 系统弹窗 层级高 LOADING = 20006, -- loading圆圈 TOAST = 20007, -- BLOCK_TOUCH = 30001 -- 屏蔽全局touch事件 } UILayer.TWEEN = { SCALE = 0, FADE = 1 } local UI_MASK_NAME = "__UIMASK__" function UILayer:ctor(go) local uiContainer = go or UILayerUtil:_getPrefabGameObject() uiContainer:SetName(go and string.format("__uiContainerOf[%s]_%s", self.class.__cls_name, go.name) or string.format("__uiContainerOf[%s]", self.class.__cls_name)) printInfo("UILayer", "创建%s", go and go.name or tostring(self.__cls_name)) self.__uiContainer = uiContainer -- self.__uiContainer.__uiLayerInst = self self.__priority = UILayer.UI_ORDER.DEFAULT self.__subType = "default" self.__traceback = debug.traceback() self.__goListWithClickEvents = {} self.isKeyboardAdaptEnable = true self.__layerIndex = UILayerUtil:_addLayerIndex() UILayerUtil:tryAddAppExitMsg() end function UILayer:setSubType(subType) self.__subType = subType end function UILayer:onLoad() -- override me end ---@return CS.UnityEngine.GameObject function UILayer:getUIContainer() return self.__uiContainer end function UILayer:addChild(child) if child and self.__rootUI then child:SetParent(self.__uiContainer, false) -- child.__uiLayerInst = self self.__lastChild = child end return self end function UILayer:useTweenOnOpen(go, tweenType) self.__tweenOnOpenGo = go or self.__uiContainer self.__tweenOnOpen = tweenType or UILayer.TWEEN.SCALE end function UILayer:useTweenOnClose(go, tweenType) self.__tweenOnCloseGo = go or self.__tweenOnOpenGo or self.__uiContainer self.__tweenOnClose = tweenType or UILayer.TWEEN.FADE end function UILayer:show(isGlobal) self.enterTime = os.time() if self.isPopup and not UILayerUtil:canShowPopup() then return UILayerUtil:genEmptyUILayerProxy(self) end if not self.__shown then UILayerUtil.camera:SetActive(true) local bm if util.perf then bm = util.perf.BenchMark() end local rootUI, uinode if isGlobal then rootUI, uinode = UILayerUtil:getGlobalUI() else rootUI, uinode = UILayerUtil:getLocalUI() end self.__uinode = uinode ---@type UIRoot self.__rootUI = rootUI self.__uiContainer:SetParent(rootUI:getGameObject(), false) self.__isGlobal = isGlobal self.originalPos = self.__uiContainer.transform.localPosition self.curInputFieldGo = nil self.curInputFieldGoOriginalH = 0 self.__uiContainerUpateId = Event.add(self.__uiContainer, Event.Update, function(...) self:__update() end) rootUI:addUILayer(self) self:setPriority(self.__priority) self:onLoad() if not self:valid() then return self end self:_refreshDeviceSafeArea() Msg.send(Msg.UI_LAYER_ON_LOAD, self) if bm then bm:dump("UI加载:" .. self.class.__cls_name) end self.__shown = true if self.__tweenOnOpen == UILayer.TWEEN.SCALE then self:__runTweenScale(0, 1) end end return self end function UILayer:showMask(alpha) local mask = self[UI_MASK_NAME] alpha = alpha or UILayerUtil:getMaskAlpha() if not mask then mask = UILayerUtil:_getMaskGameObject() self[UI_MASK_NAME] = mask mask:SetName(UI_MASK_NAME) mask:SetParent(self.__uiContainer, false) if alpha then local img = mask[UGUI.Image] if img then local color = img.color color.a = alpha img.color = color end end -- 在最下面 end mask:SetActive(true) mask.transform:SetSiblingIndex(0) UILayerUtil:refreshMask() return self end function UILayer:setMaskClickCb(clickCb, once) local mask = self[UI_MASK_NAME] if not mask then return self end if not clickCb then return self end if not mask.__listenerAdded then local localUIRoot = UILayerUtil:getLocalUI() local globalUIRoot = UILayerUtil:getGlobalUI() ---@param rootUI UIRoot ---@return boolean local function checkClick(rootUI) local stack = rootUI:getUILayerStack() local len = #stack if len > 0 then local last for i = len, 1, -1 do local layer = stack[i] if layer.__maskClickCb then last = layer break end end if last then local cb = last.__maskClickCb if cb then if last.__maskClickOnce then last.__maskClickCb = nil end cb() return true end end end end util.ugui.addClickEvent(mask, function() if not checkClick(globalUIRoot) then -- 先处理global的click,再处理local的click checkClick(localUIRoot) end end) mask.__listenerAdded = true end self.__maskClickCb = clickCb self.__maskClickOnce = once return self end function UILayer:hideMask() local mask = self[UI_MASK_NAME] if mask then UnityEngine.GameObject.Destroy(mask) self[UI_MASK_NAME] = nil end UILayerUtil:refreshMask() end function UILayer:enableCloseWhenClickMask(cb) self:setMaskClickCb(function() if cb then cb() end self:close() end, true) return self end ---@private function UILayer:__saveEnabledRaycastTargets() if self.__uiContainer then self.__enabledRaycastTargets__ = {} local coms = self.__uiContainer:GetComponentsInChildren(typeof(UGUI.MaskableGraphic)) for _, com in cs_ipairs(coms) do table.insert(self.__enabledRaycastTargets__, com) end end end function UILayer:disableAllUITouch() if self.__uiContainer then self:__saveEnabledRaycastTargets() local coms = self.__uiContainer:GetComponentsInChildren(typeof(UGUI.MaskableGraphic)) for _, com in cs_ipairs(coms) do if com.gameObject ~= self[UI_MASK_NAME] then if com.raycastTarget then com.raycastTarget = false end end end end end function UILayer:enableAllUITouch() if self.__uiContainer then -- 只enable默认打开touch的 for _, com in ipairs(self.__enabledRaycastTargets__ or {}) do com.raycastTarget = true end end end function UILayer:close(dontDestroy) self.dontDestroyOnExit = dontDestroy self:exit() return self end function UILayer:onExit() printInfo("UILayer", "onExit of %s", self.__cls_name) local dontDestroy = self.dontDestroyOnExit if not self.__rootUI then super.onExit(self) return end self.__uiContainerUpateId.remove() -- 关闭所有ui事件 self:disableAllUITouch() local bNeedDestroy = true for i = #(self.__closeCbs or {}), 1, -1 do if self.__closeCbs[i](self) then bNeedDestroy = false break end end if self.__tweenOnClose then self:__runTweenFade(1, 0, function() UnityEngine.GameObject.Destroy(self.__uiContainer) end) elseif not dontDestroy and bNeedDestroy then UnityEngine.GameObject.Destroy(self.__uiContainer) end self.__maskClickCb = nil local rootUI = self.__rootUI self.__rootUI = nil rootUI:removeUILayer(self) UILayerUtil:refreshMask() Msg.send(Msg.UI_LAYER_ON_EXIT, self) super.onExit(self) end -- 在onLoad中设置层级,会先调用onShow的setPriority,导致onLoad排序的时候后打开的ui层级index偏小 -- 需在ctor中设置 function UILayer:setPriority(priority) self.__uiContainer.__priority = priority self.__priority = priority if not self.__rootUI then return self end local parent = self.__uiContainer.transform.parent if not parent then return self end local list = {} local childCount = parent.childCount -- 遍历所有子节点 for i = 0, childCount - 1 do -- child 是 transform local child = parent:GetChild(i) table.insert(list, {transform = child, index = i, priority = child.gameObject.__priority}) end table.stableSort(list, function(l, r) if l.priority == r.priority then return l.index < r.index else return l.priority < r.priority end end) for i, content in ipairs(list) do content.transform:SetSiblingIndex(i-1) end return self end function UILayer:setVisible(b) if self.__uiContainer then self.__uiContainer:SetActive(b) end return self end function UILayer:setTag(tag) self.__tag = tag Msg.send(Msg.UI_LAYER_SET_TAG, self) return self end function UILayer:setStatTag(tag) self.__statTag = tag return self end function UILayer:setStatResource(resource) self.__resource = resource return self end function UILayer:getResource() return self.__resource end function UILayer:addCloseCallback(cb) self.__closeCbs = self.__closeCbs or {} table.insert(self.__closeCbs, cb) return self end function UILayer:removeCloseCalback(cb) if self.__closeCbs then table.removeByValue(self.__closeCbs, cb, true) end end ---@private ---更新 ExtendedRectTransform DeviceSafeArea=true 的子节点,适配 真正的安全框(留海屏) function UILayer:_refreshDeviceSafeArea() local rect,offsetMin, offsetMax = util.ugui.getSafeAreaRectInCanvasSpace(nil, UILayerUtil:getCanvas(), UILayerUtil:getCamera()) local coms = self.__uiContainer:GetComponentsInChildren(typeof(CS.ExtendedRectTransform)) for _, com in cs_ipairs(coms) do local child = com.gameObject local rectTransform = child[RectTransform] rectTransform.anchorMin = Vector2(0, 0) -- 设置左下角的 Anchor rectTransform.anchorMax = Vector2(1, 1) -- 设置右上角的 Anchor rectTransform.offsetMin = offsetMin rectTransform.offsetMax = offsetMax end end -- 手动设置,一般不需要,会根据priority排序,除非有特殊需求 function UILayer:setSiblingIndexMaunally(index) self.__uiContainer.transform:SetSiblingIndex(index) return self end function UILayer:getSceneArgs() return UILayerUtil:getCurScene():getSceneArgs() end ---@private ---响应屏幕尺寸变化 function UILayer:__resize() local __uinode = self.__uinode local canvas = __uinode:Seek("UICanvas") if canvas then UILayerUtil:calcCanvasScalerFactor(canvas) end self:_refreshDeviceSafeArea() end ---@private function UILayer:__update() if self.__uiPreScreenSize == nil then self.__uiPreScreenSize = UnityEngine.Vector2(UnityEngine.Screen.width, UnityEngine.Screen.height) else if self.__uiPreScreenSize.x ~= UnityEngine.Screen.width or self.__uiPreScreenSize.y ~= UnityEngine.Screen.height then self.__uiPreScreenSize = UnityEngine.Vector2(UnityEngine.Screen.width, UnityEngine.Screen.height) self:__resize() end end self:__tryUpdateKeyboardAdapt() end ---@private function UILayer:__tryUpdateKeyboardAdapt() if not self.isKeyboardAdaptEnable then return end local eventSystem = UILayerUtil:_getEventSystem() local go = eventSystem.currentSelectedGameObject if CS.LuaHelper.IsNull(go) or (not go) or (not go:GetComponent(typeof(UGUI.InputField))) or (not go:GetComponent(typeof(UGUI.InputField)).touchScreenKeyboard) then self.curInputFieldGo = nil self.__uiContainer.transform.localPosition = self.originalPos return end if go ~= self.curInputFieldGo then self.curInputFieldGo = go local trans = go.transform self.curInputFieldGoOriginalH = util.ugui.localSpaceToScreenSpace(UnityEngine.Vector2(0, -trans.rect.height / 2), trans, UILayerUtil:getCamera()).y end if not UnityEngine.TouchScreenKeyboard.area then return end local h = UnityEngine.TouchScreenKeyboard.area.height local dh = h - self.curInputFieldGoOriginalH if dh <= 0 then self.__uiContainer.transform.localPosition = self.originalPos return end local p = util.ugui.screenSpaceToLocalSpace(UnityEngine.Vector2(0, dh), self.__uiContainer:GetParent().transform, UILayerUtil:getCamera()) self.__uiContainer.transform.localPosition = self.originalPos + UnityEngine.Vector3(0, p.y, 0) end ---@private function UILayer:__runTweenScale(srcScale, dstScale) if self._pauseList then return end self.__tweenOnOpenGo:SetScalef(srcScale) self:disableAllUITouch() self.__tweenOnOpenGo:RunAction(ua.Sequence({ua.ease.BackOut(ua.ScaleTo(0.3, dstScale)), ua.cb(function() self:enableAllUITouch() end)})) end ---@private function UILayer:__runTweenFade(srcAlpha, dstAlpha, cb) local coms = self.__tweenOnOpenGo:GetComponentsInChildren(typeof(UGUI.Graphic)) local function setAlpha(alpha) for _, com in cs_ipairs(coms) do local color = com.color color.a = alpha com.color = color end end self:disableAllUITouch() setAlpha(srcAlpha) self.__tweenOnCloseGo:RunAction(ua.Sequence({ua.Tween(0.2, function(r) local alpha = srcAlpha * (1 - r) + dstAlpha * r setAlpha(alpha) return true end), ua.cb(function() setAlpha(dstAlpha) if cb then cb() end end)})) end MainScene--[[ 主场景,默认显示餐厅地图 author:{zhangpeng} time:2025-06-04 14:54:07 ]] local MainScene, super = defClass("MainScene", Scene) require("modules/ui/mainui/MainViewUI") local LOGTAG = "MainScene" function MainScene:ctor(sceneInfo) super.ctor(self) self.sceneInfo = sceneInfo self.args = sceneInfo.args or {} self.R = ResLoader.loadResLink("modules/restaurant/restaurantscenereslink") self.customerR = ResLoader.loadResLink("modules/role/customer/customerreslink") self.employeR = ResLoader.loadResLink("modules/role/employee/employerreslink") self.coinResLink = ResLoader.loadResLink("modules/currency/currencyreslink") printInfo(LOGTAG,"MainScene:ctor R:%s", self.R) end function MainScene:info() return { trans = nil, asset = self.R, } end function MainScene:onLoad() super.onLoad(self) MapsMgr:setScene(self) -- 初始化触摸 self:initTouch() -- 初始化数据 self:initData() -- 初始化管理器 self:initMgr() -- 初始化地图 self:initMapView() -- 初始化UI self:initUI() -- 启动游戏流程 self:startGame() end -- 初始化触摸 function MainScene:initTouch() self.touchCom = TouchCom.new(self.rootNode, GameUtil:getCamera()) self.touchCom:disable() end -- 初始化数据 function MainScene:initData() -- 初始化用户数据 UserDataMgr:init() -- 初始化建筑数据 -- 初始化角色数据额 end -- 初始化UI function MainScene:initUI() self.mainViewUI = MainViewUI.new() self.mainViewUI:show() end -- 初始化地图 function MainScene:initMapView() -- 餐厅地图 local _restaurant_mapNode = self.rootNode:Seek(MapsConst.mapNodeNames[MapsConst.MapType.restaurant]) MapsMgr:createMap(MapsConst.MapType.restaurant, self, _restaurant_mapNode) -- 音乐烤吧地图 -- local _musicbbq_mapNode = self.rootNode:Seek(MapsConst.mapNodeNames[MapsConst.MapType.music_bar]) -- MapsMgr:createMap(MapsConst.MapType.music_bar, self, _musicbbq_mapNode) -- printInfo(LOGTAG, "地图初始化完成") -- 钓鱼岛地图 local _fishing_mapNode = self.rootNode:Seek(MapsConst.mapNodeNames[MapsConst.MapType.restaurant]) --local _fishing_mapNode = self.rootNode:Seek(MapsConst.mapNodeNames[MapsConst.MapType.fishing_island]) MapsMgr:createMap(MapsConst.MapType.fishing_island, self, _fishing_mapNode) printInfo(LOGTAG, "地图初始化完成") end -- 初始化并启动各个管理器 function MainScene:initMgr() -- BonusMgr:init() CurrencyMgr:init() CuisineMgr:init() -- 初始化建筑 BuildingMgr:init() -- OrderDishesMgr:init() -- CoinDeskMgr:init() -- 员工 EmployeMgr:init() -- 顾客 CustomerMgr:init() -- 特殊顾客 SpecialCustomerMgr:init() -- 摊主 VendorMgr:init() -- 玩偶 DollMgr:init() -- 初始化任务管理器 TaskMgr:init() -- 解锁 UnlockMgr:init() -- 音乐初始化 AudioMgr:init() -- 初始化引导管理器 GuideMgr:init() end -- 开始餐厅流程 function MainScene:startGame() -- 启动各个管理器 -- 加成 BonusMgr:start() -- 货币 CurrencyMgr:start() -- 顾客 CustomerMgr:start() -- 摊主 VendorMgr:start() -- 添加一个可点击跟随的测试顾客到地图上 -- CustomerMgr:createTestCustomer() -- 添加一些测试员工到地图 -- EmployeMgr:addEmployeesToScene() -- 添加一个测试小偷到地图上 -- SpecialCustomerMgr:addThiefToScene() -- 启动餐厅管理器 RestaurantMgr:start() self.touchCom:enable() -- EmployeMgr:startGame() -- 启动任务管理器 TaskMgr:start() -- 解锁 UnlockMgr:start() -- 启动引导 -- GuideMgr:startMainGuide() end function MainScene:defineComs() super.defineComs(self) self.guideCom = GuideCom.new(self) end function MainScene:onExit() -- 清理地图资源todo:: super.onExit(self) end return MainScene main?-- data require("modules/fishing/data/FishingUserData") require("modules/fishing/data/FishermanInfo") -- tool require("modules/fishing/tool/BezierMover") -- minigame require("modules/fishing/minigame/main") -- mgr require("modules/fishing/mgr/FishingMgr") -- map require("modules/fishing/map/FishingMap") VendorInfoh-- 摊主信息 local VendorInfo = defClass("VendorInfo") -- log local LOGTAG = "VendorInfo" -- local Sprite = typeof(CS.UnityEngine.Sprite) -- 构造函数 function VendorInfo:ctor(vendorId) self.id = vendorId self:init() end -- 初始化 function VendorInfo:init() self:initData() self:initStatus() end -- 初始化数据 function VendorInfo:initData() -- 摊主配置 self.config = self:getVendorConfig(self.id) end -- 初始化状态 function VendorInfo:initStatus() -- 摊主来访状态 self.state = VendorMgr.userData:getVendorVisitState(self.id) -- 摊主来访次数 self.visitCount = VendorMgr.userData:getVendorVisitCount(self.id) end -- 获取摊主配置 function VendorInfo:getVendorConfig() return VendorCfgParse:getVendorCfg(self.id) end -- 获取id function VendorInfo:getId() return self.id end -- 获取配置 function VendorInfo:getConfig() return self.config end -- 获取名称 function VendorInfo:getName() return self.config.name end -- 获取性格 function VendorInfo:getNature() return self.config.nature end -- 获取描述 function VendorInfo:getDesc() return self.config.desc end -- 获取剧情id function VendorInfo:getStoryId() return self.config.storyId end -- 获取来访星数 function VendorInfo:getVisitNeedStar() return self.config.visiteStar end --- 来访要求星数描述 function VendorInfo:getVisitNeedStarDesc() local str = "来访需要人气达到%s" return string.format(str, self.config.visitNeedStar) end -- 获取喜欢的鱼 function VendorInfo:getFavoriteFish() return self.config.likefishish end -- 获取喜欢的鱼描述 function VendorInfo:getFavoriteFishDesc(isVisited) local str = "喜欢的鱼:" local data = FishCfgParse:getFishCfgListByIds(self:getFavoriteFish()) local list = {} for _, v in pairs(data) do -- 判断是否解锁 if isVisited then table.insert(list, v.name) else table.insert(list, "???") end end return self:getTagDesc(str, list) end -- 获取租金货币类型 function VendorInfo:getCurrencyType() return self.config.currencyType end -- 获取租金数量下限 function VendorInfo:getCountMin() return self.config.countMin end -- 获取租金数量上限 function VendorInfo:getCountMax() return self.config.countMax end -- 获取随机租金 function VendorInfo:getRandomCount() return math.random(self.config.countMin, self.config.countMax) end -- 获取星星奖励下限 function VendorInfo:getStarMin() return self.config.starMin end -- 获取星星奖励上限 function VendorInfo:getStarMax() return self.config.starMax end -- 获取随机星星奖励 function VendorInfo:getRandomStar() return math.random(self.config.starMin, self.config.starMax) end -- 获取顾客消费下限 function VendorInfo:getConsumeMin() return self.config.consumeMin end -- 获取顾客消费上限 function VendorInfo:getConsumeMax() return self.config.consumeMax end -- 获取随机顾客消费 function VendorInfo:getRandomConsume() return math.random(self.config.consumeMin, self.config.consumeMax) end -- 获取出摊时间 function VendorInfo:getWorkTime() return self.config.worktime end -- 获取顾客生成间隔间隔 function VendorInfo:getDeltaTime() return self.config.deltatime end -- 获取艺术头像资源 function VendorInfo:getBustIconPath() return string.format("Assets/AssetsPackage/Res/modules/roles/vendor/images/bust/%s.png", self.config.artBustRes) end -- 获取SpineUI路径 function VendorInfo:getSpineUIPath() return string.format("Assets/AssetsPackage/Res/modules/roles/vendor/prefabs/ui/vendor_%s.png", self.id) end -- 获取图片资源 function VendorInfo:getImgRes(path) if ResLoader.hasAsset(path) then return ResLoader.loadAsset(path, Sprite) end return nil end -- 获取预制体资源 function VendorInfo:getPrefabRes(path) if ResLoader.hasAsset(path) then return ResLoader.loadAsset(path) end return nil end -- 获取半身图标 function VendorInfo:getBustIcon() return self:getImgRes(self:getBustIconPath()) end -- 获取SpineUI资源 function VendorInfo:getSpineUI() return self:getPrefabRes(self:getSpineUIPath()) end function VendorInfo:getIncomeDesc() return "" end -- 获取来访状态 function VendorInfo:getVisitState() return self.state end -- 是否已来访 function VendorInfo:isVisited() return self.state >= VendorConst.VendorVisitState.Visited end -- 是否已查看 function VendorInfo:isViewed() return self.state >= VendorConst.VendorVisitState.Viewed end -- 是否已分享 function VendorInfo:isShared() return self.state >= VendorConst.VendorVisitState.Shared end -- 设置来访状态 function VendorInfo:setVisitState(state) self.state = VendorMgr.userData:setVendorVisitState(self.id, state) end -- 获取来访次数 function VendorInfo:getVisitCount() return self.visitCount end -- 增加来访次数 function VendorInfo:addVisitCount(count) self.visitCount = VendorMgr.userData:addVendorVisitCount(self.id, count) end -- tool --- 处理标签字符串 function VendorInfo:getTagDesc(base, list) if list then for _, v in pairs(list) do base = base .. "" .. v .. " " end return base end return nil end return VendorInfo TaskCfgParse --[[ 任务配置解析 author:{zhangpeng} time:2025-05-26 11:48:55 ]] local TaskCfgData = require("data/config/taskCfg") local TaskCfgParse = defClassStatic("TaskCfgParse") local LOGTAG = "TaskCfgParse" -- 任务达成类型 TaskCfgParse.TaskReachType = { -- 获得一定数量星星 get_star = 1, -- 招待一定数量顾客 entertain_customer_count = 2, -- 收入金币累计达到 income_coin = 3, -- 解锁菜品 unlock_cuisine = 4, -- 解锁设施 unlock_building = 5, -- 解锁顾客数量 unlock_customer = 6, -- 累计宣传N次 promote_count = 7, -- 累计完成视频宣传次数 promote_video_count = 8, -- 累计观看广告达到N次 watch_ad_count = 9, -- 解锁烧烤菜达到N次 unlock_cuisine_count = 10, -- 累计完成扭蛋N次 use_eggs_count = 11, -- 与特殊顾客互动次数达到 interact_special_customer_count = 12, -- 累计钓鱼次数达到 fish_count = 13, -- 获得音符数量累计达到 get_note_count = 14, -- 解锁员工累计达到 unlock_employee_count = 15, -- 访问好友餐厅次数累计达到 visit_friend_count = 16, -- 招揽摊主累计达到 recruit_count = 17, -- 招揽顾客累计达到 recruit_customer_count = 18, -- 解锁场景累计达到 unlock_scene_count = 19, -- 任务进度累计达到 task_progress_count = 20, -- 招待指定顾客的次数达到 recruit_specific_customer_count = 21, -- 招揽指定顾客的次数达到 entertain_specific_customer_count = 22, } local task_cfg = {} function TaskCfgParse:init() for k, v in pairs(TaskCfgData) do table.insert(task_cfg, v) end end function TaskCfgParse:getTaskCfg(id) for _, v in pairs(task_cfg) do if v.id == id then return v end end return nil end function TaskCfgParse:getTaskCfgList() return task_cfg end -- 找出任务类型 function TaskCfgParse:getTaskListByType(taskType) local task_list = {} for _, v in pairs(task_cfg) do if v.taskType == taskType then table.insert(task_list, v) end end return task_list end -- 根据完成条件类型类型获取任务列表 function TaskCfgParse:getTaskBootCfgListByTerms(completeCond) for k, v in pairs(TaskCfgParse.TaskReachType) do if v == completeCond then return k end end return nil end -- 获取与当前任务类型相同的下一任务 function TaskCfgParse:getNextTaskByType(currentTaskId) local currCfg = self:getTaskCfg(currentTaskId) if currCfg.nextTaskId and (currCfg.nextTaskId ~= -1) then return self:getTaskCfg(currCfg.nextTaskId) end return nil end TaskCfgParse:init() commonBonusType--[[ from file:通用加成类型.xlsx --]] local commonBonusType = { [1] = { id = 2001, type = "star", desc = "提高人气 +%d", }, [2] = { id = 2002, type = "tip", desc = "增加小费收入 +%d/每分钟", }, [3] = { id = 2003, type = "tipTime", desc = "小费收入最大累计时间增加%d小时", }, [4] = { id = 2004, type = "consume", desc = "客人每次消费可获得收入+%d", }, [5] = { id = 2005, type = "cook", desc = "提高做菜效率%d%%", }, [6] = { id = 2006, type = "extra30s", desc = "每隔30秒可获得额外收入+%d", }, [7] = { id = 2007, type = "grill", desc = "每小时自动收入+%d", }, [8] = { id = 2008, type = "music", desc = "每日音符收入+%d", }, [9] = { id = 2009, type = "move", desc = "移动速度+%d", }, [10] = { id = 2010, type = "musiccount", desc = "歌曲数量+%d", }, [11] = { id = 2011, type = '', desc = "扭蛋次数", }, } return commonBonusType BuildingMgreX--[[ 建筑管理 author:zhangpeng time:2025-05-23 18:23:19 ]] local BuildingMgr = defClassStatic("BuildingMgr") local LOGTAG = "BuildingMgr" function BuildingMgr:init() self:initEvent() self:initUserData() self:initBuildingInfoData() -- 初始化建筑实例列表 self.buildingInstances = {} end function BuildingMgr:startGame() self:setEvent() end -- 初始化事件 function BuildingMgr:initEvent() self.buildBuyEvent = SimpleEvent.new("BuildBuyEvent") end -- 初始化用户数据 function BuildingMgr:initUserData() self.userData = UserDataMgr.buildingUserData end -- 初始化所有建筑数据 function BuildingMgr:initBuildingInfoData() self.infoList = {} -- 预加载所有建筑配置,创建BuildingInfo对象 local allBuildingCfgList = BuildingCfgParse:getAllBuildingCfgs() if allBuildingCfgList then for _, cfg in pairs(allBuildingCfgList) do local buildingInfo = BuildingInfo.new(cfg.uid) self.infoList[cfg.uid] = buildingInfo end printInfo(LOGTAG, "初始化了 %d 个建筑配置数据", table.count(self.infoList)) end -- 把存档数据设置上 self:applyUserDataToInfoList() end -- 设置事件 function BuildingMgr:setEvent() -- todo 此判断绑定为场景解锁事件 local bbqNeedStar = SceneCfgParse:getSceneCfgById(MapsConst.MapType.music_bar).unlockStarNum if bbqNeedStar > CurrencyMgr:getStar() then BuildingMgr:setBuildingCashierInuse() end end -- 设置收银台使用状态 function BuildingMgr:setBuildingCashierInuse() self.userData:getBuildingUseState(BuildingConst.buildingType.cashier) end -- 将用户数据应用到建筑信息中 function BuildingMgr:applyUserDataToInfoList() local userBuildingList = self.userData:getBuildingList() for buildingCfgId, userBuildingInfo in pairs(userBuildingList) do local buildingInfo = self.infoList[buildingCfgId] if buildingInfo then if buildingInfo:isInUse() then printInfo(LOGTAG, "建筑 %s 在存档中是使用状态", buildingInfo:getName()) end -- 应用用户的购买/使用状态 buildingInfo:initDynamicData(userBuildingInfo) end end printInfo(LOGTAG, "应用了 %d 个用户建筑数据", table.count(userBuildingList)) end -- 根据id创建地图上显示的建筑 function BuildingMgr:createBuildingInMap(buildingId) local buildingInfo = self:getBuildingInfo(buildingId) if buildingInfo then local createdBuilding = nil -- 餐桌 if self:isDiningDeskBuilding(buildingInfo.buildingType) then createdBuilding = DiningDeskMgr:createDesk(buildingInfo) -- 灶台 elseif self:isCookingBenchBuilding(buildingInfo.buildingType) then createdBuilding = CookingBenckMgr:createCookingBench(buildingInfo) -- 迎宾台 elseif self:isReceptionBuilding(buildingInfo.buildingType) then createdBuilding = ReceptionMgr:createReception(buildingInfo) -- 闲逛类 elseif self:isPassivityBuilding(buildingInfo.buildingType) then createdBuilding = PassivityDeskMgr:createPassivityDesk(buildingInfo) -- 被动收益类 elseif self:isPassiveIncomeBuilding(buildingInfo.buildingType) then createdBuilding = PassivityIncomeDeskMgr:createPassiveIncomeDesk(buildingInfo) -- 舞台 elseif self:isStageBuilding(buildingInfo.buildingType) then createdBuilding = StageDeskMgr:createStageDesk(buildingInfo) -- 烤架建筑 elseif self:isBbqBuilding(buildingInfo.buildingType) then createdBuilding = BbqDeskMgr:createBbqDesk(buildingInfo) -- 扭蛋机 elseif self:isGachaBuilding(buildingInfo.buildingType) then createdBuilding = GachaDeskMgr:createGachaDesk(buildingInfo) end -- 将创建的建筑实例加入到总列表中 if createdBuilding then if not self.buildingInstances[buildingId] then self.buildingInstances[buildingId] = createdBuilding end printInfo(LOGTAG, "建筑实例已加入总列表: %s[%d]", buildingInfo:getName(), buildingId) end -- 创建建筑后,自动打开对应区域的阻挡 self:updateBuildingBlockForCreatedBuilding(buildingInfo) return createdBuilding end return nil end -- 为新创建的建筑更新阻挡状态 function BuildingMgr:updateBuildingBlockForCreatedBuilding(buildingInfo) -- 获取餐厅地图实例 local restaurantMap = MapsMgr:getMap(MapsConst.MapType.restaurant) if restaurantMap and restaurantMap.updateBuildingBlock then -- 打开建筑所在区域的阻挡 (isOpen=true) restaurantMap:updateBuildingBlock(buildingInfo.buildingType, true) printInfo(LOGTAG, string.format("建筑创建完成,已打开阻挡区域: %s", buildingInfo.buildingType)) else printWarn(LOGTAG, "无法获取餐厅地图实例或updateBuildingBlock方法不存在") end end -- 是否是餐桌类建筑 function BuildingMgr:isDiningDeskBuilding(buildingType) for k,v in pairs(DiningConst.diningDeskIds) do if v == buildingType then return true end end return false end -- 是否是灶台类建筑 function BuildingMgr:isCookingBenchBuilding(buildingType) for k,v in pairs(CookingConst.cookingBenchIds) do if v == buildingType then return true end end return false end -- 是否是迎宾台建筑 function BuildingMgr:isReceptionBuilding(buildingType) return buildingType == BuildingConst.buildingType.reception end -- 是否是闲逛类建筑 function BuildingMgr:isPassivityBuilding(buildingType) for k,v in pairs(PassivityConst.ids) do if v == buildingType then return true end end return false end -- 是否是舞台建筑 function BuildingMgr:isStageBuilding(buildingType) return buildingType == BuildingConst.buildingType.stage end -- 是否是烤吧收银台建筑 function BuildingMgr:isCashierBuilding(buildingType) return buildingType == BuildingConst.buildingType.cashier end -- 是否是扭蛋机建筑 function BuildingMgr:isGachaBuilding(buildingType) return buildingType == BuildingConst.buildingType.gashapon end -- 是否是被动收益类建筑 function BuildingMgr:isPassiveIncomeBuilding(buildingType) for k,v in pairs(PassivityIncomeConst.ids) do if v == buildingType then return true end end return false end -- 是否是摊位建筑 function BuildingMgr:isStallBuilding(buildingType) for k,v in pairs(StallConst.ids) do if v == buildingType then return true end end return false end -- 是否是烤架建筑 function BuildingMgr:isBbqBuilding(buildingType) return buildingType == BuildingConst.buildingType.bbqgrill1 or buildingType == BuildingConst.buildingType.bbqgrill2 or buildingType == BuildingConst.buildingType.bbqgrill3 end -- 根据存档初始化地图上建筑 function BuildingMgr:initRestaurantBuildingView() DiningDeskMgr:init() CookingBenckMgr:init() ReceptionMgr:init() PassivityDeskMgr:init() PassivityIncomeDeskMgr:init() StallDeskMgr:init() StageDeskMgr:init() BbqDeskMgr:init() CashierDeskBbqMgr:init() GachaDeskMgr:init() -- local test_building_list = { 100101, 100301, 100401, 100501 } -- 餐桌 -- local test_building_list = { 101501, 101601, 102101, 102401 } -- 灶台+水吧台 -- local test_building_list = { 100201 } -- 迎宾台 -- local test_building_list = { 100901, 101001, 101101 } -- 闲逛建筑 -- local test_building_list = {101501, 101601, 102101, 102401 } -- 被动收益建筑 for _, buildingInfo in pairs(self.infoList) do local isInUse = buildingInfo:isInUse() if isInUse then self:createBuildingInMap(buildingInfo.buildingCfgId) end end -- 创建所有摊位 local stall_list = { [1] = {id = 171, self.userData:getBuildingState(171)}, [2] = {id = 172, self.userData:getBuildingState(172)}, [3] = {id = 173, self.userData:getBuildingState(173)}, [4] = {id = 174, self.userData:getBuildingState(174)}, [5] = {id = 175, self.userData:getBuildingState(175)}, [6] = {id = 176, self.userData:getBuildingState(176)}, } for k,v in pairs(stall_list) do StallDeskMgr:createStallDesk(v) end -- 创建烤吧收银台 CashierDeskBbqMgr:createCashierDesk() -- 创建扭蛋机 -- GachaDeskMgr:createGachaDesk() end --- 获取建筑配置 function BuildingMgr:getConfig(buildingId) return BuildingCfgParse:getBuildingCfg(buildingId) end -- 获取一个建筑数据 --@param buildingCfgId 建筑配置id --@return 建筑数据 function BuildingMgr:getBuildingInfo(buildingCfgId) if self.infoList[buildingCfgId] then return self.infoList[buildingCfgId] end if BuildingMgr:isStallBuilding(buildingCfgId) then local info = StallInfo.new(buildingCfgId) self.infoList[buildingCfgId] = info return info end if BuildingMgr:isCashierBuilding(buildingCfgId) then local info = CashierDeskInfo.new(buildingCfgId) self.infoList[buildingCfgId] = info return info end if BuildingMgr:isGachaBuilding(buildingCfgId) then local info = GachaDeskInfo.new(buildingCfgId) self.infoList[buildingCfgId] = info return info end local info = BuildingInfo.new(buildingCfgId) self.infoList[buildingCfgId] = info return info end -- 获取所有建筑信息 function BuildingMgr:getAllBuildingInfoList() return self.infoList end -- 获取指定分类的建筑信息列表 --@param categoryId 建筑分类ID(餐厅/烤吧) --@return 建筑信息列表 function BuildingMgr:getBuildingInfoListByCategory(categoryId) local result = {} for buildingCfgId, buildingInfo in pairs(self.infoList) do local buildingCategoryId = BuildingTypesCfgParse:getCategoryIdByBuildingTypeId(buildingInfo.buildingType) if buildingCategoryId == categoryId then table.insert(result, buildingInfo) end end return result end -- 获取指定建筑类型的信息列表 --@param buildingType 建筑类型ID --@return 建筑信息列表 function BuildingMgr:getBuildingInfoListByType(buildingType) local result = {} for buildingCfgId, buildingInfo in pairs(self.infoList) do if buildingInfo.buildingType == buildingType then table.insert(result, buildingInfo) end end return result end -- 获取烤吧里已经建造好的建筑 function BuildingMgr:getMusicbbqBuildingInfoList() local result = {} for buildingCfgId, buildingInfo in pairs(self.infoList) do -- 获取建筑所属场景 local mapType = BuildingTypesCfgParse:getCategoryIdByBuildingTypeId(buildingInfo.buildingType) -- 烤吧的已解锁建筑 printInfo(LOGTAG, "buildingInfo.buildingType: %s", buildingInfo.buildingType) if mapType == MapsConst.MapType.music_bar and buildingInfo:isInUse() then table.insert(result, buildingInfo) end end return result end --- 检查建筑是否解锁 ---@param buildingCfgId number 建筑配置id ---@return boolean function BuildingMgr:isBuildingUnlocked(buildingCfgId) return self:getBuildingInfo(buildingCfgId):isUnlocked() end --- 检查建筑是否已购买 ---@param buildingCfgId number 建筑配置id ---@return boolean function BuildingMgr:isBuildingPurchased(buildingCfgId) return self:getBuildingInfo(buildingCfgId):isPurchased() end -- 检查建筑是否在使用中 --@param buildingCfgId 建筑配置id --@return boolean function BuildingMgr:isBuildingInUse(buildingCfgId) return self:getBuildingInfo(buildingCfgId):isInUse() end -- 解锁一个建筑 --@param buildingCfgId 建筑配置id function BuildingMgr:unlockBuilding(buildingCfgId) local buildingInfo = self:getBuildingInfo(buildingCfgId) if buildingInfo then buildingInfo:unlock() self:saveBuildingToUserData(buildingInfo) printInfo(LOGTAG, "解锁建筑: %s[%d]", buildingInfo:getName(), buildingCfgId) -- 触发解锁事件 self:onBuildingUnlocked(buildingInfo) end end -- 购买一个建筑 --@param buildingCfgId 建筑配置id --@return boolean 是否购买成功 function BuildingMgr:buyBuilding(buildingCfgId, shortage) local buildingInfo = self:getBuildingInfo(buildingCfgId) if not buildingInfo then printInfo(LOGTAG, "建筑配置不存在: %d", buildingCfgId) return false end -- 检查是否已解锁 if not self:isBuildingUnlocked(buildingCfgId) then printInfo(LOGTAG, "建筑未解锁,无法购买: %s[%d]", buildingInfo:getName(), buildingCfgId) return false end -- 检查是否已购买 if self:isBuildingPurchased(buildingCfgId) then printInfo(LOGTAG, "建筑已购买: %s[%d]", buildingInfo:getName(), buildingCfgId) return true end -- 检查购买条件(金币等) if not self:checkBuyCondition(buildingInfo) then printInfo(LOGTAG, "购买条件不满足: %s[%d]", buildingInfo:getName(), buildingCfgId) return false end -- 执行购买 buildingInfo:buy() self:saveBuildingToUserData(buildingInfo) self:consumeBuyResources(buildingInfo, shortage or 0) printInfo(LOGTAG, "购买建筑成功: %s[%d]", buildingInfo:getName(), buildingCfgId) -- 增加建筑加成 BonusMgr:addBonusIdByBuildingId(buildingCfgId) -- 触发购买事件 self:onBuildingPurchased(buildingInfo) return true end -- 使用一个建筑 --@param buildingCfgId 建筑配置id --@return boolean 是否使用成功 function BuildingMgr:useBuilding(buildingCfgId) local buildingInfo = self:getBuildingInfo(buildingCfgId) if not buildingInfo then printError(LOGTAG, "建筑配置不存在: %d", buildingCfgId) return false end -- 检查是否已购买 if not self:isBuildingPurchased(buildingCfgId) then printError(LOGTAG, "建筑未购买,无法使用: %s[%d]", buildingInfo:getName(), buildingCfgId) return false end -- 检查同类型建筑是否已在使用(替换逻辑) local currentUsedBuilding = self:getCurrentUsedBuildingByType(buildingInfo.buildingType) if currentUsedBuilding and currentUsedBuilding.buildingCfgId ~= buildingCfgId then -- 取消当前使用的建筑 self:stopUsingBuilding(currentUsedBuilding.buildingCfgId) end -- 执行使用 buildingInfo:use() local usedId = self.userData:getBuildingUseState(buildingInfo.buildingType) local info2 = nil if usedId ~= -1 then info2 = self:getBuildingInfo(usedId) info2:replace() end self:saveBuildingToUserData(buildingInfo, info2) printInfo(LOGTAG, "使用建筑: %s[%d]", buildingInfo:getName(), buildingCfgId) -- 触发使用事件 self:onBuildingUsed(buildingInfo) return true end function BuildingMgr:applyBuilding(buildingCfgId) local buildingInfo = self:getBuildingInfo(buildingCfgId) if not buildingInfo then printError(LOGTAG, "建筑配置不存在: %d", buildingCfgId) return false end -- 触发使用事件 self:onBuildingUsed(buildingInfo) return true end --- 停止使用一个建筑 ---@param buildingCfgId number 建筑配置id function BuildingMgr:stopUsingBuilding(buildingCfgId) local buildingInfo = self:getBuildingInfo(buildingCfgId) if buildingInfo and self:isBuildingInUse(buildingCfgId) then buildingInfo.used = BuildingConst.buildingState.purchased self:saveBuildingToUserData(buildingInfo) printInfo(LOGTAG, "停止使用建筑: %s[%d]", buildingInfo:getName(), buildingCfgId) -- 触发停止使用事件 self:onBuildingStopUsed(buildingInfo) end end --- 替换一个同类型建筑 ---@param buildingCfgId number 新建筑配置id ---@return boolean 是否替换成功 function BuildingMgr:replaceBuilding(buildingCfgId) return self:useBuilding(buildingCfgId) -- 使用建筑时自动处理替换逻辑 end --- 获取当前使用中的指定类型建筑 ---@param buildingType number 建筑类型 ---@return BuildingInfo|nil function BuildingMgr:getCurrentUsedBuildingByType(buildingType) for buildingCfgId, buildingInfo in pairs(self.infoList) do if buildingInfo.buildingType == buildingType and self:isBuildingInUse(buildingCfgId) then return buildingInfo end end return nil end -- 检查购买条件 --@param buildingInfo 建筑信息 --@return boolean function BuildingMgr:checkBuyCondition(buildingInfo) -- 检查是否满足购买条件 local coin = CurrencyMgr:getCoin() - buildingInfo:getBuyPrice() if coin < 0 then PopUpUI.new("金币不足,还差" .. -coin):show():showMask():enableCloseWhenClickMask() return false end if not BuyMgr:canBuy(buildingInfo.buildingBuyConditionId) then PopUpUI.new(BuyMgr:getLimitDescById(buildingInfo.buildingBuyConditionId)):show():showMask():enableCloseWhenClickMask() return false end return true end -- 消耗购买资源 --@param buildingInfo 建筑信息 function BuildingMgr:consumeBuyResources(buildingInfo, shortage) local price = buildingInfo:getBuyPrice() CurrencyMgr:changeCoin(-price + shortage) -- 例如:扣除金币、道具等 printInfo(LOGTAG, "消耗购买资源: %s[%d]", buildingInfo:getName(), buildingInfo.buildingCfgId) end -- 保存建筑数据到用户数据 --@param buildingInfo 建筑信息 function BuildingMgr:saveBuildingToUserData(buildingInfo, buildingInfo2) --local userData = { -- buildingCfgId = buildingInfo.buildingCfgId, -- state = buildingInfo.state --} --self.userData:insertBuilding(userData) self.userData:setBuildingState(buildingInfo.id, buildingInfo.state) if buildingInfo.state == BuildingConst.buildingState.used then if buildingInfo2 then -- 被替换的建筑 self.userData:setBuildingState(buildingInfo2.id, buildingInfo2.state) self.userData:setBuildingUseState(buildingInfo2.buildingType, buildingInfo2.id) else self.userData:setBuildingUseState(buildingInfo.buildingType, buildingInfo.id) end end -- TODO: 触发保存到服务器或本地 end -- 保存所有数据 function BuildingMgr:saveAll() -- TODO: 实现保存到服务器或本地存档 printInfo(LOGTAG, "保存所有建筑数据") self.userData.save() end -- 事件回调:建筑解锁 function BuildingMgr:onBuildingUnlocked(buildingInfo) -- TODO: 触发相关事件,如UI刷新、音效播放等 printInfo(LOGTAG, "建筑解锁事件: %s", buildingInfo:getName()) end -- 事件回调:建筑购买 function BuildingMgr:onBuildingPurchased(buildingInfo) printInfo(LOGTAG, "建筑购买事件: %s", buildingInfo:getName()) -- 显示在地图上 self:createBuildingInMap(buildingInfo.buildingCfgId) self.buildBuyEvent:triggerEvent(buildingInfo.id) -- 任务进度 UserDataMgr:addUnlockBuildingCount(1) end -- 事件回调:建筑使用 function BuildingMgr:onBuildingUsed(buildingInfo) -- TODO: 触发相关事件,如地图建筑显示更新等 printInfo(LOGTAG, "建筑使用事件: %s", buildingInfo:getName()) end -- 事件回调:建筑停止使用 function BuildingMgr:onBuildingStopUsed(buildingInfo) -- TODO: 触发相关事件,如地图建筑显示更新等 printInfo(LOGTAG, "建筑停止使用事件: %s", buildingInfo:getName()) end -- save --- 获取当前使用中的建筑ID function BuildingMgr:getInUseId(buildingType) return self.userData:getBuildingUseState(buildingType) end --- 设置当前使用中的建筑ID function BuildingMgr:setInUseId(buildingType, buildingId) self.userData:setBuildingUseState(buildingType, buildingId) end -- 获取建筑实例 --@param buildingId 建筑配置id --@return 建筑实例对象 function BuildingMgr:getBuildingInstance(buildingId) return self.buildingInstances[buildingId] end -- 获取所有建筑实例 --@return 建筑实例列表 function BuildingMgr:getAllBuildingInstances() return self.buildingInstances end -- 获取所有建筑实例(简洁版本) --@return 建筑实例列表 function BuildingMgr:getAllBuildings() return self.buildingInstances end -- 获取参与排序的建筑 function BuildingMgr:getSortingBuilding() local result = {} for buildingId, instance in pairs(self.buildingInstances) do if not BuildingConst.noSortingBuilding[instance.buildingInfo.buildingType] then table.insert(result, instance) end end return result end -- 移除建筑实例 --@param buildingId 建筑配置id function BuildingMgr:removeBuildingInstance(buildingId) if self.buildingInstances[buildingId] then self.buildingInstances[buildingId] = nil printInfo(LOGTAG, "建筑实例已从总列表移除: %d", buildingId) end end -- 根据建筑类型获取所有实例 --@param buildingType 建筑类型 --@return 建筑实例列表 function BuildingMgr:getBuildingInstancesByType(buildingType) local result = {} for buildingId, instance in pairs(self.buildingInstances) do local buildingInfo = self:getBuildingInfo(buildingId) if buildingInfo and buildingInfo.buildingType == buildingType then table.insert(result, instance) end end return result end -- 获取烤吧里已经建造了的闲逛建筑 function BuildingMgr:getBbqWanderBuilding() local result = {} for buildingId, instance in pairs(self.buildingInstances) do -- 判断建筑类型是否在烤吧闲逛建筑列表中 -- 遍历BuildingConst.bbqWanderBuilding中的建筑大类id for _, buildingType in pairs(BuildingConst.bbqWanderBuilding) do if instance.buildingInfo.buildingType == buildingType then table.insert(result, instance.buildingInfo) break end end end return result end return BuildingMgr PondFishSalesCell -- 池塘鱼贩卖格子 local PondFishSalesCell = defClass("PondFishSalesCell") local LOGTAG = "PondFishSalesCell" local Image = CS.UnityEngine.UI.Image local TMProUGUI = CS.TMPro.TextMeshProUGUI local Sprite = typeof(CS.UnityEngine.Sprite) -- 构造函数 function PondFishSalesCell:ctor(go, fishSalesId, fishCount) self.go = go self.fishSalesId = fishSalesId self.fishCount = fishCount self:init() end -- 初始化 function PondFishSalesCell:init() self:initUI() self:showUI() end -- 初始化UI组件 function PondFishSalesCell:initUI() self.icon = self.go:Seek("icon") self.price = self.go:Seek("price") self.count = self.go:Seek("count") self.buy_btn = self.go:Seek("buy_btn") util.ugui.addClickEvent(self.buy_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:click() end) end -- 展示UI function PondFishSalesCell:showUI() local hasFish = self.fishSalesId and (self.fishSalesId ~= -1) and (self.fishCount > 0) self.icon:SetActive(hasFish) self.count:SetActive(hasFish) self.buy_btn:SetActive(hasFish) if hasFish then local salesConfig = FishSalesCfgParse:getFishSalesCfgById(self.fishSalesId) local fishConfig = FishCfgParse:getFishCfgById(salesConfig.fishId) local path = string.format("Assets/AssetsPackage/Res/modules/fishing/map/image/fishing/%s.png", fishConfig.artRes) if ResLoader.hasAsset(path) then local image = self.icon[Image] image.sprite = ResLoader.loadAsset(path, Sprite) util.ugui:setImageTileSizeToRectangles(image, 110, 220) end self.price[TMProUGUI].text = "".. tostring(salesConfig.price) self.count[TMProUGUI].text = "剩余:" .. tostring(self.fishCount) end end -- 设置数据 function PondFishSalesCell:setData(fishSalesId, fishCount) self.fishSalesId = fishSalesId self.fishCount = fishCount self:showUI() end -- 点击购买按钮 function PondFishSalesCell:click() printInfo(LOGTAG, "点击购买按钮") local salesConfig = FishSalesCfgParse:getFishSalesCfgById(self.fishSalesId) if salesConfig.price > CurrencyMgr:getCoin() then PopUpUI.new("金币不足"):show():showMask():enableCloseWhenClickMask() return end CurrencyMgr:changeCoin(-salesConfig.price) UserDataMgr.fishingUserData:addFishingFishCount(salesConfig.fishId, 1) UserDataMgr.fishingUserData:reducePondVendingCurrentItem(self.fishSalesId, 1) self.fishCount = UserDataMgr.fishingUserData:getPondVendingCurrentItemCount(self.fishSalesId) self:showUI() end return PondFishSalesCell FishingMap)--[[ 捕鱼地图 author:zhengpeng time:2025-06-03 11:41:02 ]] local FishingMap, super = defClass("FishingMap", MapBase) local LOGTAG = "FishingMap" require("modules/ui/pondui/PondFishSalesUI") -- 初始化 function FishingMap:ctor(scene, mapRoot) super.ctor(self, scene, mapRoot) -- 初始化捕鱼特有的功能 self:initFishingNodes() -- 初始化捕鱼游戏 FishingGameMgr:init() end -- 初始化捕鱼特有节点 function FishingMap:initFishingNodes() -- 捕鱼特有的节点 self.boatNode = self.rootNode:Seek("boat") -- 船 self.creel = self.rootNode:Seek("creel_2") -- 鱼篓 local touchCom = FishingMgr:getFishingScene().touchCom touchCom:addListener(self.boatNode:Seek("spine"), TouchCom.LISTENER_TYPE.CLICK, function() self:onBoatClick() end) touchCom:addListener(self.creel:Seek("spine"), TouchCom.LISTENER_TYPE.CLICK, function() self:onCreelClick() end) end -- 船点击 function FishingMap:onBoatClick() printInfo(LOGTAG, "点击了捕鱼船") -- 捕鱼游戏启动 FishingGameMgr:gameEnter() end -- 鱼篓点击 function FishingMap:onCreelClick() printInfo(LOGTAG, "点击了鱼篓") -- 打开鱼篓界面 PondFishSalesUI.new():show():showMask():enableCloseWhenClickMask() end -- 重写父类方法:退出清理 function FishingMap:onExit() -- 清理捕鱼地图特有资源 printInfo(LOGTAG, "清理捕鱼地图资源") super.onExit(self) end return FishingMapPondFishSalesUI -- 鱼塘鱼贩卖界面 local PondFishSalesUI, super = defClass("PondFishSalesUI", UILayer) require("modules/ui/pondui/PondFishSalesCell") local TMProUGUI = CS.TMPro.TextMeshProUGUI local Image = CS.UnityEngine.UI.Image local Sprite = typeof(CS.UnityEngine.Sprite) function PondFishSalesUI:ctor() super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/pondui/ponduireslink") end function PondFishSalesUI:onLoad() self.ui = GameObject.Instantiate(self.R.pond_fish_sales_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end function PondFishSalesUI:initUI() self.close_btn = self.ui:Seek("close_btn") self.time = self.ui:Seek("time")[TMProUGUI] self.cellList = {} self.nextFishList = {} for i = 1, 3 do self.cellList[i] = PondFishSalesCell.new(self.ui:Seek("fish_sales_cell_" .. i)) end for i = 1, 3 do self.nextFishList[i] = self.ui:Seek("next_fish_icon_" .. i) end util.ugui.addClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) end function PondFishSalesUI:showUI() -- 判断当前时间是否为售卖时间 8:00 - 10:00, 12:00 - 14:00, 19:00 - 21:00 local spanList = {{8, 10}, {12, 14}, {19, 21}} local currentTime = os.date("*t") local hour = currentTime.hour local isSellingTime = (hour >= 8 and hour < 10) or (hour >= 12 and hour < 14) or (hour >= 19 and hour < 21) local currItem, nextItem = self:updateItem(isSellingTime, spanList) -- 在售卖时间内,展示鱼贩卖格子 if isSellingTime then local idx = 1 for i, v in pairs(currItem) do self.cellList[idx]:setData(i, v) idx = idx + 1 end end -- 展示下次售卖的鱼 local idx = 1 for i, v in pairs(nextItem) do local salesConfig = FishSalesCfgParse:getFishSalesCfgById(i) local fishConfig = FishCfgParse:getFishCfgById(salesConfig.fishId) local path = string.format("Assets/AssetsPackage/Res/modules/fishing/map/image/fishing/%s.png", fishConfig.artRes) if ResLoader.hasAsset(path) then local image = self.nextFishList[idx][Image] image.sprite = ResLoader.loadAsset(path, Sprite) util.ugui:setImageTileSizeToRectangles(image, 110, 220) end idx = idx + 1 end -- 更新售卖时间描述 local targetDay = 0 local targetHour = -1 if isSellingTime then local targetIdx = self:getInTimeSpanIndex(spanList, hour) targetHour = spanList[targetIdx][2] else local targetIdx = self:getOutTimeSpanIndex(spanList, hour) if spanList[targetIdx + 1] then targetHour = spanList[targetIdx + 1][1] else targetHour = spanList[1][1] -- 如果是最后一个时间段,设置为下一个时间段的开始时间 end end local targetDate = os.date("*t") targetDate.day = targetDate.day + targetDay -- 如果是下一个时间段,可能需要加一天 targetDate.hour = targetHour targetDate.min = 0 targetDate.sec = 0 local targetTime = os.time(targetDate) self.tmr = self:timer(function() self:updateTimeDesc(isSellingTime, targetTime) end, 1, 0) -- 每秒更新一次时间显示 end function PondFishSalesUI:updateTimeDesc(isSellingTime, targetTime) local remaining = targetTime - os.time() if remaining < 0 then remaining = 0 end if isSellingTime then local base = "本期售卖结束: %02d小时%02d分%02d秒" self.time.text = string.format(base, math.floor(remaining / 3600), math.floor((remaining % 3600) / 60), math.floor(remaining % 60)) else local base = "下期售卖开始: %02d小时%02d分%02d秒" self.time.text = string.format(base, math.floor(remaining / 3600), math.floor((remaining % 3600) / 60), math.floor(remaining % 60)) end if remaining <= 0 then self:clear(true, self.tmr) end end -- 更新售卖物品信息表 function PondFishSalesUI:updateItem(isSellingTime, spanList) -- 判断上次更新时间是否在此次售卖时间内 local lastUpdateTime = UserDataMgr.fishingUserData:getPondVendingLastUpdateTime() local now = os.time() local saveDate = os.date("*t", lastUpdateTime) local nowDate = os.date("*t", now) -- 与上次更新时间在同一时间段内 local isCurr = false -- 与上次更新时间相差一个时间段 local isNext = false local saveIdx = -1 local nowIdx = -1 if isSellingTime then saveIdx = self:getInTimeSpanIndex(spanList, saveDate.hour) nowIdx = self:getInTimeSpanIndex(spanList, nowDate.hour) else saveIdx = self:getOutTimeSpanIndex(spanList, saveDate.hour) nowIdx = self:getOutTimeSpanIndex(spanList, nowDate.hour) end -- 判断当天内时间 if saveDate.day == nowDate.day then isCurr = (saveIdx == nowIdx) isNext = (saveIdx + 1 == nowIdx) or (saveIdx == #spanList and nowIdx == 1) else isCurr = false isNext = (saveDate.day + 1 == nowDate.day) and ((saveIdx == #spanList) and (nowIdx == 1)) end -- 不是当前售卖时间,更新售卖数据 if isCurr then return self:getCurrentItemList() end if isNext then return self:getNextItemList() end return self:getNewItemList() end -- 获取全新的售卖物品信息表 function PondFishSalesUI:getNewItemList() -- 当前售卖鱼 local curr = FishSalesCfgParse:getFishSalesCfgListByRandom() UserDataMgr.fishingUserData:setPondVendingLastUpdateTime(os.time()) local export = {} for _, v in ipairs(curr) do export[v.id] = v.count end local currentItem = UserDataMgr.fishingUserData:setPondVendingCurrentItem(export) -- 下次售卖的鱼id列表 local next = FishSalesCfgParse:getFishSalesCfgListByRandom() local nextExport = {} for _, v in ipairs(next) do nextExport[v.id] = v.count end local nextItem = UserDataMgr.fishingUserData:setPondVendingNextItem(nextExport) return currentItem, nextItem end -- 获取当前售卖物品信息表 function PondFishSalesUI:getCurrentItemList() local currentItem = UserDataMgr.fishingUserData:getPondVendingCurrentItem() local nextItem = UserDataMgr.fishingUserData:getPondVendingNextItem() return currentItem, nextItem end -- 获取下一组售卖物品信息表 function PondFishSalesUI:getNextItemList() -- 当前售卖鱼 local curr = UserDataMgr.fishingUserData:getPondVendingNextItem() local currentItem = UserDataMgr.fishingUserData:setPondVendingCurrentItem(curr) -- 下次售卖的鱼id列表 local next = FishSalesCfgParse:getFishSalesCfgListByRandom() local nextExport = {} for _, v in ipairs(next) do nextExport[v.id] = v.count end local nextItem = UserDataMgr.fishingUserData:setPondVendingNextItem(nextExport) return currentItem, nextItem end -- 获取所在开始时间区间索引 function PondFishSalesUI:getTimeSpanIndex(spanList, hour) --local spanList = {{8, 10}, {12, 14}, {19, 21}} for i = 1, #spanList - 1 do if hour >= spanList[i][1] and hour < spanList[i + 1][1] then return i end end if hour >= spanList[#spanList][1] or hour < spanList[1][1] then return #spanList end return -1 -- 不在任何时间段内 end -- 获取所在时间区间内索引 function PondFishSalesUI:getInTimeSpanIndex(spanList, hour) --local spanList = {{8, 10}, {12, 14}, {19, 21}} for i = 1, #spanList do if hour >= spanList[i][1] and hour < spanList[i][2] then return i end end return -1 -- 不在任何时间段内 end -- 获取所在时间区间外索引 function PondFishSalesUI:getOutTimeSpanIndex(spanList, hour) --local spanList = {{8, 10}, {12, 14}, {19, 21}} for i = 1, #spanList - 1 do if hour >= spanList[i][2] and hour < spanList[i + 1][1] then return i end end if hour >= spanList[#spanList][2] or hour < spanList[1][1] then return #spanList end return -1 -- 不在任何时间段内 end return PondFishSalesUICommonBonusTypeParse-- 通用加成类型 local CommonBonusType = require("data/config/commonBonusType") local CommonBonusTypeParse = defClassStatic("CommonBonusTypeParse") local LOGTAG = "CommonBonusTypeParse" -- 初始化 function CommonBonusTypeParse:init() self.idDic = {} for _, v in ipairs(CommonBonusType) do self.idDic[v.id] = v end end -- 获取所有的通用加成类型 function CommonBonusTypeParse:getAllCommonBonusTypes() return CommonBonusType end -- 通过id获取 function CommonBonusTypeParse:getCommonBonusTypeById(bonusTypeId) return self.idDic[bonusTypeId] end -- 通过字符串获取 function CommonBonusTypeParse:getCommonBonusTypeCfgByType(bonusType) if type(bonusType) == "string" then return self:getCommonBonusTypeById(BonusConst.BonusTypeString[bonusType]) end if type(bonusType) == "number" then return self:getCommonBonusTypeById(bonusType) end return nil end CommonBonusTypeParse:init()mainrequire("modules/building/restaurant/eat/DingConst") require("modules/building/restaurant/eat/DiningDesk") require("modules/building/restaurant/eat/DiningDeskSeat") require("modules/building/restaurant/eat/DiningDeskMgr") FishingPropCellR-- 捕鱼道具ui格子 local FishingPropCell = defClass("FishingPropCell") -- log local LOGTAG = "FishingPropCell" -- view require("modules/ui/fishingui/FishingPropUI") -- component local TMProUGUI = CS.TMPro.TextMeshProUGUI -- 构造函数 function FishingPropCell:ctor(go, propType) self.go = go self.propType = propType -- 道具类型 FishingConst.PropType self:initUI() self:showUI() end -- 初始化UI function FishingPropCell:initUI() self.has = self.go:Seek("has") self.has_value = self.has:Seek("value")[TMProUGUI] self.none = self.go:Seek("none") self.mask = self.go:Seek("mask") util.ugui.addClickEvent(self.go, function() self:click() end) end -- 展示UI function FishingPropCell:showUI() local count = FishingGameMgr:getFishingPropCount(self.propType) self:setCount(count) end -- 点击 function FishingPropCell:click() if self.usesRemaining <= 0 then PopUpUI.new("道具已达使用上限"):show():showMask():enableCloseWhenClickMask() return end local count = FishingGameMgr:getFishingPropCount(self.propType) if count <= 0 then FishingGameMgr:gamePause() FishingPropUI.new(self.propType):show():showMask():enableCloseWhenClickMask():addCloseCallback(function() FishingGameMgr:gameContinue() self:showUI() self:updateUsesRemaining() end) return end FishingGameMgr:useFishingProp(self.propType) self:showUI() self:updateUsesRemaining() end -- 设置数量 function FishingPropCell:setCount(count) if count > 0 then self.has:SetActive(true) self.none:SetActive(false) self.has_value.text = tostring(count) else self.has:SetActive(false) self.none:SetActive(true) end end -- 更新使用状态 function FishingPropCell:updateUsesRemaining() self.usesRemaining = FishingGameMgr:getFishingPropUsesRemaining(self.propType) self.mask:SetActive(self.usesRemaining <= 0) end return FishingPropCell CosLuaEnum-- defGlobal("CosLuaServiceType") -- ---@enum CosLuaServiceType -- CosLuaServiceType = { -- UGC = 2, -- AVATAR = 6666, -- } UpgradeConst--- --- local UpgradeConst = defClassStatic("UpgradeConst") function UpgradeConst:init() end UpgradeConst.CuisineLimitType = { Star = 1001, -- 星级限制 Build = 1002, -- 建筑限制 Task = 1003, -- 任务限制 Scene = 1004, -- 场景限制 Tag = 1005, -- 标签限定 Time = 1006, -- 时间限制 Employee = 1007, -- 员工限制 } UpgradeConst.CuisineLimitTypeDesc = { [UpgradeConst.CuisineLimitType.Star] = "人气必须达到%s", [UpgradeConst.CuisineLimitType.Build] = "需要先建造%s", [UpgradeConst.CuisineLimitType.Task] = "需要完成任务%s", [UpgradeConst.CuisineLimitType.Scene] = "需要先解锁%s场景", [UpgradeConst.CuisineLimitType.Tag] = "需要拥有%s标签", -- todo [UpgradeConst.CuisineLimitType.Time] = "需要等待%s秒", -- todo [UpgradeConst.CuisineLimitType.Employee] = "需要拥有%s员工", } UpgradeConst:init()testscenereslinkmreturn { --BASIC --ASSET SCENE = {"Assets/AssetsPackage/Res/modules/test/TestScene.unity", 0, 1}, } main%require("common/debug/debug_main") MainViewUI9--[[ 主界面ui界面 author:{zhangpeng} time:2025-05-19 11:46:13 ]] local MainViewUI, super = defClass("MainViewUI", UILayer) require("modules/ui/employeeui/EmployeeListUI") require("modules/ui/taskui/TaskUI") require("modules/ui/customerui/CustomerListUI") require("modules/ui/settingui/SettingMainUI") require("modules/ui/worldmapui/WorldMapUI") require("modules/ui/cartoonui/CartoonUI") require("modules/ui/buildinglist/buildingsui/BuildingsListUI") require("modules/ui/customerui/VideoPromotionUI") require("modules/ui/taskui/boot/TaskBootDetailUI") require("modules/ui/cuisineui/CuisineListUI") local LOGTAG = "MainViewUI" local UIImage = CS.UnityEngine.UI.Image local RectTransform = CS.UnityEngine.RectTransform function MainViewUI:ctor() super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/mainui/mainviewuireslink") end function MainViewUI:onLoad() self.ui = GameObject.Instantiate(self.R.mainview_ui) self:addChild(self.ui) self:initUI() self:initEvents() end function MainViewUI:initUI() printInfo(LOGTAG, "initUI") -- 金币 self.coin_ui = self.ui:Seek("coin_ui") self.coin_num = self.coin_ui:Seek("num") self.coin_icon = self.coin_ui:Seek("coin") self:updateCoin(CurrencyMgr:getCoin()) -- 星星 self.star_ui = self.ui:Seek("star_ui") self.star_num = self.star_ui:Seek("num") self.star_icon = self.star_ui:Seek("star") self:updateStar(CurrencyMgr:getStar()) -- 音符 self.musical_notes_ui = self.ui:Seek("music_ui") self.musical_notes_num = self.musical_notes_ui:Seek("num") self.musical_notes_icon = self.musical_notes_ui:Seek("icon") self:updateMusicalNotes(CurrencyMgr:getMusicalNotes()) -- 建造按钮 self.build_btn = self.ui:Seek("btn_building") util.ugui.addButtonClickEvent(self.build_btn, function () printInfo(LOGTAG, "建造按钮点击") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:openBuildingListUI() end) -- 菜品按钮 self.dish_btn = self.ui:Seek("btn_cuisine") util.ugui.addButtonClickEvent(self.dish_btn, function () printInfo(LOGTAG, "菜品按钮点击") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:openDishListUI() end) -- 员工按钮 self.employee_btn = self.ui:Seek("btn_employee") util.ugui.addButtonClickEvent(self.employee_btn, function () printInfo(LOGTAG, "员工按钮点击") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:openEmployeeListUI() end) -- 任务按钮 self.task_btn = self.ui:Seek("btn_task_list") if UnlockMgr:getTaskButtonUnlockState() then self.ui:Seek("task_lock"):SetActive(false) end util.ugui.addButtonClickEvent(self.task_btn, function () printInfo(LOGTAG, "任务按钮点击") -- 任务按钮锁定事件 if not UnlockMgr:getTaskButtonUnlockState() then local hint = "解锁任务需要30颗星星" PopUpUI.new(hint):show():showMask():enableCloseWhenClickMask() else -- 去掉锁 self.ui:Seek("task_lock"):SetActive(false) AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) TaskUI.new():show():showMask():enableCloseWhenClickMask() end end) -- 世界地图 self.world_map_btn = self.ui:Seek("worldmap") util.ugui.addButtonClickEvent(self.world_map_btn, function () printInfo(LOGTAG, "世界地图按钮点击") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) --CartoonUI.new():show():showMask() WorldMapUI.new():show():showMask():enableCloseWhenClickMask() --TaskOrderMgr:triggerTargetOrder(400007) end) -- 剧情测试 self.story_btn = self.ui:Seek("btn_story") util.ugui.addButtonClickEvent(self.story_btn, function () printInfo(LOGTAG, "剧情按钮点击") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) StoryDialogMgr:showRoleStoryDialogUI(400003) end) -- 图鉴 self.ill_btn = self.ui:Seek("Illustrations") util.ugui.addButtonClickEvent(self.ill_btn, function () printInfo(LOGTAG, "图鉴按钮点击") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:openCustomerListUI() end) -- 设置 self.setting_btn = self.ui:Seek("setting") util.ugui.addButtonClickEvent(self.setting_btn, function () printInfo(LOGTAG, "设置按钮点击") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) SettingMainUI.new():show():showMask():enableCloseWhenClickMask() end) -- 视频宣传 self.btn_video = self.ui:Seek("btn_video") self.solicit_hint_video = self.btn_video:Seek("solicit_hint") util.ugui.addButtonClickEvent(self.btn_video, function () printInfo(LOGTAG, "视频宣传按钮点击") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) VideoPromotionUI.new():show():showMask():enableCloseWhenClickMask() end) -- 手动宣传 self.btn_flyer = self.ui:Seek("btn_flyer") self.progress_bg = self.ui:Seek("progress_bg")[RectTransform] self.progress_bar = self.ui:Seek("progress_bar")[RectTransform] self.solicit_hint_flyer = self.btn_flyer:Seek("solicit_hint") util.ugui.addButtonClickEvent(self.btn_flyer, function () printInfo(LOGTAG, "手动宣传按钮点击") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) if CustomerQueMgr:isToLimit() then PopUpUI.new("排队人有些多,先招待他们吧"):show():showMask():enableCloseWhenClickMask() return end local count = CustomerMgr.customerSolicitation:solicitation() local limit = CustomerMgr.customerSolicitation.solicitationFlowClickLimit self:hideSolicitButtonHint() self.progress_bg.gameObject:SetActive(count > 0) if count > 0 then local width = count / limit * self.progress_bg.sizeDelta.x self.progress_bar.sizeDelta = Vector2(width, self.progress_bar.sizeDelta.y) end end) -- 任务引导 self.boot_task = self.ui:Seek("btn_boot_task") self.boot_title = self.boot_task:Seek("title") self.boot_value = self.boot_task:Seek("value") self.boot_clear = self.boot_task:Seek("clear") util.ugui.addButtonClickEvent(self.boot_task, function () printInfo(LOGTAG, "任务引导按钮点击") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) TaskBootDetailUI.new(TaskBootMgr:getCurrentTaskId()):show():showMask():enableCloseWhenClickMask() end) self:updateBootTask() -- 右侧列表展开按钮 self.right_list_btn = self.ui:Seek("list_btn") self.right_list_back_btn = self.ui:Seek("list_back_btn") self.arrow_show = self.right_list_back_btn:Seek("arrow_show") self.arrow_hide = self.right_list_back_btn:Seek("arrow_hide") self.list_bg = self.ui:Seek("list_buttons") -- 初始化rank和sign按钮(需要先找到它们在UI中的实际名称) self.rank = self.ui:Seek("rank") -- 如果UI中存在rank节点 self.sign = self.ui:Seek("sign") -- 如果UI中存在sign节点 self.btn_list = {self.task_btn, self.ill_btn, self.rank, self.sign} self.init_pos = {} for index, btn in ipairs(self.btn_list) do self.init_pos[btn.name] = btn.transform.position end util.ugui.addButtonClickEvent(self.rank, function () printInfo(LOGTAG, "rank按钮点击") CartoonUI.new():show():showMask() end) -- 初始化列表状态(默认收起) self.isRightListExpanded = false self:initRightListState() util.ugui.addButtonClickEvent(self.right_list_btn, function () printInfo(LOGTAG, "右侧列表展开按钮点击") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:expandRightList() end) util.ugui.addButtonClickEvent(self.right_list_back_btn, function () AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self.isRightListExpanded = not self.isRightListExpanded if self.isRightListExpanded then self:expandRightList() printInfo(LOGTAG, "右侧列表展开按钮点击") else self:collapseRightList() printInfo(LOGTAG, "右侧列表收起按钮点击") end end) end -- 初始化事件监听 function MainViewUI:initEvents() CurrencyMgr.currencyChangeEvent:addEvent(function (id, currencyType, value, animate) if currencyType == CurrencyConst.CurrencyType.Star then self:updateStar(value, animate) elseif currencyType == CurrencyConst.CurrencyType.Coin then self:updateCoin(value, animate) elseif currencyType == CurrencyConst.CurrencyType.MusicalNotes then self:updateMusicalNotes(value, animate) end end) end function MainViewUI:updateBootTask() local taskId = TaskBootMgr:getCurrentTaskId() if taskId and (taskId ~= -1) then self.boot_task:SetActive(true) local config = TaskBootMgr:getConfig(taskId) local taskClear = TaskBootMgr:checkTaskClear(taskId) self.boot_title:GetComponent("TextMeshProUGUI").text = config.desc self.boot_value:SetActive(not taskClear) self.boot_clear:SetActive(taskClear) if not taskClear then local t = self.boot_value:GetComponent("TextMeshProUGUI") self.boot_value:GetComponent("TextMeshProUGUI").text = TaskBootMgr:getTaskCurrentValue(taskId) .. "/" .. config.count end else self.boot_task:SetActive(false) end end function MainViewUI:updateStar(num, animate) self.star_num:GetComponent("TextMeshProUGUI").text = tostring(num) if animate then if self.star_anim then return --self.star_icon:StopAction(self.star_anim) end self.star_anim = self.star_icon:RunAction(ua.Sequence({ ua.ScaleTo(0.2, 0.8), ua.ScaleTo(0.1, 1.2), ua.ScaleTo(0.3, 1), ua.cb(function() self.star_anim = nil end) })) end end function MainViewUI:updateMusicalNotes(num, animate) self.musical_notes_num:GetComponent("TextMeshProUGUI").text = tostring(num) if animate then self.musical_notes_icon:RunAction(ua.Sequence({ ua.ScaleTo(0.2, 0.8), ua.ScaleTo(0.1, 1.2), ua.ScaleTo(0.3, 1), ua.cb(function() self.musical_notes_anim = nil end) })) end end function MainViewUI:updateCoin(num, animate) self.coin_num:GetComponent("TextMeshProUGUI").text = tostring(num) if animate then if self.coin_anim then return --self.coin_icon:StopAction(self.coin_anim) end self.coin_anim = self.coin_icon:RunAction(ua.Sequence({ ua.ScaleTo(0.2, 0.8), ua.ScaleTo(0.1, 1.2), ua.ScaleTo(0.3, 1), ua.cb(function() self.coin_anim = nil end) })) end end -- 打开建筑列表界面 function MainViewUI:openBuildingListUI() local page = 1 if MapsMgr:getCurrentMapId() == MapsConst.MapType.music_bar then page = 2 end local buildingsListUI = BuildingsListUI.new(page) buildingsListUI:show():showMask():enableCloseWhenClickMask(function() end) self.buildingsListUI = buildingsListUI end -- 打开菜品列表界面 function MainViewUI:openDishListUI() -- 可以获取当前场景直接进入对应界面 分类默认为菜类 local scene = BuildingConst.buildingCategoryId.restaurant if MapsMgr:getCurrentMapId() == MapsConst.MapType.music_bar then scene = BuildingConst.buildingCategoryId.barbecue end local cuisineListUI = CuisineListUI.new(scene, CuisineConst.CuisineType.default) cuisineListUI:show():showMask():enableCloseWhenClickMask(function() end) end -- 打开员工列表界面 function MainViewUI:openEmployeeListUI() local ui = EmployeeListUI.new(EmployeConst.PageType.Employee):show():showMask():enableCloseWhenClickMask(function() end) end -- 打开顾客列表界面 function MainViewUI:openCustomerListUI() local ui = CustomerListUI.new():show():showMask():enableCloseWhenClickMask(function() end) end function MainViewUI:getCoinUI() return self.coin_icon end --- 展示提示 function MainViewUI:showSolicitButtonHint() --self.solicit_hint_video:SetActive(true) self.solicit_hint_flyer:SetActive(true) self.anim = self.solicit_hint_flyer:RunAction(ua.RepeatForever( ua.Sequence({ uiua.AnchoredPosTo(0.5, Vector2(0, 80)), uiua.AnchoredPosTo(0.5, Vector2(0, 50)), }) )) self.timer(self, function () self:hideSolicitButtonHint() end, 5) -- 5秒后隐藏提示 end --- 隐藏提示 function MainViewUI:hideSolicitButtonHint() --self.solicit_hint_video:SetActive(false) self.solicit_hint_flyer:SetActive(false) self.solicit_hint_flyer:StopAction(self.anim) end -- 初始化右侧列表状态 function MainViewUI:initRightListState() end -- 展开右侧列表 function MainViewUI:expandRightList() if self.clickAction then self.list_bg:StopAction(self.clickAction) end self.isRightListExpanded = true self.clickAction = self.list_bg:RunAction(ua.Sequence({ ua.Delay(0.1), -- 延迟0.3秒 uiua.SizeDeltaTo(0.3, Vector2(85, 490)), ua.cb(function() self.clickAction = nil self.arrow_show:SetActive(true) self.arrow_hide:SetActive(false) end) })) end -- 收起右侧列表 function MainViewUI:collapseRightList() if self.clickAction then self.list_bg:StopAction(self.clickAction) end self.isRightListExpanded = false --local transform = self.list_bg:GetComponent(RectTransform) self.clickAction = self.list_bg:RunAction(ua.Sequence({ ua.Delay(0.1), -- 延迟0.3秒 uiua.SizeDeltaTo(0.3, Vector2(85, 55)), ua.cb(function() self.arrow_show:SetActive(false) self.arrow_hide:SetActive(true) self.clickAction = nil end) })) end function MainViewUI:onExit() super.onExit(self) end return MainViewUI CashierDeskBbqMgr--[[ 烤吧收银台管理器 author:{zhangpeng} time:2025-07-08 16:28:22 ]] local CashierDeskBbqMgr = defClassStatic("CashierDeskBbqMgr") local LOGTAG = "CashierDeskBbqMgr" -- init function CashierDeskBbqMgr:init() self.cashierDesks = {} end -- 根据建筑id创建一个灶台 function CashierDeskBbqMgr:createCashierDesk(buildingInfo) local parentNode = RestaurantMgr:getRestaurantScene().rootNode:Seek("building_pos_" .. BuildingConst.buildingType.cashier) local args = { deskId = BuildingConst.buildingType.cashier, parent = parentNode, scene = RestaurantMgr:getRestaurantScene(), } local cashierDesk = CashierDeskBbq.new(args) table.insert(self.cashierDesks, cashierDesk) return cashierDesk end return CashierDeskBbqMgr PaymentMgr--[[ 支付 ]] local PaymentMgr, super = defClassStatic("PaymentMgr") local LOG_TAG = "PaymentMgr" local IOC_FB_UTIL_CLASS_NAME = "PaymentUtil" local JavaClass = "com/fy/xgame/tilelink/billing/BillingManager" function PaymentMgr:init() if Device.isIOS() then ApplePaymentMgr:init() elseif Device.isAndroid() then GooglePaymentMgr:init() end end function PaymentMgr:check() end function PaymentMgr:buyProduct(shopKey) UIComsTool:showLoading() if Device.isIOS() then ApplePaymentMgr:payByProductId(shopKey) elseif Device.isAndroid() then GooglePaymentMgr:payByProductId(shopKey) end end function PaymentMgr:shareLink(url) if Device.isIOS() then local param = {} luaoc.callStaticMethod(IOC_FB_UTIL_CLASS_NAME, "loginWithFacebook") elseif Device.isAndroid() then local function cb(code, msg) if code == 0 then printInfo(LOG_TAG, "分享成功回调") elseif code == 1 then -- Msg.send(Msg.USER_LOGIN_FB_FAILED) elseif code == 2 then -- Msg.send(Msg.USER_LOGIN_FB_CANCLE) end end luaj.callStaticMethod(JavaClass, "shareLink", { url, cb }) end end PaymentMgr:init()SyncMgr ---@class SyncMgr:LuaStaticClass local SyncMgr = defClassStatic("SyncMgr") local LOGTAG = SyncMgr.__cls_name function SyncMgr:init(isGuestFunc) self.isGuestFunc = isGuestFunc self.historySyncTableList = {} self:clear() Msg.add( {Msg.LOGOUT, Msg.LOGIN_USER_SUCCESS}, function(...) self:msgHandler(...) end ) TimerMgr:add( function(dt) self:update(dt) end , nil, 0) end function SyncMgr:msgHandler(msgId, ...) if msgId == Msg.LOGIN_USER_SUCCESS then self:initSyncTable() self:start() elseif msgId == Msg.LOGOUT then self:stop() self:clear() end end function SyncMgr:clear() self.isStart = false self.dbTableDict = {} self:clearForOneSync() end function SyncMgr:clearForOneSync() self.time = 0 self.isSync = false self.syncDBTable = nil end function SyncMgr:initSyncTable() self:clear() local list = SqliteMgr:getDBTableList() for i, v in ipairs(list) do if v.useSync then local list = v:getAllNeedSync() if #list > 0 then self:addSyncTable(v) end --将上次正在上传状态的数据,重置成需要上传状态 local list = v:getAllIsSync() if #list > 0 then for j, record in ipairs(list) do record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) end v:setByList(list) self:addSyncTable(v) end end end end function SyncMgr:addSyncTable(dbTable) table.insert(self.historySyncTableList, dbTable) self.dbTableDict[dbTable:getName()] = dbTable end function SyncMgr:remSyncTable(dbTable) self.dbTableDict[dbTable:getName()] = nil end function SyncMgr:start() if self.isStart then return end if self.isGuestFunc() then return end self.isStart = true end function SyncMgr:stop() if not self.isStart then return end self.isStart = false end function SyncMgr:update(dt) if not self.isStart then return end if self.isSync then self.time = self.time + dt self:checkSyncTimeout() return end for i, dbTable in pairs(self.dbTableDict) do self:doSync(dbTable) break end end function SyncMgr:checkSyncTimeout() if self.time < 15 then return false end self.time = 0 local result = RpcResult.new() result:setError(-1, 0, "timeout, may not call handler in doSync") self:afterSync(result) printWarn(LOGTAG, "update, timeout") return true end function SyncMgr:doSync(dbTable) self:remSyncTable(dbTable) self.isSync = true self.syncDBTable = dbTable local recordList = self.syncDBTable:getAllNeedSync() if #recordList == 0 then printInfo(LOGTAG, "doSync, no data to sync, dbTable:%s", tostring(self.syncDBTable:getName())) self:clearForOneSync() return end for i, record in ipairs(recordList) do record:setSyncState(SqliteTable.SYNC_STATE.IS_SYNCING) end self.syncDBTable:setByList(recordList) self.syncDBTable:push( recordList, function(result) self:afterSync(result) end ) end function SyncMgr:afterSync(result) if (not result) or (not self.isSync) then printInfo(LOGTAG, "afterSync, no data to handle, isSync:%s", tostring(self.isSync)) self:clearForOneSync() return end local recordList = self.syncDBTable:getAllIsSync() if #recordList == 0 then printInfo(LOGTAG, "afterSync, no data after sync, dbTable:%s", tostring(self.syncDBTable:getName())) self:clearForOneSync() return end if not result:getResult() then printInfo(LOGTAG, "afterSync, sync fail %s count:%s", tostring(self.syncDBTable:getName()), #recordList) printInfo(LOGTAG, "afterSync, %s", result:getErrorString()) for i, record in ipairs(recordList) do record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) end else printInfo(LOGTAG, "afterSync, sync success %s count:%s", tostring(self.syncDBTable:getName()), #recordList) for i, record in ipairs(recordList) do record:setSyncState(SqliteTable.SYNC_STATE.HAVE_SYNCED) end end self.syncDBTable:setByList(recordList) self:clearForOneSync() end -- 全量同步 function SyncMgr:syncAll() printInfo(LOGTAG, "syncAll") local list = SqliteMgr:getDBTableList() for i, dbTable in ipairs(list) do if dbTable.useSync then local list = dbTable:getAll() if #list > 0 then dbTable:syncByList(list) end end end end function SyncMgr:printHistorySyncTable() local dict = {} -- printInfo(LOGTAG, "print sync table history beging") for i, dbTable in ipairs(self.historySyncTableList) do local name = dbTable.name -- printInfo(LOGTAG, "seq:%s, name:%s", i, name) dict[name] = dict[name] or 0 dict[name] = dict[name] + 1 end -- printInfo(LOGTAG, "print sync table history finish") printInfo(LOGTAG, "print sync table statistics begin") local totalLength = 0 for name, count in pairs(dict) do printInfo(LOGTAG, "name:%s, count:%s", name, count) end printInfo(LOGTAG, "print sync table statistics finish") end KVDatabase< ---@class KVDatabase:LuaClass local KVDatabase = defClass("KVDatabase") local Path = CS.System.IO.Path local Directory = CS.System.IO.Directory local File = CS.System.IO.File local LOGTAG = KVDatabase.__cls_name local AES_KEY = "a1732436c18c7ac80c29080c9aef1aff" function KVDatabase:ctor(filePath) self.filePath = filePath self.backFilePath = self.filePath .. ".back" end ----------------------------------------- public ------------------------------------------- ---打开数据库。加载文件到内存 ---@return boolean 是否成功 function KVDatabase:open(key) if not self.filePath then return false end self.key = key local str = string.format(self.filePath) local path = Path.GetDirectoryName(str) .. "/" if Directory.Exists(path) == false then Directory.CreateDirectory(path) end self:_load() return true end ---关闭数据库 ---@return boolean function KVDatabase:close() return true end ---删除数据库 function KVDatabase:remove() if File.Exists(self.filePath) then File.Delete(self.filePath) end if File.Exists(self.backFilePath) then File.Delete(self.backFilePath) end end ---获取数据库的表obj ---@param tableCls any 表的类 ---@return SqliteTable 表的 obj function KVDatabase:getTable(tableCls, tableName) tableName = tableName or tableCls.__cls_name local apis = { get = function (...) return self:_get(tableName,...) end, set = function (...) return self:_set(tableName,...) end, getKeys = function (...) return self:_getKeys(tableName) end } local table = tableCls.new(apis, tableName) return table end ----------------------------------------- private ------------------------------------------- ---获取数据库某个表的值,给 table 用 ---@param tableName string 表名 function KVDatabase:_get(tableName, key, defaultValue) local data = self.data local keys = {tableName} if type(key) == "table" and key.__cls_type == nil then table.insertTo(keys, key) else table.insert(keys, key) end for _, key in ipairs(keys) do data = data[key] if data == nil then return defaultValue end end return data end ---设置数据库某个表的值,给 table 用 ---@param tableName string 表名 function KVDatabase:_set(tableName, key, value) local data = self.data local keys = {tableName} if type(key) == "table" and key.__cls_type == nil then table.insertTo(keys, key) else table.insert(keys, key) end for i = 1, #keys - 1 do local key = keys[i] if not data[key] then data[key] = {} end data = data[key] end data[keys[#keys]] = value self:_save() end function KVDatabase:_getKeys(tableName) local data = self.data local tableData = data[tableName] or {} local keys = {} for key, value in pairs(tableData) do table.insert(keys, key) end return keys end ---保存数据到文件 function KVDatabase:_save() printInfo(LOGTAG, "_save") local data = self.data local text = json.encode(data) if self.key then text = CS.AES256.Encrypt(text, self.key) end if string.isEmpty(text) then return end local writeOk = false if CS.System.IO and CS.System.IO.File and CS.System.IO.File.WriteAllText then printInfo(LOGTAG, "filepath:%s", self.filePath) CS.System.IO.File.WriteAllText(self.filePath, text) CS.System.IO.File.Copy(self.filePath, self.backFilePath, true) writeOk = true else printInfo(LOGTAG, "WriteAllText is nil! 请检查导出/裁剪配置,或平台API注入。尝试用Lua io写入。") -- 兼容性写法:用Lua io库写入 local f, err = io.open(self.filePath, "w+b") if f then f:write(text) f:close() writeOk = true -- 备份 local fb, err2 = io.open(self.backFilePath, "w+b") if fb then fb:write(text) fb:close() end else printError(LOGTAG, "Lua io.open 写入失败: " .. tostring(err)) end end return writeOk end local function try(cb,onError) local ok,ret = xpcall(cb,function(err,a,b,c) local tb = debug.traceback() local errstr = tostring(err) .. "\n" .. tb CS.UnityEngine.Debug.LogError(errstr) if onError then onError() end end) end ---加载文件到内存 function KVDatabase:_load() self.data = {} if not File.Exists(self.filePath) then return end local data = {} try( function() data = self:_getDataFromFile(self.filePath) end, function() printInfo(LOGTAG, "_load, 读取存档文件错误:%s", self.filePath) try( function() printInfo(LOGTAG, "_load, 尝试加载备份存档:%s", self.backFilePath) File.Copy(self.backFilePath, self.filePath, true) data = self:_getDataFromFile(self.filePath) dump(data, LOGTAG) end ) end ) self.data = data end function KVDatabase:_getDataFromFile(path) local text = File.ReadAllText(path) if string.isEmpty(text) then return {} end printVerbose(LOGTAG, "getDataFromFile, text: %s", text) if self.key then text = CS.AES256.Decrypt(text, self.key) end local data = json.decode(text) or {} return data endbuildingTypesCfg --[[ from file:建筑类型表.xlsx --]] local buildingTypesCfg = { [1] = { id = 1, typeId = 104, name = "1号餐桌", scene = 1, }, [2] = { id = 2, typeId = 102, name = "1号炉灶", scene = 1, }, [3] = { id = 3, typeId = 106, name = "2号餐桌", scene = 1, }, [4] = { id = 4, typeId = 105, name = "迎宾台", scene = 1, }, [5] = { id = 5, typeId = 103, name = "2号炉灶", scene = 1, }, [6] = { id = 6, typeId = 120, name = "水吧台", scene = 1, }, [7] = { id = 7, typeId = 110, name = "备菜区", scene = 1, }, [8] = { id = 8, typeId = 107, name = "3号餐桌", scene = 1, }, [9] = { id = 9, typeId = 108, name = "4号餐桌", scene = 1, }, [10] = { id = 10, typeId = 123, name = "水池", scene = 1, }, [11] = { id = 11, typeId = 124, name = "3号炉灶", scene = 1, }, [12] = { id = 12, typeId = 119, name = "水果台", scene = 1, }, [13] = { id = 13, typeId = 101, name = "地毯", scene = 1, }, [14] = { id = 14, typeId = 111, name = "碗柜", scene = 1, }, [15] = { id = 15, typeId = 115, name = "露天电影", scene = 1, }, [16] = { id = 16, typeId = 114, name = "酒桶", scene = 1, }, [17] = { id = 17, typeId = 116, name = "冰箱", scene = 1, }, [18] = { id = 18, typeId = 117, name = "货柜", scene = 1, }, [19] = { id = 19, typeId = 122, name = "烤炉", scene = 1, }, [20] = { id = 20, typeId = 121, name = "花架", scene = 1, }, [21] = { id = 21, typeId = 151, name = "1号烧烤架", scene = 2, }, [22] = { id = 22, typeId = 152, name = "舞台", scene = 2, }, [23] = { id = 23, typeId = 153, name = "2号烧烤架", scene = 2, }, [24] = { id = 24, typeId = 154, name = "3号烧烤架", scene = 2, }, [25] = { id = 25, typeId = 155, name = "自助酒水", scene = 2, }, [26] = { id = 26, typeId = 156, name = "乐器", scene = 2, }, [27] = { id = 27, typeId = 157, name = "音箱", scene = 2, }, [28] = { id = 28, typeId = 158, name = "扭蛋机", scene = 2, }, [29] = { id = 29, typeId = 159, name = "烤吧收银台", scene = 2, }, } return buildingTypesCfg CoordUtil'local CoordUtil = {} local Screen = CS.UnityEngine.Screen local Vector3 = CS.UnityEngine.Vector3 local Rect = CS.UnityEngine.Rect local Bounds = CS.UnityEngine.Bounds -- gameObject的坐标转换成坐标系coord下的坐标 function CoordUtil.getPosInCoord(gameObject,coord) local lp = coord.transform:InverseTransformPoint(gameObject.transform.position) return lp end function CoordUtil.getScreenRectInCoord(cam,coord) local bl = cam:ViewportToWorldPoint(Vector3(0, 0, 0)) bl = coord.transform:InverseTransformPoint(bl) local tr = cam:ViewportToWorldPoint(Vector3(1, 1, 0)) tr = coord.transform:InverseTransformPoint(tr) return CS.UnityEngine.Rect(bl.x, bl.y, tr.x - bl.x, tr.y - bl.y) end function CoordUtil.getSafeScreenRectInCoord(cam,coord,isIgnoreBottom) local safeArea = CS.UnityEngine.Screen.safeArea if isIgnoreBottom then safeArea.yMin = 0 end local bl = safeArea.min local tr = safeArea.max bl = CS.UnityEngine.Vector3(bl.x, bl.y, 0) tr = CS.UnityEngine.Vector3(tr.x, tr.y, 0) bl = cam:ScreenToWorldPoint(bl) tr = cam:ScreenToWorldPoint(tr) bl = coord.transform:InverseTransformPoint(bl) tr = coord.transform:InverseTransformPoint(tr) return CS.UnityEngine.Rect(bl.x, bl.y, tr.x - bl.x, tr.y - bl.y) end function CoordUtil.getBoundsInCoord(bounds,coord) local min = coord.transform:InverseTransformPoint(bounds.min) local max = coord.transform:InverseTransformPoint(bounds.max) local lbounds = Bounds(Vector3.zero,Vector3.zero) lbounds:SetMinMax(min,max) return lbounds end -- 计算一个节点的包围盒 function CoordUtil.getNodeBounds(obj) local p_max = Vector3.zero local p_min = Vector3.zero local center = Vector3.zero local mesh = obj:GetComponent(typeof(CS.UnityEngine.Renderer)) if mesh ~= nil then local b = mesh.bounds p_max = b.max p_min = b.min center = b.center end CoordUtil.recursionCalculateBounds(p_max, p_min, obj) if mesh == nil then local xc = (p_max.x + p_min.x) / 2 local yc = (p_max.y + p_min.z) / 2 local zc = (p_max.z + p_min.z) / 2 center = Vector3(xc, yc, zc) end local size = Vector3(p_max.x - p_min.x, p_max.y - p_min.y, p_max.z - p_min.z) local bound = CS.UnityEngine.Bounds(center, size) bound.size = size bound.extents = size / 2 return bound end -- 计算包围盒顶点 function CoordUtil.recursionCalculateBounds(p_max, p_min, obj) if obj.transform.childCount <= 0 then return end for i = 1, obj.transform.childCount do local item = obj.transform:GetChild(i - 1).gameObject local m = item:GetComponent(typeof(CS.UnityEngine.Renderer)) if m ~= nil and item.activeSelf then local b = m.bounds if p_max:Equals(Vector3.zero) and p_min:Equals(Vector3.zero) then p_max = b.max p_min = b.min end if b.max.x > p_max.x then p_max.x = b.max.x end if b.max.y > p_max.y then p_max.y = b.max.y end if b.max.z > p_max.z then p_max.z = b.max.z end if b.min.x < p_min.x then p_min.x = b.min.x end if b.min.y < p_min.y then p_min.y = b.min.y end if b.min.z < p_min.z then p_min.z = b.min.z end end CoordUtil.recursionCalculateBounds(item) end return p_max, p_min end return CoordUtil taskOrderCfg--[[ from file:订单表.xlsx --]] local taskOrderCfg = { [1] = { id = 580001, title = "小伞的储蓄粮", desc = "准备一些干燥、易于储存的食物,小伞可以留着过冬。", triggerType = 1, triggerParam_1 = 1, triggerParam_2 = 300, customerId = 400003, time = 18000, plot_1 = 680101, plot_2 = 680102, plot_3 = 680103, rewardType_1 = 1, rewardParam_1 = 10000, rewardType_2 = 2, rewardParam_2 = 20, rewardType_3 = -1, rewardParam_3 = -1, rewardType_4 = -1, rewardParam_4 = -1, }, [2] = { id = 580002, title = "阿呆的早安餐品", desc = "阿呆睡了一个好觉,早上起来的时候可以吃一顿美味的早餐迎接新的一天。", triggerType = 2, triggerParam_1 = 400007, triggerParam_2 = -1, customerId = 400007, time = 28800, plot_1 = 680201, plot_2 = 680202, plot_3 = 680203, rewardType_1 = 1, rewardParam_1 = 20000, rewardType_2 = 2, rewardParam_2 = 30, rewardType_3 = -1, rewardParam_3 = -1, rewardType_4 = -1, rewardParam_4 = -1, }, [3] = { id = 580003, title = "嘟嘟的寿司", desc = "让嘟嘟的朋友们也品尝一些美味的寿司吧!", triggerType = 1, triggerParam_1 = 2, triggerParam_2 = 300, customerId = 400013, time = 36000, plot_1 = 680301, plot_2 = 680302, plot_3 = 680303, rewardType_1 = 1, rewardParam_1 = 40000, rewardType_2 = 2, rewardParam_2 = 35, rewardType_3 = -1, rewardParam_3 = -1, rewardType_4 = -1, rewardParam_4 = -1, }, [4] = { id = 580004, title = "毛球的露营便当", desc = "毛球即将和朋友一起去露营,为他准备好美味的表单吧。", triggerType = 2, triggerParam_1 = 400008, triggerParam_2 = -1, customerId = 400008, time = 32400, plot_1 = 680401, plot_2 = 680402, plot_3 = 680403, rewardType_1 = 1, rewardParam_1 = 50000, rewardType_2 = 2, rewardParam_2 = 25, rewardType_3 = -1, rewardParam_3 = -1, rewardType_4 = -1, rewardParam_4 = -1, }, [5] = { id = 580005, title = "糯糯的外卖便当", desc = "糯糯希望他的朋友也能品尝到美味健康的外卖,可惜我们没有送货服务,但他会在约定的时间亲自来取餐。", triggerType = 1, triggerParam_1 = 3, triggerParam_2 = 300, customerId = 400011, time = 43200, plot_1 = 680501, plot_2 = 680502, plot_3 = 680503, rewardType_1 = 1, rewardParam_1 = 60000, rewardType_2 = 2, rewardParam_2 = 40, rewardType_3 = -1, rewardParam_3 = -1, rewardType_4 = -1, rewardParam_4 = -1, }, [6] = { id = 580006, title = "垂垂的锻炼便当", desc = "如果明天天气好,他会准时来这里去便当。", triggerType = 2, triggerParam_1 = 400012, triggerParam_2 = -1, customerId = 400012, time = 32400, plot_1 = 680601, plot_2 = 680602, plot_3 = 680603, rewardType_1 = 1, rewardParam_1 = 50000, rewardType_2 = 2, rewardParam_2 = 25, rewardType_3 = -1, rewardParam_3 = -1, rewardType_4 = -1, rewardParam_4 = -1, }, [7] = { id = 580007, title = "阿叶给家人的订单", desc = "为阿叶的家人准备一些美味的菜肴。", triggerType = 1, triggerParam_1 = 4, triggerParam_2 = 300, customerId = 400004, time = 36000, plot_1 = 680701, plot_2 = 680702, plot_3 = 680703, rewardType_1 = 1, rewardParam_1 = 70000, rewardType_2 = 2, rewardParam_2 = 45, rewardType_3 = -1, rewardParam_3 = -1, rewardType_4 = -1, rewardParam_4 = -1, }, [8] = { id = 580008, title = "蜜豆的派对订单", desc = "为蜜豆和他的朋友们准备派对食物和甜点。此外,尽可能多地准备酒精和饮料", triggerType = 1, triggerParam_1 = 5, triggerParam_2 = 300, customerId = 400009, time = 46800, plot_1 = 680801, plot_2 = 680802, plot_3 = 680803, rewardType_1 = 1, rewardParam_1 = 80000, rewardType_2 = 2, rewardParam_2 = 50, rewardType_3 = -1, rewardParam_3 = -1, rewardType_4 = -1, rewardParam_4 = -1, }, [9] = { id = 580009, title = "泡芙的朋友聚餐", desc = "为泡芙烹饪一些特色菜,以招待他和他的朋友们", triggerType = 1, triggerParam_1 = 6, triggerParam_2 = 300, customerId = 400005, time = 21600, plot_1 = 680901, plot_2 = 680902, plot_3 = 680903, rewardType_1 = 1, rewardParam_1 = 100000, rewardType_2 = 2, rewardParam_2 = 45, rewardType_3 = -1, rewardParam_3 = -1, rewardType_4 = -1, rewardParam_4 = -1, }, [10] = { id = 580010, title = "雪绒的烹饪课", desc = "雪绒很想学习烹饪,准备一些食材来教导他吧!", triggerType = 1, triggerParam_1 = 7, triggerParam_2 = 300, customerId = 400014, time = 32400, plot_1 = 681001, plot_2 = 681002, plot_3 = 681003, rewardType_1 = 1, rewardParam_1 = 120000, rewardType_2 = 2, rewardParam_2 = 30, rewardType_3 = -1, rewardParam_3 = -1, rewardType_4 = -1, rewardParam_4 = -1, }, } return taskOrderCfg main*require("modules/common/util/GameUtil")SpecialCustomerUserData--- 用户数据 特殊顾客 ---@class SpecialCustomerUserData : LuaClass local SpecialCustomerUserData = defClass("SpecialCustomerUserData") function SpecialCustomerUserData:ctor() self:init() end --- 初始化 function SpecialCustomerUserData:init() -- 特殊顾客状态 self.special_customer_state = {} -- {[customerId] = state} 1.未来访 2.已来访 3.已分享 -- 特殊顾客来访次数 self.special_customer_visits = {} -- 特殊顾客存档最后更新时间 self.special_customer_last_update_time = 0 -- 特殊顾客当日来访次数 self.special_customer_visits_today = {} end function SpecialCustomerUserData:resetData() end function SpecialCustomerUserData:load() -- 如果是新玩家,重置数据 if UserDataMgr.isNewPlayer then self:resetData() else self:loadFromLocal() end self:loadComplete() end --- 从本地加载数据 function SpecialCustomerUserData:loadFromLocal() self.customer_state = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.CUSTOMER_STATE, "{}"))) self.customer_visits = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.CUSTOMER_VISITS, "{}"))) self.special_customer_last_update_time = PlayerPrefsMgr:getInt(StrogeKeyDef.SPECIAL_CUSTOMER_LAST_UPDATE_TIME, 0) self.special_customer_state = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.SPECIAL_CUSTOMER_STATE, "{}"))) end -- 加载完成 function SpecialCustomerUserData:loadComplete() -- 检查并更新特殊顾客当日来访次数 if not self:checkSpecialCustomerVisitsToday() then self:updateSpecialCustomerVisitsToday(true) end self:save() end function SpecialCustomerUserData:save() self:saveToLocal() end --- 保存数据到本地 function SpecialCustomerUserData:saveToLocal() PlayerPrefsMgr:setString(StrogeKeyDef.SPECIAL_CUSTOMER_STATE, json.encode(PlayerPrefsMgr:idToString(self.special_customer_state))) PlayerPrefsMgr:setString(StrogeKeyDef.SPECIAL_CUSTOMER_VISITS, json.encode(PlayerPrefsMgr:idToString(self.special_customer_visits))) PlayerPrefsMgr:setInt(StrogeKeyDef.SPECIAL_CUSTOMER_LAST_UPDATE_TIME, self.special_customer_last_update_time) PlayerPrefsMgr:setString(StrogeKeyDef.SPECIAL_CUSTOMER_VISITS_TODAY, json.encode(PlayerPrefsMgr:idToString(self.special_customer_visits_today))) PlayerPrefsMgr:save() end --- 获取指定特殊顾客的状态 function SpecialCustomerUserData:getSpecialCustomerState(customerId) return self.special_customer_state[customerId] or 1 -- 默认未来访 end --- 设置指定特殊顾客的状态 function SpecialCustomerUserData:setSpecialCustomerState(customerId, state, refuseSave) self.special_customer_state[customerId] = state if not refuseSave then self:save() end return state end --- 获取指定特殊顾客的来访次数 function SpecialCustomerUserData:getSpecialCustomerVisits(customerId) return self.special_customer_visits[customerId] or 0 end --- 设置指定特殊顾客的来访次数 function SpecialCustomerUserData:setSpecialCustomerVisits(customerId, value, refuseSave) self.special_customer_visits[customerId] = value if not refuseSave then self:save() end return value end --- 增加指定特殊顾客的来访次数 function SpecialCustomerUserData:addSpecialCustomerVisits(customerId, value, refuseSave) local curr = self:getSpecialCustomerVisits(customerId) + value self:setSpecialCustomerVisits(customerId, curr, refuseSave) return curr end --- 获取指定特殊顾客当日的来访次数 function SpecialCustomerUserData:getSpecialCustomerVisitsToday(customerId) return self.special_customer_visits_today[customerId] or 0 end --- 设置指定特殊顾客当日的来访次数 function SpecialCustomerUserData:setSpecialCustomerVisitsToday(customerId, value, refuseSave) self.special_customer_visits_today[customerId] = value if not refuseSave then self:save() end return value end --- 增加指定特殊顾客当日的来访次数 function SpecialCustomerUserData:addSpecialCustomerVisitsToday(customerId, value, refuseSave) local curr = self:getSpecialCustomerVisitsToday(customerId) + value self:setSpecialCustomerVisitsToday(customerId, curr, refuseSave) return curr end --- 检查特殊顾客当日的来访次数是否已更新 function SpecialCustomerUserData:checkSpecialCustomerVisitsToday() if self.special_customer_last_update_time == os.date() then return true else return false end end --- 更新特殊顾客当日的来访次数 function SpecialCustomerUserData:updateSpecialCustomerVisitsToday(refuseSave) self.special_customer_last_update_time = os.date() self.special_customer_visits_today = {} if not refuseSave then self:save() end end return SpecialCustomerUserData SqliteFunc ---@class SqliteFunc:LuaClass local SqliteFunc = defClass("SqliteFunc") local LOGTAG = SqliteFunc.__cls_name SqliteFunc.Type = { count = "COUNT", avg = "AVG", min = "MIN", max = "MAX", sum = "SUM" } function SqliteFunc:ctor(table, type) self.table = table self.type = type self.col = nil self.asName = nil self.isDistinct = false end function SqliteFunc:column(col) self.col = col return self end function SqliteFunc:as(asName) self.asName = asName return self end function SqliteFunc:distinct() if not self:canDistinct() then printWarn(LOGTAG, "distinct, %s can not distinct", self.type) end self.isDistinct = true return self end --------------------------------------------------------------------------------------------- -- 工具函数 --------------------------------------------------------------------------------------------- function SqliteFunc:isAggregateFun() local t = { SqliteFunc.Type.count, SqliteFunc.Type.avg, SqliteFunc.Type.min, SqliteFunc.Type.max, SqliteFunc.Type.sum } return table.contain(t, self.type) end function SqliteFunc:canDistinct() return self.type == SqliteFunc.Type.count end function SqliteFunc:toSqlStr() local distinctStr = "" if self.isDistinct and self:canDistinct() then distinctStr = "DISTINCT " end local asStr = "" if not string.isEmpty(self.asName) then asStr = string.format(" AS %s", self.asName) end local col = self.col or "*" local str = string.format("%s(%s%s)%s", self.type, distinctStr, col, asStr) return str end return SqliteFunc cookBookCfgw--[[ from file:菜谱.xlsx --]] local cookBookCfg = { [1] = { id = 200001, name = "培根蛋", desc = "咸香培根搭配嫩滑鸡蛋,简单却满分的早餐组合", resId = "cp01_peigendan", ctype = 1, canUp = 1, maketime = 5, buyCond = 210001, unlockCondId = -1, levelUpId = {290101,290102,290103}, bonusId = -1, dropType = -1, dropChance = -1, }, [2] = { id = 200002, name = "蛋包饭", desc = "嫩滑蛋皮包裹着喷香炒饭", resId = "cp01_danbaofan", ctype = 1, canUp = 1, maketime = 7, buyCond = 210002, unlockCondId = -1, levelUpId = {290201,290202,290203}, bonusId = -1, dropType = -1, dropChance = -1, }, [3] = { id = 200003, name = "华夫饼", desc = "外脆内软,格子状香甜烤饼", resId = "cp01_huafubing", ctype = 1, canUp = 1, maketime = 9, buyCond = 210003, unlockCondId = -1, levelUpId = {290301,290302,290303}, bonusId = -1, dropType = -1, dropChance = -1, }, [4] = { id = 200004, name = "可乐", desc = "经典的黑色碳酸气泡饮", resId = "cp01_kele", ctype = 2, canUp = 1, maketime = 11, buyCond = 210004, unlockCondId = -1, levelUpId = {290401,290402,290403}, bonusId = -1, dropType = -1, dropChance = -1, }, [5] = { id = 200005, name = "蔬菜沙拉", desc = "清新爽口,营养丰富的健康之选", resId = "cp01_shucaishala", ctype = 1, canUp = 1, maketime = 13, buyCond = 210005, unlockCondId = -1, levelUpId = {290501,290502,290503}, bonusId = -1, dropType = -1, dropChance = -1, }, [6] = { id = 200006, name = "可颂面包", desc = "黄油面团叠千层,烤出黄金蜗牛壳", resId = "cp01_kesong", ctype = 1, canUp = 1, maketime = 15, buyCond = 210006, unlockCondId = -1, levelUpId = {290601,290602,290603}, bonusId = -1, dropType = -1, dropChance = -1, }, [7] = { id = 200007, name = "甜甜圈", desc = "圈形可爱,甜蜜酥软", resId = "cp01_tiantianquan", ctype = 1, canUp = 1, maketime = 17, buyCond = 210007, unlockCondId = -1, levelUpId = {290701,290702,290703}, bonusId = -1, dropType = -1, dropChance = -1, }, [8] = { id = 200008, name = "汉堡", desc = "酥软面包夹嫩肉饼,层层丰富,饱满多汁", resId = "cp01_hanbao", ctype = 1, canUp = 1, maketime = 19, buyCond = 210008, unlockCondId = -1, levelUpId = {290801,290802,290803}, bonusId = -1, dropType = -1, dropChance = -1, }, [9] = { id = 200009, name = "椰子汁", desc = "清甜解渴的天然热带果汁", resId = "cp01_yezizhi", ctype = 2, canUp = 0, maketime = 21, buyCond = 210009, unlockCondId = -1, levelUpId = {290901}, bonusId = -1, dropType = -1, dropChance = -1, }, [10] = { id = 200010, name = "马卡龙", desc = "外脆内软,色彩斑斓", resId = "cp01_makalong", ctype = 1, canUp = 1, maketime = 23, buyCond = 210010, unlockCondId = -1, levelUpId = {291001,291002,291003}, bonusId = -1, dropType = -1, dropChance = -1, }, [11] = { id = 200011, name = "披萨", desc = "意式国民大饼!芝士拉丝,饼韧料足", resId = "cp01_pisa", ctype = 1, canUp = 0, maketime = 25, buyCond = 210011, unlockCondId = -1, levelUpId = {291101}, bonusId = -1, dropType = -1, dropChance = -1, }, [12] = { id = 200012, name = "三文鱼寿司", desc = "配上芥末,鲜辣相宜", resId = "cp01_sanwenyushousi", ctype = 1, canUp = 1, maketime = 29, buyCond = 210012, unlockCondId = -1, levelUpId = {291201,291202,291203}, bonusId = -1, dropType = -1, dropChance = -1, }, [13] = { id = 200013, name = "提拉米苏", desc = "层层口感,甜而不腻的经典甜点", resId = "cp01_tilamisu", ctype = 1, canUp = 0, maketime = 33, buyCond = 210013, unlockCondId = -1, levelUpId = {291301}, bonusId = -1, dropType = -1, dropChance = -1, }, [14] = { id = 200014, name = "牛排", desc = "肉香四溢,尽显醇厚风味", resId = "cp01_niupai", ctype = 1, canUp = 0, maketime = 37, buyCond = 210014, unlockCondId = -1, levelUpId = {291401}, bonusId = -1, dropType = -1, dropChance = -1, }, [15] = { id = 200015, name = "雪顶拿铁", desc = "浓滑咖啡融合绵密奶泡", resId = "cp01_xuedingnatie", ctype = 2, canUp = 1, maketime = 41, buyCond = 210015, unlockCondId = -1, levelUpId = {291501,291502,291503}, bonusId = -1, dropType = -1, dropChance = -1, }, [16] = { id = 200016, name = "香煎芦笋", desc = "清爽中尽显自然原味", resId = "cp01_xiangjianluhui", ctype = 1, canUp = 0, maketime = 45, buyCond = 210016, unlockCondId = -1, levelUpId = {291601}, bonusId = -1, dropType = -1, dropChance = -1, }, [17] = { id = 200017, name = "墨西哥塔克", desc = "薄饼包裹丰富馅料,街头风味十足", resId = "cp01_take", ctype = 1, canUp = 1, maketime = 50, buyCond = 210017, unlockCondId = -1, levelUpId = {291701,291702,291703}, bonusId = -1, dropType = -1, dropChance = -1, }, [18] = { id = 200018, name = "苹果糖", desc = "酸甜的苹果裹上晶亮糖衣", resId = "cp01_pingguotang", ctype = 1, canUp = 0, maketime = 55, buyCond = 210018, unlockCondId = -1, levelUpId = {291801}, bonusId = -1, dropType = -1, dropChance = -1, }, [19] = { id = 200019, name = "芒果刨冰", desc = "夏日消暑必备良品", resId = "cp01_mangguobaobing", ctype = 2, canUp = 1, maketime = 60, buyCond = 210019, unlockCondId = -1, levelUpId = {291901,291902,291903}, bonusId = -1, dropType = -1, dropChance = -1, }, [20] = { id = 200020, name = "黄油口蘑", desc = "蘑菇鲜嫩多汁,融合黄油香气,浓郁滑口", resId = "cp01_huangyoukougu", ctype = 1, canUp = 0, maketime = 65, buyCond = 210020, unlockCondId = -1, levelUpId = {292001}, bonusId = -1, dropType = -1, dropChance = -1, }, [21] = { id = 200021, name = "蜂蜜松饼", desc = "热腾松软,蜜流金黄", resId = "cp01_songbing", ctype = 1, canUp = 0, maketime = 70, buyCond = 210021, unlockCondId = -1, levelUpId = {292101}, bonusId = -1, dropType = -1, dropChance = -1, }, [22] = { id = 200022, name = "爆米花", desc = "搭配可乐,欢乐开怀!", resId = "cp01_baomihua", ctype = 1, canUp = 1, maketime = 75, buyCond = 210022, unlockCondId = -1, levelUpId = {292201,292202,292203}, bonusId = -1, dropType = -1, dropChance = -1, }, [23] = { id = 200023, name = "番茄意面", desc = "酸甜浓郁,意式经典面食", resId = "cp01_yimian", ctype = 1, canUp = 0, maketime = 80, buyCond = 210023, unlockCondId = -1, levelUpId = {292301}, bonusId = -1, dropType = -1, dropChance = -1, }, [24] = { id = 200024, name = "蒙布朗", desc = "栗子泥丝滑包裹轻盈奶油,香甜细腻", resId = "cp01_mengbulang", ctype = 1, canUp = 0, maketime = 85, buyCond = 210024, unlockCondId = -1, levelUpId = {292401}, bonusId = -1, dropType = -1, dropChance = -1, }, [25] = { id = 200025, name = "冰淇淋球", desc = "一勺尽享冰爽甜蜜", resId = "cp01_bingqilin", ctype = 1, canUp = 0, maketime = 90, buyCond = 210025, unlockCondId = -1, levelUpId = {292501}, bonusId = -1, dropType = -1, dropChance = -1, }, [26] = { id = 200026, name = "卡布奇诺", desc = "香浓顺口,经典意式风味", resId = "cp01_kabuqinuo", ctype = 1, canUp = 1, maketime = 95, buyCond = 210026, unlockCondId = -1, levelUpId = {292601,292602,292603}, bonusId = -1, dropType = -1, dropChance = -1, }, [27] = { id = 200027, name = "热可可", desc = "浓醇巧克力香,丝滑暖心", resId = "cp01_rekeke", ctype = 2, canUp = 0, maketime = 100, buyCond = 210027, unlockCondId = -1, levelUpId = {292701}, bonusId = -1, dropType = -1, dropChance = -1, }, [28] = { id = 200028, name = "水果茶", desc = "鲜果沁茶,清润解腻", resId = "cp01_shuiguocha", ctype = 2, canUp = 0, maketime = 105, buyCond = 210028, unlockCondId = -1, levelUpId = {292801}, bonusId = -1, dropType = -1, dropChance = -1, }, [29] = { id = 200029, name = "姜饼人", desc = "它的手是不是动了一下?", resId = "cp01_jiangbingren", ctype = 1, canUp = 1, maketime = 110, buyCond = 210029, unlockCondId = -1, levelUpId = {292901,292902,292903}, bonusId = -1, dropType = -1, dropChance = -1, }, [30] = { id = 200030, name = "扎啤", desc = "一口下去透心凉", resId = "cp01_zhapi", ctype = 2, canUp = 0, maketime = 115, buyCond = 210030, unlockCondId = -1, levelUpId = {293001}, bonusId = -1, dropType = -1, dropChance = -1, }, [31] = { id = 200031, name = "鸡尾酒", desc = "果香、酒烈、微醺交织的魔法", resId = "cp01_jiweijiu", ctype = 2, canUp = 0, maketime = 120, buyCond = 210031, unlockCondId = -1, levelUpId = {293101}, bonusId = -1, dropType = -1, dropChance = -1, }, [32] = { id = 200032, name = "贝果", desc = "麦香在齿间反复回弹,搭配什么都好吃", resId = "cp01_beiguo", ctype = 1, canUp = 1, maketime = 125, buyCond = 210032, unlockCondId = -1, levelUpId = {293201,293202,293203}, bonusId = -1, dropType = -1, dropChance = -1, }, [33] = { id = 200033, name = "烤羊排", desc = "炭火炙烤的羊油滴落声,是晚餐桌上最野性的开餐铃", resId = "cp01_kaoyangpai", ctype = 1, canUp = 0, maketime = 130, buyCond = 210033, unlockCondId = -1, levelUpId = {293301}, bonusId = -1, dropType = -1, dropChance = -1, }, [34] = { id = 200034, name = "薯饼", desc = "酥壳裹着滚烫薯泥炸弹", resId = "cp01_shubing", ctype = 1, canUp = 0, maketime = 135, buyCond = 210034, unlockCondId = -1, levelUpId = {293401}, bonusId = -1, dropType = -1, dropChance = -1, }, [35] = { id = 200035, name = "墨西哥鸡肉卷", desc = "烙饼包裹着烟熏鸡肉,辣酱在舌尖放烟花", resId = "cp01_jiroujuan", ctype = 1, canUp = 1, maketime = 140, buyCond = 210035, unlockCondId = -1, levelUpId = {293501,293502,293503}, bonusId = -1, dropType = -1, dropChance = -1, }, [36] = { id = 200036, name = "泡芙", desc = "一口咬落云朵,坠落奶味深谷", resId = "cp01_paofu", ctype = 1, canUp = 0, maketime = 145, buyCond = 210036, unlockCondId = -1, levelUpId = {293601}, bonusId = -1, dropType = -1, dropChance = -1, }, [37] = { id = 200037, name = "培根卷芦笋", desc = "烟熏咸香的培根,卷着青嫩多汁的芦笋", resId = "cp01_peigenjuan", ctype = 1, canUp = 0, maketime = 150, buyCond = 210037, unlockCondId = -1, levelUpId = {293701}, bonusId = -1, dropType = -1, dropChance = -1, }, [38] = { id = 200038, name = "蝴蝶酥", desc = "金黄蝶翼层层酥,焦糖甜脆黄油香", resId = "cp01_hudiesu", ctype = 1, canUp = 0, maketime = 155, buyCond = 210038, unlockCondId = -1, levelUpId = {293801}, bonusId = -1, dropType = -1, dropChance = -1, }, [39] = { id = 200039, name = "芝士棒", desc = "金黄酥脆棒,咬开拉丝热芝士", resId = "cp01_zhishibang", ctype = 1, canUp = 0, maketime = 160, buyCond = 210039, unlockCondId = -1, levelUpId = {293901}, bonusId = -1, dropType = -1, dropChance = -1, }, [40] = { id = 200040, name = "西多士", desc = "煎得焦香的厚吐司,淋上糖浆", resId = "cp01_xiduoshi", ctype = 1, canUp = 0, maketime = 165, buyCond = 210040, unlockCondId = -1, levelUpId = {294001}, bonusId = -1, dropType = -1, dropChance = -1, }, [41] = { id = 200041, name = "水果拼盘", desc = "缤纷鲜果切片,冰凉清甜多汁", resId = "cp01_shuiguopinpan", ctype = 1, canUp = 0, maketime = 170, buyCond = 210041, unlockCondId = -1, levelUpId = {294101}, bonusId = -1, dropType = -1, dropChance = -1, }, [42] = { id = 200042, name = "吐司煎蛋", desc = "热乎香脆吐司,配上嫩滑煎蛋", resId = "cp01_tusijiandan", ctype = 1, canUp = 0, maketime = 175, buyCond = 210042, unlockCondId = -1, levelUpId = {294201}, bonusId = -1, dropType = -1, dropChance = -1, }, [43] = { id = 200043, name = "饭团", desc = "简简单单一顿餐", resId = "cp01_fantuan", ctype = 1, canUp = 0, maketime = 180, buyCond = 210043, unlockCondId = -1, levelUpId = {294301}, bonusId = -1, dropType = -1, dropChance = -1, }, [44] = { id = 200044, name = "杯子蛋糕", desc = "色彩丰富的迷你甜点", resId = "cp01_beizidangao", ctype = 1, canUp = 0, maketime = 185, buyCond = 210044, unlockCondId = -1, levelUpId = {294401}, bonusId = -1, dropType = -1, dropChance = -1, }, [45] = { id = 200045, name = "柠檬茶", desc = "清新酸甜,果香与茶韵交织的夏日特饮", resId = "cp01_ningmengcha", ctype = 2, canUp = 0, maketime = 190, buyCond = 210045, unlockCondId = -1, levelUpId = {294501}, bonusId = -1, dropType = -1, dropChance = -1, }, [46] = { id = 200046, name = "烤牛奶", desc = "外酥脆,内冰滑,凝乳焦香", resId = "cp01_kaoniunai", ctype = 1, canUp = 0, maketime = 195, buyCond = 210046, unlockCondId = -1, levelUpId = {294601}, bonusId = -1, dropType = -1, dropChance = -1, }, [47] = { id = 200047, name = "芒果慕斯杯", desc = "鲜芒坠云间,柔滑漫舌尖", resId = "cp01_musibei", ctype = 1, canUp = 0, maketime = 200, buyCond = 210047, unlockCondId = -1, levelUpId = {294701}, bonusId = -1, dropType = -1, dropChance = -1, }, [48] = { id = 200048, name = "烤肠拼盘", desc = "烟熏焦香裂脆皮,盘聚德式肉欲狂欢", resId = "cp01_kaochangpinpan", ctype = 1, canUp = 0, maketime = 205, buyCond = 210048, unlockCondId = -1, levelUpId = {294801}, bonusId = -1, dropType = -1, dropChance = -1, }, [49] = { id = 200049, name = "苹果酥", desc = "脆云片片裂,烫口苹果流金", resId = "cp01_pingguosu", ctype = 1, canUp = 0, maketime = 210, buyCond = 210049, unlockCondId = -1, levelUpId = {294901}, bonusId = -1, dropType = -1, dropChance = -1, }, [50] = { id = 200050, name = "棉花糖", desc = "甜蜜如云朵般柔软", resId = "cp01_mianhuatang", ctype = 1, canUp = 0, maketime = 215, buyCond = 210050, unlockCondId = -1, levelUpId = {295001}, bonusId = -1, dropType = -1, dropChance = -1, }, [51] = { id = 200051, name = "蛋挞", desc = "酥皮千层脆,蛋奶嫩心甜香滑", resId = "cp01_danta", ctype = 1, canUp = 0, maketime = 220, buyCond = 210051, unlockCondId = -1, levelUpId = {295101}, bonusId = -1, dropType = -1, dropChance = -1, }, [52] = { id = 200052, name = "煎蛋三明治", desc = "热乎香脆吐司夹嫩滑煎蛋,蛋香麦香交融", resId = "cp01_sanmingzhi", ctype = 1, canUp = 0, maketime = 225, buyCond = 210052, unlockCondId = -1, levelUpId = {295201}, bonusId = -1, dropType = -1, dropChance = -1, }, [53] = { id = 200053, name = "酸梅汤", desc = "冰镇酸甜汤,消食解腻好帮手", resId = "cp01_suanmeitang", ctype = 2, canUp = 0, maketime = 230, buyCond = 210053, unlockCondId = -1, levelUpId = {295301}, bonusId = -1, dropType = -1, dropChance = -1, }, [54] = { id = 200054, name = "西瓜汁", desc = "鲜榨西瓜汁,冰凉清甜,消暑解渴", resId = "cp01_xiguazhi", ctype = 2, canUp = 0, maketime = 235, buyCond = 210054, unlockCondId = -1, levelUpId = {295401}, bonusId = -1, dropType = -1, dropChance = -1, }, [55] = { id = 200055, name = "啤酒", desc = "麦香沁凉,泡沫绵密消暑气", resId = "cp01_pijiu", ctype = 2, canUp = 0, maketime = 240, buyCond = 210055, unlockCondId = -1, levelUpId = {295501}, bonusId = -1, dropType = -1, dropChance = -1, }, [56] = { id = 200056, name = "抹茶草莓爆珠", desc = "抹茶香草莓甜,咬破爆珠冰爽惊喜", resId = "cp01_caomeibaozhu", ctype = 2, canUp = 0, maketime = 245, buyCond = 210056, unlockCondId = -1, levelUpId = {295601}, bonusId = -1, dropType = -1, dropChance = -1, }, [57] = { id = 200057, name = "芒果西米露", desc = "椰奶打底,芒果鲜甜,西米弹滑冰爽", resId = "cp01_mangguoximilu", ctype = 2, canUp = 0, maketime = 250, buyCond = 210057, unlockCondId = -1, levelUpId = {295701}, bonusId = -1, dropType = -1, dropChance = -1, }, [58] = { id = 200058, name = "西柚爆珠汁", desc = "清新西柚香,酸甜微涩,爆珠在舌尖炸开冰凉水感", resId = "cp01_xiyoubaozhu", ctype = 2, canUp = 0, maketime = 255, buyCond = 210058, unlockCondId = -1, levelUpId = {295801}, bonusId = -1, dropType = -1, dropChance = -1, }, [59] = { id = 200059, name = "青提茉莉茶", desc = "颗颗青提剥鲜爽,清幽茉莉漫回甘", resId = "cp01_molihuacha", ctype = 2, canUp = 0, maketime = 260, buyCond = 210059, unlockCondId = -1, levelUpId = {295901}, bonusId = -1, dropType = -1, dropChance = -1, }, [60] = { id = 200060, name = "绿豆沙牛乳", desc = "绵密绿豆沙香醇,沁凉牛乳解暑意", resId = "cp01_lvdouniuru", ctype = 2, canUp = 0, maketime = 265, buyCond = 210060, unlockCondId = -1, levelUpId = {296001}, bonusId = -1, dropType = -1, dropChance = -1, }, [61] = { id = 200801, name = "肉串", desc = "外焦里嫩,孜然飘香", resId = "cp02_rouchuan", ctype = 3, canUp = 0, maketime = 0, buyCond = 210801, unlockCondId = -1, levelUpId = {}, bonusId = 250801, dropType = -1, dropChance = -1, }, [62] = { id = 200802, name = "骨肉相连", desc = "骨脆肉嫩,烤香四溢", resId = "cp02_gurouxianglian", ctype = 3, canUp = 0, maketime = 0, buyCond = 210802, unlockCondId = -1, levelUpId = {}, bonusId = 250802, dropType = 1, dropChance = 5, }, [63] = { id = 200803, name = "烤鱿鱼", desc = "炭火烘烤的海味诱惑", resId = "cp02_kaoyouyu", ctype = 3, canUp = 0, maketime = 0, buyCond = 210803, unlockCondId = -1, levelUpId = {}, bonusId = 250803, dropType = -1, dropChance = -1, }, [64] = { id = 200804, name = "豆皮金针菇", desc = "豆香包裹菌香,外酥内嫩", resId = "cp02_kaodoupijinzhengu", ctype = 3, canUp = 0, maketime = 0, buyCond = 210804, unlockCondId = -1, levelUpId = {}, bonusId = 250804, dropType = -1, dropChance = -1, }, [65] = { id = 200805, name = "烤馒头", desc = "简单食材烤出独特风味", resId = "cp02_kaomantou", ctype = 3, canUp = 0, maketime = 0, buyCond = 210805, unlockCondId = -1, levelUpId = {}, bonusId = 250805, dropType = 2, dropChance = 5, }, [66] = { id = 200806, name = "烤尖椒", desc = "焦皮包裹汁嫩椒肉,尝上一口,嘶~不辣", resId = "cp02_kaojianjiao", ctype = 3, canUp = 0, maketime = 0, buyCond = 210806, unlockCondId = -1, levelUpId = {}, bonusId = 250806, dropType = -1, dropChance = -1, }, [67] = { id = 200807, name = "烤面筋", desc = "酱香浓郁,街头烧烤的经典", resId = "cp02_kaomianjin", ctype = 3, canUp = 0, maketime = 0, buyCond = 210807, unlockCondId = -1, levelUpId = {}, bonusId = 250807, dropType = -1, dropChance = -1, }, [68] = { id = 200808, name = "烤淀粉肠", desc = "油刷翻飞火星溅,童年随香气悄悄醒来", resId = "cp02_kaodianfenchang", ctype = 3, canUp = 0, maketime = 0, buyCond = 210808, unlockCondId = -1, levelUpId = {}, bonusId = 250808, dropType = 3, dropChance = 5, }, [69] = { id = 200809, name = "烤香菇", desc = "炭火微熏,鲜嫩多汁,菌香浓郁", resId = "cp02_kaoxianggu", ctype = 3, canUp = 0, maketime = 0, buyCond = 210809, unlockCondId = -1, levelUpId = {}, bonusId = 250809, dropType = -1, dropChance = -1, }, [70] = { id = 200810, name = "烤虾", desc = "火候恰到好处,肉质弹嫩", resId = "cp02_kaoxia", ctype = 3, canUp = 0, maketime = 0, buyCond = 210810, unlockCondId = -1, levelUpId = {}, bonusId = 250810, dropType = -1, dropChance = -1, }, [71] = { id = 200811, name = "烤黄鱼", desc = "金黄酥脆,鱼肉鲜嫩", resId = "cp02_kaohuangyu", ctype = 3, canUp = 0, maketime = 0, buyCond = 210811, unlockCondId = -1, levelUpId = {}, bonusId = 250811, dropType = 1, dropChance = 5, }, [72] = { id = 200812, name = "烤鳗鱼", desc = "甘甜酱香裹身,肉质细腻软滑", resId = "cp02_kaomanyu", ctype = 3, canUp = 0, maketime = 0, buyCond = 210812, unlockCondId = -1, levelUpId = {}, bonusId = 250812, dropType = -1, dropChance = -1, }, [73] = { id = 200813, name = "烤玉米", desc = "焦香四溢,粒粒饱满,带着炭火烟熏味", resId = "cp02_kaoyumi", ctype = 3, canUp = 0, maketime = 0, buyCond = 210813, unlockCondId = -1, levelUpId = {}, bonusId = 250813, dropType = -1, dropChance = -1, }, [74] = { id = 200814, name = "蔬菜什锦串", desc = "色彩斑斓,鲜嫩多汁", resId = "cp02_shucaishijinchuan", ctype = 3, canUp = 0, maketime = 0, buyCond = 210814, unlockCondId = -1, levelUpId = {}, bonusId = 250814, dropType = 2, dropChance = 5, }, [75] = { id = 200815, name = "烤翅", desc = "外皮焦香酥脆,肉质鲜嫩多汁", resId = "cp02_kaochi", ctype = 3, canUp = 0, maketime = 0, buyCond = 210815, unlockCondId = -1, levelUpId = {}, bonusId = 250815, dropType = -1, dropChance = -1, }, [76] = { id = 200816, name = "烤菠萝肉粒", desc = "甜酸菠萝搭配鲜嫩肉粒,焦香诱人", resId = "cp02_kaoboluorouli", ctype = 3, canUp = 0, maketime = 0, buyCond = 210816, unlockCondId = -1, levelUpId = {}, bonusId = 250816, dropType = -1, dropChance = -1, }, [77] = { id = 200817, name = "烤土豆片", desc = "薄脆金黄,外酥里软", resId = "cp02_kaotudoupian", ctype = 3, canUp = 0, maketime = 0, buyCond = 210817, unlockCondId = -1, levelUpId = {}, bonusId = 250817, dropType = 3, dropChance = 5, }, [78] = { id = 200818, name = "烤午餐肉", desc = "带有微微烟熏味的怀旧下饭佳品", resId = "cp02_kaowucanrou", ctype = 3, canUp = 0, maketime = 0, buyCond = 210818, unlockCondId = -1, levelUpId = {}, bonusId = 250818, dropType = -1, dropChance = -1, }, [79] = { id = 200819, name = "黄油面包片", desc = "浓郁黄油香气,温暖诱人", resId = "cp02_huangyoukaomianbaopian", ctype = 3, canUp = 0, maketime = 0, buyCond = 210819, unlockCondId = -1, levelUpId = {}, bonusId = 250819, dropType = -1, dropChance = -1, }, [80] = { id = 200820, name = "火山石烤肠", desc = "外皮微焦,肉质鲜嫩多汁", resId = "cp02_kaochang", ctype = 3, canUp = 0, maketime = 0, buyCond = 210820, unlockCondId = -1, levelUpId = {}, bonusId = 250820, dropType = 1, dropChance = 5, }, [81] = { id = 200821, name = "烤藕片", desc = "薄脆爽口,藕香浓郁", resId = "cp02_kaooupian", ctype = 3, canUp = 0, maketime = 0, buyCond = 210821, unlockCondId = -1, levelUpId = {}, bonusId = 250821, dropType = -1, dropChance = -1, }, [82] = { id = 200822, name = "烤韭菜", desc = "韭香四溢,口感鲜嫩的炭火蔬菜美味", resId = "cp02_kaojiucai", ctype = 3, canUp = 0, maketime = 0, buyCond = 210822, unlockCondId = -1, levelUpId = {}, bonusId = 250822, dropType = -1, dropChance = -1, }, [83] = { id = 200823, name = "蒜蓉茄子", desc = "茄子上撒满了蒜蓉酱料,烤制茄肉软糯,鲜香无比", resId = "cp02_suanrongqiezi", ctype = 3, canUp = 0, maketime = 0, buyCond = 210823, unlockCondId = -1, levelUpId = {}, bonusId = 250823, dropType = 2, dropChance = 5, }, [84] = { id = 200824, name = "蒜蓉烤口蘑", desc = "菌香浓郁,炭火微焦", resId = "cp02_suanrongkaokoumo", ctype = 3, canUp = 0, maketime = 0, buyCond = 210824, unlockCondId = -1, levelUpId = {}, bonusId = 250824, dropType = -1, dropChance = -1, }, [85] = { id = 200825, name = "锡纸金针菇", desc = "锡纸包裹锁鲜,金针菇嫩滑多汁", resId = "cp02_xizhijinzhengu", ctype = 3, canUp = 0, maketime = 0, buyCond = 210825, unlockCondId = -1, levelUpId = {}, bonusId = 250825, dropType = -1, dropChance = -1, }, [86] = { id = 200826, name = "烤生蚝", desc = "浓郁海味与蒜香完美交融的鲜美佳肴", resId = "cp02_kaoshenghao", ctype = 3, canUp = 0, maketime = 0, buyCond = 210826, unlockCondId = -1, levelUpId = {}, bonusId = 250826, dropType = 3, dropChance = 5, }, [87] = { id = 200827, name = "花甲粉丝", desc = "花甲鲜美,粉丝吸满汤汁,鲜辣浓郁的海鲜盛宴", resId = "cp02_huajiafensi", ctype = 3, canUp = 0, maketime = 0, buyCond = 210827, unlockCondId = -1, levelUpId = {}, bonusId = 250827, dropType = -1, dropChance = -1, }, [88] = { id = 200828, name = "锡纸烤大虾", desc = "鲜虾肥美,蒜香浓郁", resId = "cp02_xizhikaodaxia", ctype = 3, canUp = 0, maketime = 0, buyCond = 210828, unlockCondId = -1, levelUpId = {}, bonusId = 250828, dropType = 1, dropChance = 5, }, [89] = { id = 200829, name = "锡纸豆腐", desc = "豆腐嫩滑细腻,香气四溢", resId = "cp02_xizhidoufu", ctype = 3, canUp = 0, maketime = 0, buyCond = 210829, unlockCondId = -1, levelUpId = {}, bonusId = 250829, dropType = -1, dropChance = -1, }, [90] = { id = 200830, name = "烤鱼豆腐", desc = "烟火炙香,嫩弹可口", resId = "cp02_kaoyudoufu", ctype = 3, canUp = 0, maketime = 0, buyCond = 210830, unlockCondId = -1, levelUpId = {}, bonusId = 250830, dropType = -1, dropChance = -1, }, [91] = { id = 200831, name = "烤鲫鱼", desc = "鱼肉细嫩鲜美,外皮微焦香脆", resId = "cp02_kaojiyu", ctype = 3, canUp = 0, maketime = 0, buyCond = 210831, unlockCondId = -1, levelUpId = {}, bonusId = 250831, dropType = 2, dropChance = 5, }, [92] = { id = 200832, name = "烤培根卷", desc = "培根香脆,烟熏浓郁", resId = "cp02_kaopeigenjuan", ctype = 3, canUp = 0, maketime = 0, buyCond = 210832, unlockCondId = -1, levelUpId = {}, bonusId = 250832, dropType = -1, dropChance = -1, }, [93] = { id = 200833, name = "烤扇贝", desc = "鲜嫩扇贝与爽滑粉丝相遇,蒜香浓郁", resId = "cp02_shanbei", ctype = 3, canUp = 0, maketime = 0, buyCond = 210833, unlockCondId = -1, levelUpId = {}, bonusId = 250833, dropType = -1, dropChance = -1, }, [94] = { id = 200834, name = "彩椒肉串", desc = "鲜嫩多汁,彩椒脆甜", resId = "cp02_caijiaorouchuan", ctype = 3, canUp = 0, maketime = 0, buyCond = 210834, unlockCondId = -1, levelUpId = {}, bonusId = 250834, dropType = 3, dropChance = 5, }, [95] = { id = 200835, name = "烤包菜", desc = "包菜香甜,炭火微熏带来独特的烟火味", resId = "cp02_kaobaocai", ctype = 3, canUp = 0, maketime = 0, buyCond = 210835, unlockCondId = -1, levelUpId = {}, bonusId = 250835, dropType = -1, dropChance = -1, }, } return cookBookCfg Device  Device ={} local platform = CS.UnityEngine.Application.platform local RuntimePlatform = CS.UnityEngine.RuntimePlatform local Screen = CS.UnityEngine.Screen local ScreenOrientation = CS.UnityEngine.ScreenOrientation function Device.isIOS() return platform == RuntimePlatform.IPhonePlayer end function Device.isApple() return platform == RuntimePlatform.IPhonePlayer or platform == RuntimePlatform.OSXEditor end function Device.isAndroid() return platform == RuntimePlatform.Android end function Device.isHarmonyOS() return platform == RuntimePlatform.OpenHarmony end function Device.isWindows() return platform == RuntimePlatform.WindowsPlayer or platform == RuntimePlatform.WindowsEditor end function Device.isMac() return platform == RuntimePlatform.OSXEditor or platform == RuntimePlatform.OSXPlayer end function Device.isEditor() return platform == RuntimePlatform.OSXEditor or platform == RuntimePlatform.WindowsEditor end function Device.isPlayer() return platform == RuntimePlatform.WindowsPlayer or platform == RuntimePlatform.IPhonePlayer or platform == RuntimePlatform.OSXPlayer end function Device.isWebGL() return platform == RuntimePlatform.WebGLPlayer end function Device.isScreenLandscape() local orientation = Screen.orientation return orientation == ScreenOrientation.LandscapeLeft or orientation == ScreenOrientation.LandscapeRight end function Device.getPlatformStr() if platform == RuntimePlatform.IPhonePlayer then return "ios" elseif platform == RuntimePlatform.Android then return "android" elseif platform == RuntimePlatform.OSXEditor then return "macos" elseif platform == RuntimePlatform.WindowsEditor then return "windows" elseif platform == RuntimePlatform.WebGLPlayer then return "webgl" end return "unknow" end function Device.getPlatformLowerStr(ignoreEditor) if Device.isIOS() then return "ios" elseif Device.isAndroid() then return "android" elseif Device.isWindows() then return ignoreEditor and "ios" or "windows" elseif Device.isMac() then return ignoreEditor and "ios" or "mac" end return "ios" end ---是否桌面端 function Device.isDesktop() return not Device.isIOS() and not Device.isAndroid() and not Device.isHarmonyOS() end ---是否桌面端非编辑器 function Device.isDesktopPlayer() return platform == RuntimePlatform.WindowsPlayer or platform == RuntimePlatform.OSXPlayer end print("当前平台:" .. Device.getPlatformStr())PopUpUIu--- ---@class PopUpUI : UILayer local PopUpUI, super = defClass("PopUpUI", UILayer) function PopUpUI:ctor(title) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/commonui/commonuireslink") self.title = title end function PopUpUI:onLoad() self.ui = GameObject.Instantiate(self.R.pop_up_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) local value = self.ui:Seek("value") value:GetComponent("TextMeshProUGUI").text = self.title self.tmr = self.timer(self, function (dt) self:close() self.clear(self, true, self.tmr) end, 1, 1) end return PopUpUIFirebaseAnalyticsUtil#--[[ 埋点工具 author:{zhangpeng} time:2023-12-07 15:44:24 ]] local FirebaseAnalyticsUtil, super = defClassStatic("FirebaseAnalyticsUtil") local IOC_FA_UTIL_CLASS_NAME = "FirebaseUtil" local JavaFireBaseClass = "com/fy/xgame/tilelink/util/FireBaseUtil" function FirebaseAnalyticsUtil:init() end function FirebaseAnalyticsUtil:sendAnalyticsEvent(eventName, params) local param = {} for k,v in pairs(params) do local key = string.format("eventParam_%s",k) param[key] = v end if Device.isIOS() then param.eventName = eventName luaoc.callStaticMethod(IOC_FA_UTIL_CLASS_NAME, "logEventWithName", param) elseif Device.isAndroid() then local paramStr = json.encode(param) luaj.callStaticMethod(JavaFireBaseClass, "logEventWithName", { eventName, paramStr }) end end function FirebaseAnalyticsUtil:setUserId(userId) if Device.isIOS() then local param = {} param.userId = userId luaoc.callStaticMethod(IOC_FA_UTIL_CLASS_NAME, "setUserId", param) elseif Device.isAndroid() then luaj.callStaticMethod(JavaFireBaseClass, "setUserId", { tostring(userId) }) end end -- 上传错误信息 function FirebaseAnalyticsUtil:sendErrorInfo(errorStr) end FirebaseAnalyticsUtil:init() SocketCmdMgr --[[ 业务指令收发 author:{zhangpeng} time:2023-08-23 18:01:16 ]] local SocketCmdMgr,_ = defClassStatic("SocketCmdMgr") local Guid = CS.System.Guid local pb = raw_require("pb") local PROTO_PATH = "Assets/AssetsPackage/Res/common/proto/" local LOG_TAG = "SocketCmdMgr" function SocketCmdMgr:init() self.protoNameList = { "handshake.pb", "chat.pb", "head.pb" } self.R = Res.loadResLink("common/network/socket/pbreslink") printInfo(LOG_TAG, "init, url:%s, port:%s", self.url, self.port) end function SocketCmdMgr:send(cmdId,callback) PBSocketPack.headProtoName = "head.Head" -- 聊天指令 if cmdId == CmdDef.SendDef.SingleChat then local head = { msg_type = CmdDef.MsgType.DATA, headData = { ["tag"] = cmdId, ["messageId"] = Guid.NewGuid():ToString("N") }, request_name = "SingleChat" } local body = { ["toUserId"] = "64e41f7735a9d490710fd4b0", ["content"] = "测试聊天", } local reqest_proto = "chat.SendSingleChat" local response_proto = "chat.ReceiveSingleChat" SocketMgr:send(head,body ,reqest_proto,response_proto,function (suc,rspData) printInfo(LOG_TAG,string.format("收到聊天信息:%s",rspData.reqBody.content)) if callback then callback(suc,rspData) end end) end end function SocketCmdMgr:receive() end SocketCmdMgr:init()mainrequire("modules/building/restaurant/cooking/CookingConst") require("modules/building/restaurant/cooking/CookingBench") require("modules/building/restaurant/cooking/CookingBenchStove") require("modules/building/restaurant/cooking/CookingBenchMgr") sceneCfg--[[ from file:场景配置.xlsx --]] local sceneCfg = { [1] = { id = 1, name = "餐厅", unlockStarNum = -1, buyNeedStarNum = -1, buyCoinNum = -1, }, [2] = { id = 2, name = "音乐烤吧", unlockStarNum = 10, buyNeedStarNum = 0, buyCoinNum = 100, }, [3] = { id = 3, name = "钓鱼岛", unlockStarNum = 5, buyNeedStarNum = 0, buyCoinNum = 100, }, [4] = { id = 4, name = "小家", unlockStarNum = 1500, buyNeedStarNum = 1500, buyCoinNum = 300000, }, [5] = { id = 5, name = "未解锁", unlockStarNum = -1, buyNeedStarNum = -1, buyCoinNum = -1, }, } return sceneCfg XSdkConstants--[[ author:{zhangpeng} time:2023-09-25 17:34:49 ]] local XSdkConstants,_ = defClassStatic("XSdkConstants") function XSdkConstants:init() end XSdkConstants.EnvironmentType = { Development = 1, Test = 2, Staging = 3, Production = 4 } XSdkConstants.TransactionState ={ PaymentSuc = 1; -- 支付成功 PaymentFailed = 2; -- 支付失败 } XSdkConstants.PurchasedStatus = { Purchased = 1, --只对非消耗型产品有效 InTrial = 10, --只对订阅型产品有效 InSubscription = 11, --只对订阅型产品有效 Expired = 12, --只对订阅型产品有效 } XSdkConstants.ReceiptDeliverStatus = { OK = 0, ServerError = 301, NoPreparedOrder = 407, --也是成功 AlreadyBound = 408, GuestPurchaseSucceed = 409, GuestPurchaseConsumableForbidden = 410 } XSdkConstants.ExpireIntentType = { Canceled = 1, --用户取消订阅 BillingError = 2, --扣款失败 DisagreePriceIncrease = 3, --用户拒绝接受订阅商品价格上涨 ProductNotAvailable = 4, --此商品已不可购买 Unknown = 5 --未知 } XSdkConstants.IAPProductType = { Consumable = 1, --消耗型 NonConsumable = 2, --非消耗型 AutoRenewableSubscription = 3, --自动续费的订阅 NonRenewingSubscription = 4 --非自动续费的订阅 } XSdkConstants.IAPProductDiscountType = { Unsupported = -99, --优惠类型不支持,针对 iOS 11.2 以下的设备 None = -1, --无优惠,有两种情况 1. 非订阅商品 2. 订阅商品,确实无优惠 PayAsYouGo = 0, --前x个付费周期享受更低的优惠价格 PayUpFront = 1, --前x个付费周期打包购买享受折扣价格 FreeTrial = 2 --前x个付费周期免费试用 } XSdkConstants.ProductPeriodUnit = { Unsupported = -99, --订阅周期类型不支持,针对 iOS 11.2 以下的设备 None = -1, --无订阅周期,针对 非订阅商品 Day = 0, --订阅周期:天 Week = 1, --订阅周期:周 Month = 2, --订阅周期:月 Year = 3 --订阅周期:年 } XSdkConstants:init()mainrequire("common/core/app/scene/Scene") require("common/core/app/scene/SceneComponent") require("common/core/app/scene/SceneCfgList") require("common/core/app/scene/SceneCfg") require("common/core/app/scene/SceneInfo") require("common/core/app/scene/SceneMgr")mainMrequire("common/const/StrogeKeyDef") print("common.const.main.lua loaded")storydialoguireslinkreturn { --BASIC --ASSET story_dialog_ui = {"Assets/AssetsPackage/Res/modules/ui/storydialogui/prefabs/story_dialog_ui.prefab", 0, 0}, } Mine6-- 水雷 local Mine = defClass("Mine") local Sprite = typeof(CS.UnityEngine.Sprite) -- 构造函数 function Mine:ctor(worldPos, parent) self.id = 601001 self.go = GameObject.Instantiate(FishingGameMgr.resLink.mine) self.go.transform:SetParent(parent, false) self.go.transform.localPosition = worldPos or Vector3.zero self:init() self:initEvent() end -- 初始化 function Mine:init() self:initData() -- test self.sprite = self.go:Seek("image")[CS.UnityEngine.SpriteRenderer] self.sprite.sprite = ResLoader.loadAsset("Assets/AssetsPackage/Res/modules/fishing/map/image/fishing/zhangai.png", Sprite) end -- 初始化数据 function Mine:initData() local config = FishCfgParse:getFishCfgById(self.id) self.grade = config.grade end function Mine:hide() self.go:SetActive(false) end function Mine:reload(worldPos) self.go:SetActive(true) self.go.transform.localPosition = worldPos or Vector3.zero end -- 初始化事件 function Mine:initEvent() self.collider = self.go:Seek("collider") Event.add(self.collider, Event.OnTriggerEnter2D, function(other) self:onTriggerEnter(other) end) end -- 当玩家触发碰撞时 function Mine:onTriggerEnter(other) if (not FishingGameMgr.isPlaying) or FishingGameMgr.isPaused then return end if other.gameObject:CompareTag("Player") then if self.grade > FishingGameMgr:getCharacterGrade() then -- 玩家触发水雷,玩家死亡 FishingGameMgr:hurt() -- 改为减少能量 end end end return Mine MusicBbqCuisineCell) --- ---@class MusicBbqCuisineCell local MusicBbqCuisineCell = defClass("MusicBbqCuisineCell") -- UIComponent local UIImage = CS.UnityEngine.UI.Image local TMProUGUI = CS.TMPro.TextMeshProUGUI function MusicBbqCuisineCell:ctor(cuisineId, resLink, parent) self.ui = GameObject.Instantiate(resLink, parent) self.cuisineId = cuisineId self:initUI() self:showUI() end function MusicBbqCuisineCell:reload(cuisineId, parent) self.ui.transform:SetParent(parent) self.ui:SetActive(true) self.cuisineId = cuisineId self:showUI() end --- 初始化 function MusicBbqCuisineCell:initUI() -- 初始化UI元素 self.bg_lock = self.ui:Seek("bg_lock") self.bg_unlock = self.ui:Seek("bg_unlock") self.cuisine_img = self.ui:Seek("cuisine_img") self.cuisine_name = self.ui:Seek("cuisine_name") self.cuisine_price = self.ui:Seek("cuisine_price") self.cuisine_learned = self.ui:Seek("cuisine_learned") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.ui, function() self:click() end) end --- 展示ui function MusicBbqCuisineCell:showUI() local info = CuisineMgr:getCuisineInfo(self.cuisineId) local isUnlocked = info:isUnlocked() local isPurchased = info:isPurchased() self.bg_lock:SetActive(not isUnlocked) self.bg_unlock:SetActive(isUnlocked) self.cuisine_img:SetActive(isUnlocked) if isUnlocked then self.cuisine_name[TMProUGUI].text = info:getName() self.cuisine_img[UIImage].sprite = info:getIcon() util.ugui:setImageTileSize(self.cuisine_img[UIImage], 220) else self.cuisine_name[TMProUGUI].text = "?" end self.cuisine_price:SetActive(not isPurchased) self.cuisine_learned:SetActive(isPurchased) if not isPurchased then self.cuisine_price[TMProUGUI].text = info:getLearnPriceDesc() end end function MusicBbqCuisineCell:hide() self.ui:SetActive(false) end function MusicBbqCuisineCell:click() local info = CuisineMgr:getCuisineInfo(self.cuisineId) local isUnlocked = info:isUnlocked() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) if isUnlocked then MusicBbqCuisineDetailUI.new(self.cuisineId):show():showMask():enableCloseWhenClickMask():addCloseCallback(function() self:showUI() end) else PopUpUI.new(UnlockMgr:getUnlockDesc(info.unlockCondId)):show():showMask():enableCloseWhenClickMask() end end return MusicBbqCuisineCell CustomerInfo--- 顾客信息 ---@class CustomerInfo : LuaClass local CustomerInfo = defClass("CustomerInfo") local Sprite = typeof(CS.UnityEngine.Sprite) function CustomerInfo:ctor(customerId) self.id = customerId self:init() end function CustomerInfo:init() self:initData() self:initStatus() end function CustomerInfo:initData() self.config = self:getCustomerCfg() self.funList = {} for _, v in ipairs(self.config.funId) do local fun = self:getFunParamCfg(v) table.insert(self.funList, fun) fun.funValues = {fun.funParam_1, fun.funParam_2} end end function CustomerInfo:initStatus() self.state = UserDataMgr.customerUserData:getCustomerState(self.id) self.visits = UserDataMgr.customerUserData:getCustomerVisits(self.id) end --- 获取顾客配置 function CustomerInfo:getCustomerCfg() return CustomerCfgParse:getCustomerCfg(self.id) end --- 获取食物配置 function CustomerInfo:getCuisineCfgList(cuisineIds) return CookBookCfgParse:getDataByIds(cuisineIds) end --- 获取建筑配置列表 function CustomerInfo:getBuildingCfgList(buildingIds) return BuildingCfgParse:getBuildingCfgByIds(buildingIds) end --- 获取功能配置 function CustomerInfo:getFunTypeCfg(funType) return CustomerFunCfgParse:getCustomerFunCfg(funType) end --- 获取功能参数配置 function CustomerInfo:getFunParamCfg(funcId) return CustomerParamCfgParse:getCustomerParamCfg(funcId) end --- 获取名称 function CustomerInfo:getName() return self.config.name end --- 获取描述 function CustomerInfo:getDesc() return self.config.desc end function CustomerInfo:getArtRes() return self.config.artRes end function CustomerInfo:getBustIconName() return self.config.artBustRes end --- 获取性格 function CustomerInfo:getNature() return self.config.nature end --- 获取来访要求人气 function CustomerInfo:getVisitNeedStar() return self.config.visitNeedStar end --- 来访要求人气描述 function CustomerInfo:getVisitNeedStarDesc() local str = "来访需要人气达到%s" return string.format(str, self.config.visitNeedStar) end --- 图片路径 function CustomerInfo:getImgPath(name) return "Assets/AssetsPackage/Res/modules/roles/customer/images/" .. name .. ".png" end --- 获取半身图标路径 function CustomerInfo:getBustIconPath(name) return "Assets/AssetsPackage/Res/modules/roles/customer/images/bust/" .. name .. ".png" end --- 获取SpineUI路径 function CustomerInfo:getSpineUIPath(id) return "Assets/AssetsPackage/Res/modules/roles/customer/prefabs/ui/customer_" .. id .. ".prefab" end function CustomerInfo:getImg(path) if ResLoader.hasAsset(path) then return ResLoader.loadAsset(path, Sprite) end return nil end --- 获取全身图标 function CustomerInfo:getIcon() return self:getImg(self:getImgPath(self:getArtRes())) end --- 获取半身图标 function CustomerInfo:getBustIcon() return self:getImg(self:getBustIconPath(self:getBustIconName())) end --- 获取剪影图标 function CustomerInfo:getSilhouetteIcon() return self:getImg(self:getImgPath(self:getArtRes())) end --- 获取半身剪影图标 function CustomerInfo:getBustSilhouetteIcon() return self:getImg(self:getBustIconPath(self:getBustIconName())) end --- 获取SpineUI预制体 function CustomerInfo:getSpineUI() local path = self:getSpineUIPath(self.id) if ResLoader.hasAsset(path) then return ResLoader.loadAsset(path) end return nil end --- 获取来访要求物体列表集合 function CustomerInfo:getVisitItemIdLists() return self.config.visitItemId end --- 获取来访要求物体列表 function CustomerInfo:getVisitItemIdList(type) return self:getVisitItemIdLists()[type] end --- 来访要求菜品描述 function CustomerInfo:getVisitCuisineDesc() local str = "来访需要食物:" local data = self:getCuisineCfgList(self:getVisitItemIdList(CustomerConst.VisitItemType.c)) local list = {} for _, v in pairs(data) do -- 判断是否解锁 if CuisineMgr:isCuisineLearned(v.id) then table.insert(list, v.name) else table.insert(list, "???") end end return self:getTagDesc(str, list) end --- 来访要求设施描述 function CustomerInfo:getVisitFacilityDesc() local str = "来访需要设施:" local data = self:getBuildingCfgList(self:getVisitItemIdList(CustomerConst.VisitItemType.b)) local list = {} for _, v in ipairs(data) do if BuildingMgr:isBuildingPurchased(v.uid) then table.insert(list, v.name) else table.insert(list, "???") end end return self:getTagDesc(str, list) end --- 喜欢的食物 function CustomerInfo:getFavoriteFoods() return self.config.favoriteFoods end --- 喜欢的食物描述 function CustomerInfo:getFavoriteFoodsDesc(isVisited) local str = "喜欢的食物:" local data = CookBookCfgParse:getDataByIds(self.config.favoriteFoods) local list = {} for _, v in pairs(data) do -- 判断是否解锁 if isVisited then table.insert(list, v.name) else table.insert(list, "???") end end return self:getTagDesc(str, list) end --- 获取招揽条件 function CustomerInfo:getSolicitTagId() return self.config.solicitTagId end --- 招揽条件标签描述 function CustomerInfo:getSolicitTagDesc() local typeName = CustomerConst.SolicitationCustomerTypeDesc[self.config.solicitTagId] return typeName end --- 获取功能id function CustomerInfo:getFunTagIdList() return self.config.funId end --- 获取功能id function CustomerInfo:getFunTagId(funIdx) if funIdx > #self.funList then return -1 end return self.funList[funIdx].funId end --- 获取功能参数 function CustomerInfo:getFunTagValue(funIdx, paramIdx) if (funIdx > #self.funList) or (paramIdx > #self.funList.funValues) then return -1 end return self.funList[funIdx].funValues[paramIdx] end --- 获取功能id function CustomerInfo:getFunTagValues(funIdx) if funIdx > #self.funList then return -1 end local fun = self.funList[funIdx] return fun.funParam_1, fun.funParam_2 end --- 功能标签描述 function CustomerInfo:getFunTagDesc(funIdx) if funIdx > #self.funList then return "" end --local str = self:getFunTypeCfg(self:getFunTagId(funIdx)) local str = CustomerConst.FunTagDesc[self:getFunTagId(funIdx)] local p1, p2 = self:getFunTagValues(funIdx) return string.format(str, p1, p2) end --- 来访要求任务描述 function CustomerInfo:getVisitTaskDesc(num) local str = "完成订单【%s】后解锁" local name = { 5000 } -- todo 获取订单任务标题 return string.format(str, name) end --- 获取剧情id function CustomerInfo:getPlotId() return self.config.plotId end --- 获取分享获得的金币数额 function CustomerInfo:getShareCoin() return self.config.shareCoin end --- 设置状态 function CustomerInfo:setState(state) self.state = CustomerMgr.userData:setCustomerState(self.id, state) end --- 是否已来访 function CustomerInfo:isVisited() return self.state >= CustomerConst.CustomerVisitState.Visited end --- 是否首次分享 function CustomerInfo:isShared() return self.state >= CustomerConst.CustomerVisitState.Shared end --- 是否新顾客 function CustomerInfo:isNewCustomer() --return self.state < CustomerConst.CustomerVisitState.Shared return false end --- 处理标签字符串 function CustomerInfo:getTagDesc(base, list) if list then for _, v in pairs(list) do base = base .. "" .. v .. " " end return base end return nil end return CustomerInfomainrequire("common/core/base/touch/TouchCom") require("common/core/base/touch/TouchListener") require("common/core/base/touch/TouchClickListener") require("common/core/base/touch/TouchCustomListener") Msg.def("TouchComOnBegan") Msg.def("TouchComonMoved") Msg.def("TouchComOnEnd") StallConst --[[ 钓鱼中心摊位常量 author:{zhangpeng} time:2025-07-04 18:51:10 ]] local StallConst = defClassStatic("StallConst") -- 摊位序号 StallConst.ids = { BuildingConst.buildingType.booth1, BuildingConst.buildingType.booth2, BuildingConst.buildingType.booth3, BuildingConst.buildingType.booth4, BuildingConst.buildingType.booth5, BuildingConst.buildingType.booth6, } -- 摊位招到摊主后对应的摊位售卖类型 StallConst.types = { mht = 1, -- 棉花糖 rg = 2, -- 热狗摊 bmh = 3, -- 爆米花 bbt = 4,-- 刨冰摊 xxh = 5, -- 肖像画 zys = 6, -- 章鱼烧 ypt = 7, -- 饮品摊 ht = 8, -- 花摊 bjlt = 9, -- 冰激凌摊 } -- 摊位不同样式对应预设名 StallConst.stallStyle = { [StallConst.types.mht] = "tw_01_mht", -- 棉花糖 [StallConst.types.rg] = "tw_02_rg", -- 热狗摊 [StallConst.types.bmh] = "tw_03_bmh", -- 爆米花 [StallConst.types.bbt] = "tw_04_bb", -- 刨冰摊 [StallConst.types.xxh] = "tw_05_xxh", -- 肖像画 [StallConst.types.zys] = "tw_06_zys", -- 章鱼烧 [StallConst.types.ypt] = "tw_07_yp", -- 饮品摊 [StallConst.types.ht] = "tw_08_ht", -- 花摊 [StallConst.types.bjlt] = "tw_09_bql", -- 冰激凌摊 } -- 摊位状态 StallConst.State = { -- 未解锁 lock = 1, -- 解锁中 time = 2, -- 已解锁,待招租摊主 idle = 3, -- 已放置鱼,未招到摊主 fish = 4, -- 摊主已进入,运营中 used = 5, } -- 摊主角色id StallConst.vendorIds = { [StallConst.types.mht] = 490001, -- 棉花糖 [StallConst.types.rg] = 490002, -- 热狗 [StallConst.types.bmh] = 490003, -- 爆米花 [StallConst.types.bbt] = 490004, -- 刨冰摊 [StallConst.types.xxh] = 490005, -- 肖像画 [StallConst.types.zys] = 490006, -- 章鱼烧 [StallConst.types.ypt] = 490007, -- 饮品摊 [StallConst.types.ht] = 490008, -- 花摊 [StallConst.types.bjlt] = 490009, -- 冰激凌摊 } -- 摊主角色售卖类型 StallConst.vendorSaleTypes = { [490001] = StallConst.types.mht, -- 棉花糖 [490002] = StallConst.types.rg, -- 热狗 [490003] = StallConst.types.bmh, -- 爆米花 [490004] = StallConst.types.bbt, -- 刨冰摊 [490005] = StallConst.types.xxh, -- 肖像画 [490006] = StallConst.types.zys, -- 章鱼烧 [490007] = StallConst.types.ypt, -- 饮品摊 [490008] = StallConst.types.ht, -- 花摊 [490009] = StallConst.types.bjlt, -- 冰激凌摊 } return StallConstMsg--[[ 消息定义 author:{zhangpeng} time:2022-05-12 18:10:34 ]] Msg.def("APP_INIT") Msg.def("APP_INIT_FINISH") Msg.def("APP_MAIN") Msg.def("APP_EXIT") Msg.def("APP_PAUSED") Msg.def("APP_RESUME") Msg.def("APP_NATIVE") Msg.def("APP_LUAMSG") Msg.def("APP_BACKGROUND") -- 切到后台 Msg.def("APP_FOREGROUND") -- 切到前台 Msg.def("SCENE_PREPARE_LOAD") Msg.def("SCENE_BEFORE_LOAD") Msg.def("SCENE_AFTER_LOAD_SCENE") Msg.def("SCENE_ON_LOAD") Msg.def("SCENE_EXIT") Msg.def("ROLE_UPDATE") --热更begin Msg.def("HOTUPDATE_DOWNLOAD_PROGRESS") Msg.def("HOTUPDATE_DELETE_PROGRESS") Msg.def("HOTUPDATE_DOWNLOAD_QUENCE_FINISHED") -- 登录 -- Msg.def("USER_TOKEN_EXPIRED") -- 用户token失效 -- facebook Msg.def("USER_LOGIN_FB_SUC") -- facebook 登录成功 Msg.def("USER_LOGIN_FB_FAILED") -- 登录失败 Msg.def("USER_LOGIN_FB_CANCLE") -- 取消登录 -- AppleId Msg.def("USER_LOGIN_APPLE_SUC") -- appleid 登录成功 Msg.def("USER_LOGIN_APPLE_FAILED") -- 登录失败 Msg.def("USER_LOGIN_APPLE_AUTH_FILED") -- 授权失败 -- 绑定 -- Msg.def("USER_LOGIN_APPLE_BIND_SUC") -- 绑定发起Apple的登录成功 Msg.def("USER_LOGIN_FB_BIND_SUC") -- 绑定发起facebook的登录成功 Msg.def("LOGIN_SUCCESS") -- 登录成功 Msg.def("LOGIN_FAIL") Msg.def("LOGIN_CANCEL") -- 在用户取消情况下,关闭登录相关界面 -- socket msg-- Msg.def("SOCKET_MSG_ACK_SUC") --握手成功 -- 商店 Msg.def("SHOP_iOS_PURCHASE_SUC") -- 苹果服务器的支付成功回调 Msg.def("SHOP_iOS_PURCHASE_FAILED") -- 购买失败 Msg.def("SHOP_GOOGLE_PURCHASE_SUC") -- google服务器的支付成功回调 Msg.def("SHOP_GOOGLE_PURCHASE_FAILED") -- 购买失败 Msg.def("SHOP_PRODUCT_LIST_UPDATE") -- 广告 Msg.def("AD_REWARD_VIEWO_WATCH_SUC") -- 激励视频观看成功,领奖 -- 货币刷新 Msg.def("COIN_UPDATE_COUNT") -- 刷新金币数量 Msg.def("GEM_UPDATE_COUNT") -- 刷新钻石数量 -- 道具数量刷新 Msg.def("ITEM_CHANGED") -- 道具数量刷新 -- 红点 Msg.def("RED_DOT_UPDATE") -- 刷新红点 BezierMover_  -- 需要引入 Unity 的 Vector3 类型(假设在 Unity 环境中使用 xLua) local Vector3 = UnityEngine.Vector3 -- 定义贝塞尔曲线移动控制器 local BezierMover = defClass("BezierMover") -- 构造函数:初始化路径点和移动参数 -- @param go 绑定的 GameObject(用于更新位置和朝向) -- @param pathPoints 路径点列表(Vector3 数组,至少 2 个点) -- @param speed 移动速度(单位:米/秒) -- @param loop 是否循环移动 -- @param reverse 是否往返移动(需配合 loop 使用) -- @param isDirectional 是否展示转向(可选,默认为 false) -- @param offsetAngle 是否反转转向(可选,默认为 false,需配合 loop 使用) function BezierMover:ctor(go, pathPoints, speed, loop, reverse, isDirectional, offsetAngle) self.gameObject = go -- 绑定当前 GameObject self.transform = go.transform -- 获取 Transform 组件 self.pathPoints = pathPoints or {} -- 路径点列表 self.speed = speed or 1.0 -- 移动速度 self.loop = loop or false -- 默认不循环 self.reverse = reverse or false -- 默认不往返 self.isDirectional = isDirectional or false -- 是否展示转向 self.offsetAngle = offsetAngle or 0 -- 是否反转转向 self.lastPos = self.transform.localPosition -- 上一帧位置(用于计算方向) self.currentSegment = 0 -- 当前贝塞尔曲线段索引(每 4 点为一段) self.t = 0 -- 当前段内的参数(0~1) self.segmentLength = 0 -- 当前段的弧长(用于匀速控制) self.totalDistance = 0 -- 总路径长度(用于循环判断) self.lastTime = 0 -- 上一帧时间(用于计算 dt) -- 预计算所有贝塞尔曲线段的弧长(初始化时完成) self:precomputeSegmentLengths() end -- 预计算所有贝塞尔曲线段的弧长(近似计算) function BezierMover:precomputeSegmentLengths() self.segments = {} -- 存储每段贝塞尔曲线的控制点和弧长 local pointCount = #self.pathPoints -- 遍历路径点,每 4 个点生成一段三次贝塞尔曲线 for i = 1, pointCount, 3 do -- 取 4 个控制点(不足 4 个时用最后一个点补全) local p0 = self.pathPoints[i] local p1 = self.pathPoints[i+1] or p0 local p2 = self.pathPoints[i+2] or p0 local p3 = self.pathPoints[i+3] or p0 -- 计算该段的弧长(通过采样近似) local segmentLength = 0 local samples = 20 -- 采样点数(越多越精确,性能消耗越大) for j = 0, samples do local t = j / samples local pos = self:calculateBezierPoint(p0, p1, p2, p3, t) if j > 0 then segmentLength = segmentLength + (pos - self.lastPos).magnitude end self.lastPos = pos end table.insert(self.segments, { p0 = p0, p1 = p1, p2 = p2, p3 = p3, length = segmentLength }) self.totalDistance = self.totalDistance + segmentLength end end -- 计算三次贝塞尔曲线上的点(公式:B(t) = (1-t)^3P0 + 3(1-t)^2tP1 + 3(1-t)t^2P2 + t^3P3) function BezierMover:calculateBezierPoint(p0, p1, p2, p3, t) local mt = 1 - t local mt2 = mt * mt local mt3 = mt2 * mt local t2 = t * t local t3 = t2 * t return Vector3( mt3 * p0.x + 3 * mt2 * t * p1.x + 3 * mt * t2 * p2.x + t3 * p3.x, mt3 * p0.y + 3 * mt2 * t * p1.y + 3 * mt * t2 * p2.y + t3 * p3.y, mt3 * p0.z + 3 * mt2 * t * p1.z + 3 * mt * t2 * p2.z + t3 * p3.z ) end -- 更新物体位置(在 Unity 的 Update 中调用) function BezierMover:update(deltaTime) if #self.pathPoints < 2 then return end -- 至少需要 2 个点 -- 计算目标移动距离(速度 * 时间) local moveDistance = self.speed * deltaTime -- 遍历路径段,直到移动完当前段或到达终点 while moveDistance > 0 and self.currentSegment < #self.segments do local segment = self.segments[self.currentSegment + 1] -- Lua 索引从 1 开始 local remainingDistance = segment.length - self.t * segment.length if moveDistance <= remainingDistance then -- 当前段内可以完成移动 self.t = self.t + moveDistance / segment.length moveDistance = 0 else -- 当前段移动完毕,进入下一段 moveDistance = moveDistance - remainingDistance self.t = 0 self.currentSegment = self.currentSegment + 1 -- 处理循环逻辑 if self.currentSegment >= #self.segments and self.loop then self.currentSegment = 0 self.t = 0 elseif self.currentSegment >= #self.segments and not self.loop then -- 到达终点,停止移动 self.t = 1 moveDistance = 0 end end end -- 计算最终位置(处理往返逻辑) local segment = self.segments[self.currentSegment + 1] local rawPosition = self:calculateBezierPoint(segment.p0, segment.p1, segment.p2, segment.p3, self.t) -- 如果启用往返,反向时 t 从 1 递减到 0 if self.reverse and self.loop and self.currentSegment == #self.segments - 1 and self.t >= 1 then self.t = 1 - (self.t - 1) -- 反向时 t 从 1 回到 0 end -- 设置朝向 if self.isDirectional and self.lastPos then local direction = rawPosition - self.lastPos if direction.magnitude > 0 then local angle = self:atan2(direction.y, direction.x) * (180 / math.pi) self.transform.rotation = Quaternion.Euler(0, 0, angle + self.offsetAngle) --self.transform.up = direction.normalized -- 设置垂直朝向为方向向量 --self.transform.right = -direction.normalized -- 设置水平朝向为方向向量 end self.lastPos = rawPosition end -- 更新物体位置 self.transform.localPosition = rawPosition end -- 设置新的移动速度 function BezierMover:setSpeed(speed) self.speed = speed or 1.0 -- 设置新的移动速度 end -- 设置是否转向 function BezierMover:setDirectional(isDirectional, offsetAngle) self.isDirectional = isDirectional or false self.offsetAngle = offsetAngle or 0 if not self.isDirectional then self.transform.rotation = Quaternion.identity -- 如果不转向,重置旋转 else -- 如果需要转向,重置 lastPos 以便下次计算方向 self.lastPos = self.transform.localPosition end end -- 自定义 atan2 方法 function BezierMover:atan2(y, x) if x > 0 then return math.atan(y / x) elseif x < 0 and y >= 0 then return math.atan(y / x) + math.pi elseif x < 0 and y < 0 then return math.atan(y / x) - math.pi elseif x == 0 and y > 0 then return math.pi / 2 elseif x == 0 and y < 0 then return -math.pi / 2 else return 0 -- x == 0 and y == 0 end end -- 示例:在 Unity 中挂载到 GameObject 并使用 -- 1. 创建一个空 GameObject,命名为 "BezierMover" -- 2. 在 Inspector 中添加一个脚本组件,复制以下代码 -- 3. 在 Start 中初始化路径点和移动参数 --[[ local path = { Vector3(0, 0, 0), Vector3(5, 0, 0), Vector3(5, 5, 0), Vector3(10, 5, 0), Vector3(10, 0, 0), Vector3(15, 0, 0) -- 最后一个点会与前几个点组合成段 } local mover = BezierMover.New(gameObject, path, 2.0, true, false) -- 速度 2m/s,循环,不往返 function Update() mover:update(Time.deltaTime) end --]] return BezierMover RoleMgr --[[ 角色管理 author:{zhangpeng} time:2025-05-15 12:00:42 ]] local RoleMgr = defClassStatic("RoleMgr") -- 生成唯一id function RoleMgr:genUniqueId(roleType) if self.roleIdCounter == nil then self.roleIdCounter = 0 end if roleType == RoleConst.roleType.customer then local id = RoleConst.roleIdPrefix.customer .. tostring(self.roleIdCounter) self.roleIdCounter = self.roleIdCounter + 1 return id elseif roleType == RoleConst.roleType.employee then local id = RoleConst.roleIdPrefix.employee .. tostring(self.roleIdCounter) self.roleIdCounter = self.roleIdCounter + 1 return id end end -- 重置角色id计数器 function RoleMgr:resetRoleIdCounter() self.roleIdCounter = 0 end return RoleMgr SqliteQuery5 ---@class SqliteQuery:LuaClass local SqliteQuery = defClass("SqliteQuery") local LOGTAG = SqliteQuery.__cls_name function SqliteQuery:ctor(table) self.table = table end --------------------------------------------------------------------------------------------- -- 构造sql语句 --------------------------------------------------------------------------------------------- function SqliteQuery:select(...) self.selectColumnList = {...} return self end function SqliteQuery:selectDistinct(...) return self:select(...):distinct() end function SqliteQuery:distinct() local sqlFunc = self.aggregateFunList[#self.aggregateFunList] if not sqlFunc then self.isSelectDistinct = true return self end sqlFunc:distinct() return self end function SqliteQuery:count(column, asName) self.aggregateFunList = self.aggregateFunList or {} local sqlFunc = self.table:genFunc(SqliteFunc.Type.count):column(column):as(asName) table.insert(self.aggregateFunList, sqlFunc) return self end function SqliteQuery:countDistinct(column, asName) return self:count(column, asName):distinct() end function SqliteQuery:sum(column, asName) self.aggregateFunList = self.aggregateFunList or {} local sqlFunc = self.table:genFunc(SqliteFunc.Type.sum):column(column):as(asName) table.insert(self.aggregateFunList, sqlFunc) return self end function SqliteQuery:max(column, asName) self.aggregateFunList = self.aggregateFunList or {} local sqlFunc = self.table:genFunc(SqliteFunc.Type.max):column(column):as(asName) table.insert(self.aggregateFunList, sqlFunc) return self end function SqliteQuery:min(column, asName) self.aggregateFunList = self.aggregateFunList or {} local sqlFunc = self.table:genFunc(SqliteFunc.Type.min):column(column):as(asName) table.insert(self.aggregateFunList, sqlFunc) return self end function SqliteQuery:avg(column, asName) self.aggregateFunList = self.aggregateFunList or {} local sqlFunc = self.table:genFunc(SqliteFunc.Type.avg):column(column):as(asName) table.insert(self.aggregateFunList, sqlFunc) return self end function SqliteQuery:as(asName) local func = self.aggregateFunList[#self.aggregateFunList] if not func then printWarn(LOGTAG, "as, 应该在聚合函数后") return self end func:as(asName) return self end function SqliteQuery:join(sqlTable) return self:innerJoin(sqlTable) end function SqliteQuery:innerJoin(sqlTable) self.joinList = self.joinList or {} table.insert(self.joinList, {op = "INNER JOIN", table = sqlTable}) return self end function SqliteQuery:leftJoin(sqlTable) self.joinList = self.joinList or {} table.insert(self.joinList, {op = "LEFT JOIN", table = sqlTable}) return self end function SqliteQuery:rightJoin(sqlTable) self.joinList = self.joinList or {} table.insert(self.joinList, {op = "RIGHT JOIN", table = sqlTable}) return self end function SqliteQuery:fullJoin(sqlTable) self.joinList = self.joinList or {} table.insert(self.joinList, {op = "FULL JOIN", table = sqlTable}) return self end function SqliteQuery:on(column1, column2) local t = self.joinList[#self.joinList] if not t then printWarn(LOGTAG, "on, 应该在join后调用") return self end t.column1 = column1 t.column2 = column2 return self end function SqliteQuery:union(query) self.unionQuery = query return self end function SqliteQuery:unionAll(query) self.unionQuery = query self.isUnionAll = true return self end function SqliteQuery:where(condition) self.whereCondition = condition return self end function SqliteQuery:groupBy(...) self.groupColumnList = {...} return self end function SqliteQuery:haveing(condition) self.havingCondition = condition return self end function SqliteQuery:orderBy(...) self.orderColumnList = {...} return self end function SqliteQuery:asc() if self:isColumnNil(self.orderColumnList) then printWarn(LOGTAG, "asc, 应该在orderBy后调用") return end self.isDesc = false return self end function SqliteQuery:desc() if self:isColumnNil(self.orderColumnList) then printWarn(LOGTAG, "desc, 应该在orderBy后调用") return end self.isDesc = true return self end function SqliteQuery:reverse() if self:isColumnNil(self.orderColumnList) then printWarn(LOGTAG, "reverse, 应该在orderBy后调用") return end if self.isDesc == nil then self.isDesc = false end self.isDesc = not self.isDesc return self end function SqliteQuery:limit(count) self.limitCount = count return self end function SqliteQuery:offset(count) self.offsetCount = count return self end --------------------------------------------------------------------------------------------- -- 构造sql语句 where的便捷函数 --------------------------------------------------------------------------------------------- function SqliteQuery:filter(column, op, value) local oldCondition = self.whereCondition local condition = self.table:genCondition(column, op, value) if oldCondition then condition = oldCondition:xand(condition) end self:where(condition) return self end function SqliteQuery:filterEqual(column, value) return self:filter(column, SqliteCondition.CompareOp.E, value) end SqliteQuery.filterE = SqliteQuery.filterEqual function SqliteQuery:filterNotEqual(column, value) return self:filter(column, SqliteCondition.CompareOp.NE, value) end SqliteQuery.filterNE = SqliteQuery.filterNotEqual function SqliteQuery:filterLessThan(column, number) return self:filter(column, SqliteCondition.CompareOp.L, number) end SqliteQuery.filterL = SqliteQuery.filterLessThan function SqliteQuery:filterLessThanOrEqual(column, number) return self:filter(column, SqliteCondition.CompareOp.LE, number) end SqliteQuery.filterLE = SqliteQuery.filterLessThanOrEqual function SqliteQuery:filterGreaterThan(column, number) return self:filter(column, SqliteCondition.CompareOp.G, number) end SqliteQuery.filterG = SqliteQuery.filterGreaterThan function SqliteQuery:filterGreaterThanOrEqual(column, number) return self:filter(column, SqliteCondition.CompareOp.GE, number) end SqliteQuery.filterGE = SqliteQuery.filterGreaterThanOrEqual function SqliteQuery:mingle(column, op, value) local oldCondition = self.whereCondition local condition = self.table:genCondition(column, op, value) if oldCondition then condition = oldCondition:xor(condition) end self:where(condition) return self end function SqliteQuery:mingleEqual(column, value) return self:mingle(column, SqliteCondition.CompareOp.E, value) end SqliteQuery.mingleE = SqliteQuery.mingleEqual function SqliteQuery:mingleNotEqual(column, value) return self:mingle(column, SqliteCondition.CompareOp.NE, value) end SqliteQuery.mingleNE = SqliteQuery.mingleNotEqual function SqliteQuery:mingleLessThan(column, number) return self:mingle(column, SqliteCondition.CompareOp.L, number) end SqliteQuery.mingleL = SqliteQuery.mingleLessThan function SqliteQuery:mingleLessThanOrEqual(column, number) return self:mingle(column, SqliteCondition.CompareOp.LE, number) end SqliteQuery.mingleLE = SqliteQuery.mingleLessThanOrEqual function SqliteQuery:mingleGreaterThan(column, number) return self:mingle(column, SqliteCondition.CompareOp.G, number) end SqliteQuery.mingleG = SqliteQuery.mingleGreaterThan function SqliteQuery:mingleGreaterThanOrEqual(column, number) return self:mingle(column, SqliteCondition.CompareOp.GE, number) end SqliteQuery.mingleGE = SqliteQuery.mingleGreaterThanOrEqual --------------------------------------------------------------------------------------------- -- 执行sql语句 --------------------------------------------------------------------------------------------- function SqliteQuery:get(funcOrKey) local str = self:toSelectSqlStr() local list = {} -- printVerbose(LOGTAG, "get, sqlstr:%s", str) if not funcOrKey then for row in self.table.databaseApis.nrows(str) do table.insert(list, row) end elseif type(funcOrKey) == "string" then for row in self.table.databaseApis.nrows(str) do table.insert(list, row[funcOrKey]) end else for row in self.table.databaseApis.nrows(str) do table.insert(list, funcOrKey(row)) end end return list end function SqliteQuery:getFirst(funcOrKey) local list = self:get(funcOrKey) return list[1] end function SqliteQuery:getRecordList() if not self:isAggregateFunNil() or not self:isColumnNil(self.selectColumnList) then printWarn(LOGTAG, "getRecordList, 请勿使用select以及聚合函数") end local list = self:get( function(row) local record = self.table.recordCls.new(self.table) record:setSqliteData(row) self.table:setCache(record) return record end ) return list end function SqliteQuery:getFirstRecord() local list = self:getRecordList() return list[1] end function SqliteQuery:update(data) local str = self:toUpdateSqlStr(data) printVerbose(LOGTAG, "update, sqlstr:%s", str) self.table.databaseApis.exec(str) end function SqliteQuery:updateRecord(record) local data = {} for i, col in ipairs(self.table.columnList) do local key = col.name local value = record[key] if value ~= nil then data[key] = value end end self:update(data) end function SqliteQuery:delete() local str = self:toDeleteSqlStr() printVerbose(LOGTAG, "update, delete:%s", str) self.table.databaseApis.exec(str) end --------------------------------------------------------------------------------------------- -- 工具函数 --------------------------------------------------------------------------------------------- function SqliteQuery:isColumnNil(columnList) return columnList == nil or #columnList == 0 end function SqliteQuery:getColumnStr(columnList) if self:isColumnNil(columnList) then return "*" end local str = table.concat(columnList, ",") return str end function SqliteQuery:isAggregateFunNil() return self.aggregateFunList == nil or #self.aggregateFunList == 0 end function SqliteQuery:getAggregateFunStr() if self:isAggregateFunNil() then return "" end local list = {} for i, sqlFunc in ipairs(self.aggregateFunList) do table.insert(list, sqlFunc:toSqlStr()) end local str = table.concat(list, ",") return str end function SqliteQuery:canUnion() return self:isColumnNil(self.orderColumnList) end function SqliteQuery:toSelectSqlStr() local str = "SELECT" if self.isSelectDistinct then str = str .. " DISTINCT" end if not self:isAggregateFunNil() then str = string.format("%s %s\n", str, self:getAggregateFunStr()) if self:isColumnNil(self.selectColumnList) then str = string.format("%s,%s", str, self:getColumnStr(self.selectColumnList)) end else str = string.format("%s %s\n", str, self:getColumnStr(self.selectColumnList)) end str = str .. string.format("FROM %s\n", self.table:getName()) for i, v in ipairs(self.joinList or {}) do local op = v.op local name = v.table:getName() local column1 = v.column1 local colmun2 = v.column2 str = str .. string.format("%s %s\n", op, name) str = str .. string.format("ON %s = %s\n", column1, colmun2) end if self.unionQuery then if not self.unionQuery:canUnion() then printWarn(LOGTAG, "toSelectSqlStr, can not Union") end if self.isUnionAll then str = str .. "UNION ALL\n" else str = str .. "UNION\n" end str = str .. self.unionQuery:toSelectSqlStr() .. "\n" end if self.whereCondition then str = str .. string.format("WHERE %s\n", self.whereCondition:toSqlStr()) end if not self:isColumnNil(self.groupColumnList) then str = str .. string.format("GROUP BY %s\n", self:getColumnStr(self.groupColumnList)) if self.havingCondition then str = str .. string.format("HAVING %s\n", self.havingCondition:toSqlStr()) end end if not self:isColumnNil(self.orderColumnList) then local subStr = "ASC" if self.isDesc then subStr = "DESC" end str = str .. string.format("ORDER BY %s %s\n", self:getColumnStr(self.orderColumnList), subStr) end if self.limitCount and self.limitCount > 0 then str = str .. " LIMIT " .. self.limitCount end if self.offsetCount and self.offsetCount > 0 then str = str .. " OFFSET " .. self.offsetCount end return str end function SqliteQuery:toUpdateSqlStr(data) local str = string.format("UPDATE %s\nSET ", self.table:getName()) local list = {} for k, v in pairs(data) do table.insert(list, string.format("%s = %s", k, SqliteUtil:luaValueToStr(v))) end str = str .. table.concat(list, ",") .. "\n" if self.whereCondition then str = str .. string.format("WHERE %s\n", self.whereCondition:toSqlStr()) end return str end function SqliteQuery:toDeleteSqlStr() local str = string.format("DELETE FROM %s\n", self.table:getName()) if self.whereCondition then str = str .. string.format("WHERE %s\n", self.whereCondition:toSqlStr()) end return str end return SqliteQuery customerreslinkE return { --BASIC --ASSET bubble = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/bubble.prefab", 0, 0}, eat_progress = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/eat_progress.prefab", 0, 0}, customer_400001 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_400001.prefab", 0, 0}, customer_400002 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_400002.prefab", 0, 0}, customer_400003 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_400003.prefab", 0, 0}, customer_400004 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_400004.prefab", 0, 0}, customer_400005 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_400005.prefab", 0, 0}, customer_400006 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_400006.prefab", 0, 0}, customer_400007 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_400007.prefab", 0, 0}, customer_400008 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_400008.prefab", 0, 0}, customer_400009 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_400009.prefab", 0, 0}, customer_400010 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_400010.prefab", 0, 0}, customer_400011 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_400011.prefab", 0, 0}, customer_400012 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_400012.prefab", 0, 0}, customer_400013 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_400013.prefab", 0, 0}, customer_400014 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_400014.prefab", 0, 0}, customer_401001 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_401001.prefab", 0, 0}, customer_401002 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_401002.prefab", 0, 0}, customer_401003 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_401003.prefab", 0, 0}, customer_401004 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_401004.prefab", 0, 0}, customer_401005 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_401005.prefab", 0, 0}, customer_410001 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_410001.prefab", 0, 0}, customer_410002 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_410002.prefab", 0, 0}, customer_410003 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_410003.prefab", 0, 0}, customer_410004 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_410004.prefab", 0, 0}, customer_410005 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_410005.prefab", 0, 0}, customer_410006 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_410006.prefab", 0, 0}, customer_410007 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_410007.prefab", 0, 0}, customer_410008 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_410008.prefab", 0, 0}, customer_410009 = {"Assets/AssetsPackage/Res/modules/roles/customer/prefabs/customer_410009.prefab", 0, 0}, } PassivityIncomeDesk^ --[[ 被动收益建筑 author:{zhangpeng} time:2025-06-13 09:57:45 ]] local PassivityIncomeDesk,super = defClass("PassivityIncomeDesk",BuildingBase) local LOGTAG = "PassivityIncomeDesk" function PassivityIncomeDesk:ctor(args) super.ctor(self,args) self.deskId = args.deskId self.resId = "building_"..self.buildingInfo.buildingType --self.state = PassiveIncomeDesk.State.idle self.currentCustomer = nil self:initDeskView() end function PassivityIncomeDesk:initDeskView() self.buildingNode = GameObject.Instantiate(self.scene.R[self.resId]) self:setBuildingParent(self.buildingParent) self:setBuildingNodeName(self.resId) self.sortBaseNode = self.buildingNode:Seek("base") -- 设置层级 local sprite_render = self.buildingNode:Seek("img"):GetComponent("Renderer") -- sprite_render.sortingOrder = 11 -- 被动收益建筑中只有备菜区和烤炉会有金币放置点,需要点击收取,其他的金币产出进自动收银台 if self.buildingInfo.buildingType == BuildingConst.buildingType.preparea or self.buildingInfo.buildingType == BuildingConst.buildingType.oven then -- 金币放置点 self.coinPosNode = self.buildingNode:Seek("coin_pos") -- 金币收集点击区域 if self.coinPosNode then self.coinCollectNode = self.coinPosNode:Seek("touch_mask") if self.coinCollectNode then self:addCoinPosClickEvent() end end end end -- 给建筑上的金币放置点添加点击事件 function PassivityIncomeDesk:addCoinPosClickEvent() local restaurantScene = RestaurantMgr:getRestaurantScene() if not restaurantScene then printError(LOGTAG, "找不到餐厅场景") return end local touchCom = restaurantScene.touchCom touchCom:addListener( self.coinCollectNode, TouchCom.LISTENER_TYPE.CLICK, function(p) printInfo(LOGTAG, "建筑上的金币放置点被点击") local coinCount = CoinDeskMgr:getCoinCountOnDesk(self:getBuildingId()) if coinCount > 0 then -- 收集当前建筑上的所有金币 CoinDeskMgr:clearCoinsOnDesk(self:getBuildingId()) -- 播放金币收集动画和音效 if coinCount > 0 then printInfo(LOGTAG, string.format("收集了建筑%s上的%d个金币", self:getBuildingId(), coinCount)) -- todo: 播放金币收集动画和音效,增加玩家金币数量 end end end ) end return PassivityIncomeDesk debug_mainlocal LOG_TAG = "debug_main" -- luaide 调试 local debugXpCall local breakSocketHandle local platform = CS.UnityEngine.Application.platform local can_debug = (platform == CS.UnityEngine.RuntimePlatform.WindowsEditor or platform == CS.UnityEngine.RuntimePlatform.WindowsPlayer or false) local DEBUG_LUA = true--false and can_debug if DEBUG_LUA then breakSocketHandle,debugXpCall = require("common/debug/LuaDebug")("localhost",7003) printInfo(LOG_TAG,"init lua debug :",platform) end UIToastf  ---@class UIToast:UILayer local UIToast, super = defClass("UIToast", UILayer) local LOGTAG = "UIToast" local TMPUGUI = CS.TMPro.TextMeshProUGUI local MAX_WIDTH = 600 --最大宽度 local MIN_WIDTH = 242 --最大宽度 local MIN_HEIGHT = 100 --最小高度 local MIN_HEIGHT_DET = 54 --文本和高度之间的delta local SHOW_TIME = 3 --显示时间 function UIToast:ctor(content, time) super.ctor(self) self.content = content or "默认toast" self.show_time = time or SHOW_TIME self.R = Res.loadResLink("common/ui/uicoms/reslink/uitoastreslink") end function UIToast:onLoad() if self.__closed then return end self:setPriority(UILayer.UI_ORDER.TOAST) local ui = UnityEngine.GameObject.Instantiate(self.R.toast) self:addChild(ui) self.ui = ui -- self.show_time = SHOW_TIME self:addCloseCallback( function() if UIToast.current == self then UIToast.current = nil end end ) if UIToast.current then UIToast.current:close() end UIToast.current = self self:setContentText(self.content) end function UIToast:setContentText(text) local ui_text = self.ui:Seek("text")[TMPUGUI] ui_text.text = text self.ui_text = ui_text self:autoSize() self:delayHide() return self end function UIToast:delayHide() self.ui:Delay( self.show_time, function() self:close() end ) end function UIToast:autoSize() printInfo(LOGTAG, "UIToast:autoSize") local preferredHeight = self.ui_text.preferredHeight -- if preferredHeight < (MIN_HEIGHT - MIN_HEIGHT_DET) then -- return -- end local preferredWidth = self.ui_text.preferredWidth if preferredWidth > MAX_WIDTH then preferredWidth = MAX_WIDTH end if preferredWidth < MIN_WIDTH then preferredWidth = MIN_WIDTH end local transform = self.ui:Seek("bg"):GetComponent(typeof(UnityEngine.RectTransform)) transform.sizeDelta = UnityEngine.Vector2(preferredWidth + 100, preferredHeight + MIN_HEIGHT_DET) end ---@param show_time number? ---@return UIToast function UIToast:setShowTime(show_time) if self.__closed then return self end if show_time == nil or type(show_time) ~= "number" then return self end self.show_time = show_time return self end return UIToast DollMainUIj-- 玩偶主页 local DollMainUI, super = defClass("DollMainUI", UILayer) require("modules/ui/dollui/DollRowCell") -- 构造函数 function DollMainUI:ctor() super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/dollui/dolluireslink") end -- 当加载 function DollMainUI:onLoad() self.ui = GameObject.Instantiate(self.R.doll_main_ui) self:addChild(self.ui) --self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end -- 初始化UI function DollMainUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.scrollView = self.ui:Seek("Scroll View") self.content = self.ui:Seek("Content") self.to_gacha_ui = self.ui:Seek("to_gacha_ui") self.star_ui = self.ui:Seek("star_ui") self.star_num = self.star_ui:Seek("num") self.star_icon = self.star_ui:Seek("star") self.coin_ui = self.ui:Seek("coin_ui") self.coin_num = self.coin_ui:Seek("num") self.coin_icon = self.coin_ui:Seek("coin") self.music_ui = self.ui:Seek("music_ui") self.musical_notes_num = self.music_ui:Seek("num") self.musical_notes_icon = self.music_ui:Seek("icon") -- 设置UI元素的属性和事件 util.ugui.addClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) UILayerUtil:closeUIByName("GachaUI") self:close() end) util.ugui.addClickEvent(self.to_gacha_ui, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) end -- 展示UI function DollMainUI:showUI() -- 更新货币显示 self:updateStar(CurrencyMgr:getStar()) self:updateCoin(CurrencyMgr:getCoin()) self:updateMusicalNotes(CurrencyMgr:getMusicalNotes()) -- 获取玩偶配置 local configs = DollCfgParse:getData() self.rows = {} -- 创建行格子 local rowCount = math.ceil(#configs / 3) for i = 1, rowCount do local startIdx = (i - 1) * 3 + 1 local endIdx = math.min(i * 3, #configs) local ids = {} for j = startIdx, endIdx do table.insert(ids, configs[j].id) end local rowCell = self.rows[i] if rowCell then rowCell:setIds(ids) rowCell:showUI() else rowCell = DollRowCell.new(ids, self.R, self.content.transform) table.insert(self.rows, rowCell) end end end function DollMainUI:updateStar(num, animate) self.star_num:GetComponent("TextMeshProUGUI").text = tostring(num) if animate then if self.star_anim then return --self.star_icon:StopAction(self.star_anim) end self.star_anim = self.star_icon:RunAction(ua.Sequence({ ua.ScaleTo(0.2, 0.8), ua.ScaleTo(0.1, 1.2), ua.ScaleTo(0.3, 1), ua.cb(function() self.star_anim = nil end) })) end end function DollMainUI:updateMusicalNotes(num, animate) self.musical_notes_num:GetComponent("TextMeshProUGUI").text = tostring(num) if animate then self.musical_notes_icon:RunAction(ua.Sequence({ ua.ScaleTo(0.2, 0.8), ua.ScaleTo(0.1, 1.2), ua.ScaleTo(0.3, 1), ua.cb(function() self.musical_notes_anim = nil end) })) end end function DollMainUI:updateCoin(num, animate) self.coin_num:GetComponent("TextMeshProUGUI").text = tostring(num) if animate then if self.coin_anim then return --self.coin_icon:StopAction(self.coin_anim) end self.coin_anim = self.coin_icon:RunAction(ua.Sequence({ ua.ScaleTo(0.2, 0.8), ua.ScaleTo(0.1, 1.2), ua.ScaleTo(0.3, 1), ua.cb(function() self.coin_anim = nil end) })) end end return DollMainUI UnlockConst--- ---@class UnlockConst : LuaStaticClass local UnlockConst = defClassStatic("UnlockConst") function UnlockConst:init() end --- 解锁限制类型 UnlockConst.LimitType = { Star = "star", Scene = "scene" } --- 解锁限制类型描述 UnlockConst.LimitTypeDesc = { [UnlockConst.LimitType.Star] = "解锁需要人气到达%d", [UnlockConst.LimitType.Scene] = "解锁需要先解锁%s场景" } UnlockConst:init()customerParamCfg< --[[ from file:顾客参数.xlsx --]] local customerParamCfg = { [1] = { id = 40001, funId = 401, funParam_1 = 0.05, funParam_2 = 8.0, }, [2] = { id = 40002, funId = 401, funParam_1 = 0.05, funParam_2 = 9.0, }, [3] = { id = 40003, funId = 401, funParam_1 = 0.1, funParam_2 = 10.0, }, [4] = { id = 40004, funId = 401, funParam_1 = 0.1, funParam_2 = 12.0, }, [5] = { id = 40005, funId = 401, funParam_1 = 0.15, funParam_2 = 10.0, }, [6] = { id = 40006, funId = 401, funParam_1 = 0.15, funParam_2 = 12.0, }, [7] = { id = 40007, funId = 401, funParam_1 = 0.2, funParam_2 = 10.0, }, [8] = { id = 40008, funId = 401, funParam_1 = 0.2, funParam_2 = 12.0, }, [9] = { id = 40009, funId = 401, funParam_1 = 0.25, funParam_2 = 13.0, }, [10] = { id = 40010, funId = 401, funParam_1 = 0.25, funParam_2 = 14.0, }, [11] = { id = 40011, funId = 402, funParam_1 = 0.1, funParam_2 = 1.0, }, [12] = { id = 40012, funId = 402, funParam_1 = 0.1, funParam_2 = 2.0, }, [13] = { id = 40013, funId = 402, funParam_1 = 0.15, funParam_2 = 1.0, }, [14] = { id = 40014, funId = 402, funParam_1 = 0.15, funParam_2 = 2.0, }, [15] = { id = 40015, funId = 402, funParam_1 = 0.2, funParam_2 = 1.0, }, [16] = { id = 40016, funId = 402, funParam_1 = 0.2, funParam_2 = 2.0, }, [17] = { id = 40017, funId = 402, funParam_1 = 0.2, funParam_2 = 3.0, }, [18] = { id = 40018, funId = 402, funParam_1 = 0.15, funParam_2 = 3.0, }, [19] = { id = 40019, funId = 402, funParam_1 = 0.25, funParam_2 = 2.0, }, [20] = { id = 40020, funId = 402, funParam_1 = 0.25, funParam_2 = 3.0, }, [21] = { id = 40021, funId = 403, funParam_1 = 3.0, funParam_2 = 0.2, }, [22] = { id = 40022, funId = 403, funParam_1 = 4.0, funParam_2 = 0.2, }, [23] = { id = 40023, funId = 403, funParam_1 = 5.0, funParam_2 = 0.2, }, [24] = { id = 40024, funId = 403, funParam_1 = 6.0, funParam_2 = 0.2, }, [25] = { id = 40025, funId = 403, funParam_1 = 7.0, funParam_2 = 0.3, }, [26] = { id = 40026, funId = 403, funParam_1 = 8.0, funParam_2 = 0.3, }, [27] = { id = 40027, funId = 403, funParam_1 = 9.0, funParam_2 = 0.3, }, [28] = { id = 40028, funId = 403, funParam_1 = 10.0, funParam_2 = 0.3, }, [29] = { id = 40029, funId = 403, funParam_1 = 11.0, funParam_2 = 0.3, }, [30] = { id = 40030, funId = 403, funParam_1 = 12.0, funParam_2 = 0.3, }, } return customerParamCfg CuisineIconMgrs--[[ 菜品图标管理,UI界面用到的图标,图标创建等 author:{zhangpeng} time:2025-05-17 12:05:24 ]] local CuisineIconMgr = defClassStatic("CuisineIconMgr") function CuisineIconMgr:init() end function CuisineIconMgr:createCuisineIcon(cuisineId) local cuisineIcon = CuisineIcon.new() return cuisineIcon end return CuisineIconMgr buildingCfg--[[ from file:建筑表.xlsx --]] local buildingCfg = { [1] = { uid = 100101, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌", desc = "简约自然,常见的小桌子", buildingType = 104, effectId = 120101, buyConditionId = 110101, unlockConditionId = -1, themeId = 1, mapId = 1, }, [2] = { uid = 100102, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅱ", desc = "实木材质的桌子,结实耐用", buildingType = 104, effectId = 120102, buyConditionId = 110102, unlockConditionId = -1, themeId = 2, mapId = 1, }, [3] = { uid = 100103, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅲ", desc = "实木材质的桌子,结实耐用", buildingType = 104, effectId = 120103, buyConditionId = 110103, unlockConditionId = -1, themeId = 3, mapId = 1, }, [4] = { uid = 100104, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅳ", desc = "实木材质的桌子,结实耐用", buildingType = 104, effectId = 120104, buyConditionId = 110104, unlockConditionId = -1, themeId = 4, mapId = 1, }, [5] = { uid = 100105, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅴ", desc = "实木材质的桌子,结实耐用", buildingType = 104, effectId = 120105, buyConditionId = 110105, unlockConditionId = -1, themeId = 5, mapId = 1, }, [6] = { uid = 100201, resid = "bd01_shouyintai_01", uiresid = "ui01_shouyintai_01", name = "简易迎宾台", desc = "简约大方,木质的很结实", buildingType = 105, effectId = 120201, buyConditionId = 110201, unlockConditionId = -1, themeId = 1, mapId = 1, }, [7] = { uid = 100202, resid = "bd01_shouyintai_01", uiresid = "ui01_shouyintai_01", name = "简易迎宾台Ⅱ", desc = "高级的木材加上精美的雕工,很显品质", buildingType = 105, effectId = 120202, buyConditionId = 110202, unlockConditionId = -1, themeId = 2, mapId = 1, }, [8] = { uid = 100203, resid = "bd01_shouyintai_01", uiresid = "ui01_shouyintai_01", name = "简易迎宾台Ⅲ", desc = "高级的木材加上精美的雕工,很显品质", buildingType = 105, effectId = 120203, buyConditionId = 110203, unlockConditionId = -1, themeId = 3, mapId = 1, }, [9] = { uid = 100204, resid = "bd01_shouyintai_01", uiresid = "ui01_shouyintai_01", name = "简易迎宾台Ⅳ", desc = "高级的木材加上精美的雕工,很显品质", buildingType = 105, effectId = 120204, buyConditionId = 110204, unlockConditionId = -1, themeId = 4, mapId = 1, }, [10] = { uid = 100205, resid = "bd01_shouyintai_01", uiresid = "ui01_shouyintai_01", name = "简易迎宾台Ⅴ", desc = "高级的木材加上精美的雕工,很显品质", buildingType = 105, effectId = 120205, buyConditionId = 110205, unlockConditionId = -1, themeId = 5, mapId = 1, }, [11] = { uid = 100301, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌", desc = "简约自然,常见的小桌子", buildingType = 106, effectId = 120301, buyConditionId = 110301, unlockConditionId = -1, themeId = 1, mapId = 1, }, [12] = { uid = 100302, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅱ", desc = "实木材质的桌子,结实耐用", buildingType = 106, effectId = 120302, buyConditionId = 110302, unlockConditionId = -1, themeId = 2, mapId = 1, }, [13] = { uid = 100303, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅲ", desc = "实木材质的桌子,结实耐用", buildingType = 106, effectId = 120303, buyConditionId = 110303, unlockConditionId = -1, themeId = 3, mapId = 1, }, [14] = { uid = 100304, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅳ", desc = "实木材质的桌子,结实耐用", buildingType = 106, effectId = 120304, buyConditionId = 110304, unlockConditionId = -1, themeId = 4, mapId = 1, }, [15] = { uid = 100305, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅴ", desc = "实木材质的桌子,结实耐用", buildingType = 106, effectId = 120305, buyConditionId = 110305, unlockConditionId = -1, themeId = 5, mapId = 1, }, [16] = { uid = 100401, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌", desc = "简约自然,常见的小桌子", buildingType = 107, effectId = 120401, buyConditionId = 110401, unlockConditionId = -1, themeId = 1, mapId = 1, }, [17] = { uid = 100402, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅱ", desc = "实木材质的桌子,结实耐用", buildingType = 107, effectId = 120402, buyConditionId = 110402, unlockConditionId = -1, themeId = 2, mapId = 1, }, [18] = { uid = 100403, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅲ", desc = "实木材质的桌子,结实耐用", buildingType = 107, effectId = 120403, buyConditionId = 110403, unlockConditionId = -1, themeId = 3, mapId = 1, }, [19] = { uid = 100404, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅳ", desc = "实木材质的桌子,结实耐用", buildingType = 107, effectId = 120404, buyConditionId = 110404, unlockConditionId = -1, themeId = 4, mapId = 1, }, [20] = { uid = 100405, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅴ", desc = "实木材质的桌子,结实耐用", buildingType = 107, effectId = 120405, buyConditionId = 110405, unlockConditionId = -1, themeId = 5, mapId = 1, }, [21] = { uid = 100501, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌", desc = "简约自然,常见的小桌子", buildingType = 108, effectId = 120501, buyConditionId = 110501, unlockConditionId = -1, themeId = 1, mapId = 1, }, [22] = { uid = 100502, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅱ", desc = "实木材质的桌子,结实耐用", buildingType = 108, effectId = 120502, buyConditionId = 110502, unlockConditionId = -1, themeId = 2, mapId = 1, }, [23] = { uid = 100503, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅲ", desc = "实木材质的桌子,结实耐用", buildingType = 108, effectId = 120503, buyConditionId = 110503, unlockConditionId = -1, themeId = 3, mapId = 1, }, [24] = { uid = 100504, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅳ", desc = "实木材质的桌子,结实耐用", buildingType = 108, effectId = 120504, buyConditionId = 110504, unlockConditionId = -1, themeId = 4, mapId = 1, }, [25] = { uid = 100505, resid = "bd01_zhuoyi_01", uiresid = "ui01_zhuoyi_01", name = "原木餐桌Ⅴ", desc = "实木材质的桌子,结实耐用", buildingType = 108, effectId = 120505, buyConditionId = 110505, unlockConditionId = -1, themeId = 5, mapId = 1, }, [26] = { uid = 100901, resid = "bd01_jiutong_01", uiresid = "ui01_jiutong_01", name = "原木酒桶", desc = "小麦啤酒的口感真不错", buildingType = 114, effectId = 120901, buyConditionId = 110901, unlockConditionId = -1, themeId = 1, mapId = 1, }, [27] = { uid = 100902, resid = "bd01_jiutong_01", uiresid = "ui01_jiutong_01", name = "原木酒桶Ⅱ", desc = "看桶就知道酒的口感很好", buildingType = 114, effectId = 120902, buyConditionId = 110902, unlockConditionId = -1, themeId = 2, mapId = 1, }, [28] = { uid = 100903, resid = "bd01_jiutong_01", uiresid = "ui01_jiutong_01", name = "原木酒桶Ⅲ", desc = "看桶就知道酒的口感很好", buildingType = 114, effectId = 120903, buyConditionId = 110903, unlockConditionId = -1, themeId = 3, mapId = 1, }, [29] = { uid = 100904, resid = "bd01_jiutong_01", uiresid = "ui01_jiutong_01", name = "原木酒桶Ⅳ", desc = "看桶就知道酒的口感很好", buildingType = 114, effectId = 120904, buyConditionId = 110904, unlockConditionId = -1, themeId = 4, mapId = 1, }, [30] = { uid = 100905, resid = "bd01_jiutong_01", uiresid = "ui01_jiutong_01", name = "原木酒桶Ⅴ", desc = "看桶就知道酒的口感很好", buildingType = 114, effectId = 120905, buyConditionId = 110905, unlockConditionId = -1, themeId = 5, mapId = 1, }, [31] = { uid = 101001, resid = "bd01_dianying_01", uiresid = "ui01_dianying_01", name = "木架影院", desc = "看到幕布就想起了小时候", buildingType = 115, effectId = 121001, buyConditionId = 111001, unlockConditionId = -1, themeId = 1, mapId = 1, }, [32] = { uid = 101002, resid = "bd01_dianying_01", uiresid = "ui01_dianying_01", name = "木架影院Ⅱ", desc = "精致的电影幕布,携带很方便", buildingType = 115, effectId = 121002, buyConditionId = 111002, unlockConditionId = -1, themeId = 2, mapId = 1, }, [33] = { uid = 101003, resid = "bd01_dianying_01", uiresid = "ui01_dianying_01", name = "木架影院Ⅲ", desc = "精致的电影幕布,携带很方便", buildingType = 115, effectId = 121003, buyConditionId = 111003, unlockConditionId = -1, themeId = 3, mapId = 1, }, [34] = { uid = 101004, resid = "bd01_dianying_01", uiresid = "ui01_dianying_01", name = "木架影院Ⅳ", desc = "精致的电影幕布,携带很方便", buildingType = 115, effectId = 121004, buyConditionId = 111004, unlockConditionId = -1, themeId = 4, mapId = 1, }, [35] = { uid = 101005, resid = "bd01_dianying_01", uiresid = "ui01_dianying_01", name = "木架影院Ⅴ", desc = "精致的电影幕布,携带很方便", buildingType = 115, effectId = 121005, buyConditionId = 111005, unlockConditionId = -1, themeId = 5, mapId = 1, }, [36] = { uid = 101101, resid = "bd01_shuiguotai_01", uiresid = "ui01_shuiguotai_01", name = "原木水果台", desc = "满满的新鲜水果", buildingType = 119, effectId = 121101, buyConditionId = 111101, unlockConditionId = -1, themeId = 1, mapId = 1, }, [37] = { uid = 101102, resid = "bd01_shuiguotai_01", uiresid = "ui01_shuiguotai_01", name = "原木水果台Ⅱ", desc = "餐后来点水果真的很不错", buildingType = 119, effectId = 121102, buyConditionId = 111102, unlockConditionId = -1, themeId = 2, mapId = 1, }, [38] = { uid = 101103, resid = "bd01_shuiguotai_01", uiresid = "ui01_shuiguotai_01", name = "原木水果台Ⅲ", desc = "餐后来点水果真的很不错", buildingType = 119, effectId = 121103, buyConditionId = 111103, unlockConditionId = -1, themeId = 3, mapId = 1, }, [39] = { uid = 101104, resid = "bd01_shuiguotai_01", uiresid = "ui01_shuiguotai_01", name = "原木水果台Ⅳ", desc = "餐后来点水果真的很不错", buildingType = 119, effectId = 121104, buyConditionId = 111104, unlockConditionId = -1, themeId = 4, mapId = 1, }, [40] = { uid = 101105, resid = "bd01_shuiguotai_01", uiresid = "ui01_shuiguotai_01", name = "原木水果台Ⅴ", desc = "餐后来点水果真的很不错", buildingType = 119, effectId = 121105, buyConditionId = 111105, unlockConditionId = -1, themeId = 5, mapId = 1, }, [41] = { uid = 101301, resid = "bd01_huajia_01", uiresid = "ui01_huajia_01", name = "简易花架", desc = "虽然花还没开,但是很期待开了后的美丽", buildingType = 121, effectId = 121301, buyConditionId = 111301, unlockConditionId = -1, themeId = 1, mapId = 1, }, [42] = { uid = 101302, resid = "bd01_huajia_01", uiresid = "ui01_huajia_01", name = "简易花架Ⅱ", desc = "木质的花架搭配上鲜花真的很好看", buildingType = 121, effectId = 121302, buyConditionId = 111302, unlockConditionId = -1, themeId = 2, mapId = 1, }, [43] = { uid = 101303, resid = "bd01_huajia_01", uiresid = "ui01_huajia_01", name = "简易花架Ⅲ", desc = "木质的花架搭配上鲜花真的很好看", buildingType = 121, effectId = 121303, buyConditionId = 111303, unlockConditionId = -1, themeId = 3, mapId = 1, }, [44] = { uid = 101304, resid = "bd01_huajia_01", uiresid = "ui01_huajia_01", name = "简易花架Ⅳ", desc = "木质的花架搭配上鲜花真的很好看", buildingType = 121, effectId = 121304, buyConditionId = 111304, unlockConditionId = -1, themeId = 4, mapId = 1, }, [45] = { uid = 101305, resid = "bd01_huajia_01", uiresid = "ui01_huajia_01", name = "简易花架Ⅴ", desc = "木质的花架搭配上鲜花真的很好看", buildingType = 121, effectId = 121305, buyConditionId = 111305, unlockConditionId = -1, themeId = 5, mapId = 1, }, [46] = { uid = 101401, resid = "bd01_ditan_01", uiresid = "ui01_ditan_01", name = "圈绒地毯", desc = "非常好搭理的一种地毯", buildingType = 101, effectId = 121401, buyConditionId = 111401, unlockConditionId = -1, themeId = 1, mapId = 1, }, [47] = { uid = 101402, resid = "bd01_ditan_01", uiresid = "ui01_ditan_01", name = "圈绒地毯Ⅱ", desc = "纯手工打造的地毯,很高级", buildingType = 101, effectId = 121402, buyConditionId = 111402, unlockConditionId = -1, themeId = 2, mapId = 1, }, [48] = { uid = 101403, resid = "bd01_ditan_01", uiresid = "ui01_ditan_01", name = "圈绒地毯Ⅲ", desc = "纯手工打造的地毯,很高级", buildingType = 101, effectId = 121403, buyConditionId = 111403, unlockConditionId = -1, themeId = 3, mapId = 1, }, [49] = { uid = 101404, resid = "bd01_ditan_01", uiresid = "ui01_ditan_01", name = "圈绒地毯Ⅳ", desc = "纯手工打造的地毯,很高级", buildingType = 101, effectId = 121404, buyConditionId = 111404, unlockConditionId = -1, themeId = 4, mapId = 1, }, [50] = { uid = 101405, resid = "bd01_ditan_01", uiresid = "ui01_ditan_01", name = "圈绒地毯Ⅴ", desc = "纯手工打造的地毯,很高级", buildingType = 101, effectId = 121405, buyConditionId = 111405, unlockConditionId = -1, themeId = 5, mapId = 1, }, [51] = { uid = 101501, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台", desc = "简单的灶台,炉火较小", buildingType = 102, effectId = 121501, buyConditionId = 111501, unlockConditionId = -1, themeId = 1, mapId = 1, }, [52] = { uid = 101502, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台Ⅱ", desc = "精致的原木灶台,炉火很旺", buildingType = 102, effectId = 121502, buyConditionId = 111502, unlockConditionId = -1, themeId = 2, mapId = 1, }, [53] = { uid = 101503, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台Ⅲ", desc = "精致的原木灶台,炉火很旺", buildingType = 102, effectId = 121503, buyConditionId = 111503, unlockConditionId = -1, themeId = 3, mapId = 1, }, [54] = { uid = 101504, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台Ⅳ", desc = "精致的原木灶台,炉火很旺", buildingType = 102, effectId = 121504, buyConditionId = 111504, unlockConditionId = -1, themeId = 4, mapId = 1, }, [55] = { uid = 101505, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台Ⅴ", desc = "精致的原木灶台,炉火很旺", buildingType = 102, effectId = 121505, buyConditionId = 111505, unlockConditionId = -1, themeId = 5, mapId = 1, }, [56] = { uid = 101601, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台", desc = "简单的灶台,炉火较小", buildingType = 103, effectId = 121601, buyConditionId = 111601, unlockConditionId = -1, themeId = 1, mapId = 1, }, [57] = { uid = 101602, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台Ⅱ", desc = "精致的原木灶台,炉火很旺", buildingType = 103, effectId = 121602, buyConditionId = 111602, unlockConditionId = -1, themeId = 2, mapId = 1, }, [58] = { uid = 101603, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台Ⅲ", desc = "精致的原木灶台,炉火很旺", buildingType = 103, effectId = 121603, buyConditionId = 111603, unlockConditionId = -1, themeId = 3, mapId = 1, }, [59] = { uid = 101604, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台Ⅳ", desc = "精致的原木灶台,炉火很旺", buildingType = 103, effectId = 121604, buyConditionId = 111604, unlockConditionId = -1, themeId = 4, mapId = 1, }, [60] = { uid = 101605, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台Ⅴ", desc = "精致的原木灶台,炉火很旺", buildingType = 103, effectId = 121605, buyConditionId = 111605, unlockConditionId = -1, themeId = 5, mapId = 1, }, [61] = { uid = 101701, resid = "bd01_beicai_01", uiresid = "ui01_beicai_01", name = "老式备菜区", desc = "木板搭建而成,有它洗菜很方便", buildingType = 110, effectId = 121701, buyConditionId = 111701, unlockConditionId = -1, themeId = 1, mapId = 1, }, [62] = { uid = 101702, resid = "bd01_beicai_01", uiresid = "ui01_beicai_01", name = "老式备菜区Ⅱ", desc = "原木制成,又好看又结实", buildingType = 110, effectId = 121702, buyConditionId = 111702, unlockConditionId = -1, themeId = 2, mapId = 1, }, [63] = { uid = 101703, resid = "bd01_beicai_01", uiresid = "ui01_beicai_01", name = "老式备菜区Ⅲ", desc = "原木制成,又好看又结实", buildingType = 110, effectId = 121703, buyConditionId = 111703, unlockConditionId = -1, themeId = 3, mapId = 1, }, [64] = { uid = 101704, resid = "bd01_beicai_01", uiresid = "ui01_beicai_01", name = "老式备菜区Ⅳ", desc = "原木制成,又好看又结实", buildingType = 110, effectId = 121704, buyConditionId = 111704, unlockConditionId = -1, themeId = 4, mapId = 1, }, [65] = { uid = 101705, resid = "bd01_beicai_01", uiresid = "ui01_beicai_01", name = "老式备菜区Ⅴ", desc = "原木制成,又好看又结实", buildingType = 110, effectId = 121705, buyConditionId = 111705, unlockConditionId = -1, themeId = 5, mapId = 1, }, [66] = { uid = 101801, resid = "bd01_wangui_01", uiresid = "ui01_wangui_01", name = "原木碗柜", desc = "虽然不豪华,但是很实用", buildingType = 111, effectId = 121801, buyConditionId = 111801, unlockConditionId = -1, themeId = 1, mapId = 1, }, [67] = { uid = 101802, resid = "bd01_wangui_01", uiresid = "ui01_wangui_01", name = "原木碗柜Ⅱ", desc = "原木制作而成,很结实", buildingType = 111, effectId = 121802, buyConditionId = 111802, unlockConditionId = -1, themeId = 2, mapId = 1, }, [68] = { uid = 101803, resid = "bd01_wangui_01", uiresid = "ui01_wangui_01", name = "原木碗柜Ⅲ", desc = "原木制作而成,很结实", buildingType = 111, effectId = 121803, buyConditionId = 111803, unlockConditionId = -1, themeId = 3, mapId = 1, }, [69] = { uid = 101804, resid = "bd01_wangui_01", uiresid = "ui01_wangui_01", name = "原木碗柜Ⅳ", desc = "原木制作而成,很结实", buildingType = 111, effectId = 121804, buyConditionId = 111804, unlockConditionId = -1, themeId = 4, mapId = 1, }, [70] = { uid = 101805, resid = "bd01_wangui_01", uiresid = "ui01_wangui_01", name = "原木碗柜Ⅴ", desc = "原木制作而成,很结实", buildingType = 111, effectId = 121805, buyConditionId = 111805, unlockConditionId = -1, themeId = 5, mapId = 1, }, [71] = { uid = 101901, resid = "bd01_bingxiang _01", uiresid = "ui01_bingxiang_01", name = "家用冰箱", desc = "常见的普通家用冰箱", buildingType = 116, effectId = 121901, buyConditionId = 111901, unlockConditionId = -1, themeId = 1, mapId = 1, }, [72] = { uid = 101902, resid = "bd01_bingxiang _01", uiresid = "ui01_bingxiang_01", name = "家用冰箱Ⅱ", desc = "智能冰箱的保鲜效果更好", buildingType = 116, effectId = 121902, buyConditionId = 111902, unlockConditionId = -1, themeId = 2, mapId = 1, }, [73] = { uid = 101903, resid = "bd01_bingxiang _01", uiresid = "ui01_bingxiang_01", name = "家用冰箱Ⅲ", desc = "智能冰箱的保鲜效果更好", buildingType = 116, effectId = 121903, buyConditionId = 111903, unlockConditionId = -1, themeId = 3, mapId = 1, }, [74] = { uid = 101904, resid = "bd01_bingxiang _01", uiresid = "ui01_bingxiang_01", name = "家用冰箱Ⅳ", desc = "智能冰箱的保鲜效果更好", buildingType = 116, effectId = 121904, buyConditionId = 111904, unlockConditionId = -1, themeId = 4, mapId = 1, }, [75] = { uid = 101905, resid = "bd01_bingxiang _01", uiresid = "ui01_bingxiang_01", name = "家用冰箱Ⅴ", desc = "智能冰箱的保鲜效果更好", buildingType = 116, effectId = 121905, buyConditionId = 111905, unlockConditionId = -1, themeId = 5, mapId = 1, }, [76] = { uid = 102001, resid = "bd01_huogui_01", uiresid = "ui01_huogui_01", name = "简易货柜", desc = "简易的货柜,用来储存粮食蔬菜", buildingType = 117, effectId = 122001, buyConditionId = 112001, unlockConditionId = -1, themeId = 1, mapId = 1, }, [77] = { uid = 102002, resid = "bd01_huogui_01", uiresid = "ui01_huogui_01", name = "简易货柜Ⅱ", desc = "结实的原木制成,容量更大", buildingType = 117, effectId = 122002, buyConditionId = 112002, unlockConditionId = -1, themeId = 2, mapId = 1, }, [78] = { uid = 102003, resid = "bd01_huogui_01", uiresid = "ui01_huogui_01", name = "简易货柜Ⅲ", desc = "结实的原木制成,容量更大", buildingType = 117, effectId = 122003, buyConditionId = 112003, unlockConditionId = -1, themeId = 3, mapId = 1, }, [79] = { uid = 102004, resid = "bd01_huogui_01", uiresid = "ui01_huogui_01", name = "简易货柜Ⅳ", desc = "结实的原木制成,容量更大", buildingType = 117, effectId = 122004, buyConditionId = 112004, unlockConditionId = -1, themeId = 4, mapId = 1, }, [80] = { uid = 102005, resid = "bd01_huogui_01", uiresid = "ui01_huogui_01", name = "简易货柜Ⅴ", desc = "结实的原木制成,容量更大", buildingType = 117, effectId = 122005, buyConditionId = 112005, unlockConditionId = -1, themeId = 5, mapId = 1, }, [81] = { uid = 102101, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台", desc = "简单的灶台,炉火较小", buildingType = 124, effectId = 122101, buyConditionId = 112101, unlockConditionId = -1, themeId = 1, mapId = 1, }, [82] = { uid = 102102, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台Ⅱ", desc = "精致的原木灶台,炉火很旺", buildingType = 124, effectId = 122102, buyConditionId = 112102, unlockConditionId = -1, themeId = 2, mapId = 1, }, [83] = { uid = 102103, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台Ⅲ", desc = "精致的原木灶台,炉火很旺", buildingType = 124, effectId = 122103, buyConditionId = 112103, unlockConditionId = -1, themeId = 3, mapId = 1, }, [84] = { uid = 102104, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台Ⅳ", desc = "精致的原木灶台,炉火很旺", buildingType = 124, effectId = 122104, buyConditionId = 112104, unlockConditionId = -1, themeId = 4, mapId = 1, }, [85] = { uid = 102105, resid = "bd01_zaotai_01", uiresid = "ui01_zaotai_01", name = "简易灶台Ⅴ", desc = "精致的原木灶台,炉火很旺", buildingType = 124, effectId = 122105, buyConditionId = 112105, unlockConditionId = -1, themeId = 5, mapId = 1, }, [86] = { uid = 102201, resid = "bd01_kaolu_01", uiresid = "ui01_kaolu_01", name = "老式烤炉", desc = "温度很高,烤出来的食物很好吃", buildingType = 122, effectId = 122201, buyConditionId = 112201, unlockConditionId = -1, themeId = 1, mapId = 1, }, [87] = { uid = 102202, resid = "bd01_kaolu_01", uiresid = "ui01_kaolu_01", name = "老式烤炉Ⅱ", desc = "砖砌成的炉子,好看又好用", buildingType = 122, effectId = 122202, buyConditionId = 112202, unlockConditionId = -1, themeId = 2, mapId = 1, }, [88] = { uid = 102203, resid = "bd01_kaolu_01", uiresid = "ui01_kaolu_01", name = "老式烤炉Ⅲ", desc = "砖砌成的炉子,好看又好用", buildingType = 122, effectId = 122203, buyConditionId = 112203, unlockConditionId = -1, themeId = 3, mapId = 1, }, [89] = { uid = 102204, resid = "bd01_kaolu_01", uiresid = "ui01_kaolu_01", name = "老式烤炉Ⅳ", desc = "砖砌成的炉子,好看又好用", buildingType = 122, effectId = 122204, buyConditionId = 112204, unlockConditionId = -1, themeId = 4, mapId = 1, }, [90] = { uid = 102205, resid = "bd01_kaolu_01", uiresid = "ui01_kaolu_01", name = "老式烤炉Ⅴ", desc = "砖砌成的炉子,好看又好用", buildingType = 122, effectId = 122205, buyConditionId = 112205, unlockConditionId = -1, themeId = 5, mapId = 1, }, [91] = { uid = 102301, resid = "bd01_shuichi_01", uiresid = "ui01_shuichi_01", name = "简易水池", desc = "虽然简易,但是很干净", buildingType = 123, effectId = 122301, buyConditionId = 112301, unlockConditionId = -1, themeId = 1, mapId = 1, }, [92] = { uid = 102302, resid = "bd01_shuichi_01", uiresid = "ui01_shuichi_01", name = "简易水池Ⅱ", desc = "原木制作而成,用起来很方便", buildingType = 123, effectId = 122302, buyConditionId = 112302, unlockConditionId = -1, themeId = 2, mapId = 1, }, [93] = { uid = 102303, resid = "bd01_shuichi_01", uiresid = "ui01_shuichi_01", name = "简易水池Ⅲ", desc = "原木制作而成,用起来很方便", buildingType = 123, effectId = 122303, buyConditionId = 112303, unlockConditionId = -1, themeId = 3, mapId = 1, }, [94] = { uid = 102304, resid = "bd01_shuichi_01", uiresid = "ui01_shuichi_01", name = "简易水池Ⅳ", desc = "原木制作而成,用起来很方便", buildingType = 123, effectId = 122304, buyConditionId = 112304, unlockConditionId = -1, themeId = 4, mapId = 1, }, [95] = { uid = 102305, resid = "bd01_shuichi_01", uiresid = "ui01_shuichi_01", name = "简易水池Ⅴ", desc = "原木制作而成,用起来很方便", buildingType = 123, effectId = 122305, buyConditionId = 112305, unlockConditionId = -1, themeId = 5, mapId = 1, }, [96] = { uid = 102401, resid = "bd01_shuiba_01", uiresid = "ui01_shuiba_01", name = "简易水吧台", desc = "木板搭建而成,摆放着各种饮料机器", buildingType = 120, effectId = 122401, buyConditionId = 112401, unlockConditionId = -1, themeId = 1, mapId = 1, }, [97] = { uid = 102402, resid = "bd01_shuiba_01", uiresid = "ui01_shuiba_01", name = "简易水吧台Ⅱ", desc = "原木制成,看上就去很有格调", buildingType = 120, effectId = 122402, buyConditionId = 112402, unlockConditionId = -1, themeId = 2, mapId = 1, }, [98] = { uid = 102403, resid = "bd01_shuiba_01", uiresid = "ui01_shuiba_01", name = "简易水吧台Ⅲ", desc = "原木制成,看上就去很有格调", buildingType = 120, effectId = 122403, buyConditionId = 112403, unlockConditionId = -1, themeId = 3, mapId = 1, }, [99] = { uid = 102404, resid = "bd01_shuiba_01", uiresid = "ui01_shuiba_01", name = "简易水吧台Ⅳ", desc = "原木制成,看上就去很有格调", buildingType = 120, effectId = 122404, buyConditionId = 112404, unlockConditionId = -1, themeId = 4, mapId = 1, }, [100] = { uid = 102405, resid = "bd01_shuiba_01", uiresid = "ui01_shuiba_01", name = "简易水吧台Ⅴ", desc = "原木制成,看上就去很有格调", buildingType = 120, effectId = 122405, buyConditionId = 112405, unlockConditionId = -1, themeId = 5, mapId = 1, }, [101] = { uid = 105101, resid = "bd02_shaokaotan_01", uiresid = "ui02_shaokaotan_01", name = "简约烧烤架", desc = "实用又美观,为烧烤添加了一份自然风味", buildingType = 151, effectId = 125101, buyConditionId = 115101, unlockConditionId = -1, themeId = 1, mapId = 2, }, [102] = { uid = 105102, resid = "bd02_shaokaotan_01", uiresid = "ui02_shaokaotan_01", name = "简约烧烤架Ⅱ", desc = "精致设计,耐用且美观的烧烤架", buildingType = 151, effectId = 125102, buyConditionId = 115102, unlockConditionId = -1, themeId = 2, mapId = 2, }, [103] = { uid = 105103, resid = "bd02_shaokaotan_01", uiresid = "ui02_shaokaotan_01", name = "简约烧烤架Ⅲ", desc = "1号烧烤架", buildingType = 151, effectId = 125103, buyConditionId = 115103, unlockConditionId = -1, themeId = 3, mapId = 2, }, [104] = { uid = 105201, resid = "bd02_wutai_01", uiresid = "ui02_wutai_01", name = "原木舞台", desc = "原木搭建的舞台,为表演提供了一个自然朴素的环境", buildingType = 152, effectId = 125201, buyConditionId = 115201, unlockConditionId = -1, themeId = 1, mapId = 2, }, [105] = { uid = 105202, resid = "bd02_wutai_01", uiresid = "ui02_wutai_01", name = "原木舞台Ⅱ", desc = "现代格调,为表演者提供了一个自然发表演弓箭", buildingType = 152, effectId = 125202, buyConditionId = 115202, unlockConditionId = -1, themeId = 2, mapId = 2, }, [106] = { uid = 105203, resid = "bd02_wutai_01", uiresid = "ui02_wutai_01", name = "原木舞台Ⅲ", desc = "舞台", buildingType = 152, effectId = 125203, buyConditionId = 115203, unlockConditionId = -1, themeId = 3, mapId = 2, }, [107] = { uid = 105301, resid = "bd02_shaokaotan_01", uiresid = "ui02_shaokaotan_01", name = "简约烧烤架", desc = "实用又美观,为烧烤添加了一份自然风味", buildingType = 153, effectId = 125301, buyConditionId = 115301, unlockConditionId = -1, themeId = 1, mapId = 2, }, [108] = { uid = 105302, resid = "bd02_shaokaotan_01", uiresid = "ui02_shaokaotan_01", name = "简约烧烤架Ⅱ", desc = "精致设计,耐用且美观的烧烤架", buildingType = 153, effectId = 125302, buyConditionId = 115302, unlockConditionId = -1, themeId = 2, mapId = 2, }, [109] = { uid = 105303, resid = "bd02_shaokaotan_01", uiresid = "ui02_shaokaotan_01", name = "简约烧烤架Ⅲ", desc = "2号烧烤架", buildingType = 153, effectId = 125303, buyConditionId = 115303, unlockConditionId = -1, themeId = 3, mapId = 2, }, [110] = { uid = 105401, resid = "bd02_shaokaotan_01", uiresid = "ui02_shaokaotan_01", name = "简约烧烤架", desc = "实用又美观,为烧烤添加了一份自然风味", buildingType = 154, effectId = 125401, buyConditionId = 115401, unlockConditionId = -1, themeId = 1, mapId = 2, }, [111] = { uid = 105402, resid = "bd02_shaokaotan_01", uiresid = "ui02_shaokaotan_01", name = "简约烧烤架Ⅱ", desc = "精致设计,耐用且美观的烧烤架", buildingType = 154, effectId = 125402, buyConditionId = 115402, unlockConditionId = -1, themeId = 2, mapId = 2, }, [112] = { uid = 105403, resid = "bd02_shaokaotan_01", uiresid = "ui02_shaokaotan_01", name = "简约烧烤架Ⅲ", desc = "3号烧烤架", buildingType = 154, effectId = 125403, buyConditionId = 115403, unlockConditionId = -1, themeId = 3, mapId = 2, }, [113] = { uid = 105501, resid = "bd02_jiushuizihu_01", uiresid = "ui02_jiushuizihu_01", name = "原木酒水台", desc = "设计简约大方,摆放着各类酒水,让聚会更加多彩", buildingType = 155, effectId = 125501, buyConditionId = 115501, unlockConditionId = -1, themeId = 1, mapId = 2, }, [114] = { uid = 105502, resid = "bd02_jiushuizihu_01", uiresid = "ui02_jiushuizihu_01", name = "原木酒水台Ⅱ", desc = "原木材质打造的精装桌面,摆放着各式各样的酒水", buildingType = 155, effectId = 125502, buyConditionId = 115502, unlockConditionId = -1, themeId = 2, mapId = 2, }, [115] = { uid = 105503, resid = "bd02_jiushuizihu_01", uiresid = "ui02_jiushuizihu_01", name = "原木酒水台Ⅲ", desc = "自助酒水", buildingType = 155, effectId = 125503, buyConditionId = 115503, unlockConditionId = -1, themeId = 3, mapId = 2, }, [116] = { uid = 105601, resid = "bg02_yueqi_01", uiresid = "ui02_yueqi_01", name = "复古钢琴", desc = "优雅栖居,音符在建筑间自然流转", buildingType = 156, effectId = 125601, buyConditionId = 115601, unlockConditionId = -1, themeId = 1, mapId = 2, }, [117] = { uid = 105602, resid = "bg02_yueqi_01", uiresid = "ui02_yueqi_01", name = "复古钢琴Ⅱ", desc = "殿堂级艺术居所,奏响生活巅峰乐章", buildingType = 156, effectId = 125602, buyConditionId = 115602, unlockConditionId = -1, themeId = 2, mapId = 2, }, [118] = { uid = 105603, resid = "bg02_yueqi_01", uiresid = "ui02_yueqi_01", name = "复古钢琴Ⅲ", desc = "乐器", buildingType = 156, effectId = 125603, buyConditionId = 115603, unlockConditionId = -1, themeId = 3, mapId = 2, }, [119] = { uid = 105701, resid = "bg02_yinxiang_01", uiresid = "ui02_yinxiang_01", name = "复古音响", desc = "实用又美观,为时光增添经典音色", buildingType = 157, effectId = 125701, buyConditionId = 115701, unlockConditionId = -1, themeId = 1, mapId = 2, }, [120] = { uid = 105702, resid = "bg02_yinxiang_01", uiresid = "ui02_yinxiang_01", name = "复古音箱Ⅱ", desc = "温柔的旋律,响彻森林", buildingType = 157, effectId = 125702, buyConditionId = 115702, unlockConditionId = -1, themeId = 2, mapId = 2, }, [121] = { uid = 105703, resid = "bg02_yinxiang_01", uiresid = "ui02_yinxiang_01", name = "复古音箱Ⅲ", desc = "音箱", buildingType = 157, effectId = 125703, buyConditionId = 115703, unlockConditionId = -1, themeId = 3, mapId = 2, }, [122] = { uid = 105801, resid = "bd02_niudanji_01", uiresid = "ui02_niudanji_01", name = "复古扭蛋机", desc = "最复古的就是最时尚的", buildingType = 158, effectId = 125801, buyConditionId = 115801, unlockConditionId = -1, themeId = 1, mapId = 2, }, [123] = { uid = 105802, resid = "bd02_niudanji_01", uiresid = "ui02_niudanji_01", name = "复古扭蛋机Ⅱ", desc = "里面装着各式各样的扭蛋", buildingType = 158, effectId = 125802, buyConditionId = 115802, unlockConditionId = -1, themeId = 2, mapId = 2, }, [124] = { uid = 105803, resid = "bd02_niudanji_01", uiresid = "ui02_niudanji_01", name = "复古扭蛋机Ⅲ", desc = "扭蛋机", buildingType = 158, effectId = 125803, buyConditionId = 115803, unlockConditionId = -1, themeId = 3, mapId = 2, }, } return buildingCfg mainrequire("modules/building/barbecue/gacha/GachaConst") require("modules/building/barbecue/gacha/GachaDesk") require("modules/building/barbecue/gacha/GachaDeskMgr") require("modules/building/barbecue/gacha/GachaDeskInfo") require("modules/building/barbecue/gacha/GachaUserData") GuideMgr,--[[ 新手引导管理器 author:{zhangpeng} time:2025-06-12 11:28:57 ]] local GuideMgr = defClassStatic("GuideMgr") local LOGTAG = "GuideMgr" -- 初始化引导管理器 function GuideMgr:init() self.data = GuideCfgParse:getData() self.guideComplete = #self.data self.currentStep = UserDataMgr:getForceGuideStep() self.isGuiding = false if self.currentStep >= self.guideComplete then printInfo(LOGTAG, "新手引导已完成,当前步骤: %d", self.currentStep) end printInfo(LOGTAG, "引导管理器初始化,当前步骤: %d", self.currentStep) end -- 开始引导流程 function GuideMgr:startMainGuide() -- 如果引导已完成,直接返回 if self.currentStep >= self.guideComplete then self.isGuiding = false self.customerDic = {} printInfo(LOGTAG, "新手引导已完成,当前步骤: %d", self.currentStep) return end self.isGuiding = true self.customerDic = {} self:addMainFunctions() self:startGuideFunctions() end function GuideMgr:addMainFunctions() self:applyGuideConfigList(self.data) end -- 方法队列 -- 开始执行引导函数 function GuideMgr:startGuideFunctions() self.currentStep = self.currentStep - 1 self:nextGuideFunction() end -- 执行下一个引导函数 function GuideMgr:nextGuideFunction() -- 保存当前进度 UserDataMgr:setForceGuideStep(self.currentStep) -- 检查是否已完成所有引导 if self.currentStep >= self.guideComplete then self.isGuiding = false self.customerDic = {} self:onGuideComplete() return end -- 下一步 self.currentStep = self.currentStep + 1 local func = self.funList[self.currentStep] local rootNode = RestaurantMgr:getRestaurantScene().rootNode rootNode:Delay(0.2, function() func(self.currentStep) end) end -- 添加引导函数到列表 function GuideMgr:addGuideFunction(func) if not self.funList then self.funList = {} end local step = table.count(self.funList) + 1 self.funList[step] = func return step end -- 引导结束 -- 引导完成回调 function GuideMgr:onGuideComplete() printInfo(LOGTAG, "新手引导全部完成!") self.isGuiding = false -- 保存引导完成状态 UserDataMgr:setForceGuideStep(GuideMgr.guideComplete) -- 可以在这里添加引导完成的奖励或提示 -- 例如:显示完成提示、给予奖励等 CustomerMgr.customerNormalVisit:onStart() end -- 应用配置 -- 应用一组引导配置 function GuideMgr:applyGuideConfigList(guideConfigList) for i, v in ipairs(guideConfigList) do self:applyGuideConfig(i, v) end end -- 应用一个引导配置 function GuideMgr:applyGuideConfig(step, guideConfig) if guideConfig.opt == GuideConst.OptType.dialog then self:addGuideDialog(guideConfig) elseif guideConfig.opt == GuideConst.OptType.buttonClick then self:addGuideUiClick(guideConfig) elseif guideConfig.opt == GuideConst.OptType.customerEnter then self:addGuideCustomerEnter(guideConfig) elseif guideConfig.opt == GuideConst.OptType.worldClick then self:addGuideWorldClick(guideConfig) end end -- 对话 -- 添加对话引导函数 function GuideMgr:addGuideDialog(config) self:addGuideFunction(function(step) printInfo(LOGTAG, "新手引导执行步骤%s: %s", step, config.desc) self:showDialogGuide(step, function() printInfo(LOGTAG, "步骤%s: %s 完成", step, config.desc) self:nextGuideFunction() -- 执行下一个引导函数 end) end) end -- 展示对话引导 function GuideMgr:showDialogGuide(step, func) -- 获取对话id local dialogId = GuideCfgParse:getDialogId(step) if (not dialogId) or dialogId == -1 then self:nextGuideFunction() printError(LOGTAG, "无法获取对话id,引导失败,步骤跳过") return end -- 展示对话界面 StoryDialogMgr:showGuideDialogUI(dialogId, func) end -- ui点击 -- 添加UI点击引导函数 function GuideMgr:addGuideUiClick(config) self:addGuideFunction(function(step) printInfo(LOGTAG, "新手引导执行步骤%s: %s", step, config.desc) local scene = RestaurantMgr:getRestaurantScene() scene.rootNode:Delay(config.time, function() local view, delayFunc = self:getViewUi(config.guideView, config.params) local button, buttonConfig = self:getViewUiButton(view, config.guideNode, config.params) local isForce = config.guideType == 1 if not self:checkUiClickGuide(view, button, buttonConfig, config.backId) then return end if delayFunc then delayFunc() end scene.guideCom:showTouchClickGuide(button, isForce, buttonConfig, function() printInfo(LOGTAG, "步骤%s: %s 完成", step, config.desc) self:nextGuideFunction() end, function() end) end) end) end -- 展示ui点击引导 function GuideMgr:checkUiClickGuide(view, button, buttonConfig, backId) if not view then if backId ~= -1 then self.currentStep = backId - 1 self:nextGuideFunction() printInfo(LOGTAG, "无法获取视图UI,引导位置有问题,步骤回退至第%s步", backId) return false end self:nextGuideFunction() printInfo(LOGTAG, "无法获取视图UI,引导失败,步骤跳过") return false end if not button then self:nextGuideFunction() printError(LOGTAG, "无法获取UI按钮,引导失败,步骤跳过") return false end if not buttonConfig then self:nextGuideFunction() printError(LOGTAG, "无法获取UI按钮配置,引导失败,步骤跳过") return false end return true end ---- 引导点击指定ui按钮 --function GuideMgr:showUiClickGuide(delayTime, button, isForce, buttonConfig, delayFunc, guideCallback, cancelCallback) -- local scene = RestaurantMgr:getRestaurantScene() -- scene.rootNode:Delay(delayTime, function() -- if delayFunc then -- delayFunc() -- end -- scene.guideCom:showTouchClickGuide(button, isForce, buttonConfig, guideCallback, cancelCallback) -- end) --end -- 获取指定视图UI function GuideMgr:getViewUi(viewName, params) local view = UILayerUtil:findUIByName(GuideConst.ViewName[viewName]) local func = nil if GuideConst.ViewName[viewName] == "BuildingsListUI" then func = function() view:scrollToRow(params[1]) end end return view, func end -- 获取指定UI按钮 function GuideMgr:getViewUiButton(ui, buttonName, params) if not ui then return nil, nil end if #params <= 0 then return ui[GuideConst.UIButtonName[buttonName]], GuideConst.UIButtonConfig[buttonName] elseif #params == 1 then return ui:getCellNodeById(params[1]), GuideConst.UIButtonConfig[buttonName] elseif #params == 2 then return ui:getCellNodeById(params[2]), GuideConst.UIButtonConfig[buttonName] end end -- 顾客 -- 添加顾客进店引导函数 function GuideMgr:addGuideCustomerEnter(config) self:addGuideFunction(function(step) printInfo(LOGTAG, "新手引导执行步骤%s: %s", step, config.desc) local customerId = config.params[1] self:createCustomer(customerId) self:customerToBench(customerId) self:nextGuideFunction() -- 执行下一个引导函数 end) end -- 创建顾客 function GuideMgr:createCustomer(customerId) -- 创建顾客并进入餐厅 local customer = CustomerMgr:createCustomer(customerId) CustomerMgr:normalCustomerEnterTheRestaurant(customer) printInfo(LOGTAG, "创建顾客完成") self.customerDic[customerId] = customer -- 保存当前顾客对象 end -- 顾客入座 function GuideMgr:customerToBench(cuisineId) local customer = self.customerDic[cuisineId] CustomerMgr:addRestaurantWaitingQueue(customer) printInfo(LOGTAG, "顾客进入餐厅完成") end -- 世界场景点击 -- 添加世界点击引导函数 function GuideMgr:addGuideWorldClick(config) self:addGuideFunction(function(step) printInfo(LOGTAG, "新手引导执行步骤%s: %s", step, config.desc) local scene = RestaurantMgr:getRestaurantScene() scene.rootNode:Delay(config.time, function() local target = self:getWorldTarget(config.guideView, config.params) local button, buttonConfig = self:getWorldTargetButton(target, config.guideNode, config.params) local isForce = config.guideType == 1 if not self:checkWorldClick(target, button, buttonConfig, config.backId) then return end scene.guideCom:showTouchClickGuide(button, isForce, buttonConfig, function() printInfo(LOGTAG, "步骤%s: %s 完成", step, config.desc) self:nextGuideFunction() end, function() end) end) end) end -- 获取世界点击查找对象 function GuideMgr:getWorldTarget(targetType, params) if targetType == GuideConst.WorldTargetType.customer then return self.customerDic[params[1]] end return nil end -- 获取世界点击查找对象 function GuideMgr:getWorldTargetButton(target, buttonName, params) if not target then return nil, nil end return target[GuideConst.WorldButtonName[buttonName]], GuideConst.WorldButtonConfig[buttonName] end -- 引导顾客气泡按钮 function GuideMgr:checkWorldClick(target, button, buttonConfig, backId) if not target then if backId ~= -1 then self.currentStep = backId - 1 self:nextGuideFunction() printInfo(LOGTAG, "无法获取目标,引导位置有问题,步骤回退至第%s步", backId) return false end self:nextGuideFunction() printInfo(LOGTAG, "无法获取目标,引导失败,步骤跳过") return false end if not button then self:nextGuideFunction() printError(LOGTAG, "无法获取目标按钮,引导失败,步骤跳过") return false end if not buttonConfig then self:nextGuideFunction() printError(LOGTAG, "无法获取目标按钮配置,引导失败,步骤跳过") return false end return true end -- 其他 -- 重置引导状态(用于调试) function GuideMgr:resetGuideState() printInfo(LOGTAG, "重置引导状态") UserDataMgr:setForceGuideStep(0) self.currentStep = 0 self.isGuiding = false end -- 跳过当前引导步骤(用于调试) function GuideMgr:skipCurrentStep() printInfo(LOGTAG, "跳过当前引导步骤: %d", self.currentStep) self:nextGuideFunction() end -- 检查是否正在引导中 function GuideMgr:isInGuide() return self.isGuiding end -- 获取当前引导步骤 function GuideMgr:getCurrentStep() return self.currentStep end return GuideMgrSpecialCustomerCell@--- 顾客展示格子 ---@class SpecialCustomerCell : LuaClass local SpecialCustomerCell = defClass("SpecialCustomerCell") -- view require("modules/ui/customerui/SpecialCustomerDetailUI") -- UIComponent local UIImage = CS.UnityEngine.UI.Image local TMProUGUI = CS.TMPro.TextMeshProUGUI --- 构造函数 function SpecialCustomerCell:ctor(go, customerId) self.ui = go self.customerId = customerId self:initUI() self:showUI() end --- 重置 function SpecialCustomerCell:reload(entityId) self.customerId = entityId self:showUI() self.ui:SetActive(true) end --- 初始化UI function SpecialCustomerCell:initUI() -- 初始化UI元素 self.customer_locked = self.ui:Seek("customer_locked") self.customer_unlocked = self.ui:Seek("customer_unlocked") self.tag_dong = self.ui:Seek("tag_dong") self.customer_icon = self.customer_unlocked:Seek("customer_icon") self.customer_name = self.customer_unlocked:Seek("customer_name") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.ui, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:click() end) end --- 展示UI function SpecialCustomerCell:showUI() local info = SpecialCustomerMgr:getSpecialCustomerInfo(self.customerId) local isUnlocked = info:isVisited() local isNewCustomer = info:isNewCustomer() self.customer_locked:SetActive(not isUnlocked) self.customer_unlocked:SetActive(isUnlocked) self.tag_dong:SetActive(isNewCustomer) if isUnlocked then --self.customer_icon[UIImage].sprite = info:getBustIcon() self.customer_icon[UIImage]:SetNativeSize() self.customer_name[TMProUGUI].text = info:getName() end end --- 隐藏 function SpecialCustomerCell:hide() self.ui:SetActive(false) end --- 点击 function SpecialCustomerCell:click() local view = SpecialCustomerDetailUI.new(self.customerId, false):show():showMask():enableCloseWhenClickMask(function() end) end return SpecialCustomerCellmainrequire("modules/building/BuildingConst") -- 订单信息 require("modules/building/restaurant/order/main") -- building require("modules/building/BuildingInfo") require("modules/building/BuildingUserData") require("modules/building/BuildingBase") require("modules/building/BuildingMgr") -- restaurant require("modules/building/restaurant/cooking/main") -- 灶台类 require("modules/building/restaurant/eat/main") -- 餐桌类 require("modules/building/restaurant/reception/main") -- 接待类 require("modules/building/restaurant/passivity/main") -- 闲逛类 require("modules/building/restaurant/passiveincome/main") -- 被动收益类 -- 烤吧 require("modules/building/barbecue/stage/main") -- 舞台类 require("modules/building/barbecue/cashier/main") -- 烤吧收银台类 require("modules/building/barbecue/gacha/main") -- 扭蛋机 require("modules/building/barbecue/bbq/main") -- 烤架类 -- 钓鱼中心 require("modules/building/fishingcenter/stall/main") -- 摊位 PassivityIncomeDeskMgr& --[[ 被动收益建筑管理器 author:{zhangpeng} time:2025-06-13 11:11:43 ]] local PassivityIncomeDeskMgr = defClassStatic("PassivityIncomeDeskMgr") local LOGTAG = "PassivityIncomeDeskMgr" -- init function PassivityIncomeDeskMgr:init() printInfo(LOGTAG, "------ 被动收益建筑管理 初始化 ------") self.passiveIncomeDesks = {} end -- 根据建筑id创建一个被动收益建筑 function PassivityIncomeDeskMgr:createPassiveIncomeDesk(buildingInfo) local buildingType = buildingInfo.buildingType local parentNode = RestaurantMgr:getRestaurantScene().rootNode:Seek("building_pos_" .. buildingType) local args = { deskId = buildingInfo.buildingCfgId, parent = parentNode, scene = RestaurantMgr:getRestaurantScene(), } local passiveIncomeDesk = nil if not self.passiveIncomeDesks then self.passiveIncomeDesks = {} end if self:hasSameTypeDesk(buildingType) then self:replaceOldDesk(buildingType, buildingInfo.buildingCfgId) else passiveIncomeDesk = PassivityIncomeDesk.new(args) table.insert(self.passiveIncomeDesks, passiveIncomeDesk) end return passiveIncomeDesk end -- 是否有同类不同id的被动收益建筑 function PassivityIncomeDeskMgr:hasSameTypeDesk(buildingType) for i = 1, table.nums(self.passiveIncomeDesks) do local passiveIncomeDesk = self.passiveIncomeDesks[i] if passiveIncomeDesk.buildingInfo.buildingType == buildingType then return true end end return false end -- 查找相同类型建筑的旧被动收益建筑,替换旧的被动收益建筑 -- @param buildingType 建筑类型 -- @param newDeskCfgId 新被动收益建筑配置id function PassivityIncomeDeskMgr:replaceOldDesk(buildingType, newDeskCfgId) for i = 1, #self.passiveIncomeDesks do local passiveIncomeDesk = self.passiveIncomeDesks[i] -- 大类相同,配置id不同,则是旧被动收益建筑 if passiveIncomeDesk.buildingInfo.buildingType == buildingType and passiveIncomeDesk.buildingInfo.buildingCfgId ~= newDeskCfgId then -- 只修改一下prefab的名字为配置id passiveIncomeDesk.buildingNode:SetName(newDeskCfgId) break end end end return PassivityIncomeDeskMgr mainbrequire("common/core/base/resmgr/YooAssetAdapter") require("common/core/base/resmgr/ResLoader") MusicbbqMap\--[[ 音乐烤吧地图 author:zhengpeng time:2025-06-03 11:41:02 ]] local MusicbbqMap, super = defClass("MusicbbqMap", MapBase) local LOGTAG = "MusicbbqMap" -- 测试角色寻路(可根据需要开启) local test_role_path_find = false -- 初始化 function MusicbbqMap:ctor(scene, mapRoot) super.ctor(self, scene, mapRoot) -- 音乐烤吧特有的节点 self.stageNode = self.rootNode:Seek("stage") -- 舞台 self.barNode = self.rootNode:Seek("bar") -- 吧台 self.danceFloorNode = self.rootNode:Seek("dance_floor") -- 舞池 end return MusicbbqMapMapsMgr--[[ 地图管理器 author:zhengnan time:2025-06-03 10:50:04 ]] local MapsMgr = defClassStatic("MapsMgr") local LOGTAG = "MapsMgr" -- init function MapsMgr:init() self.maps = {} self.currentMapId = 1 self.currentMap = nil end function MapsMgr:setScene(scene) self.scene = scene RestaurantMgr:setRestaurantScene(scene) MusicbbqMgr:setMusicbbqScene(scene) FishingMgr:setFishingScene(scene) end -- 创建地图实例 function MapsMgr:createMap(mapId, scene, mapNode) if self.maps[mapId] then return self.maps[mapId] end local mapInstance = nil -- 餐厅,烤吧,钓鱼全都在换一个场景,不再单独创建地图 if mapId == MapsConst.MapType.restaurant or mapId == MapsConst.MapType.music_bar then -- 餐厅地图 mapInstance = RestaurantMap.new(scene, mapNode) self:insertMap(mapId, mapInstance) -- 默认关闭所有建筑的阻挡区域 mapInstance:hideAllBuildingBlock() -- 初始化建筑 BuildingMgr:initRestaurantBuildingView(mapInstance) -- 初始化地图上的员工 EmployeMgr:initEmployeeOnMap() elseif mapId == MapsConst.MapType.fishing_island then -- 钓鱼 mapInstance = FishingMap.new(scene, mapNode) self:insertMap(mapId, mapInstance) end return mapInstance end -- 插入地图列表 function MapsMgr:insertMap(mapId, mapInstance) self.maps[mapId] = mapInstance end -- 获取当前地图 function MapsMgr:getCurrentMap() return self.currentMap end -- 获取当前地图ID function MapsMgr:getCurrentMapId() return self.currentMapId end -- 获取指定地图实例 function MapsMgr:getMap(mapId) do return self.maps[MapsConst.MapType.restaurant] end return self.maps[mapId] end -- 切换到地图 function MapsMgr:switchMap(mapId) if self.currentMapId == mapId then return self.currentMap end -- 地图固定位置 local mapWidth = 16.8 -- 地图固定宽度 local mapPositions = { [MapsConst.MapType.music_bar] = -mapWidth, -- 音乐烤吧在左侧 [MapsConst.MapType.restaurant] = 0, -- 餐厅在中间 [MapsConst.MapType.fishing_island] = mapWidth -- 钓鱼地图在右侧 } local main_camera = self.scene.cams.scene if self.scene and main_camera then local targetX = mapPositions[mapId] or 0 local currentPos = main_camera.transform.position local newCameraPos = Vector3(targetX, 0, currentPos.z) main_camera.transform.position = newCameraPos end -- 切换到新地图 self.currentMapId = mapId -- 获取或创建对应的地图实例 self.currentMap = self:getMap(mapId) -- 更新当前地图的边界,使其与相机位置匹配 if self.currentMap and self.currentMap.updateMapBoundary then self.currentMap:updateMapBoundary() end printInfo(LOGTAG, string.format("切换到地图: %s", mapId)) return self.currentMap end -- 根据地图类型获取对应的场景 function MapsMgr:getSceneForMap(mapId) if mapId == MapsConst.MapType.restaurant then return RestaurantMgr:getRestaurantScene() elseif mapId == MapsConst.MapType.music_bar then -- 假设有MusicbbqMgr管理音乐烤吧场景 if MusicbbqMgr and MusicbbqMgr.getMusicbbqScene then return MusicbbqMgr:getMusicbbqScene() else printWarn(LOGTAG, "MusicbbqMgr未实现,使用餐厅场景") return RestaurantMgr:getRestaurantScene() end elseif mapId == MapsConst.MapType.fishing_island then return FishingMgr:getFishingScene() else -- 其他地图类型暂时使用餐厅场景 printWarn(LOGTAG, string.format("地图类型 %s 的场景管理器未实现,使用餐厅场景", mapId)) return RestaurantMgr:getRestaurantScene() end end -- 清理所有地图 function MapsMgr:cleanup() for mapId, map in pairs(self.maps) do if map and map.onExit then map:onExit() end end self.maps = {} self.currentMapId = nil self.currentMap = nil printInfo(LOGTAG, "清理所有地图实例") end -- 重新加载当前地图 function MapsMgr:reloadCurrentMap() if not self.currentMapId then printWarn(LOGTAG, "没有当前地图需要重新加载") return end local mapId = self.currentMapId -- 清理当前地图 if self.currentMap and self.currentMap.onExit then self.currentMap:onExit() end -- 移除地图实例 self.maps[mapId] = nil self.currentMap = nil -- 重新切换到该地图 self:switchMap(mapId) end return MapsMgrFishingGameCameraCtl)-- 钓鱼玩法相机控件 local FishingGameCameraCtl = defClass("FishingGameCameraCtl") -- log local LOGTAG = "FishingGameCameraCtl" -- component local Camera = UnityEngine.Camera -- 构造函数 function FishingGameCameraCtl:ctor(go, size) self.go = go self.camera = go[Camera] self.transform = go.transform self.gradient = go:Seek("gradient") if not self.camera then printError(LOGTAG, "没有找到相机") return end self.size = size or 10 self:init() end -- 初始化 function FishingGameCameraCtl:init() self.camera.transform.localPosition = Vector3(0, 0, -10) -- 设置摄像机位置 self.camera.orthographic = true -- 设置摄像机为正交投影 self:setCameraSize(self.size) self:initGradientSize() end -- 设置相机的大小 function FishingGameCameraCtl:setCameraSize(value) self.camera.orthographicSize = value -- 设置摄像机正交大小 self.size = value self:updateCameraWorldSize() end -- 更新摄像机位置 function FishingGameCameraCtl:updateCameraPosition(pos) if pos.x > self.activeArea.right + self.expandArea.right then pos.x = self.activeArea.right + self.expandArea.right end if pos.x < self.activeArea.left + self.expandArea.left then pos.x = self.activeArea.left + self.expandArea.left end if pos.y > self.activeArea.top + self.expandArea.top then pos.y = self.activeArea.top + self.expandArea.top end if pos.y < self.activeArea.bottom + self.expandArea.bottom then pos.y = self.activeArea.bottom + self.expandArea.bottom end pos.z = -10 -- 确保相机在正确的深度 self.camera.transform.localPosition = pos end -- 设置摄像机位置 function FishingGameCameraCtl:setCameraPosition(pos) pos.z = -10 -- 确保相机在正确的深度 self.camera.transform.localPosition = pos end -- 获取当前相机大小 function FishingGameCameraCtl:getCameraSize() return self.size end -- 获取当前相机大小 function FishingGameCameraCtl:getCameraWorldSize() return self.worldSize end -- 更新相机的世界尺寸 function FishingGameCameraCtl:updateCameraWorldSize() self.worldSize = self:getOrthographicCameraWorldSize(self.camera) end function FishingGameCameraCtl:updateActiveArea(mapSize) self.activeArea = { top = 0 - self.worldSize.height / 2, bottom = -mapSize.height + self.worldSize.height / 2, left = -mapSize.width / 2 + self.worldSize.width / 2, right = mapSize.width / 2 - self.worldSize.width / 2 } end -- 设置扩展区域 function FishingGameCameraCtl:setExpandArea(expandArea) self.expandArea = { top = expandArea.top or 0, bottom = expandArea.bottom or 0, left = expandArea.left or 0, right = expandArea.right or 0, } end -- 获取正交相机的世界尺寸 function FishingGameCameraCtl:getOrthographicCameraWorldSize(camera) -- 获取相机的正交尺寸 local height = camera.orthographicSize * 2 local width = height * camera.aspect return { width = width, height = height } end function FishingGameCameraCtl:initGradientSize() self.gradientSize = self:getRendererWorldSize(self.gradient) self.gradient.transform.localScale = Vector3(self.worldSize.width * (1 / self.gradientSize.width), self.worldSize.height * (1 / self.gradientSize.height), 1) end function FishingGameCameraCtl:updateGradientSize() self.gradient.transform.localScale = Vector3(self.worldSize.width * (1 / self.gradientSize.width), self.worldSize.height * (1 / self.gradientSize.height), 1) end -- 获取一个Renderer的世界尺寸 function FishingGameCameraCtl:getRendererWorldSize(targetObject) local renderer = targetObject:GetComponent("Renderer") if renderer == nil then printError(LOGTAG, "目标对象没有 Renderer 组件") return Vector3.zero end -- 获取物体的世界尺寸 return {width = renderer.bounds.size.x, height = renderer.bounds.size.y} end return FishingGameCameraCtlBuildingsListUIn9--[[ 新建筑列表主界面 author:{zhangpeng} time:2025-05-27 18:40:48 ]] local BuildingsListUI, super = defClass("BuildingsListUI", UILayer) local TMPUGUI = CS.TMPro.TextMeshProUGUI require("modules/ui/buildinglist/buildingsui/BuildingCell") require("modules/ui/buildinglist/themeui/BuildingThemesUI") require("modules/ui/buildinglist/themeui/ThemeUIConst") local LOGTAG = "BuildingsListUI" -- 首行距离顶部距离 local row_top_dis = -80 local row_left_dis = 120 local rowHeight = 360 -- 每行高度 local rowSpacing = 20 -- 行间距 function BuildingsListUI:ctor(curTabIndex) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/buildinglist/buildinglistuireslink") self.curTabIndex = curTabIndex end function BuildingsListUI:onLoad() self.ui = GameObject.Instantiate(self.R.building_list_ui) self:addChild(self.ui) --self:useTweenOnOpen(self.ui) self:initUI() --self:timer(function() -- if GuideMgr:isInGuide() then -- return -- end -- self.outerScrollRect.verticalNormalizedPosition = 1 --end, 0.3) end function BuildingsListUI:initUI() -- init building list self:initBuildingList() -- init theme tab self:initThemeTab() -- init page tab self:initPageTab() -- 设置默认页签状态 self:selectTab(self.curTabIndex) -- self.ui:Delay(0.2,function () -- self:scrollToRow(2) -- end) self.close_btn = self.ui:Seek("close_btn") util.ugui.addClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) end -- 初始化建筑列表 function BuildingsListUI:initBuildingList() self.scrollView = self.ui:Seek("building_list_scrollview") self.buildingList = {} -- 获取 scrollView 的 content self.outerScrollRect = self.scrollView:GetComponent(typeof(UGUI.ScrollRect)) self.content_ct = self.ui:Seek("content_ct") self.content_kb = self.ui:Seek("content_kb") -- 初始状态:显示餐厅,隐藏烤吧 self.content_ct:SetActive(true) self.content_kb:SetActive(false) self.restaurantRowCount = 0 self.barbecueRowCount = 0 for k,v in pairs(BuildingConst.buildingCategoryId) do if v == BuildingConst.buildingCategoryId.restaurant then local ct_currentY = 0 -- 餐厅 local oneRowData = BuildingCfgParse:getDataForTableView(v) for rowIndex, rowData in ipairs(oneRowData) do self.restaurantRowCount = self.restaurantRowCount + 1 -- 创建一行建筑 local oneRowBuilding = GameObject.Instantiate(self.R.one_row_building,self.content_ct.transform) -- 设置内层ScrollRect的配置以支持嵌套滚动 util.ugui.addDragEvent(oneRowBuilding,function (eventData) self.outerScrollRect:OnDrag(eventData) end) util.ugui.addBeginDragEvent(oneRowBuilding,function (eventData) self.outerScrollRect:OnBeginDrag(eventData) end) util.ugui.addEndDragEvent(oneRowBuilding,function (eventData) self.outerScrollRect:OnEndDrag(eventData) end) -- 设置大类名字 local typeNameNode = oneRowBuilding:Seek("typeName") local name = BuildingTypesCfgParse:getCategoryNameById(rowData.value[1].buildingType) typeNameNode[TMPUGUI].text = name -- 处理格子点击 local cells = oneRowBuilding:SearchPattern("one_building_cell_\\d",true) for index,cell in ipairs(cells) do cell:SetActive(rowData.value[index]) -- 先隐藏所有格子 if rowData.value[index] then local buildingCell = BuildingCell.new(cell, rowData.value[index]) local pos_x = row_left_dis +(index - 1) * 230 buildingCell:setCellPositionX(pos_x) table.insert(self.buildingList, buildingCell) end end -- 设置位置 local rectTransform = oneRowBuilding:GetComponent(typeof(CS.UnityEngine.RectTransform)) rectTransform.anchorMin = Vector2(0, 1) rectTransform.anchorMax = Vector2(1, 1) rectTransform.pivot = Vector2(0.5, 1) rectTransform.anchoredPosition = Vector2(0, -ct_currentY) rectTransform.sizeDelta = Vector2(0, rowHeight) -- 更新Y位置 ct_currentY = ct_currentY + rowHeight + rowSpacing end -- 设置餐厅content的高度 local ct_sizeDelta = self.content_ct:GetComponent(typeof(CS.UnityEngine.RectTransform)).sizeDelta local ct_height = self.restaurantRowCount * 360 + (self.restaurantRowCount - 1) * 20 + math.abs(row_top_dis) ct_sizeDelta.y = ct_height self.content_ct:GetComponent(typeof(CS.UnityEngine.RectTransform)).sizeDelta = ct_sizeDelta printInfo(LOGTAG, "创建了 %d 行餐厅建筑,设置高度: %f", #oneRowData, ct_height) elseif v == BuildingConst.buildingCategoryId.barbecue then local kb_currentY = 0 -- 烤吧 local oneRowData = BuildingCfgParse:getDataForTableView(v) for rowIndex, rowData in ipairs(oneRowData) do self.barbecueRowCount = self.barbecueRowCount + 1 -- 创建一行建筑 local oneRowBuilding = GameObject.Instantiate(self.R.one_row_building,self.content_kb.transform) -- 设置内层ScrollRect的配置以支持嵌套滚动 util.ugui.addDragEvent(oneRowBuilding,function (eventData) self.outerScrollRect:OnDrag(eventData) end) util.ugui.addBeginDragEvent(oneRowBuilding,function (eventData) self.outerScrollRect:OnBeginDrag(eventData) end) util.ugui.addEndDragEvent(oneRowBuilding,function (eventData) self.outerScrollRect:OnEndDrag(eventData) end) -- 设置大类名字 local typeNameNode = oneRowBuilding:Seek("typeName") local name = BuildingTypesCfgParse:getCategoryNameById(rowData.value[1].buildingType) typeNameNode[TMPUGUI].text = name -- 处理格子点击 local cells = oneRowBuilding:SearchPattern("one_building_cell_\\d",true) for index,cell in ipairs(cells) do cell:SetActive(rowData.value[index]) -- 先隐藏所有格子 if rowData.value[index] then local buildingCell = BuildingCell.new(cell, rowData.value[index]) local pos_x = row_left_dis +(index - 1) * 230 buildingCell:setCellPositionX(pos_x) table.insert(self.buildingList, buildingCell) end end -- 设置位置 local rectTransform = oneRowBuilding:GetComponent(typeof(CS.UnityEngine.RectTransform)) rectTransform.anchorMin = Vector2(0, 1) rectTransform.anchorMax = Vector2(1, 1) rectTransform.pivot = Vector2(0.5, 1) rectTransform.anchoredPosition = Vector2(0, -kb_currentY) rectTransform.sizeDelta = Vector2(0, rowHeight) -- 更新Y位置 kb_currentY = kb_currentY + rowHeight + rowSpacing end -- 设置烤吧content的高度 local kb_sizeDelta = self.content_kb:GetComponent(typeof(CS.UnityEngine.RectTransform)).sizeDelta local kb_height = self.barbecueRowCount * 360 + (self.barbecueRowCount - 1) * 20 + math.abs(row_top_dis) kb_sizeDelta.y = kb_height self.content_kb:GetComponent(typeof(CS.UnityEngine.RectTransform)).sizeDelta = kb_sizeDelta printInfo(LOGTAG, "创建了 %d 行烤吧建筑,设置高度: %f", #oneRowData, kb_height) end end -- 确保外层ScrollRect配置正确 if self.outerScrollRect then self.outerScrollRect.horizontal = false self.outerScrollRect.vertical = true self.outerScrollRect.verticalNormalizedPosition = 1 -- 滚动到顶部 -- 设置默认content为餐厅content(因为默认显示餐厅) self.outerScrollRect.content = self.content_ct:GetComponent(typeof(CS.UnityEngine.RectTransform)) printInfo(LOGTAG, "初始化ScrollRect content为餐厅") end self.scrollView:GetComponent("ScrollRect").normalizedPosition = Vector2(0, 1) -- 滚动到顶部 printInfo(LOGTAG, "restaurantRowCount:%d, barbecueRowCount:%d", self.restaurantRowCount, self.barbecueRowCount) end -- 主题选择 function BuildingsListUI:initThemeTab() self.themeScrollView = self.ui:Seek("tab_scroll_view") self.themeList = {} local content = self.themeScrollView:GetComponent(typeof(UGUI.ScrollRect)).content local cellCount = content.transform.childCount for i = 1, cellCount do local theme = content:GetChild(i - 1).gameObject util.ugui.addButtonClickEvent(theme, function() printInfo(LOGTAG, "点击了主题" .. i) AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) if self.curTabIndex == 2 and i > 3 then PopUpUI.new("敬请期待"):show():showMask():enableCloseWhenClickMask() return end self:selectTheme(i) end) table.insert(self.themeList, theme) end end -- 初始化页签 function BuildingsListUI:initPageTab() local tab_names = {"tab_ct","tab_sk"} self.tab_list = {} for i,v in ipairs(tab_names) do local tab = self.ui:Seek(v) table.insert(self.tab_list, tab) -- click local click_mask = tab:Seek("clickmask") util.ugui.addClickEvent(click_mask, function() self.curTabIndex = i printInfo(LOGTAG, "点击了第" .. i .. "个页签") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:selectTab(i) end) end end -- 选择页签 function BuildingsListUI:selectTab(index) self.curTabIndex = index -- 安全检查 if not self.tab_list then printInfo(LOGTAG, "tab_list not initialized, skipping tab visual update") else -- 更新页签显示状态 for i,v in ipairs(self.tab_list) do if v and not CS.LuaHelper.IsNull(v) then -- 显示选择的页签 local selectNode = v:Seek("select") local unselectNode = v:Seek("unselect") if selectNode then selectNode:SetActive(i == index) end if unselectNode then unselectNode:SetActive(i ~= index) end end end end -- 安全检查content对象 if not self.content_ct or CS.LuaHelper.IsNull(self.content_ct) or not self.content_kb or CS.LuaHelper.IsNull(self.content_kb) then printError(LOGTAG, "Content objects not properly initialized") return end -- 切换content显示状态 self.content_ct:SetActive(index == 1) self.content_kb:SetActive(index == 2) -- 动态更新外层ScrollRect的content引用 if self.outerScrollRect then if index == 1 then -- 切换到餐厅页签,设置content为content_ct self.outerScrollRect.content = self.content_ct:GetComponent(typeof(CS.UnityEngine.RectTransform)) self.outerScrollRect.verticalNormalizedPosition = 1 printInfo(LOGTAG, "切换ScrollRect content到餐厅") elseif index == 2 then -- 切换到烤吧页签,设置content为content_kb self.outerScrollRect.content = self.content_kb:GetComponent(typeof(CS.UnityEngine.RectTransform)) self.outerScrollRect.verticalNormalizedPosition = 1 printInfo(LOGTAG, "切换ScrollRect content到烤吧") end end -- 根据内容设置一下scrollView的contentSize if index == 1 then local ct_sizeDelta = self.content_ct:GetComponent(typeof(CS.UnityEngine.RectTransform)).sizeDelta local height = self.restaurantRowCount * 360 + (self.restaurantRowCount - 1) * 20 + math.abs(row_top_dis) ct_sizeDelta.y = height self.content_ct:GetComponent(typeof(CS.UnityEngine.RectTransform)).sizeDelta = ct_sizeDelta self.outerScrollRect.verticalNormalizedPosition = 1 elseif index == 2 then local kb_sizeDelta = self.content_kb:GetComponent(typeof(CS.UnityEngine.RectTransform)).sizeDelta local height = self.barbecueRowCount * 360 + (self.barbecueRowCount - 1) * 20 + math.abs(row_top_dis) kb_sizeDelta.y = height self.content_kb:GetComponent(typeof(CS.UnityEngine.RectTransform)).sizeDelta = kb_sizeDelta self.outerScrollRect.verticalNormalizedPosition = 1 end end -- 选择主题 function BuildingsListUI:selectTheme(index) local themeUI = BuildingThemesUI.new(self.curTabIndex, index) themeUI:show():showMask():enableCloseWhenClickMask() end -- 获取某一行某一列的节点 function BuildingsListUI:getCellNode(rowIndex, colIndex) local row = self.buildingList[rowIndex] if row then return row.cellNode:Seek("building_img") end end -- 获取某一行某一列的节点 function BuildingsListUI:getCellNodeById(id) for i, v in pairs(self.buildingList) do if v.data.uid == id then return v.cellNode:Seek("building_img") end end end -- ScrollView滚动到指定位置 function BuildingsListUI:scrollToRow(rowIndex) local totalHeight = self.restaurantRowCount * rowHeight + (self.restaurantRowCount - 1) * rowSpacing + math.abs(row_top_dis) -- 从顶部开始计算:第1行在顶部(position=1),最后一行在底部(position=0) local targetPosition = 1 - (rowIndex - 1) * (rowHeight + rowSpacing) / totalHeight self.outerScrollRect.verticalNormalizedPosition = targetPosition printInfo(LOGTAG, "滚动到第%d行,位置: %f", rowIndex, targetPosition) end return BuildingsListUI gachareslink|return { --BASIC --ASSET gacha_ui = {"Assets/AssetsPackage/Res/modules/ui/gacha/prefabs/gacha_ui.prefab", 0, 0}, } PBSocketPackp--[[ author:{zhangpeng} time:2023-06-13 20:02:04 ]] local PBSocketPack, super = defClass("PBSocketPack") PBSocketPack.seq = 0 PBSocketPack.headProtoName = "handshake.Request" function PBSocketPack:ctor(head, body, reqBodyProtoName, rspBodyProtoName, callback) self.reqHead = head self.reqBody = body self.rspHead = nil self.rspBody = nil self.rspMsgType = nil self.reqHeadProtoName = PBSocketPack.headProtoName self.reqBodyProtoName = reqBodyProtoName self.rspHeadProtoName = nil--PBSocketPack.headProtoName self.rspBodyProtoName = rspBodyProtoName self.callback = callback self.connection = nil -- seq是数据包的客户端序列号,用于唯一标识数据包 PBSocketPack.seq = PBSocketPack.seq + 1 self.client_seq = PBSocketPack.seq end function PBSocketPack:setConnection(connection) self.connection = connection end function PBSocketPack:getConnection() return self.connection end function PBSocketPack:getSeq() return self.client_seq end function PBSocketPack:getRpcName() if type(self.reqHead) == "table" then if self.reqHead.request_name then return self.reqHead.request_name end else return "no rpc name" end end function PBSocketPack:setReqTime(time) self.reqTime = time or os.time() end function PBSocketPack:getReqTime() return self.reqTime or 0 end function PBSocketPack:setRspTime(time) self.rspTime = time or os.time() end function PBSocketPack:getRspTime() return self.rspTime or 0 end function PBSocketPack:setReqLength(len) self.reqLength = len end function PBSocketPack:getReqLength() return self.reqLength or 0 end function PBSocketPack:setRspLength(len) self.rspLength = len end function PBSocketPack:getRspLength() return self.rspLength or 0 end -- 检查数据包是否完整,是否有请求头和请求体 function PBSocketPack:isComplete() return self.rspHead ~= nil and self.rspBody ~= nil end function PBSocketPack:getError() if not self:isComplete() then return "pack is not complete" end if self:getSeq() ~= self.client_seq then return string.format("pack seq is wrong (%s, %s)", self:getSeq(), self.client_seq) end return nil end local PBTAG = "ProtoBuf" if CS.UnityEngine.Application.isEditor then PBTAG = "" .. PBTAG .. "" end function PBSocketPack:printReq() printInfo(PBTAG, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") printInfo(PBTAG, "REQ, seq:%s, rpcName:%s", self:getSeq(), self:getRpcName()) printInfo(PBTAG, "REQ, reqHead:%s", util.serpent.block(self.reqHead)) printInfo(PBTAG, "REQ, reqBody:%s", util.serpent.block(self.reqBody)) printInfo(PBTAG, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") end function PBSocketPack:printRsp() printInfo(PBTAG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") printInfo(PBTAG, "RSP, seq:%s, rpcName:%s", self:getSeq(), self:getRpcName()) printInfo(PBTAG, "RSP, rspHead:%s", util.serpent.block(self.rspHead)) printInfo(PBTAG, "RSP, rspBody:%s", util.serpent.block(self.rspBody)) printInfo(PBTAG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") end function PBSocketPack:printError(errorCode, errorMsg) printInfo(PBTAG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") printWarn(PBTAG, "ERR, seq:%s, rpcName:%s", self:getSeq(), self:getRpcName()) printWarn(PBTAG, "ERR, error code:%s", errorCode) printWarn(PBTAG, "ERR, error msg:%s", errorMsg) printInfo(PBTAG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") end return PBSocketPack CustomerSpecial[,--[[ 特殊顾客基类 无就餐行为,直接继承RoleBase author:{zhangpeng} time:2025-05-15 11:05:56 ]] local CustomerSpecial, super = defClass("CustomerSpecial", RoleBase) local LOGTAG = "CustomerSpecial" -- 构造函数 function CustomerSpecial:ctor(reslink, specialCustomerType) super.ctor(self,RoleConst.roleType.customer) self.specialCustomerTypeId = specialCustomerType self.resId = string.format("customer_%d", self.specialCustomerTypeId) self.reslink = reslink[self.resId] end --- 初始化显示 function CustomerSpecial:initDisplay(parentName) self.display = GameObject.Instantiate(self.reslink) self:setSpecialCustomerParent(parentName) self.display:SetName(self.resId) -- 设置初始层级(基于Y坐标动态计算) self:updateDynamicSortingOrder() -- 添加寻路组件 self:addPathFind(self.display) self:setRolePathGrid(MapsConst.GridGraphsName[MapsConst.MapType.restaurant]) end -- 设置父节点 function CustomerSpecial:setSpecialCustomerParent(parentName) local scene = RestaurantMgr:getRestaurantScene() local specialCustomerRoot = scene.rootNode:Seek("special_customer") local parent = specialCustomerRoot:Seek(parentName) self.specialCustomerParent = parent self.display:SetParent(parent) end function CustomerSpecial:setSortingOrder(sortingOrder) local gameObject = self.display:Seek(self.specialCustomerTypeId) if not gameObject then printError(LOGTAG, string.format("特殊顾客[%s]节点[%s]不存在,无法设置层级", self.specialCustomerTypeId, self.specialCustomerTypeId)) return end -- SpriteRender local sprite_render = gameObject:GetComponent("Renderer") -- SkeletonAnimation local skeletonAnimation = gameObject:GetComponent("SkeletonAnimation") if sprite_render then sprite_render.sortingOrder = sortingOrder -- printInfo(LOGTAG, string.format("特殊顾客[%s]通过SpriteRender设置层级: %d", self.specialCustomerTypeId, sortingOrder)) elseif skeletonAnimation then local spine_render = skeletonAnimation:GetComponent("Renderer") if spine_render then spine_render.sortingOrder = sortingOrder -- printInfo(LOGTAG, string.format("特殊顾客[%s]通过SkeletonAnimation设置层级: %d", self.specialCustomerTypeId, sortingOrder)) else -- printError(LOGTAG, string.format("特殊顾客[%s]SkeletonAnimation没有Renderer组件", self.specialCustomerTypeId)) end else -- printError(LOGTAG, string.format("特殊顾客[%s]节点没有找到Renderer或SkeletonAnimation组件", self.specialCustomerTypeId)) end end function CustomerSpecial:getSpecialCustomerEnterPoint() local scene = RestaurantMgr:getRestaurantScene() local sp_enter_point = scene.rootNode:Seek("sp_enter_point") return sp_enter_point end function CustomerSpecial:getSpecialCustomerExitPoint() local scene = RestaurantMgr:getRestaurantScene() local sp_out_point = scene.rootNode:Seek("sp_out_point") return sp_out_point end function CustomerSpecial:getSpecialCustomerCommonStandbyPoint() local scene = RestaurantMgr:getRestaurantScene() local sp_common_standby_point = scene.rootNode:Seek("sp_common_standby_point") return sp_common_standby_point end -- 获取巡逻点列表 function CustomerSpecial:getIdlePointList() if self.idlePointList then return self.idlePointList end self.idlePointList = {} local wanderPoints = self.specialCustomerParent:Seek("wander_points") if wanderPoints then self.idlePointList = wanderPoints:SearchPattern("wander_\\d") end return self.idlePointList end -- 获取寻路组件 (重写父类方法) function CustomerSpecial:getPathFindSeeker() return self.pathFinder end -- 设置顾客初始位置 (重写父类方法) function CustomerSpecial:setCurPosition(pos) self.curPosition = pos self.display.transform.position = self.curPosition end -- 获取当前位置 (重写父类方法) function CustomerSpecial:getCurPosition() if self.display then return self.display.transform.position end return self.curPosition end -- 设置地图根节点 function CustomerSpecial:setMapRoot(mapRoot) self.mapRoot = mapRoot end --- 开始计时 function CustomerSpecial:startTimer(interval) self.time = 0 if not interval then interval = 1 end self.tmr = self.timer(self, function (t) self:timeupdate(t) end, interval, 0) end --- 停止计时 function CustomerSpecial:stopTimer() self.clear(self, true, self.tmr) end --- 时间更新(继承) ---@param seconds number 秒数 function CustomerSpecial:timeupdate(seconds) end --- 交互 function CustomerSpecial:addClickEvent(func) local restaurantScene = RestaurantMgr:getRestaurantScene() if not restaurantScene then printError(LOGTAG, "找不到餐厅场景") return end local touchCom = restaurantScene.touchCom touchCom:addListener( self.display:Seek(self.specialCustomerTypeId), TouchCom.LISTENER_TYPE.CLICK, function(p) printInfo(LOGTAG, string.format("点击特殊顾客[%s]", self.specialCustomerTypeId)) -- 处理点击事件 func() end ) end --- 展示剧情 function CustomerSpecial:showPlot(id, callback) StoryDialogMgr:showPageStoryDialogUI(id, callback) end -- 走到某一个坐标 (重写父类方法) function CustomerSpecial:walkToPos(tarPos, moveSpeed, resolve) -- 调用父类方法处理通用逻辑(方向计算、距离计算等) super.walkToPos(self, tarPos, moveSpeed, nil) -- 计算移动时间 local currentPos = self:getCurPosition() local distance = (tarPos - currentPos).magnitude local moveTime = distance / moveSpeed printInfo(LOGTAG, string.format("特殊顾客[%s]移动到目标位置,移动时间:%s", self.specialCustomerTypeId, moveTime)) -- 创建一个定时器来在移动过程中更新层级 local updateInterval = 0.1 local updateTimer = self:timer(function() self:updateDynamicSortingOrder() end, updateInterval, 0) -- 使用RunAction进行移动 self.display:RunAction( ua.Sequence{ ua.MoveTo(moveTime, tarPos), ua.cb(function() -- 停止层级更新定时器 if updateTimer then TimerMgr:rem(updateTimer) updateTimer = nil end -- 最后更新一次层级 self:updateDynamicSortingOrder() if resolve then resolve() end end) } ) end --- 展示惊叹号 function CustomerSpecial:showDong() if self.display then self.display:Seek("dong"):SetActive(true) end end --- 隐藏惊叹号 function CustomerSpecial:hideDong() if self.display then self.display:Seek("dong"):SetActive(false) end end --- 初始化点击进度 function CustomerSpecial:initClickProgress() self.progress = self.display:Seek("progress") self.progress_bar = self.progress:Seek("progress_bar"):GetComponent("Renderer") end --- 展示点击进度 function CustomerSpecial:showClickProgress(value) self.progress:SetActive(value > 0) local material = self.progress_bar.material -- 获取绑定的材质球 material:SetFloat("_FillAmount", value) -- 设置_FillAmount参数为0.75 end --- 销毁 function CustomerSpecial:destroy() if self.display then GameObject.Destroy(self.display) self.display = nil end -- super.destroy(self) end -- 获取当前物体的真实SortingOrder (重写父类方法) function CustomerSpecial:getCurrentSortingOrder() if not self.display then printError(LOGTAG, string.format("特殊顾客[%s]display节点不存在", self.specialCustomerTypeId)) return nil end -- 尝试从specialCustomerTypeId节点获取SortingOrder local specialNode = self.display:Seek(self.specialCustomerTypeId) if specialNode then local renderer = specialNode:GetComponent("Renderer") if renderer then local sortingOrder = renderer.sortingOrder printInfo(LOGTAG, string.format("特殊顾客[%s]从specialNode获取SortingOrder: %d", self.specialCustomerTypeId, sortingOrder)) return sortingOrder end -- 如果是SkeletonAnimation,尝试获取其Renderer local skeletonAnimation = specialNode:GetComponent("SkeletonAnimation") if skeletonAnimation then local spineRenderer = skeletonAnimation:GetComponent("Renderer") if spineRenderer then local sortingOrder = spineRenderer.sortingOrder printInfo(LOGTAG, string.format("特殊顾客[%s]从SkeletonAnimation获取SortingOrder: %d", self.specialCustomerTypeId, sortingOrder)) return sortingOrder end end end -- 如果specialNode没有Renderer,尝试从display的子节点查找 local renderer = self.display:GetComponent("Renderer") if renderer then local sortingOrder = renderer.sortingOrder printInfo(LOGTAG, string.format("特殊顾客[%s]从display获取SortingOrder: %d", self.specialCustomerTypeId, sortingOrder)) return sortingOrder end -- 如果都没有找到,尝试从子节点查找 local childCount = self.display.transform.childCount for i = 0, childCount - 1 do local child = self.display.transform:GetChild(i) local childRenderer = child:GetComponent("Renderer") if childRenderer then local sortingOrder = childRenderer.sortingOrder printInfo(LOGTAG, string.format("特殊顾客[%s]从子节点[%s]获取SortingOrder: %d", self.specialCustomerTypeId, child.name, sortingOrder)) return sortingOrder end -- 检查子节点是否有SkeletonAnimation local childSkeletonAnimation = child:GetComponent("SkeletonAnimation") if childSkeletonAnimation then local spineRenderer = childSkeletonAnimation:GetComponent("Renderer") if spineRenderer then local sortingOrder = spineRenderer.sortingOrder printInfo(LOGTAG, string.format("特殊顾客[%s]从子节点[%s]的SkeletonAnimation获取SortingOrder: %d", self.specialCustomerTypeId, child.name, sortingOrder)) return sortingOrder end end end printWarn(LOGTAG, string.format("特殊顾客[%s]无法找到任何Renderer组件", self.specialCustomerTypeId)) return nil end return CustomerSpecial Userq--[[ author:{zhangpeng} time:2024-01-03 15:30:06 ]] local User = defClassStatic("User") local LOGTAG = "User" function User:init() printInfo(LOGTAG, "User init") end function User:initUser(userId) userId = userId or self:getUserId() printInfo(LOGTAG, "initUser, userId:%s", userId) self:setUserId(userId) end function User:initUserDB(userId) printInfo(LOGTAG, "initData, userId:%s", userId) local newUser = self:isNewUser() printInfo(LOGTAG, "initData, 是否新用户:%s", newUser) SqliteMgr:openCurUserDB(userId) -- user db里添加表 self.user = SqliteMgr.userTable:getOrCreate(userId) self.user:save() if newUser then end end function User:resetLoginData() self:setUserId(nil) self.user = nil self.roleList = {} self.lastAuthTime = 0 end function User:getGuestUserId() return self:getDeviceId() end -- 关卡 function User:setCurLevel(level) self.user.curLevel = level self.user:save() end function User:getCurLevel() return self.user.curLevel end -- 用户id function User:setUserId(userId) self.userId = userId end function User:getUserId() return self.userId or self:getGuestUserId() end function User:getUserModel() return self.user end --放大镜道具 function User:setItemFindNum(itemFindNum) self.user.itemFindNum = itemFindNum self.user:save() end function User:getItemFindNum() return self.user.itemFindNum end -- 免费金币的已观看广告次数 function User:setFreeCoinsAdWatchTimes(freeCoinsAdCount) self.user.freeCoinsAdCount = freeCoinsAdCount self.user:save() end function User:getFreeCoinsAdWatchTimes() return self.user.freeCoinsAdCount end -- 获得道具看广告的已观看广告次数 function User:setItemGetAdWatchTimes(itemGetAdCount) self.user.itemGetAdCount = itemGetAdCount self.user:save() end function User:getItemGetAdWatchTimes() return self.user.itemGetAdCount end -- 获得道具看广告的时间戳 function User:setItemGetAdSucTime(time) self.user.itemGetAdSucTime = time self.user:save() end function User:getItemGetAdSucTime() return self.user.itemGetAdSucTime end -- 移除广告标记(未移除是1,已经移除是2) function User:removeAd() self.user.adRemoveFlag = 2 self.user:save() end function User:getAdRemoveFlag() return self.adRemoveFlag end -- 看满5次广告得金币的时间戳 function User:setFreeCoinsSucTime(time) self.user.freeCoinsSucTime = time self.user:save() end function User:getFreeCoinsSucTime() return self.user.freeCoinsSucTime end -- 新手礼包购买状态 function User:setNewUserBundleState(state) self.user.newUserBundle = state self.user:save() end function User:getNewUserBundleState() return self.user.newUserBundle end -- 签到数据 function User:setLastSignDay(day) self.user.lastSignDay = day self.user:save() end function User:getLastSignDay() return self.user.lastSignDay end function User:setHasSignedToday(state) self.user.hasSignedToday = state self.user:save() end function User:getHasSignedToday() return self.user.hasSignedToday end -- 获取某一天是否签到了(初始是这样的字符串:"1_1_1_1_1_1_1") function User:getSingFlagByDataIndex(index) local strs = self:getSignDatas() local flags = string.split(strs, "_") return tonumber(flags[index]) == tonumber(GameConst.signDataState.SIGN_YES) end -- 设置某一天的签到状态 function User:setSignFlagByDataIndex(index, flag) local strs = self:getSignDatas() local flags = string.split(strs, "_") flags[index] = flag self:setSignDatas(table.concat(flags, "_")) end -- 今天是否签到了 function User:isTodaySigned() local today_flag = self:getHasSignedToday() return today_flag == GameConst.signDataState.SIGN_YES end function User:setSignDatas(signDatas) self.user.signDatas = signDatas self.user:save() end function User:getSignDatas() return self.user.signDatas end function User:getDeviceId() local deviceid = LocalStorageMgr:get(StrogeKeyDef.DEVICE_ID) if not deviceid then self.newUser = true deviceid = CS.MessageIdUtil:GenerateUniqueId() LocalStorageMgr:set(StrogeKeyDef.DEVICE_ID,deviceid) else self.newUser = false end return deviceid end function User:isNewUser() return self.newUser end User:init()StorydialogCfgParse"--[[ 剧情对话数据解析 author:{zhangpeng} time:2025-05-28 10:48:09 ]] local StorydialogCfgData = require("data/config/storydialogCfg") local StorydialogCfgParse = defClassStatic("StorydialogCfgParse") local LOGTAG = "StorydialogCfgParse" local storydialog_list = {} local function initAllIds() for k, v in pairs(StorydialogCfgData) do table.insert(storydialog_list, v.dialogId) end end function StorydialogCfgParse:init() initAllIds() end function StorydialogCfgParse:getDialogCfg(id) for _, v in pairs(StorydialogCfgData) do if v.dialogId == id then return v end end return nil end -- 重新组装一下数据,根据times字段,把对话内容分在不同的表中 local storydialog_list_by_times = {} function StorydialogCfgParse:getDialogCfgList(dialogId) local list = {} for _, v in pairs(StorydialogCfgData) do if v.dialogId == dialogId then table.insert(list, v) end end return list end StorydialogCfgParse:init() CustomerListUIW--- 顾客列表ui ---@class CustomerListUI : UILayer local CustomerListUI, super = defClass("CustomerListUI", UILayer) require("modules/ui/customerui/CustomerCell") require("modules/ui/customerui/SpecialCustomerCell") require("modules/ui/customerui/VendorCell") ---@param pageId number 页签id function CustomerListUI:ctor(pageId, pageType) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/customerui/customeruireslink") self.pageId = pageId self.pageType = pageType self.indexes = {} self.special_indexes = {} self.vendor_indexes = {} end function CustomerListUI:onLoad() self.ui = GameObject.Instantiate(self.R.customer_list_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() self:timer(function() local scrollView = self.ui:Seek("Scroll View"):GetComponent("UnityEngine.UI.ScrollRect") scrollView.verticalNormalizedPosition = 1 end, 0.2) end --- 初始化 function CustomerListUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.toggle_customer = self.ui:Seek("toggle_customer") self.toggle_customer_mask = self.ui:Seek("toggle_customer_mask") self.toggle_owner = self.ui:Seek("toggle_owner") self.toggle_owner_mask = self.ui:Seek("toggle_owner_mask") self.content_customer = self.ui:Seek("content_customer") self.content_owner = self.ui:Seek("content_owner") self.customer_toggles = self.ui:Seek("customer_toggles") self.owner_locked = self.ui:Seek("owner_locked") self.toggle_customer_normal = self.ui:Seek("toggle_customer_normal") self.toggle_customer_normal_mask = self.ui:Seek("toggle_customer_normal_mask") self.toggle_customer_special = self.ui:Seek("toggle_customer_special") self.toggle_customer_special_mask = self.ui:Seek("toggle_customer_special_mask") self.scrollView = self.ui:Seek("Scroll View") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addToggleValueChangeEvent(self.toggle_customer, function(isOn) self:customerTab(isOn) end) util.ugui.addToggleValueChangeEvent(self.toggle_owner, function(isOn) self:ownerTab(isOn) end) util.ugui.addToggleValueChangeEvent(self.toggle_customer_normal, function(isOn) self:customerNormalTab(isOn) end) util.ugui.addToggleValueChangeEvent(self.toggle_customer_special, function(isOn) self:customerSpecialTab(isOn) end) util.ugui.addButtonClickEvent(self.owner_locked, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) PopUpUI.new("敬请期待"):show():showMask():enableCloseWhenClickMask() end) end --- 展示UI function CustomerListUI:showUI() if not self.pageId then self.pageId = 1 end if not self.pageType then self.pageType = 1 end self:triggerPage(self.pageId, self.pageType) --self.scrollView:GetComponent("ScrollRect").normalizedPosition = Vector2(0, 1) -- 滚动到顶部 end function CustomerListUI:triggerPage(pageId, pageType) self:customerTab(pageId == 1) self:ownerTab(pageId == 2) self:customerNormalTab((pageId == 1) and (pageType == 1)) self:customerSpecialTab((pageId == 1) and (pageType == 2)) end function CustomerListUI:triggerCustomerPageType(pageType) self:customerNormalTab(pageType == 1) self:customerSpecialTab(pageType == 2) end --- 页签切换 function CustomerListUI:customerTab(isOn) self.content_customer:SetActive(isOn) self.toggle_customer_mask:SetActive(isOn) self.customer_toggles:SetActive(isOn) printInfo("CustomerListUI", "customerTab %s", isOn) if isOn then self.pageId = 1 self:triggerCustomerPageType(1) end end --- 页签切换 function CustomerListUI:ownerTab(isOn) self.content_owner:SetActive(isOn) self.toggle_owner_mask:SetActive(isOn) printInfo("CustomerListUI", "ownerTab %s", isOn) if isOn then self.pageId = 2 local data = VendorCfgParse:getData() self:createTargetAll(data, self.vendor_indexes, function(idx, id, parent) return self:createVendorCell(idx, id, parent) end, self.content_owner.transform) end end --- 页签切换 function CustomerListUI:customerNormalTab(isOn) self.toggle_customer_normal_mask:SetActive(isOn) printInfo("CustomerListUI", "customerNormalTab %s", isOn) if isOn then self.pageType = 1 local data = CustomerCfgParse:getData() self:createTargetAll(data, self.indexes, function(idx, id, parent) return self:createCustomerCell(idx, id, parent) end, self.content_customer.transform) --self.content_customer:GetComponent("ContentSizeFitter"):SetLayoutVertical() --self.scrollView:GetComponent("ScrollRect").normalizedPosition = Vector2(0, 1) -- 滚动到顶部 else for _, v in ipairs(self.indexes) do v:hide() end end end --- 页签切换 function CustomerListUI:customerSpecialTab(isOn) self.toggle_customer_special_mask:SetActive(isOn) printInfo("CustomerListUI", "customerSpecialTab %s", isOn) if isOn then self.pageType = 2 local data = CustomerSpecialCfgParse:getData() self:createTargetAll(data, self.special_indexes, function(idx, id, parent) return self:createSpecialCustomerCell(idx, id, parent) end, self.content_customer.transform) --self.content_customer:GetComponent("ContentSizeFitter"):SetLayoutVertical() --self.scrollView:GetComponent("ScrollRect").normalizedPosition = Vector2(0, 1) -- 滚动到顶部 else for _, v in ipairs(self.special_indexes) do v:hide() end end end -- 创建所有格子 function CustomerListUI:createTargetAll(data, list, newCellFunc, parent) for i, v in ipairs(data) do local cell = list[i] if cell then cell:reload(v.id) else list[i] = newCellFunc(i, v.id, parent) end end for i = #data + 1, #list do list[i]:hide() end end function CustomerListUI:createCustomerCell(idx, id, parent) local cellNode = GameObject.Instantiate(self.R.customer_cell, parent) cellNode:SetName("customer_cell_" .. idx) return CustomerCell.new(cellNode, id) end function CustomerListUI:createSpecialCustomerCell(idx, id, parent) local cellNode = GameObject.Instantiate(self.R.special_customer_cell, parent) cellNode:SetName("special_customer_cell_" .. idx) return SpecialCustomerCell.new(cellNode, id) end function CustomerListUI:createVendorCell(idx, id, parent) local cellNode = GameObject.Instantiate(self.R.vendor_cell, parent) cellNode:SetName("vendor_cell_" .. idx) return VendorCell.new(cellNode, id) end return CustomerListUImain-- mgr require("modules/maps/MapsConst") require("modules/maps/MapBase") require("modules/maps/MapsMgr") -- init MapsMgr:init() AudioConst local AudioConst = defClassStatic("AudioConst") function AudioConst:init() end AudioConst.audioClipName = { -- 音效 click = "click", building = "building", coin = "coin", -- 音乐 bgm = "bgm", } AudioConst:init()GuideUI--[[ 引导UI author:{zhangpeng} time:2025-06-13 16:22:54 ]] local GuideUI, super = defClass("GuideUI", UILayer) local LOGTAG = "GuideUI" local Vector2 = CS.UnityEngine.Vector2 local Vector3 = CS.UnityEngine.Vector3 local Vector4 = CS.UnityEngine.Vector4 local Rect = CS.UnityEngine.Rect local RectTransform = CS.UnityEngine.RectTransform local GameObject = CS.UnityEngine.GameObject function GuideUI:ctor() super.ctor(self) self.R = ResLoader.loadResLink("modules/guide/guidereslink") end function GuideUI:onLoad() self.ui = GameObject.Instantiate(self.R.guide_ui) self:addChild(self.ui) self.bgMask = self.ui:Seek("mask") self:setMaskActive(false) end function GuideUI:isShowMask() return self.bgMask.activeSelf end function GuideUI:setMaskActive(bool) self.bgMask:SetActive(bool) end function GuideUI:setMaskCenter(x, y, cfg) local r = cfg.r or 100 local w = cfg.w or 0 local h = cfg.h or 0 local d = cfg.d or 50 self.maskCenter = {x = x, y = y, w = w, h = h, r = r, d = d} local image = self.bgMask:GetComponent("Image") local material = image.materialForRendering material:SetVector("pos", Vector4(x, y, 0)) material:SetInt("r", r) material:SetInt("w", w) material:SetInt("h", h) material:SetInt("d", d) end function GuideUI:setTouchHandler(centerHandler, maskHandler) centerHandler = centerHandler or function() end maskHandler = maskHandler or function() end util.ugui.addClickEvent( self.bgMask, function(data) if self:checkTouch(data) then centerHandler(data) else maskHandler(data) end end, true ) end function GuideUI:setCheckTouchFunc(func) self.checkTouchFunc = func end function GuideUI:checkTouch(data) if self.checkTouchFunc then return self.checkTouchFunc(data, self.maskCenter) end local p = util.ugui.screenSpaceToCanvasSpace(data.position,UILayerUtil.canvas,UILayerUtil.cameraCom) local w = self.maskCenter.w + self.maskCenter.r * 2 local h = self.maskCenter.h + self.maskCenter.r * 2 local x = self.maskCenter.x - w / 2 local y = self.maskCenter.y - h / 2 local rect = Rect(x, y, w, h) local bool = rect:Contains(p) return bool end function GuideUI:playSound(audioPath) self:stopSound() self.audioId = self.ui:PlaySound(audioPath) end function GuideUI:stopSound() if not self.audioId then return end self.ui:StopSound(self.audioId) self.audioId = nil end --[[ cfg = { audioPath = , text = , delay, interval, duration = , times, callback = , dx,dy = , r, w, h, d =, } ]] function GuideUI:showTouchClick(pos, cfg) local x, y = self:getXAndY(pos, cfg) self:setMaskCenter(x, y, cfg) self:addHand(x, y, self.R.hand_tips, "click", cfg) -- self:addTextLabel(x, y, cfg) end function GuideUI:getXAndY(pos, cfg) local x = pos.x + (cfg.dx or 0) local y = pos.y + (cfg.dy or 0) return x, y end function GuideUI:addHand(x, y, res, anim, cfg) self.hand = GameObject.Instantiate(self.R.guide_hand) self.hand:SetParent(self.ui, false) self.hand.transform.localPosition = Vector3(x, y, 0) self.hand.transform.localScale = Vector3.zero if cfg.isHideHand then self.hand:SetActive(false) end local delay = cfg.delay or 0 local interval = cfg.interval or 0 local duration = nil if cfg.audioPath then local audioClip = ResLoader.loadAsset(cfg.audioPath) duration = audioClip.length end duration = duration or (1.167 * 3) local times = cfg.times or 0 local seq = { ua.cb( function() -- todo::播放小手动画 -- util.spine.play(self.hand:Seek("hand"), anim, true) -- self.hand.transform.localScale = Vector3.one -- if cfg.audioPath then -- self:playSound(cfg.audioPath) -- end end ), ua.Delay(duration), ua.cb( function() if not cfg.hideHand then -- util.spine.play(self.hand:Seek("hand"), anim, false) else self.hand.transform.localScale = Vector3.zero end if cfg.audioPath then self:stopSound() end end ), ua.Delay(interval) } if times < 1 then self.ui:RunAction( ua.Sequence( { ua.Delay(delay), ua.RepeatForever(ua.Sequence(seq)) } ) ) else self.ui:RunAction( ua.Sequence( { ua.Delay(delay), ua.Repeat(ua.Sequence(seq), times - 1), ua.cb( function() self:close() if cfg.callback then cfg.callback() end end ) } ) ) end end function GuideUI:addTextLabel(x, y, cfg) if string.isEmpty(cfg.text) then return end self.label = GameObject.Instantiate(self.R.label_tip) self.label:SetParent(self.ui, false) local lab = self.label:Seek("Text") lab[UGUI.Text].text = cfg.text local bg = self.label:Seek("Image") util.ugui.rebuildLayout(self.label:Seek("Text")) util.ugui.rebuildLayout(bg) local safeRect = util.ugui.getSafeAreaRectInCanvasSpace() local rect = bg[RectTransform].rect local r = cfg.r or 150 local dx = r + (rect.width / 2) + 5 local dy = r + (rect.height / 2) + 5 local list = { Vector2(x, y + dy), --上 Vector2(x, y - dy), --下 Vector2(x - dx, y), --左 Vector2(x + dx, y) --右 } for i, pos in ipairs(list) do rect.center = pos if safeRect:Contains(rect.min) and safeRect:Contains(rect.max) then self.label.transform.localPosition = Vector3(pos.x, pos.y, 0) return end end printWarn(LOGTAG, "addTextLabel, 没有合适位置显示提示文本,尝试缩减文本字数或换行") end return GuideUIEmployeSkinCfgParse--[[ 员工皮肤配置解析 author:{zhangpeng} time:2025-05-21 10:07:43 ]] local EmployeSkinCfg = require("data/config/employeSkinCfg") local EmployeSkinCfgParse = defClassStatic("EmployeSkinCfgParse") local LOG_TAG = "EmployeSkinCfgParse" local employe_skin_list = {} local function initAllIds() for k, v in pairs(EmployeSkinCfg) do table.insert(employe_skin_list, v.id) end end -- init function EmployeSkinCfgParse:init() initAllIds() end -- 根据员工皮肤配置id取一个员工皮肤配置的数据 function EmployeSkinCfgParse:getEmployeSkinCfg(employeSkinId) for k, v in pairs(EmployeSkinCfg) do if v.id == employeSkinId then return v end end end -- function EmployeSkinCfgParse:getEmployeeSkinCfgByEmployeeCfgId(employeeCfgId) local ids = {} for k, v in pairs(EmployeSkinCfg) do if v.roleRid == employeeCfgId then table.insert(v) end end return ids end EmployeSkinCfgParse:init() TaskUserDatalocal TaskUserData = defClass("TaskUserData") local TaskOrderUserData = require("modules/task/data/TaskOrderUserData") local TaskBootUserData = require("modules/task/data/TaskBootUserData") local LOGTAG = "TaskUserData" -- 构造函数 function TaskUserData:ctor() self:init() end -- 初始化 function TaskUserData:init() self.order = TaskOrderUserData.new() -- 订单任务数据 self.boot = TaskBootUserData.new() -- 引导任务数据 end -- 加载数据 function TaskUserData:load() self.order:load() self.boot:load() end -- 保存数据 function TaskUserData:save() self.order:save() self.boot:save() end return TaskUserDataLoginUIh--[[ author: zhangheng time: 2025-07-19 ]] local LoginUI, super = defClass("LoginUI", UILayer) function LoginUI:ctor() super.ctor(self) self.R = ResLoader.loadResLink("modules/login/loginuireslink") end function LoginUI:onLoad() self.ui = GameObject.Instantiate(self.R.login_ui) self:addChild(self.ui) end return LoginUI worldmapuireslinkreturn { --BASIC --ASSET world_map_ui = {"Assets/AssetsPackage/Res/modules/ui/worldmap/prefabs/world_map_ui.prefab", 0, 0}, } queue_action--@desc: 将table里的lua function, 封装成闭包, 用于异步回调 --@desc: 用法: queue_action.table_process(args) local queueAction = CS.LuaGlobal.QueueAction local queue_action = {} function queue_action.func_wrapper(func) return function(...) local args = {...} -- print("func_wrapper before run --------------") queueAction(function() -- print("func_wrapper run --------------") func(table.unpack(args)) end) end end --将table里的lua function, 封装成闭包 function queue_action.table_process(args) if args == nil then return end local need_wrapper = {} for k,v in pairs(args) do if type(v) == "function" then need_wrapper[k] = v end end for k_n,v_n in pairs(need_wrapper) do args[k_n] = queue_action.func_wrapper(v_n) end end return queue_action CookingBench --[[ 灶台,一个灶台可以有多个炉子 author:{zhangpeng} time:2025-05-16 11:06:20 ]] local CookingBench,super = defClass("CookingBench",BuildingBase) local LOGTAG = "CookingBench" function CookingBench:ctor(args) super.ctor(self,args) self.benchCfgId = args.deskId self.resId = "building_"..self.buildingInfo.buildingType self.state = CookingConst.State.idle self.stoves = {} -- 初始化出餐类型 self:initCookingType() -- 初始化灶台view self:initBenchView() -- 初始化炉子 self:initStoves() end -- 设置出餐类型(菜类/饮料类) function CookingBench:initCookingType() -- 是否属于菜类灶台 local isDish = false for _,v in pairs(CookingConst.dishIds) do if self.buildingInfo.buildingType == v then isDish = true break end end -- 是否属于饮料类 local isDrink = false for _,v in pairs(CookingConst.drinkIds) do if self.buildingInfo.buildingType == v then isDrink = true break end end if isDish then self.cookingType = CookingConst.CookingType.dish elseif isDrink then self.cookingType = CookingConst.CookingType.drink end printInfo(LOGTAG, string.format("灶台[%s]初始化出餐类型:%s", self.benchCfgId, self:getCookingTypeString())) end -- 获取出餐类型文本 function CookingBench:getCookingTypeString() return CookingConst.CookingTypeName[self.cookingType] end function CookingBench:initBenchView() self.buildingNode = GameObject.Instantiate(self.scene.R[self.resId]) self.sortBaseNode = self.buildingNode:Seek("base") self:setBuildingParent(self.buildingParent) self:setBuildingNodeName(self.resId) -- 设置层级 local sprite_render = self.buildingNode:Seek("img"):GetComponent("Renderer") -- sprite_render.sortingOrder = 11 end -- 初始化炉子 function CookingBench:initStoves() -- 根据灶台类型决定炉子数量 local stoveCount = self:getStoveCount() printInfo(LOGTAG, string.format("灶台[%s]初始化炉子,数量:%s", self.benchCfgId, stoveCount)) -- 创建炉子 for i = 1, stoveCount do local stove = CookingBenchStove.new(self, i) table.insert(self.stoves, stove) end end -- 根据灶台id获取出餐口数量 function CookingBench:getStoveCount() return CookingConst.CookingBenchStoveCount[self.buildingInfo.buildingType] end -- 获取出餐类型 function CookingBench:getCookingType() return self.cookingType end --- 获取灶台上的炉子 function CookingBench:getAllStoves() return self.stoves end -- 获取空闲的炉子 function CookingBench:getIdleStove() for i = 1, #self.stoves do if not self.stoves[i]:isWorking() then return self.stoves[i] end end return nil end -- 是否有空闲炉子 function CookingBench:hasIdleStove() return self:getIdleStove() ~= nil end return CookingBenchWaiter--[[ 服务员 author:{zhangpeng} time:2025-05-30 15:13:09 ]] local Waiter,super = defClass("Waiter",Employe) local LOGTAG = "Waiter" ---构造函数 function Waiter:ctor(reslink,cfgId) super.ctor(self,reslink,cfgId) self.curWanderIndex = 1 self:init() end function Waiter:init() super.init(self) -- 服务员工作配置 self.waiterConfig = { workDuration = 30, -- 工作持续时间(秒) restDuration = 10, -- 休息时间(秒) checkInterval = 1, -- 检查间隔(秒) orderProcessTime = 2, -- 处理一个订单需要的时间(秒) } -- 服务员当前状态 self.waiterStatus = { isProcessingOrder = false, -- 是否正在处理订单 currentCustomer = nil, -- 当前正在处理的顾客 isMoving = false, -- 是否正在移动中 } self.pointWithDiningDesk = { [1] = BuildingConst.buildingType.diningdesk1, [2] = BuildingConst.buildingType.diningdesk2, [3] = BuildingConst.buildingType.diningdesk3, [4] = BuildingConst.buildingType.diningdesk4, } printInfo(LOGTAG, string.format("服务员[%s]初始化完成", self.employeCfgId)) end function Waiter:action() self:playIdleAnim() local state = self:getState() if state == EmployeConst.State.Resting then printInfo(LOGTAG, string.format("服务员[%s]正在休息,暂不处理点餐", self.employeCfgId)) return end if state == EmployeConst.State.Idle then self:startAutoOrderWork() self:doAutoOrder() end end -- 开始自动点餐工作 function Waiter:startAutoOrderWork() if self:getState() ~= EmployeConst.State.Busy then self:setState(EmployeConst.State.Busy) end -- 开始巡逻所有停留点 self:goToWanderPoint() end -- 开始巡逻模式 function Waiter:goToWanderPoint() local wanderPoints = self.wanderPoints local curWanderIndex = self.curWanderIndex util.async.foreach( wanderPoints, function(wanderPoint, resolve, i) self:rolePathFind(wanderPoint.transform.position, function () printInfo(LOGTAG, string.format("服务员[%s]到达巡逻点[%d]", self.employeCfgId, i)) if self:isDiningDeskPoint(i) then -- 每个桌子旁边停留10秒检测是否有客人 self:startCheckOrderTimer(i, function() -- 检测到客人 self:playRecordAnim(function() resolve() end) end, function() printInfo(LOGTAG, string.format("服务员[%s]去下一关停靠点[%d]", self.employeCfgId, i)) resolve() end) else -- 非餐桌点位,待机3秒,然后去下一关停靠点 self.display:Delay(3, function() printInfo(LOGTAG, string.format("服务员[%s]去下一关停靠点[%d]", self.employeCfgId, i)) resolve() end) end end) end, function() printInfo(LOGTAG, string.format("服务员[%s]到达所有巡逻点", self.employeCfgId)) self.display:Delay(2, function() -- 继续下一轮 self:goToWanderPoint() end) end ) end -- 是否是餐桌点 function Waiter:isDiningDeskPoint(pointIndex) if pointIndex == 1 or pointIndex == 2 or pointIndex == 3 or pointIndex == 4 then return true end return false end -- 判断当前点位所在的餐桌是否有顾客,只要有顾客就播一下下单动画,和自动下单逻辑完全无关 function Waiter:isDiningDeskHasCustomer(pointIndex) local diningDeskId = self.pointWithDiningDesk[pointIndex] local allDiningDesks = DiningDeskMgr:getAllDiningDesks() for _, diningDesk in pairs(allDiningDesks) do local deskTypeId = diningDesk.buildingInfo.buildingType if deskTypeId == diningDeskId then local seats = diningDesk:getAllSeats() for _, seat in pairs(seats) do local customer = seat:getCustomer() -- 有客人且在座位上坐着 if customer and customer.isOrdering then printInfo(LOGTAG, string.format("服务员[%s]附近餐桌[%d]有顾客", self.employeCfgId, diningDeskId)) return true end end end end -- 这个餐桌没有待处理订单 -- printInfo(LOGTAG, string.format("服务员[%s]餐桌[%d]没有待处理订单", self.employeCfgId, diningDeskId)) return false end -- 播放点餐动画 function Waiter:playRecordAnim(cb) local actions = EmployeConst.EmployeeAction[self.employeCfgId] if actions and actions.record then util.spine.play(self.display:Seek("employe_anim"), actions.record, false, function() if cb then cb() end end) end end -- 启动检测客人计时器 function Waiter:startCheckOrderTimer(deskId, haveOrderCb, timeOverCb) -- 每个餐桌旁边的停留时间 local stayTime = 10 self.checkOrderStartTime = 0 -- 10秒之后停止计时器,并执行回调 self.checkOrderTimer = TimerMgr:add(function() self.checkOrderStartTime = self.checkOrderStartTime + 0.3 -- printInfo(LOGTAG, string.format("检测是否有客人点餐, 已经停留了%s秒", self.checkOrderStartTime)) if self:isDiningDeskHasCustomer(deskId) then printInfo(LOGTAG, "检测到有客人点餐") if haveOrderCb then haveOrderCb() self:stopCheckOrderTimer() end end end, 0.3, true) self.delayTimerAction = self.display:Delay(stayTime, function() self:stopCheckOrderTimer() if timeOverCb then timeOverCb() end end) end -- 停止检测客人计时器 function Waiter:stopCheckOrderTimer() if self.checkOrderTimer then TimerMgr:rem(self.checkOrderTimer) printInfo(LOGTAG, "到达停留时间,停止检测客人计时器") self.checkOrderTimer = nil end if self.delayTimerAction then self.display:StopAction(self.delayTimerAction) self.delayTimerAction = nil end end -- 自动点单 function Waiter:doAutoOrder() local workTimeLimit = self.employeeInfo:getWorkTimeLimit() local workTime = 0 local timer = nil -- 开始工作 self.employeeInfo:setWorkState(EmployeConst.State.Busy) timer = self:timer(function() workTime = workTime + 2 self.employeeInfo:setWorkTime(workTime) -- 执行自动点单逻辑 OrderDishesMgr:autoOrder() if workTime >= workTimeLimit then -- 停止定时器 if timer then TimerMgr:rem(timer) timer = nil -- 休息 EmployFunsMgr:doResting(self, function(emp) emp:doAutoOrder() end) end return end end, 2, 0) end return WaiterFirebaseEventEnum--[[ 埋点事件名字定义,按功能模块分类 author:{zhangpeng} time:2023-12-07 15:59:46 ]] local FirebaseEventEnum,_ = defClassStatic("FirebaseEventEnum") FirebaseEventEnum.currency = { earn_virtual_currency = "earn_virtual_currency", -- 获得虚拟货币 参数:{virtual_currency_name:string(Gem/GoldCoin), value:number} spend_virtual_currency = " spend_virtual_currency" -- 消耗虚拟货币 } -- 登录注册埋点 FirebaseEventEnum.login = { login_by_email = "login_by_email" } -- 大地图埋点 FirebaseEventEnum.worldmap = { click_building = "click_building" -- 点击大地图建筑 参数:大地图id, 建筑名字 } -- 互动场景埋点 FirebaseEventEnum.interactivescene = { } -- 换装埋点 FirebaseEventEnum.skinswitch = { } -- 形象列表场景埋点 FirebaseEventEnum.roleshowscene = { } -- 任务系统 FirebaseEventEnum.tasksystem = { } function FirebaseEventEnum:init() end FirebaseEventEnum:init() TextCfgParse--[[ 多语言配置表 author:{zhangpeng} time:2024-11-20 11:54:36 ]] local TextCfgData = require("data/config/TextCfg") local TextCfgParse = defClassStatic("TextCfgParse") local LOG_TAG = "TextCfgParse" TextCfgParse.Lan = { cn = 1, en = 2, jp = 3 } local text_list = {} local function initAllIds() for k, v in pairs(TextCfgData) do table.insert(text_list, v.textId) end end -- usage: -- local str = TextCfgParse:getTextStr("login_deviceid_suc",TextCfgParse.Lan.en) -- login_deviceid_suc 在TextCfg中定义 function TextCfgParse:getTextStr(id, language) if language == "cn" then language = TextCfgParse.Lan.cn elseif language == "en" then language = TextCfgParse.Lan.en elseif language == "jp" then language = TextCfgParse.Lan.jp end local result for k,v in pairs(TextCfgData) do if v.textId == id then result = v break end end if language == TextCfgParse.Lan.cn then return result.cn elseif language == TextCfgParse.Lan.en then return result.en elseif language == TextCfgParse.Lan.jp then return result.jp else local str = string.format("not define key:%s", id) printWarn(LOG_TAG, str) return str end end function TextCfgParse:init() initAllIds() end -- 检查id是否重复 function TextCfgParse:checkIdDuplicate(textCfg) local textIdSet = {} local duplicates = {} for _, entry in ipairs(textCfg) do local textId = entry.textId if textIdSet[textId] then duplicates[textId] = (duplicates[textId] or 1) + 1 else textIdSet[textId] = true end end if next(duplicates) then for textId, count in pairs(duplicates) do printWarn(string.format("- %s (重复次数: %d)", textId, count)) end else printInfo(LOG_TAG, "多语言表无重复id") end return duplicates end TextCfgParse:init()CommonUserData--[[ luaide 模板位置位于 Template/FunTemplate/NewFileTemplate.lua 其中 Template 为配置路径 与luaide.luaTemplatesDir luaide.luaTemplatesDir 配置 https://www.showdoc.cc/web/#/luaide?page_id=713062580213505 author:{author} time:2025-06-11 14:52:10 ]] local CommonUserData = defClass("CommonUserData") local GetString = UnityEngine.PlayerPrefs.GetString -- 获取指定key的值 fun(key, defValue):string local SetString = UnityEngine.PlayerPrefs.SetString -- 设置指定key的值 fun(key, value):void local GetInt = UnityEngine.PlayerPrefs.GetInt -- 获取指定key的值 fun(key, defValue):int local SetInt = UnityEngine.PlayerPrefs.SetInt -- 设置指定key的值 fun(key, value):void local Save = UnityEngine.PlayerPrefs.Save -- 保存所有修改 fun():void function CommonUserData:ctor() self:init() end function CommonUserData:init() printInfo("CommonUserData", "------ 通用用户数据初始化 ------") self.isNewPlayer = true -- 是否新玩家 self.isFirstLogin = true -- 是否第一次登录 self.isFirstGuide = true -- 是否第一次引导 end return CommonUserData PassivityDesk ;--[[ 闲逛消费建筑 author:{zhangpeng} time:2025-05-16 17:29:02 ]] local PassivityDesk,super = defClass("PassivityDesk",BuildingBase) local LOGTAG = "PassivityDesk" -- 闲逛建筑状态 PassivityDesk.State = { idle = 1, -- 空闲 used = 2, -- 使用中 dirty = 3, -- 脏 } function PassivityDesk:ctor(args) super.ctor(self,args) self.deskId = args.deskId self.resId = "building_"..self.buildingInfo.buildingType self.state = PassivityDesk.State.idle -- 修改为支持多顾客接待 self.currentCustomers = {} -- 当前正在接待的顾客列表 self.waitingCustomers = {} -- 等待队列 self.customerTimers = {} -- 顾客服务计时器 -- 当前建筑如果有正在接待的顾客,新进来顾客得等待位置 self.stayPoints = {} self.stayPointsOccupancy = {} -- stay_points的占用状态 -- 如果是露天电影院,初始化椅子管理 if self.buildingInfo.buildingType == BuildingConst.buildingType.outdoormovie then self.chairOccupancy = {} -- 椅子占用状态 end self:initDeskView() end function PassivityDesk:initDeskView() self.buildingNode = GameObject.Instantiate(self.scene.R[self.resId]) self.sortBaseNode = self.buildingNode:Seek("base") self:setBuildingParent(self.buildingParent) self:setBuildingNodeName(self.resId) -- stay_points self.stayPoints = self.buildingNode:SearchPattern("stay_point_\\d", true) -- 初始化stay_points占用状态 for i = 1, table.nums(self.stayPoints) do self.stayPointsOccupancy[i] = nil end -- 设置层级 local sprite_render = self.buildingNode:Seek("img"):GetComponent("Renderer") -- sprite_render.sortingOrder = 11 -- 金币放置点 self.coinPosNode = self.buildingNode:Seek("coin_pos") -- 金币收集点击区域 if self.coinPosNode then self.coinCollectNode = self.coinPosNode:Seek("touch_mask") if self.coinCollectNode then self:addCoinPosClickEvent() end end end -- 获取建筑位置 function PassivityDesk:getPosition() if self.buildingNode then return self.buildingNode.transform.position end return nil end -- 获取建筑ID function PassivityDesk:getDeskId() return self.deskId end -- 获取建筑状态 function PassivityDesk:getState() return self.state end -- 设置建筑状态 function PassivityDesk:setState(state) self.state = state end -- 获取当前建筑类型可接待的最大顾客数量 function PassivityDesk:getMaxAcceptCount() return PassivityConst.acceptCount[self.buildingInfo.buildingType] or 1 end -- 获取当前建筑类型的接待时间 function PassivityDesk:getAcceptTime() return PassivityConst.acceptTime[self.buildingInfo.buildingType] or 2 end -- 获取空闲的stay_point function PassivityDesk:getAvailableStayPoint() for i = 1, table.nums(self.stayPoints) do if not self.stayPointsOccupancy[i] then return i, self.stayPoints[i] end end return nil, nil end -- 占用stay_point function PassivityDesk:occupyStayPoint(pointIndex, customer) if pointIndex and pointIndex <= table.nums(self.stayPoints) then self.stayPointsOccupancy[pointIndex] = customer return true end return false end -- 释放stay_point function PassivityDesk:releaseStayPoint(pointIndex) if pointIndex and pointIndex <= table.nums(self.stayPoints) then self.stayPointsOccupancy[pointIndex] = nil return true end return false end -- 是否可以接待顾客 function PassivityDesk:canAcceptCustomer() local maxCount = self:getMaxAcceptCount() local currentServiceCount = table.nums(self.currentCustomers) local waitingCount = table.nums(self.waitingCustomers) local maxWaitingCount = table.nums(self.stayPoints) -- 如果正在接待的顾客数量小于最大数量,可以直接接待 if currentServiceCount < maxCount then return true, "direct" end -- 如果等待队列未满,可以加入等待队列 if waitingCount < maxWaitingCount then return true, "waiting" end -- 否则不能接待 return false, "full" end -- 接待顾客 function PassivityDesk:acceptCustomer(customer) local canAccept, acceptType = self:canAcceptCustomer() if not canAccept then printWarn(LOGTAG, string.format("建筑[%s]无法接待更多顾客", self.deskId)) return false end if acceptType == "direct" then -- 直接接待 return self:startServiceCustomer(customer) elseif acceptType == "waiting" then -- 加入等待队列 return self:addToWaitingQueue(customer) end return false end -- 开始为顾客服务 function PassivityDesk:startServiceCustomer(customer) table.insert(self.currentCustomers, customer) self.state = PassivityDesk.State.used -- 为露天电影院分配椅子 if self.buildingInfo.buildingType == BuildingConst.buildingType.outdoormovie then local chair = self:getAvailableChair() if chair then self:occupyChair(chair.name, customer) customer.assignedChair = chair.name end end -- 设置服务计时器 local acceptTime = self:getAcceptTime() self.customerTimers[customer] = self.buildingNode:Delay(acceptTime, function() self:customerServiceComplete(customer) end) printInfo(LOGTAG, string.format("建筑[%s]开始为顾客[%s-%s]服务,预计%s秒完成", self.deskId, customer.customerCfgId, customer.roleUniqueId, acceptTime)) return true end -- 顾客加入等待队列 function PassivityDesk:addToWaitingQueue(customer) local pointIndex, stayPoint = self:getAvailableStayPoint() if not pointIndex then printWarn(LOGTAG, string.format("建筑[%s]等待点已满", self.deskId)) return false end table.insert(self.waitingCustomers, customer) self:occupyStayPoint(pointIndex, customer) customer.waitingPointIndex = pointIndex printInfo(LOGTAG, string.format("顾客[%s-%s]加入建筑[%s]的等待队列,等待点%d", customer.customerCfgId, customer.roleUniqueId, self.deskId, pointIndex)) return true end -- 顾客服务完成 function PassivityDesk:customerServiceComplete(customer) -- 移除计时器 if self.customerTimers[customer] then self.customerTimers[customer] = nil end -- 从当前服务列表中移除 for i = table.nums(self.currentCustomers), 1, -1 do if self.currentCustomers[i] == customer then table.remove(self.currentCustomers, i) break end end -- 为露天电影院释放椅子 if self.buildingInfo.buildingType == BuildingConst.buildingType.outdoormovie and customer.assignedChair then self:releaseChair(customer.assignedChair, customer) customer.assignedChair = nil end -- 创建金币 if self.coinPosNode then local buildingInfo = BuildingMgr:getBuildingInfo(self:getDeskId()) local price = buildingInfo:getBuyPrice() or 10 -- 如果获取不到价格,默认给10金币 CoinDeskMgr:createCoin(self, price, self.buildingInfo:getMapId()) -- 延迟1秒后自动收集金币 self.buildingNode:Delay(1.0, function() CoinDeskMgr:clearCoinsOnDesk(self:getBuildingId()) end) end printInfo(LOGTAG, string.format("顾客[%s-%s]在建筑[%s]的服务完成", customer.customerCfgId, customer.roleUniqueId, self.deskId)) -- 通知顾客服务完成 if customer.onPassivityServiceComplete then customer:onPassivityServiceComplete() else printWarn(LOGTAG, string.format("顾客[%s-%s]没有onPassivityServiceComplete方法", customer.customerCfgId, customer.roleUniqueId)) end -- 检查是否有等待的顾客可以开始服务 self:processWaitingQueue() -- 更新建筑状态 self:updateBuildingState() end -- 处理等待队列 function PassivityDesk:processWaitingQueue() local maxCount = self:getMaxAcceptCount() local currentServiceCount = table.nums(self.currentCustomers) -- 如果还有服务容量且有等待的顾客 while currentServiceCount < maxCount and table.nums(self.waitingCustomers) > 0 do local waitingCustomer = table.remove(self.waitingCustomers, 1) -- 释放等待点 if waitingCustomer.waitingPointIndex then self:releaseStayPoint(waitingCustomer.waitingPointIndex) waitingCustomer.waitingPointIndex = nil end -- 开始为该顾客服务 if self:startServiceCustomer(waitingCustomer) then currentServiceCount = currentServiceCount + 1 printInfo(LOGTAG, string.format("等待顾客[%s-%s]开始在建筑[%s]接受服务", waitingCustomer.customerCfgId, waitingCustomer.roleUniqueId, self.deskId)) end end end -- 更新建筑状态 function PassivityDesk:updateBuildingState() if table.nums(self.currentCustomers) == 0 and table.nums(self.waitingCustomers) == 0 then self.state = PassivityDesk.State.idle else self.state = PassivityDesk.State.used end end -- 顾客离开(兼容旧接口) function PassivityDesk:customerLeave() -- 这个方法保持兼容性,但现在服务完成通过customerServiceComplete处理 printWarn(LOGTAG, "customerLeave方法已废弃,请使用customerServiceComplete") end -- 强制顾客离开 function PassivityDesk:forceCustomerLeave(customer) -- 取消计时器 if self.customerTimers[customer] then self.customerTimers[customer]:Stop() self.customerTimers[customer] = nil end -- 从当前服务列表中移除 for i = table.nums(self.currentCustomers), 1, -1 do if self.currentCustomers[i] == customer then table.remove(self.currentCustomers, i) break end end -- 从等待队列中移除 for i = table.nums(self.waitingCustomers), 1, -1 do if self.waitingCustomers[i] == customer then table.remove(self.waitingCustomers, i) -- 释放等待点 if customer.waitingPointIndex then self:releaseStayPoint(customer.waitingPointIndex) customer.waitingPointIndex = nil end break end end -- 为露天电影院释放椅子 if self.buildingInfo.buildingType == BuildingConst.buildingType.outdoormovie and customer.assignedChair then self:releaseChair(customer.assignedChair, customer) customer.assignedChair = nil end -- 处理等待队列 self:processWaitingQueue() -- 更新建筑状态 self:updateBuildingState() printInfo(LOGTAG, string.format("顾客[%s-%s]强制离开建筑[%s]", customer.customerCfgId, customer.roleUniqueId, self.deskId)) end -- 清理建筑 function PassivityDesk:clean() if self.state == PassivityDesk.State.dirty then self.state = PassivityDesk.State.idle end end -- 获取金币放置点 function PassivityDesk:getCoinPosNode() return self.coinPosNode end -- 获取当前接待和等待的顾客总数 function PassivityDesk:getTotalCustomerCount() return table.nums(self.currentCustomers) + table.nums(self.waitingCustomers) end -- 获取等待队列剩余容量 function PassivityDesk:getWaitingQueueCapacity() return table.nums(self.stayPoints) - table.nums(self.waitingCustomers) end -- ==================== 露天电影院椅子管理 ==================== -- 获取空闲椅子 function PassivityDesk:getAvailableChair() if self.buildingInfo.buildingType ~= BuildingConst.buildingType.outdoormovie then return nil end local chairNames = {"chair_1", "chair_2", "chair_3"} local availableChairs = {} for _, chairName in ipairs(chairNames) do -- 检查椅子是否被占用 if not self.chairOccupancy[chairName] then local chair = self.buildingNode:Seek(chairName) if chair then table.insert(availableChairs, {name = chairName, node = chair}) printInfo(LOGTAG, string.format("露天电影院找到空闲椅子: %s", chairName)) end end end if table.nums(availableChairs) > 0 then -- 随机选择一个空闲椅子 local randomIndex = math.random(1, table.nums(availableChairs)) return availableChairs[randomIndex] end return nil end -- 占用椅子 function PassivityDesk:occupyChair(chairName, customer) if self.buildingInfo.buildingType ~= BuildingConst.buildingType.outdoormovie then return false end if self.chairOccupancy[chairName] then printWarn(LOGTAG, string.format("椅子 %s 已被占用", chairName)) return false end self.chairOccupancy[chairName] = customer printInfo(LOGTAG, string.format("顾客[%s-%s]占用椅子: %s", customer.customerCfgId, customer.roleUniqueId, chairName)) return true end -- 释放椅子 function PassivityDesk:releaseChair(chairName, customer) if self.buildingInfo.buildingType ~= BuildingConst.buildingType.outdoormovie then return end if self.chairOccupancy[chairName] == customer then self.chairOccupancy[chairName] = nil printInfo(LOGTAG, string.format("顾客[%s-%s]释放椅子: %s", customer.customerCfgId, customer.roleUniqueId, chairName)) end end -- 检查椅子是否被占用 function PassivityDesk:isChairOccupied(chairName) if self.buildingInfo.buildingType ~= BuildingConst.buildingType.outdoormovie then return false end return self.chairOccupancy[chairName] ~= nil end -- 给建筑上的金币放置点添加点击事件 function PassivityDesk:addCoinPosClickEvent() local restaurantScene = RestaurantMgr:getRestaurantScene() if not restaurantScene then printError(LOGTAG, "找不到餐厅场景") return end local touchCom = restaurantScene.touchCom touchCom:addListener( self.coinCollectNode, TouchCom.LISTENER_TYPE.CLICK, function(p) printInfo(LOGTAG, "建筑上的金币放置点被点击") local coinCount = CoinDeskMgr:getCoinCountOnDesk(self:getBuildingId()) if coinCount > 0 then -- 收集当前建筑上的所有金币 CoinDeskMgr:clearCoinsOnDesk(self:getBuildingId()) -- 播放金币收集动画和音效 if coinCount > 0 then printInfo(LOGTAG, string.format("收集了建筑%s上的%d个金币", self:getBuildingId(), coinCount)) -- todo: 播放金币收集动画和音效,增加玩家金币数量 end end end ) end return PassivityDeskEmployeC--[[ 员工基类,地图上可行走的员工 author:{zhangpeng} time:2025-05-21 10:09:58 ]] local Employe,super = defClass("Employe",RoleBase) local LOGTAG = "Employee" ---构造函数 function Employe:ctor(reslink,cfgId) super.ctor(self,RoleConst.roleType.employee) self.R = reslink self.employeCfgId = cfgId end function Employe:init() self:initEmployeCfg() self:initDisplay() -- 初始化状态为空闲 self:setState(EmployeConst.State.Idle) --self.state = EmployeConst.State.Idle end -- 初始化员工配置 function Employe:initEmployeCfg() self.employeeInfo = EmployeMgr:getEmployeeInfo(self.employeCfgId) -- 移动速度 self.moveSpeed = self.employeeInfo.movespeed end function Employe:initDisplay() local employeName = string.format("employe_%s", self.employeCfgId) self.asset = self.R[employeName] self.display = GameObject.Instantiate(self.asset) self:setEmployeName(employeName) -- 员工spine动画节点 self.employeeAnim = self.display:Seek("employe_anim") -- 初始化排序基准节点 self.sortBaseNode = self.display:Seek("base_point") -- 设置初始层级(基于Y坐标动态计算) self:updateDynamicSortingOrder() -- 添加寻路组件 self:addPathFind(self.display) -- 设置寻路网格 self:setRolePathGrid(MapsConst.GridGraphsName[MapsConst.MapType.restaurant]) -- 当前播放的动画 self.currentAnim = nil end -- 获取寻路组件 (重写父类方法) function Employe:getPathFindSeeker() return self.pathFinder end -- 设置当前位置 (重写父类方法) function Employe:setCurPosition(pos) self.curPosition = pos self.display.transform.position = self.curPosition end -- 获取当前位置 (重写父类方法) function Employe:getCurPosition() -- 优先使用sortBaseNode作为排序基准点 if self.sortBaseNode then return self.sortBaseNode.transform.position end -- 后备方案:使用display根节点 return self.display.transform.position end -- 走到某一个坐标 (重写父类方法) function Employe:walkToPos(tarPos, moveSpeed, resolve) -- 调用父类方法处理通用逻辑(方向计算、距离计算等) super.walkToPos(self, tarPos, moveSpeed, nil) -- 计算移动时间 local currentPos = self:getCurPosition() local distance = (tarPos - currentPos).magnitude local moveTime = distance / moveSpeed -- printInfo(LOGTAG, string.format("员工[%s]移动到目标位置,移动时间:%s", self.employeCfgId, moveTime)) -- 开始移动时播放行走动画 self:playWalkAnim() -- 创建一个定时器来在移动过程中更新层级 local updateInterval = 0.1 local updateTimer = self:timer(function() self:updateDynamicSortingOrder() end, updateInterval, 0) -- 使用RunAction进行移动 self.display:RunAction( ua.Sequence{ ua.MoveTo(moveTime, tarPos), ua.cb(function() -- 停止层级更新定时器 if updateTimer then TimerMgr:rem(updateTimer) updateTimer = nil end -- 最后更新一次层级 self:updateDynamicSortingOrder() if resolve then resolve() end end) } ) end -- 员工升级 function Employe:upgrade() end -- 员工行为,子类重写 function Employe:action() end -- ==================== 动画播放相关方法 ==================== -- 通用动画播放方法 function Employe:playAnimation(animName, loop, callback) if self.currentAnim == animName then return end if not self.employeeAnim then printError(LOGTAG, string.format("员工[%s]动画节点不存在,display=%s", self.employeCfgId, tostring(self.display))) -- 尝试重新查找动画节点 if self.display then self.employeeAnim = self.display:Seek("employe_anim") printInfo(LOGTAG, string.format("重新查找动画节点,employeeAnim=%s", tostring(self.employeeAnim))) end if not self.employeeAnim then return end end if not animName then printError(LOGTAG, string.format("员工[%s]动画名称为空", self.employeCfgId)) return end -- 记录当前播放的动画 self.currentAnim = animName printInfo(LOGTAG, string.format("员工[%s]播放动画: %s, loop: %s", self.employeCfgId, animName, tostring(loop or false))) -- 播放动画 util.spine.play(self.employeeAnim, animName, loop or false, callback) end -- 播放待机动画 function Employe:playIdleAnim() self:stopWalkState() local actions = EmployeConst.EmployeeAction[self.employeCfgId] if actions and actions.idle then self:playAnimation(actions.idle, true) printInfo(LOGTAG, string.format("员工[%s]播放待机动画: %s", self.employeCfgId, actions.idle)) end end -- 播放行走动画 function Employe:playWalkAnim() -- 如果是行走状态,则不重复播放 if self.isMoving then return end self.isMoving = true local actions = EmployeConst.EmployeeAction[self.employeCfgId] if actions and actions.walk then self:playAnimation(actions.walk, true) -- printInfo(LOGTAG, string.format("员工[%s]播放行走动画: %s", self.employeCfgId, actions.walk)) end end -- 随机播放工作动画 function Employe:playRandomWorkAnim() local actions = EmployeConst.EmployeeAction[self.employeCfgId] if not actions then return end self:stopWalkState() -- 收集除了idle和walk之外的动作 local workAnims = {} for key, animName in pairs(actions) do if key ~= "idle" and key ~= "walk" then table.insert(workAnims, animName) end end if #workAnims > 0 then local randomIndex = math.random(1, #workAnims) local selectedAnim = workAnims[randomIndex] self:playAnimation(selectedAnim, false) -- 工作动画播放一次即可 printInfo(LOGTAG, string.format("员工[%s]播放随机工作动画: %s", self.employeCfgId, selectedAnim)) else -- 如果没有工作动画,播放待机动画 self:playIdleAnim() end end -- 停止行走状态 function Employe:stopWalkState() self.isMoving = false end -- 获取当前播放的动画 function Employe:getCurrentAnim() return self.currentAnim end -- 开始闲逛 function Employe:startWandering() -- 如果已经在闲逛或者状态不是空闲,则不开始闲逛 if self.isWandering or self.state ~= EmployeConst.State.Idle then return end printInfo(LOGTAG, string.format("员工[%s]开始闲逛", self.employeCfgId)) -- 设置闲逛标志 self.isWandering = true -- 获取闲逛点 if not self.wanderPoints or #self.wanderPoints == 0 then printWarn(LOGTAG, string.format("员工[%s]没有闲逛点", self.employeCfgId)) return end -- 随机生成闲逛时间 (20-30秒) local wanderingTime = math.random(20, 30) printInfo(LOGTAG, string.format("员工[%s]将闲逛%s秒", self.employeCfgId, wanderingTime)) -- 开始闲逛流程 self:wanderBetweenPoints(wanderingTime) end -- 在指定点之间闲逛指定时间 function Employe:wanderBetweenPoints(time) -- 计算每个点的停留时间 local pointCount = math.min(3, #self.wanderPoints) -- 最多访问3个点 local stayTimePerPoint = math.floor(time / pointCount) -- 随机选择要访问的点 local selectedPoints = {} local availableIndices = {} -- 初始化可用索引 for i = 1, #self.wanderPoints do -- 检查闲逛点是否有效 local wanderPoint = self.wanderPoints[i] if wanderPoint and wanderPoint.transform then table.insert(availableIndices, i) printInfo(LOGTAG, string.format("员工[%s]可用闲逛点[%d]位置: %s", self.employeCfgId, i, tostring(wanderPoint.transform.position))) else printError(LOGTAG, string.format("员工[%s]闲逛点[%d]无效", self.employeCfgId, i)) end end if #availableIndices == 0 then printError(LOGTAG, string.format("员工[%s]没有有效的闲逛点", self.employeCfgId)) return end -- 随机选择点 for i = 1, pointCount do if #availableIndices == 0 then break end -- 随机选择一个索引 local randomIndex = math.random(1, #availableIndices) local selectedIndex = availableIndices[randomIndex] -- 获取该索引对应的闲逛点 local selectedPoint = self.wanderPoints[selectedIndex] table.insert(selectedPoints, selectedPoint) printInfo(LOGTAG, string.format("员工[%s]选择闲逛点[%d],位置: %s", self.employeCfgId, selectedIndex, tostring(selectedPoint.transform.position))) -- 从可用索引中移除已选择的索引 table.remove(availableIndices, randomIndex) end -- 递归函数,按顺序访问所有闲逛点 local visitNextPoint local currentPointIndex = 1 visitNextPoint = function() -- 如果不在闲逛状态或者状态不是空闲,则停止闲逛 if not self.isWandering or self.state ~= EmployeConst.State.Idle then self.isWandering = false printInfo(LOGTAG, string.format("员工[%s]停止闲逛,isWandering=%s, state=%s", self.employeCfgId, tostring(self.isWandering), tostring(self.state))) return end -- 如果已经访问完所有点或者时间到了,重新开始闲逛 if currentPointIndex > #selectedPoints then printInfo(LOGTAG, string.format("员工[%s]完成当前闲逛循环,重新开始", self.employeCfgId)) -- 重新开始闲逛 self:startWandering() return end -- 获取当前闲逛点 local targetPoint = selectedPoints[currentPointIndex] if not targetPoint or not targetPoint.transform then printError(LOGTAG, string.format("员工[%s]闲逛点[%d]无效,跳过", self.employeCfgId, currentPointIndex)) currentPointIndex = currentPointIndex + 1 visitNextPoint() return end -- 移动到该点 local targetPos = targetPoint.transform.position printInfo(LOGTAG, string.format("员工[%s]开始移动到闲逛点[%d],目标位置: %s", self.employeCfgId, currentPointIndex, tostring(targetPos))) self:rolePathFind(targetPos, function() printInfo(LOGTAG, string.format("员工[%s]到达闲逛点%d,停留%d秒", self.employeCfgId, currentPointIndex, stayTimePerPoint)) -- 在当前点停留一段时间,然后前往下一个点 self.display:RunAction(ua.Sequence({ ua.Delay(stayTimePerPoint), ua.cb(function() currentPointIndex = currentPointIndex + 1 visitNextPoint() end) })) end) end -- 开始访问第一个点 visitNextPoint() end -- 移动到指定点 function Employe:moveToPoint(targetPoint, onArriveCallback) -- 如果没有目标点,直接调用回调 if not targetPoint then if onArriveCallback then onArriveCallback() end return end -- 获取目标点的世界坐标 local targetPosition = targetPoint.transform.position -- 使用角色寻路系统移动到目标点 self:rolePathFind(targetPosition, function() if onArriveCallback then onArriveCallback() end end) end -- 定时执行函数 function Employe:scheduleOnce(callback, delay) self.display:RunAction(ua.Sequence({ ua.Delay(delay), ua.cb(callback) })) end -- 设置员工节点名字 function Employe:setEmployeName(name) self.display:SetName(name) end -- 设置父节点 function Employe:setEmployeParent(parent) self.display:SetParent(parent) end -- 设置员工sortingOrder function Employe:setSortingOrder(sortingOrder) if not self.employeeAnim then printError(LOGTAG, string.format("员工[%s]employeeAnim节点不存在,无法设置层级", self.employeCfgId)) return end -- SpriteRender local sprite_render = self.employeeAnim:GetComponent("Renderer") -- SkeletonAnimation local skeletonAnimation = self.employeeAnim:GetComponent("SkeletonAnimation") if sprite_render then sprite_render.sortingOrder = sortingOrder -- printInfo(LOGTAG, string.format("员工[%s]通过SpriteRender设置层级: %d", self.employeCfgId, sortingOrder)) elseif skeletonAnimation then local spine_render = skeletonAnimation:GetComponent("Renderer") if spine_render then spine_render.sortingOrder = sortingOrder -- printInfo(LOGTAG, string.format("员工[%s]通过SkeletonAnimation设置层级: %d", self.employeCfgId, sortingOrder)) else -- printError(LOGTAG, string.format("员工[%s]SkeletonAnimation没有Renderer组件", self.employeCfgId)) end else -- printError(LOGTAG, string.format("员工[%s]employeeAnim节点没有找到Renderer或SkeletonAnimation组件", self.employeCfgId)) end end -- 获取员工状态 function Employe:getState() return self.state end -- 设置员工状态 function Employe:setState(state) self.state = state printInfo(LOGTAG, string.format("员工[%s]状态变更为:%s", self.employeCfgId, state)) end -- 员工类型 function Employe:getEmployeType() return self.employeCfgId end -- 获取当前物体的真实SortingOrder (重写父类方法) function Employe:getCurrentSortingOrder() if not self.display then printError(LOGTAG, string.format("员工[%s]display节点不存在", self.employeCfgId)) return nil end -- 尝试从employeeAnim节点获取SortingOrder if self.employeeAnim then local renderer = self.employeeAnim:GetComponent("Renderer") if renderer then local sortingOrder = renderer.sortingOrder printInfo(LOGTAG, string.format("员工[%s]从employeeAnim获取SortingOrder: %d", self.employeCfgId, sortingOrder)) return sortingOrder end -- 如果是SkeletonAnimation,尝试获取其Renderer local skeletonAnimation = self.employeeAnim:GetComponent("SkeletonAnimation") if skeletonAnimation then local spineRenderer = skeletonAnimation:GetComponent("Renderer") if spineRenderer then local sortingOrder = spineRenderer.sortingOrder printInfo(LOGTAG, string.format("员工[%s]从SkeletonAnimation获取SortingOrder: %d", self.employeCfgId, sortingOrder)) return sortingOrder end end end -- 如果employeeAnim没有Renderer,尝试从display的子节点查找 local renderer = self.display:GetComponent("Renderer") if renderer then local sortingOrder = renderer.sortingOrder printInfo(LOGTAG, string.format("员工[%s]从display获取SortingOrder: %d", self.employeCfgId, sortingOrder)) return sortingOrder end -- 如果都没有找到,尝试从子节点查找 local childCount = self.display.transform.childCount for i = 0, childCount - 1 do local child = self.display.transform:GetChild(i) local childRenderer = child:GetComponent("Renderer") if childRenderer then local sortingOrder = childRenderer.sortingOrder printInfo(LOGTAG, string.format("员工[%s]从子节点[%s]获取SortingOrder: %d", self.employeCfgId, child.name, sortingOrder)) return sortingOrder end -- 检查子节点是否有SkeletonAnimation local childSkeletonAnimation = child:GetComponent("SkeletonAnimation") if childSkeletonAnimation then local spineRenderer = childSkeletonAnimation:GetComponent("Renderer") if spineRenderer then local sortingOrder = spineRenderer.sortingOrder printInfo(LOGTAG, string.format("员工[%s]从子节点[%s]的SkeletonAnimation获取SortingOrder: %d", self.employeCfgId, child.name, sortingOrder)) return sortingOrder end end end printWarn(LOGTAG, string.format("员工[%s]无法找到任何Renderer组件", self.employeCfgId)) return nil end return Employe XSdk--[[ luaide 模板位置位于 Template/FunTemplate/NewFileTemplate.lua 其中 Template 为配置路径 与luaide.luaTemplatesDir luaide.luaTemplatesDir 配置 https://www.showdoc.cc/web/#/luaide?page_id=713062580213505 author:{author} time:2023-09-25 23:13:54 ]] local XSdk, super = defClassStatic("XSdk", XSdkBase) function XSdk:init() self.appVersion = "1.1.1" self.groupId = 46 self.showUserId = "" self.deviceId = "" end function XSdk:getDeviceId() if not string.isEmpty(self.deviceId) then return self.deviceId end local deviceId = "1" --SystemInfo.deviceUniqueIdentifier--"616fda05b7e5e8417c000001"-- deviceId = string.gsub(deviceId, "-", "") deviceId = string.lower(deviceId) local totalDeviceIdLength = 24 deviceId = string.sub(deviceId, 1, totalDeviceIdLength) for i = 1, totalDeviceIdLength - #deviceId do deviceId = "1" .. deviceId end return deviceId end XSdk:init() SceneMsgEnumdefGlobal("GameSceneMsg") ---@enum GameSceneMsg GameSceneMsg = { InitScoller = "GameSceneMsg.InitScoller", -- IndoorMapAutoScroll = "GameSceneMsg.IndoorMarAutoScroll", IndoorMapStopAutoScroll = "GameSceneMsg.IndoorMapStopAutoScroll", SceneMapScroll = "GameSceneMsg.SceneMapScroll", SceneMapRefreshPos = "GameSceneMsg.SceneMapRefreshPos", ScrollTopDownForced = "GameSceneMsg.ScrollTopDownForced", } SqliteCondition ---@class SqliteCondition:LuaClass local SqliteCondition = defClass("SqliteCondition") SqliteCondition.Op = { E = "=", NE = "!=", G = ">", GE = ">=", L = "<", LE = "<=", LIKE = "LIKE", GOLB = "GOLB", IN = "IN", IS = "IS", BETWEEN = "BETWEEN", A = "AND", O = "OR", N = "NOT" } SqliteCondition.CompareOp = { E = SqliteCondition.Op.E, NE = SqliteCondition.Op.NE, G = SqliteCondition.Op.G, GE = SqliteCondition.Op.GE, L = SqliteCondition.Op.L, LE = SqliteCondition.Op.LE } SqliteCondition.LogicalOp = { A = SqliteCondition.Op.A, O = SqliteCondition.Op.O, N = SqliteCondition.Op.N } function SqliteCondition:ctor(table) self.table = table self.col = nil self.op = nil self.value = nil self.isNot = false self.conditionList = {} self.aggregateFunc = nil end function SqliteCondition:column(col) self.col = col return self end function SqliteCondition:operate(op, value) self.op = op self.value = value return self end function SqliteCondition:aggregate(aggregateFunc) self.aggregateFunc = aggregateFunc self.aggregateFunc:as() return self end --------------------------------------------------------------------------------------------- -- 比较操作 --------------------------------------------------------------------------------------------- function SqliteCondition:isEqual(value) return self:operate(SqliteCondition.CompareOp.E, SqliteUtil:luaValueToStr(value)) end SqliteCondition.isE = SqliteCondition.isEqual function SqliteCondition:isNotEqual(value) return self:operate(SqliteCondition.CompareOp.NE, SqliteUtil:luaValueToStr(value)) end SqliteCondition.isNE = SqliteCondition.isNotEqual function SqliteCondition:isLessThan(number) return self:operate(SqliteCondition.CompareOp.L, number) end SqliteCondition.isL = SqliteCondition.isLessThan function SqliteCondition:isLessThanOrEqual(number) return self:operate(SqliteCondition.CompareOp.LE, number) end SqliteCondition.isLE = SqliteCondition.isLessThanOrEqual function SqliteCondition:isGreaterThan(number) return self:operate(SqliteCondition.CompareOp.G, number) end SqliteCondition.isG = SqliteCondition.isGreaterThan function SqliteCondition:isGreaterThanOrEqual(number) return self:operate(SqliteCondition.CompareOp.GE, number) end SqliteCondition.isGE = SqliteCondition.isGreaterThanOrEqual --------------------------------------------------------------------------------------------- -- 其他操作 --------------------------------------------------------------------------------------------- function SqliteCondition:isLike(pattern) return self:operate(SqliteCondition.Op.LIKE, SqliteUtil:luaValueToStr(pattern)) end function SqliteCondition:isGlob(pattern) return self:operate(SqliteCondition.Op.GLOB, SqliteUtil:luaValueToStr(pattern)) end function SqliteCondition:isIn(list) local list = {} for i, value in ipairs(list) do table.insert(list, SqliteUtil:luaValueToStr(value)) end local str = "(" .. table.concat(list, ",") .. ")" return self:operate(SqliteCondition.Op.IN, str) end function SqliteCondition:isBetween(numbler1, number2) local str = string.format("%s AND %s", numbler1, number2) return self:operate(SqliteCondition.Op.BETWEEN, str) end function SqliteCondition:isNull() return self:operate(SqliteCondition.Op.IS, SqliteUtil.null.str) end --------------------------------------------------------------------------------------------- -- 逻辑操作 --------------------------------------------------------------------------------------------- function SqliteCondition:xnot() self.isNot = not self.isNot return self end function SqliteCondition:xand(condition) table.insert(self.conditionList, {op = SqliteCondition.LogicalOp.A, condition = condition}) return self end function SqliteCondition:xor(condition) table.insert(self.conditionList, {op = SqliteCondition.LogicalOp.O, condition = condition}) return self end --------------------------------------------------------------------------------------------- -- 工具函数 --------------------------------------------------------------------------------------------- function SqliteCondition:isCompareOp() return table.contain(SqliteCondition.CompareOp, self.op) end function SqliteCondition:isLogicalOp() return table.contain(SqliteCondition.LogicalOp, self.op) end function SqliteCondition:toSqlStr() local str = self.col if self.aggregateFunc then str = self.aggregateFunc:toSqlStr() end local str = string.format("%s %s %s", str, self.op, self.value) if self.isNot then str = string.format("(%s %s)", SqliteCondition.LogicalOp.N, str) end for i, v in ipairs(self.conditionList) do local op = v.op local condition = v.condition local subStr = string.format(" %s %s", op, condition:toSqlStr()) str = str .. subStr end return str end return SqliteCondition TaskAchMgr--[[ 成就任务管理 author:{zhangpeng} time:2025-05-27 11:11:34 ]] local TaskAchMgr = defClassStatic("TaskAchMgr") -- 辅助函数:检查表中是否包含指定值 local function tableContains(tbl, value) for _, v in ipairs(tbl) do if v == value then return true end end return false end function TaskAchMgr:init() printInfo("TaskAchMgr", "------ 成就任务管理 初始化 ------") end -- 获取所有成就任务 function TaskAchMgr:getAllAchTasks() local allAchTasks = TaskCfgParse:getTaskListByType(TaskConst.TaskType.achievement) -- 过滤掉已领取的成就任务 local activeTasks = {} for _, taskData in ipairs(allAchTasks) do if not self:isTaskClaimed(taskData.id) then table.insert(activeTasks, taskData) end end return activeTasks end -- 获取任务状态 function TaskAchMgr:getTaskState(taskId) -- 如果任务已领取,返回已领取状态 if tableContains(UserDataMgr.claimed_ach_task_ids, taskId) then return TaskConst.TaskState.claimed end -- 如果任务已完成,返回可完成状态 if tableContains(UserDataMgr.completed_ach_task_ids, taskId) then return TaskConst.TaskState.success end -- 从成就任务状态表中获取状态 local state = UserDataMgr.ach_task_states[taskId] if state then return state end -- 默认返回进行中 return TaskConst.TaskState.in_progress end -- 设置任务状态 function TaskAchMgr:setTaskState(taskId, state) -- 更新成就任务状态表 UserDataMgr.ach_task_states[taskId] = state -- 如果任务可完成,添加到已完成列表 if state == TaskConst.TaskState.success and not tableContains(UserDataMgr.completed_ach_task_ids, taskId) then table.insert(UserDataMgr.completed_ach_task_ids, taskId) end -- 如果任务已领取,添加到已领取列表 if state == TaskConst.TaskState.claimed and not tableContains(UserDataMgr.claimed_ach_task_ids, taskId) then table.insert(UserDataMgr.claimed_ach_task_ids, taskId) end end -- 检查任务是否已领取 function TaskAchMgr:isTaskClaimed(taskId) return tableContains(UserDataMgr.claimed_ach_task_ids, taskId) end -- 标记任务为已领取 function TaskAchMgr:setTaskClaimed(taskId) -- 如果任务已经在已领取列表中,直接返回 if self:isTaskClaimed(taskId) then return end -- 添加到已领取列表 table.insert(UserDataMgr.claimed_ach_task_ids, taskId) -- 更新状态 self:setTaskState(taskId, TaskConst.TaskState.claimed) end -- 检查任务是否已完成 function TaskAchMgr:isTaskCompleted(taskId) return tableContains(UserDataMgr.completed_ach_task_ids, taskId) end -- 标记任务为已完成 function TaskAchMgr:setTaskCompleted(taskId) -- 如果任务已经在已完成列表中,直接返回 if self:isTaskCompleted(taskId) then return end -- 添加到已完成列表 table.insert(UserDataMgr.completed_ach_task_ids, taskId) -- 更新状态 self:setTaskState(taskId, TaskConst.TaskState.success) end -- 更新所有成就任务的状态 function TaskAchMgr:updateAllTaskStates() -- 获取所有成就任务 local allAchTasks = TaskCfgParse:getTaskListByType(TaskConst.TaskType.achievement) -- 遍历所有成就任务,更新状态 for _, taskData in ipairs(allAchTasks) do local taskId = taskData.id -- 如果任务未领取,检查并更新状态 if not self:isTaskClaimed(taskId) then -- 获取任务进度 local currentValue, targetValue = TaskMgr:getTaskProgress(taskId) -- 如果任务已完成,更新状态 if currentValue >= targetValue then self:setTaskCompleted(taskId) end end end end -- 生成可用的成就任务列表 function TaskAchMgr:genAvailableAchTasks() local taskList = TaskCfgParse:getTaskListByType(TaskConst.TaskType.achievement) local availableTasks = {} for _, taskData in ipairs(taskList) do -- 判断前置任务是否已完成 local preTaskId = taskData.preTaskId local canShowTask = true -- 默认可以显示任务 -- 如果有前置任务,检查是否已完成 if preTaskId and preTaskId > 0 then local preTaskState = TaskMgr:getTaskState(preTaskId) canShowTask = (preTaskState == TaskConst.TaskState.claimed) end -- 只有当前置任务已完成时,才检查其他解锁条件 if canShowTask then -- 判断解锁条件 local unlockCond = taskData.unlockCond if unlockCond == -1 then -- -1为无任何限制条件 table.insert(availableTasks, taskData) else -- 从通用解锁条件表中取出解锁限制数据 local unlockCondData = UnlockCommonCfgParse:getUnlockCondData(unlockCond) if unlockCondData then -- 判断是否满足解锁条件 if unlockCondData.limitType == TaskConst.UnlockCondType.star then -- 星星达到 if UserDataMgr:getStarCount() >= unlockCondData.value then table.insert(availableTasks, taskData) end elseif unlockCondData.limitType == TaskConst.UnlockCondType.scene then -- 场景解锁 if UserDataMgr:getSceneUnlocked(unlockCondData.value) then table.insert(availableTasks, taskData) end end end end end end return availableTasks end -- 获取成就任务同类型下一任务 function TaskAchMgr:getAchNextMissions(currId) return TaskCfgParse:getNextTaskByType(currId) end return TaskAchMgr SpecialCustomerCfgParse--[[ 特殊顾客配置解析 author:{zhangpeng} time:2025-05-30 12:23:00 ]] local SpecialCustomerCfgData = require("data/config/specialCustomerCfg") local SpecialCustomerCfgParse = defClassStatic("SpecialCustomerCfgParse") local LOGTAG = "SpecialCustomerCfgParse" local special_customer_list = {} local function initAllIds() for k, v in pairs(SpecialCustomerCfgData) do table.insert(special_customer_list, v.id) end end function SpecialCustomerCfgParse:init() initAllIds() end function SpecialCustomerCfgParse:getSpecialCustomerCfg(id) for _, v in pairs(SpecialCustomerCfgData) do if v.id == id then return v end end return nil end return SpecialCustomerCfgParse main require("modules/building/restaurant/reception/ReceptionConst") require("modules/building/restaurant/reception/ReceptionDesk") require("modules/building/restaurant/reception/ReceptionMgr")TouchComw---@class TouchCom local TouchCom = defClass("TouchCom") ---@enum TouchCom.LISTENER_TYPE TouchCom.LISTENER_TYPE = { CLICK = 0, CUSTOM = 10, } local LOGTAG = "TouchCom" local Input = CS.UnityEngine.Input function TouchCom:ctor(rootNode, camera) self:setCamera(camera) Input.multiTouchEnabled = false local GetMouseButtonDown = Input.GetMouseButtonDown local GetMouseButton = Input.GetMouseButton local GetMouseButtonUp = Input.GetMouseButtonUp self._listeners = {} self._enabled = true self.sortDirty = false local isDown = false CS.LuaGlobal.instance:AddInput(rootNode, function() if not self._enabled then return end local camera = self:getCamera() if GetMouseButtonDown(0) then if not CS.InputMgr.IsPointerOverUIObject() then --防止ui穿透 isDown = true local p = self:convertPos(camera, Input.mousePosition) self:onBegan(p) end end if not isDown then return end if GetMouseButton(0) then local p = self:convertPos(camera, Input.mousePosition) self:onMoved(p) end if GetMouseButtonUp(0) then isDown = false local p = self:convertPos(camera, Input.mousePosition) self:onEnd(p) end end) end ---@private function TouchCom:getCamera() return self.camera end function TouchCom:setCamera(camera) self.camera = camera end --#region touch events begin ---@private function TouchCom:onBegan(point) Msg.send(Msg.TouchComOnBegan, point) self:sort() local gc = {} for i, listener in ipairs(self._listeners) do if not listener:isDead() then if listener:isEnable() then listener:onBegan(point) if listener._actived and listener._bSwallow then break else end end else table.insert(gc, i) end end if #gc > 0 then self:_deleteListeners(gc) end end ---@private function TouchCom:onMoved(point) Msg.send(Msg.TouchComonMoved, point) self:sort() local gc = {} for i, listener in ipairs(self._listeners) do if not listener:isDead() then if listener:isEnable() and listener._actived then listener:onMoved(point) if listener._bSwallow then break end end else table.insert(gc, i) end end if #gc > 0 then self:_deleteListeners(gc) end end ---@private function TouchCom:onEnd(point) Msg.send(Msg.TouchComOnEnd, point) self:sort() local gc = {} for i, listener in ipairs(self._listeners) do if not listener:isDead() then if listener._enabled and listener._actived then listener:onEnd(point) if listener._bSwallow then break end end else table.insert(gc, i) end end if #gc > 0 then self:_deleteListeners(gc) end end --#endregion ---@private function TouchCom:_deleteListeners(gc) for i = #gc, 1, -1 do if self._listeners then table.remove(self._listeners, gc[i]) end end end ---@param gameObject CS.UnityEngine.GameObject ---@param touchType TouchCom.LISTENER_TYPE ---@param cb1 fun(point:CS.UnityEngine.Vector3) | fun(point:CS.UnityEngine.Vector3): boolean ---@param cb2 fun(point:CS.UnityEngine.Vector3) ---@param cb3 fun(point:CS.UnityEngine.Vector3) ---@return TouchListener|nil function TouchCom:addListener(gameObject, touchType, cb1, cb2, cb3) local listener if touchType == self.LISTENER_TYPE.CLICK then listener = TouchClickListener.new(gameObject, self) listener:setEndCb(cb1) elseif touchType == self.LISTENER_TYPE.CUSTOM then listener = TouchCustomListener.new(gameObject, self) listener:setBeganCb(cb1) listener:setMovedCb(cb2) listener:setEndCb(cb3) end table.insert(self._listeners, 1, listener) self.sortDirty = true return listener end function TouchCom:disable() self._enabled = false end function TouchCom:enable() self._enabled = true end ---@private function TouchCom:sort() if self.sortDirty then table.stableSort(self._listeners, function(l, r) return l.priority > r.priority end) self.sortDirty = false end end function TouchCom:removeListenerByGameObject(go) for i = #self._listeners, 1, -1 do if self._listeners[i].gameObject == go then table.remove(self._listeners, i) break end end end function TouchCom:removeListener(listener) for i = #self._listeners, 1, -1 do if self._listeners[i] == listener then table.remove(self._listeners, i) break end end end -- 鼠标点击坐标转世界坐标 透视相机修改触摸点z坐标 ---@private function TouchCom:convertPos(camera, pos) pos.z = camera.transform.position.z local wordPos = camera:ScreenToWorldPoint(pos) wordPos.z = 0 return wordPos end ---point是否在go内 ---@param go CS.UnityEngine.GameObject ---@param point CS.UnityEngine.Vector3 ---@return boolean function TouchCom.intersect(go, point) local renderer = go:GetComponent(typeof(CS.UnityEngine.Renderer)) if renderer then local bounds = renderer.bounds local p = UnityEngine.Vector3(point.x, point.y, point.z) p.z = (bounds.min.z + bounds.max.z) * 0.5 return bounds:Contains(p) else return false end end SqliteTableT ---@class SqliteTable:LuaClass local SqliteTable = defClass("SqliteTable") local LOGTAG = SqliteTable.__cls_name SqliteTable.SYNC_STATE = { HAVE_SYNCED = 0, NEED_SYNC = 1, IS_SYNCING = 2 } --[[ add/del/upd/get --对应数据库的增删改查 set会根据数据库中否已有该数据自动调用add或者upd create会创建一个默认值的record,但不会对数据库做任何操作 *ByList 批量操作,使用事务, 需要调用者保证list里不包含重复主键的数据 ]] function SqliteTable:ctor(databaseApis, name) self.recordCls = SqliteModel self.databaseApis = databaseApis self.name = name self.useCache = true self.useSync = false self.recordDict = {} self.col = {} self.tcol = {} self.columnList = {} self.columnDict = {} self.primaryList = {} self.primaryDict = {} self.bindInfoList = {} -- self.changeColNameDict = {} --需要改名的字段, self.changeColNameDict[oldName] = newName if self.beforeInit then self:beforeInit() end self:init() if self.afterInit then self.afterInit() end if self.useSync then self:c("syncState", SqliteTable.SYNC_STATE.HAVE_SYNCED, false, true) end self:c("lastUpdateTime", 0, false, true) self:c("lastUpdateVersion", "", false, true) for i, column in ipairs(self.columnList) do self.columnDict[column.name] = column if column.isPrimary then self.primaryDict[column.name] = column table.insert(self.primaryList, column) end self.col[column.name] = column:toSqlStr() self.tcol[column.name] = column:toSqlStr(true) end end function SqliteTable:genColumn(name, value, isPrimary, isMetadata) local column = SqliteColumn.new(self, name, type(value), value, isPrimary, isMetadata) table.insert(self.columnList, column) return column end SqliteTable.c = SqliteTable.genColumn function SqliteTable:bindKeyToProtoBuf(keyName, pbName) local bindInfo = { keyName = keyName, pbName = pbName } table.insert(self.bindInfoList, bindInfo) return bindInfo end SqliteTable.b = SqliteTable.bindKeyToProtoBuf function SqliteTable:init() self.columnList = {} end function SqliteTable:getName() return self.name end function SqliteTable:getColumnList() return self.columnList end function SqliteTable:getColumnDict() return self.columnDict end function SqliteTable:haveColumn(colName) return self:getColumnDict()[colName] ~= nil end --------------------------------------------------------------------------------------------- --数据库 增删改查 相关接口 --------------------------------------------------------------------------------------------- function SqliteTable:create(key) local record = self.recordCls.new(self) record:setKey(key) return record end function SqliteTable:createByPB(pbData) if not pbData then return end local record = self.recordCls.new(self) record:setByPB(pbData) return record end function SqliteTable:get(key) local record = self:getCache(key) or self:doSelectKey(key) return record end function SqliteTable:getOrCreate(key) local record = self:get(key) if not record then record = self:create(key) end return record end function SqliteTable:add(record) if (not record) or record.table ~= self then return end if self.useSync then record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) SyncMgr:addSyncTable(self) end return self:doInsert(record) end function SqliteTable:upd(record) if (not record) or record.table ~= self then return end if self.useSync then record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) SyncMgr:addSyncTable(self) end return self:doUpdate(record) end function SqliteTable:del(record) if (not record) or record.table ~= self then return end local key = record:getKey() if not self:get(key) then return end self:doDelete(record) end function SqliteTable:set(record) if (not record) or record.table ~= self then return end local key = record:getKey() if (not self:get(key)) then return self:add(record) else return self:upd(record) end end function SqliteTable:getAll() self:clearCache() return self:getByCondition() end function SqliteTable:getByCondition(condition) local list = self:doSelectCondition(condition) return list end function SqliteTable:getCount() return self:getCountByCondition() end function SqliteTable:getCountByCondition(condition) local name = "count" local count = self:genQuery():count():as(name):where(condition):getFirst(name) return math.tointeger(count) or math.round(count) end function SqliteTable:addByList(list) if not self:checkRecordListPrimaryKey(list) then printError(LOGTAG, "addByList, %s has duplicate key", self:getName()) return end self.databaseApis.beginTransaction() for i, record in pairs(list) do if self.useSync then record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) end self:doInsert(record) end self.databaseApis.finishTransaction() if self.useSync then SyncMgr:addSyncTable(self) end end function SqliteTable:updByList(list) if not self:checkRecordListPrimaryKey(list) then printError(LOGTAG, "updByList, %s has duplicate key", self:getName()) return end self.databaseApis.beginTransaction() for i, record in pairs(list) do if self.useSync then record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) end self:doUpdate(record) end self.databaseApis.finishTransaction() if self.useSync then SyncMgr:addSyncTable(self) end end function SqliteTable:delByList(list) if not self:checkRecordListPrimaryKey(list) then printError(LOGTAG, "delByList, %s has duplicate key", self:getName()) return end self.databaseApis.beginTransaction() for i, record in pairs(list) do self:doDelete(record) end self.databaseApis.finishTransaction() end --会自动insert不存在的数据 function SqliteTable:setByList(list) if not self:checkRecordListPrimaryKey(list) then printError(LOGTAG, "setByList, %s has duplicate key", self:getName()) return end local insertList = {} local updateList = {} for i, record in ipairs(list) do local key = record:getKey() if (not self:get(key)) then table.insert(insertList, record) else table.insert(updateList, record) end end self.databaseApis.beginTransaction() for i, record in pairs(insertList) do if self.useSync then record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) end self:doInsert(record) end for i, record in pairs(updateList) do if self.useSync then record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) end self:doUpdate(record) end self.databaseApis.finishTransaction() if self.useSync then SyncMgr:addSyncTable(self) end end function SqliteTable:checkRecordListPrimaryKey(list) local dict = {} for i, record in ipairs(list) do local key = record:getKeyStr() if dict[key] then return false end dict[key] = record end return true end --------------------------------------------------------------------------------------------- -- orm相关接口 --------------------------------------------------------------------------------------------- function SqliteTable:genQuery() local query = SqliteQuery.new(self) return query end function SqliteTable:genCondition(column, op, value) if value then value = SqliteUtil:luaValueToStr(value) end local condition = SqliteCondition.new(self):column(column):operate(op, value) return condition end function SqliteTable:genFunc(type, column, asName) local func = SqliteFunc.new(self, type):column(column):as(asName) return func end --------------------------------------------------------------------------------------------- --网络同步相关接口 --------------------------------------------------------------------------------------------- -- sync必定save -- function SqliteTable:sync(record) -- if not self.useSync then -- self:set(record) -- printError(LOGTAG, "sync, self.useSync is false, name:%s", self:getName()) -- return -- end -- record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) -- self:set(record) -- SyncMgr:addSyncTable(self) -- end -- function SqliteTable:syncByList(list) -- if not self.useSync then -- self:setByList(list) -- printError(LOGTAG, "syncByList, self.useSync is false, name:%s", self:getName()) -- return -- end -- for i, record in pairs(list) do -- record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) -- end -- self:setByList(list) -- SyncMgr:addSyncTable(self) -- end function SqliteTable:getAllNeedSync() if not self.useSync then printError(LOGTAG, "getAllNeedSync, self.useSync is false, name:%s", self:getName()) return end local condition = self:genCondition(self.col.syncState):isEqual(SqliteTable.SYNC_STATE.NEED_SYNC) local list = self:getByCondition(condition) return list end function SqliteTable:getAllIsSync() if not self.useSync then printError(LOGTAG, "getAllIsSync, self.useSync is false, name:%s", self:getName()) return end local condition = self:genCondition(self.col.syncState):isEqual(SqliteTable.SYNC_STATE.IS_SYNCING) local list = self:getByCondition(condition) return list end function SqliteTable:push(recordList, handler) handler = handler or function() end self:doPush( recordList, function(result) handler(result) self:afterPush(result) end ) end function SqliteTable:doPush(recordList, handler) printError(LOGTAG, "doPush, must be override, name:%s", self:getName()) end function SqliteTable:afterPush(result) printError(LOGTAG, "afterPush, must be override, name:%s", self:getName()) end function SqliteTable:pull(handler) handler = handler or function() end self:doPull( function(result) local recordList = self:afterPull(result) handler(result, recordList) end ) end function SqliteTable:doPull(handler) printError(LOGTAG, "doPull, must be override, name:%s", self:getName()) end function SqliteTable:afterPull(result) printError(LOGTAG, "afterPull, must be override, name:%s", self:getName()) end function SqliteTable:merge(oldRecordList, newRecordList, mergeFunction) local keyDict = {} local oldRecordDict = {} for i, oldRecord in ipairs(oldRecordList) do local key = oldRecord:getKey() local str = self:keyToStr(key) oldRecordDict[str] = oldRecord keyDict[str] = str end local newRecordDict = {} for i, newRecord in ipairs(newRecordList) do local key = newRecord:getKey() local str = self:keyToStr(key) newRecordDict[str] = newRecord keyDict[str] = str end local recordList = {} for i, str in pairs(keyDict) do local oldRecord = oldRecordDict[str] local newRecord = newRecordDict[str] local record = mergeFunction(oldRecord, newRecord) if record then table.insert(recordList, record) end end return recordList end --以远端为主,添加远端(newRecordList)存在而本地(oldRecordList)缺失的 function SqliteTable:mergeBaseServer(oldRecordList, newRecordList, mergeFunction) local oldRecordDict = {} for i, oldRecord in ipairs(oldRecordList) do local key = oldRecord:getKey() local str = self:keyToStr(key) oldRecordDict[str] = oldRecord end local recordList = {} for i, newRecord in ipairs(newRecordList) do local key = newRecord:getKey() local str = self:keyToStr(key) local oldRecord = oldRecordDict[str] if not oldRecord then table.insert(recordList, newRecord) else local record = mergeFunction(oldRecord, newRecord) if record then table.insert(recordList, record) end end end return recordList end --以本地为主,添加本地(oldRecordList)存在而远端(newRecordList)缺失的 function SqliteTable:mergeBaseLocal(oldRecordList, newRecordList, mergeFunction) local newRecordDict = {} for i, newRecord in ipairs(newRecordList) do local key = newRecord:getKey() local str = self:keyToStr(key) newRecordDict[str] = newRecord end local recordList = {} for i, oldRecord in ipairs(oldRecordList) do local key = oldRecord:getKey() local str = self:keyToStr(key) local newRecord = newRecordDict[str] if not newRecord then table.insert(recordList, oldRecord) else local record = mergeFunction(oldRecord, newRecord) if record then table.insert(recordList, record) end end end return recordList end --------------------------------------------------------------------------------------------- -- 数据库操作底层私有函数 --------------------------------------------------------------------------------------------- function SqliteTable:doInsert(record) local time = self.databaseApis.getTimeFunc() local version = self.databaseApis.getVersionFunc() record:setLastUpdateTime(time) record:setLastUpdateVersion(version) local keyList = {} local valueList = {} local columnList = self:getColumnList() for i, col in ipairs(columnList) do local key = col.name local value = record[key] or col.defaultValue if type(value) ~= col.type then printWarn(LOGTAG, "doInsert, 类型错误 tableName:%s, key:%s, value:%s, valueType:%s, tableType:%s", self:getName(), key, value, type(value), col.type) if col.type == "string" then value = tostring(value) elseif col.type == "number" then value = tonumber(value) end if value == nil then printError(LOGTAG, "doInsert, 无法转换类型") return end end table.insert(keyList, key) table.insert(valueList, SqliteUtil:luaValueToStr(value, col.type)) end local str = string.format("INSERT INTO %s (%s) VALUES (%s)", self:getName(), table.concat(keyList, ","), table.concat(valueList, ",")) record:clearMetadata() self:setCache(record) return self.databaseApis.exec(str) end function SqliteTable:doDelete(record) self:genQuery():where(self:getKeyCondition(record:getKey())):delete() self:delCache(record) end function SqliteTable:doUpdate(record) local time = self.databaseApis.getTimeFunc() local version = self.databaseApis.getVersionFunc() record:setLastUpdateTime(time) record:setLastUpdateVersion(version) self:genQuery():where(self:getKeyCondition(record:getKey())):updateRecord(record) record:clearMetadata() self:setCache(record) end function SqliteTable:doSelectKey(key) local list = self:doSelectCondition(self:getKeyCondition(key)) if #list == 0 then return nil elseif #list > 1 then printError(LOGTAG, "doSelect, key 重复") end self:setCache(list[1]) return list[1] end function SqliteTable:doSelectCondition(condition) local list = self:genQuery():where(condition):get( function(row) local record = self.recordCls.new(self) record:setSqliteData(row) self:setCache(record) return record end ) return list end function SqliteTable:getKeyCondition(key) if key == nil then printError(LOGTAG, "getKeyCondition, key 不能为nil") return end if type(key) ~= "table" then key = { key } end local conditionList = {} for i, col in ipairs(self.primaryList) do local colName = col.name local value = key[i] if value == nil then printError(LOGTAG, "getKeyCondition, 表%s中key不合法, key:%s", self:getName(), table.concat(key, ",")) end local condition = self:genCondition(self.col[colName]):isEqual(value) table.insert(conditionList, condition) end return SqliteUtil:connectCondionWithAnd(conditionList) end --------------------------------------------------------------------------------------------- --缓存相关私有函数 --------------------------------------------------------------------------------------------- function SqliteTable:getCache(key) if not self.useCache then return end local str = self:keyToStr(key) return self.recordDict[str] end function SqliteTable:setCache(record) if not self.useCache then return end if not record then return end local key = record:getKey() local str = self:keyToStr(key) self.recordDict[str] = record end function SqliteTable:delCache(record) if not self.useCache then return end if not record then return end local key = record:getKey() local str = self:keyToStr(key) self.recordDict[str] = nil end function SqliteTable:clearCache() if not self.useCache then return end self.recordDict = {} end --------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------- function SqliteTable:keyToStr(key) if type(key) ~= "table" then key = { key } end for i, v in ipairs(key) do if math.type(v) == "integer" then v = string.format("%d", v) end key[i] = v end return table.concat(key, "__") end function SqliteTable:strToKey(str) local keys = string.split(tostring(str), "__") if #keys == 1 then return keys[1] end return keys end --------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------- function SqliteTable:getColumnListFromSql() local str = "PRAGMA table_info(" .. self:getName() .. ")" local list = {} self.databaseApis.exec( str, function(udata, colCount, values, names) local colName = nil local colType = nil for i = 1, colCount do if names[i] == "name" then colName = values[i] elseif names[i] == "type" then colType = SqliteUtil:sqlTypeNameToLuaTypeName(values[i]) end end local column = SqliteColumn.new(self, colName, colType) table.insert(list, column) return 0 end ) return list end function SqliteTable:haveColumnListFromSql(colNameList) local colList = self:getColumnListFromSql() local colNameDict = {} for i, col in ipairs(colList) do colNameDict[col.name] = col.name end for i, name in ipairs(colNameList) do if not colNameDict[name] then return false end end return true end function SqliteTable:haveColumnFromSql(colName) return self:haveColumnListFromSql({ colName }) end function SqliteTable:addColumn(column) printInfo(LOGTAG, "addColumn, tableName:%s, colName:%s, colType:%s", self.name, column.name, column:getSqlType()) local str = "ALTER TABLE " .. self.name .. " ADD COLUMN " .. column.name .. " " .. column:getSqlType() return self.databaseApis.exec(str) end -- function SqliteTable:delColumn(column) -- printInfo(LOGTAG, "delColumn, tableName:%s, colName:%s", self.name, column.name) -- local str = "ALTER TABLE " .. self.name .. " DROP COLUMN " .. column.name -- return self.databaseApis.exec(str) -- end -- function SqliteTable:changeColumn(oldColumn, newColumn) -- printInfo( -- LOGTAG, -- "changeColumn, tableName:%s, oldColName:%s, oldColType:%s, newColName:%s, newColType:%s", -- self.name, -- oldColumn.name, -- oldColumn:getSqlType(), -- newColumn.name, -- newColumn:getSqlType() -- ) -- local str = "ALTER TABLE " .. self.name .. " CHANGE COLUMN " .. oldColumn.name .. " " .. newColumn.name .. " " .. newColumn:getSqlType() -- return self.databaseApis.exec(str) -- end function SqliteTable:getLastUpdateVersion() local list = self:genQuery() :select(self.col.lastUpdateVersion) :distinct() :get("lastUpdateVersion") if #list <= 0 then return "0.0.0" end list = util.linq(list) :order(function(a, b) return util.string.compareVersion(a, b) <= 0 end) :array() return list[#list] end return SqliteTable customerFunCfg--[[ from file:顾客功能.xlsx --]] local customerFunCfg = { [1] = { id = 401, desc = "金主:每次付费,都会有{0}%的概率给予{1}倍金币奖励", }, [2] = { id = 402, desc = "好评如潮:有{0}%的概率给予{1}星好评", }, [3] = { id = 403, desc = "大胃王:会连续吃{0}道菜,受大胃王鼓励,厨房做菜的速度加快{1}%。", }, } return customerFunCfg ApplePaymentMgr --[[ 苹果支付 author:{zhangpeng} time:2024-08-13 14:33:01 ]] local ApplePaymentMgr, super = defClassStatic("ApplePaymentMgr") local LOG_TAG = "ApplePaymentMgr" local IOC_CLASS_NAME = "XIAPManager" function ApplePaymentMgr:init() self:registIAPLuaCallback() self:registMsgListener() end function ApplePaymentMgr:registMsgListener() Msg.add( { Msg.SHOP_iOS_PURCHASE_SUC, Msg.SHOP_iOS_PURCHASE_FAILED }, function(...) self:listener(...) end ) end function ApplePaymentMgr:listener(msgId,data) if msgId == Msg.SHOP_iOS_PURCHASE_SUC then printInfo(LOG_TAG, "Apple支付成功, 向服务器验签") UIComsTool:showToast(TextCfgParse:getTextStr("payment_verify_server"),2) ApplePaymentMgr:verifyTransaction(data.transactionReceipt,function (suc, rspData) if suc then self:parseProductInfo(rspData) else printError("验签失败!") end end) elseif msgId == Msg.SHOP_iOS_PURCHASE_FAILED then printInfo(LOG_TAG,"ios 支付失败 error code:%s", data.errorcode) self:handleErrorCode(data.errorcode) end end function ApplePaymentMgr:parseProductInfo(rspData) table.print_r(rspData,"支付验签返回") UIComsTool:showToast(TextCfgParse:getTextStr("payment_verify_suc"),2) local _rspData = rspData.shopItem local id = _rspData.id local count = _rspData.count local price = _rspData.price local key = _rspData.key local name = _rspData.name local platform = _rspData.platform printInfo(LOG_TAG, "解析验签返回数据: id:%s count:%s price:%s key:%s name:%s platform:%s", id, count, price, key, name, platform) Msg.send(Msg.GEM_UPDATE_COUNT, { count = count, awardType = "gem", opt = "add" }) UIComsTool:hideLoading() end -- 根据id购买 function ApplePaymentMgr:payByProductId(productId) if Device.isIOS() then luaoc.callStaticMethod( "XIAPManager", "doPayment", { productId = productId, userName = User:getUserName() } ) end end -- 交易验证(把票据信息receipt发给服务器,验签成功后,赋予用户购买的商品) function ApplePaymentMgr:verifyTransaction(originalTransactionId,cb) local param = {} param.originalTransactionId = originalTransactionId local jsonStr = json.encode(param) printInfo(LOG_TAG,"发送购买验签请求,订单id:%s", originalTransactionId) HttpCmdMgr:postCmdSync(HttpCmdDef.CMD.VERIFY_TRANS,jsonStr, function (...) if cb then cb(...) end end ) end function ApplePaymentMgr:registIAPLuaCallback() local param = { purchaseSucCallback = function(state,transactionReceipt) if state == XSdkConstants.TransactionState.PaymentSuc then printInfo(LOG_TAG,"购买成功回调到 lua call: %s transactionReceipt:%s",state,transactionReceipt) Msg.send(Msg.SHOP_iOS_PURCHASE_SUC,{state = state, transactionReceipt = transactionReceipt}) end end, purchaseFaileCallback = function (state, errorcode) if state == XSdkConstants.TransactionState.PaymentFailed then printInfo(LOG_TAG,"购买失败回调到lua %s",errorcode) Msg.send(Msg.SHOP_iOS_PURCHASE_FAILED, {errorcode = errorcode}) end end, canNotMakePaymentsCallback = function () UIComsTool:showToast(TextCfgParse:getTextStr("payment_disable") ,2) -- 购买功能不可用 end, invalidProductIdentifiersCallback = function (productId) UIComsTool:showToast(TextCfgParse:getTextStr("payment_disable_item"..productId),2)-- 无效的购买商品 end } luaoc.callStaticMethod(IOC_CLASS_NAME, "registLuaCallback", param) end function ApplePaymentMgr:handleErrorCode(code) if code == PaymentErrorCode.iOS.SKErrorPaymentCancelled then UIComsTool:showToast(TextCfgParse:getTextStr("payment_cancle"),1) -- 购买取消 else UIComsTool:showToast(TextCfgParse:getTextStr("payment_failed"),1) -- 购买失败 end UIComsTool:hideLoading() endTaskOrderDetailUI>--- 订单页详情 ---@class TaskOrderDetailUI : UILayer local TaskOrderDetailUI, super = defClass("TaskOrderDetailUI", UILayer) function TaskOrderDetailUI:ctor(taskOrderId, isNewOrder) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/taskui/order/taskorderuireslink") self.taskOrderId = taskOrderId self.isNewOrder = isNewOrder self.needReminded = false end function TaskOrderDetailUI:onLoad() self.ui = GameObject.Instantiate(self.R.task_order_detail_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) --if self.isNewOrder then -- self:addCloseCallback(function() self:confirm()() end) --end self:initUI() self:showUI() end --- 初始化UI function TaskOrderDetailUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.order_title = self.ui:Seek("order_title") self.order_desc = self.ui:Seek("order_desc") self.confirm_btn = self.ui:Seek("confirm_btn") self.receive_btn = self.ui:Seek("receive_btn") self.reset_btn = self.ui:Seek("reset_btn") self.time_limit = self.ui:Seek("time_limit") self.time_limit_out = self.ui:Seek("time_limit_out") self.completed = self.ui:Seek("completed") self.toggle_hint = self.ui:Seek("toggle_hint") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addButtonClickEvent(self.confirm_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) --self:confirm() self:close() end) util.ugui.addButtonClickEvent(self.receive_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:receive() self:close() end) util.ugui.addButtonClickEvent(self.reset_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:reset() self:close() end) self:initRewardIU() end --- 展示UI function TaskOrderDetailUI:showUI() local info = TaskOrderMgr:getOrderInfo(self.taskOrderId) local isTimeArrived = info:isTimeArrived() local isTimeOut = info:isTimeOut() local isReset = info:isReset() local canReward = isTimeArrived and (not isTimeOut) local canReset = isTimeArrived and isTimeOut and (not isReset) self.order_title:GetComponent("TextMeshProUGUI").text = info:getTitle() self.order_desc:GetComponent("TextMeshProUGUI").text = info:getDesc() self.confirm_btn:SetActive(not isTimeArrived) self.toggle_hint:SetActive(not isTimeArrived) self.time_limit:SetActive(not isTimeArrived) self.completed:SetActive(canReward) self.receive_btn:SetActive(canReward) self.time_limit_out:SetActive(canReset) self.reset_btn:SetActive(canReset) if self.isNewOrder then self.time_limit:GetComponent("TextMeshProUGUI").text = info:getTimeLimitDesc() end self:showRewardIU(info) end --- 初始化奖励UI function TaskOrderDetailUI:initRewardIU() self.coins = {} for i = 1, 3 do self.coins[i] = self.ui:Seek("reward_coin_" .. i) end self.coinIcons = {} for i = 1, #self.coins do self.coinIcons[i] = self.coins[i]:Seek("icon") end self.entities = {} for i = 1, 3 do self.entities[i] = self.ui:Seek("reward_entity_" .. i) end self.entityIcons = {} for i = 1, #self.entities do self.entityIcons[i] = self.entities[i]:Seek("icon") end end --- 显示奖励UI ---@param info TaskOrderInfo function TaskOrderDetailUI:showRewardIU(info) local rewardIds = info:getRewardIds() local rewardValues = info:getRewardValues() local coinIndex = 1 local entityIndex = 1 for i, id in ipairs(rewardIds) do local has = rewardIds[i] ~= -1 if has then if id == 1 then coinIndex = self:showCoinReward(coinIndex, rewardIds[i], rewardValues[i]) elseif id == 2 then coinIndex = self:showCoinReward(coinIndex, rewardIds[i], rewardValues[i]) elseif id == 3 then entityIndex = self:showEntityReward(entityIndex, rewardIds[i], rewardValues[i]) elseif id == 4 then entityIndex = self:showEntityReward(entityIndex, rewardIds[i], rewardValues[i]) end end end for i = coinIndex + 1, #self.coins do self.coins[i]:SetActive(false) end for i = entityIndex + 1, #self.entities do self.entities[i]:SetActive(false) end end function TaskOrderDetailUI:getCoinIcon(type) -- todo 图片 local base_path = "Assets/AssetsPackage/Res/modules/ui/common/images/" if type == 1 then local img_path = base_path .. "coin.png" return ResLoader.loadAsset(img_path,typeof(CS.UnityEngine.Sprite)) elseif type == 2 then local img_path = base_path .. "coin.png" return ResLoader.loadAsset(img_path,typeof(CS.UnityEngine.Sprite)) end end function TaskOrderDetailUI:getEntityIcon(type, entityId) -- todo 图片 local base_path = "Assets/AssetsPackage/Res/modules/ui/common/images/" if type == 3 then local img_path = base_path .. "default_role_cell.png" return ResLoader.loadAsset(img_path,typeof(CS.UnityEngine.Sprite)) elseif type == 4 then local img_path = base_path .. "default_cuisine.png" return ResLoader.loadAsset(img_path,typeof(CS.UnityEngine.Sprite)) end end function TaskOrderDetailUI:showCoinReward(index, type, value) self.coins[index]:SetActive(true) self.coins[index]:GetComponent("TextMeshProUGUI").text = "+" .. value self.coinIcons[index]:GetComponent("Image").sprite = self:getCoinIcon(type) return index + 1 end function TaskOrderDetailUI:showEntityReward(index, type, value) self.entities[index]:SetActive(true) self.entityIcons[index]:GetComponent("Image").sprite = self:getEntityIcon(type, value) return index + 1 end --function TaskOrderDetailUI:confirm() -- TaskOrderMgr:setOrderPrompt(self.taskOrderId, self.needReminded) --end function TaskOrderDetailUI:receive() -- todo 奖励领取动画 TaskOrderMgr:cleanTaskOrderId(self.taskOrderId) TaskOrderMgr:taskOrderComplete(self.taskOrderId) end function TaskOrderDetailUI:reset() TaskOrderMgr:resetOrderId(self.taskOrderId) TaskOrderMgr:taskOrderFailed(self.taskOrderId) end function TaskOrderDetailUI:triggerHint(isOn) --self.needReminded = isOn TaskOrderMgr:setOrderPrompt(self.taskOrderId, isOn) end return TaskOrderDetailUI helpuireslink9return { --BASIC --ASSET help_cell = {"Assets/AssetsPackage/Res/modules/ui/helpui/prefabs/help_cell.prefab", 0, 0}, help_list_ui = {"Assets/AssetsPackage/Res/modules/ui/helpui/prefabs/help_list_ui.prefab", 0, 0}, help_detail_ui = {"Assets/AssetsPackage/Res/modules/ui/helpui/prefabs/help_detail_ui.prefab", 0, 0}, help_category_cell = {"Assets/AssetsPackage/Res/modules/ui/helpui/prefabs/help_category_cell.prefab", 0, 0}, help_category_list_ui = {"Assets/AssetsPackage/Res/modules/ui/helpui/prefabs/help_category_list_ui.prefab", 0, 0}, } specialCustomerCfg2 --[[ from file:特殊顾客表.xlsx --]] local specialCustomerCfg = { [1] = { id = 410001, name = "胖达", nature = "温和", desc = "不在乎花钱,人生乐趣就是逛吃逛吃,遇到对胃口的馆子便会大力支持", moveSpeed = 100, solicitTag = 0, artRes = "yg_chushi_01", plotId = -1, sucDialogId = -1, faileDialogId = -1, sharRewardCoin = -1, limitTimes = 10, newTip = 0, weight = 10, activeTimeLen = 60, clickTimes = 30, param_1 = 3, param_2 = 200, param_3 = 2000, artBustRes = "role_fuerdai", }, [2] = { id = 410002, name = "小空", nature = "暴躁", desc = "抱着大大小小的礼盒穿梭在大街小巷", moveSpeed = 100, solicitTag = 0, artRes = "yg_chushi_01", plotId = -1, sucDialogId = -1, faileDialogId = -1, sharRewardCoin = -1, limitTimes = 10, newTip = 0, weight = 10, activeTimeLen = 60, clickTimes = 15, param_1 = 10, param_2 = -1, param_3 = -1, artBustRes = "role_liwu", }, [3] = { id = 410003, name = "阿花", nature = "温和", desc = "受不了自己这毛病,只能尽量躲人少的地方待着", moveSpeed = 100, solicitTag = 1, artRes = "yg_chushi_01", plotId = -1, sucDialogId = -1, faileDialogId = -1, sharRewardCoin = -1, limitTimes = 10, newTip = 1, weight = 10, activeTimeLen = 300, clickTimes = 30, param_1 = 2, param_2 = -1, param_3 = -1, artBustRes = "role_chouyou", }, [4] = { id = 410004, name = "跃跃", nature = "神秘", desc = "手捧晶莹剔透的水晶球,透着洞悉一切的光芒", moveSpeed = 100, solicitTag = 2, artRes = "yg_chushi_01", plotId = -1, sucDialogId = -1, faileDialogId = -1, sharRewardCoin = -1, limitTimes = 10, newTip = 1, weight = 10, activeTimeLen = 20, clickTimes = 1, param_1 = 10, param_2 = -1, param_3 = -1, artBustRes = "role_mofashi", }, [5] = { id = 410005, name = "莉莉", nature = "胆小", desc = "萌萌的少女,独特的嗓音,特别的主唱", moveSpeed = 100, solicitTag = 1, artRes = "yg_chushi_01", plotId = -1, sucDialogId = -1, faileDialogId = -1, sharRewardCoin = -1, limitTimes = 10, newTip = 1, weight = 10, activeTimeLen = 60, clickTimes = 15, param_1 = 15, param_2 = -1, param_3 = -1, artBustRes = "role_liulanggeshou", }, [6] = { id = 410006, name = "毛毛兄弟", nature = "暴躁", desc = "总是屯粮的一对兄弟,可惜脾气不对付,一言不合就决斗", moveSpeed = 100, solicitTag = 0, artRes = "yg_chushi_01", plotId = -1, sucDialogId = -1, faileDialogId = -1, sharRewardCoin = -1, limitTimes = 10, newTip = 1, weight = 10, activeTimeLen = 600, clickTimes = 30, param_1 = -1, param_2 = -1, param_3 = -1, artBustRes = "role_tuboshuxiongdi", }, } return specialCustomerCfg FishingSucceedUI -- 捕鱼成功界面 local FishingSucceedUI = defClass("FishingSucceedUI", UILayer) local TMProUGUI = CS.TMPro.TextMeshProUGUI local Vector2 = CS.UnityEngine.Vector2 -- 构造函数 function FishingSucceedUI:ctor() UILayer.ctor(self) self.R = ResLoader.loadResLink("modules/ui/fishingui/fishinguireslink") end -- 当页面加载 function FishingSucceedUI:onLoad() self.ui = GameObject.Instantiate(self.R.fishing_succeed_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end -- 初始化界面 function FishingSucceedUI:initUI() self.close_btn = self.ui:Seek("close_btn") self.restart_btn = self.ui:Seek("restart_btn") self.challenges = self.ui:Seek("challenges") self.progress_bar = self.ui:Seek("progress_bar") self.progress_value = self.progress_bar:Seek("value")[TMProUGUI] self.spine = self.ui:Seek("spine") self.progressList = {} self.progressValues = {0.5, 0.7, 0.85, 1.0} for i = 1, 4 do self.progressList[i] = self.ui:Seek("progress_" .. i):Seek("reward_v") end util.ugui.addClickEvent(self.close_btn, function() self:backStartPage() end) util.ugui.addClickEvent(self.restart_btn, function() self:gameRestart() end) self.close_btn:SetActive(false) self.restart_btn:SetActive(false) end -- 展示界面 function FishingSucceedUI:showUI() local count = FishingGameMgr.userData:getFishingGameCountToday() self.challenges[TMProUGUI].text = string.format("今日已挑战%d次", count) util.spine.play(self.spine, "stand_1", false, function() if self or self.challenges then self.challenges:SetActive(true) end end) self:setProgress(1) end function FishingSucceedUI:setProgress(value) local pos = Vector2(760 * value - 380, 0) self.progress_bar:RunAction(ua.Sequence({uiua.AnchoredPosTo(1, pos), ua.cb(function() for i = 1, #self.progressValues do self.progressList[i]:SetActive(value >= self.progressValues[i]) end self:getReward(value) end)})) --self.progress_bar[RectTransform].anchoredPosition = pos self.progress_value.text = (value * 100) .. "%" end -- 重新开始游戏 function FishingSucceedUI:gameRestart() FishingGameMgr:gameRestart() self:close() end function FishingSucceedUI:backStartPage() FishingGameMgr:backStartPage() self:close() end -- 获取奖励 function FishingSucceedUI:getReward(value) local rewardCfg = FishingRewardCfgParse:getFishingRewardCfgByProgress(value * 100) if not rewardCfg then return end -- 鱼奖励 local fishes = {} for _ = 1, rewardCfg.count_fish do local id = rewardCfg.param_fish[math.random(1, #rewardCfg.param_fish)] if fishes[id] then fishes[id] = fishes[id] + 1 else fishes[id] = 1 end end self.ui:Delay(0.2, function() self.close_btn:SetActive(true) self.restart_btn:SetActive(true) FishingFishListUI.new(fishes):show():showMask():enableCloseWhenClickMask() end) -- 其他奖励 for i = 1, 2 do local type = rewardCfg["reward_" .. i] if (not type) or (type == -1) then break end if type == FishingConst.PropRewardType.Star then CurrencyMgr:changeStar(rewardCfg["count_" .. i]) end end end return FishingSucceedUI employeeuireslinkqreturn { --BASIC --ASSET employee_cell = {"Assets/AssetsPackage/Res/modules/ui/roles/employee/prefabs/employee_cell.prefab", 0, 0}, employee_list_ui = {"Assets/AssetsPackage/Res/modules/ui/roles/employee/prefabs/employee_list_ui.prefab", 0, 0}, employee_detail_ui = {"Assets/AssetsPackage/Res/modules/ui/roles/employee/prefabs/employee_detail_ui.prefab", 0, 0}, employee_skin_cell = {"Assets/AssetsPackage/Res/modules/ui/roles/employee/prefabs/employee_skin_cell.prefab", 0, 0}, employee_story_cell = {"Assets/AssetsPackage/Res/modules/ui/roles/employee/prefabs/employee_story_cell.prefab", 0, 0}, attribute_curr_value = {"Assets/AssetsPackage/Res/modules/ui/roles/employee/prefabs/attribute_curr_value.prefab", 0, 0}, attribute_next_value = {"Assets/AssetsPackage/Res/modules/ui/roles/employee/prefabs/attribute_next_value.prefab", 0, 0}, } ScreenShotUtil.2--[[ 屏幕截图工具 author:{zhangpeng} time:2024-04-02 19:19:27 ]] local ScreenShotUtil = {} local LOGTAG = "ScreenShotUtil" local p_max = CS.UnityEngine.Vector3.zero local p_min = CS.UnityEngine.Vector3.zero local center = CS.UnityEngine.Vector3.zero local layer = "TopLayer" local coefficientScale = 100 local orthographicSize = nil local designSizeOne = CS.UnityEngine.Vector2(2560, 1538) local designSizeTwo = CS.UnityEngine.Vector2(2048, 1538) function ScreenShotUtil:SetOrthographicSize(size) orthographicSize = size end -- 截全屏 function ScreenShotUtil:TakeAllScreenImg(obj) if obj == nil then return nil end local oldLayer = obj.layer self:ChangeObjLayer(obj, CS.UnityEngine.LayerMask.NameToLayer(layer)) local b = self:ClacBounds(obj) local cameraPhoto = CS.UnityEngine.GameObject("CameraPhoto") cameraPhoto.hideFlags = CS.UnityEngine.HideFlags.HideAndDontSave cameraPhoto.transform.position = b.center - CS.UnityEngine.Vector3(0, 0, b.extents.z + 10) cameraPhoto.transform:LookAt(b.center) local c = cameraPhoto:AddComponent(typeof(CS.UnityEngine.Camera)) c.cullingMask = 1 << CS.UnityEngine.LayerMask.NameToLayer(layer) c.clearFlags = CS.UnityEngine.CameraClearFlags.SolidColor c.orthographic = true c.orthographicSize = orthographicSize local tex = self:CaptureCamera(c, CS.UnityEngine.Rect(0, 0, b.size.x * coefficientScale, b.size.y * coefficientScale)) CS.UnityEngine.GameObject.Destroy(cameraPhoto) self:ChangeObjLayer(obj, oldLayer) return tex end -- 拍照 返回Texture2d function ScreenShotUtil:TakePhotoTexture2D(obj) if obj == nil then return nil end local oldLayer = obj.layer self:ChangeObjLayer(obj, CS.UnityEngine.LayerMask.NameToLayer(layer)) local b = self:ClacBounds(obj) local cameraPhoto = CS.UnityEngine.GameObject("CameraPhoto") cameraPhoto.hideFlags = CS.UnityEngine.HideFlags.HideAndDontSave cameraPhoto.transform.position = b.center - CS.UnityEngine.Vector3(0, 0, b.extents.z + 10) cameraPhoto.transform:LookAt(b.center) local c = cameraPhoto:AddComponent(typeof(CS.UnityEngine.Camera)) c.cullingMask = 1 << CS.UnityEngine.LayerMask.NameToLayer(layer) c.clearFlags = CS.UnityEngine.CameraClearFlags.SolidColor c.orthographic = true local size = math.max(b.extents.x, b.extents.y) c.orthographicSize = size local tex = self:CaptureCamera(c, CS.UnityEngine.Rect(0, 0, b.size.x * coefficientScale, b.size.y * coefficientScale)) CS.UnityEngine.GameObject.Destroy(cameraPhoto) self:ChangeObjLayer(obj, oldLayer) return tex end function ScreenShotUtil:TakePhotoTexture2DWithBounds(obj, bounds) if obj == nil then return nil end local oldLayer = obj.layer self:ChangeObjLayer(obj, CS.UnityEngine.LayerMask.NameToLayer(layer)) local b = bounds local cameraPhoto = CS.UnityEngine.GameObject("CameraPhoto") cameraPhoto.hideFlags = CS.UnityEngine.HideFlags.HideAndDontSave cameraPhoto.transform.position = b.center - CS.UnityEngine.Vector3(0, 0, b.extents.z + 10) cameraPhoto.transform:LookAt(b.center) local c = cameraPhoto:AddComponent(typeof(CS.UnityEngine.Camera)) c.cullingMask = 1 << CS.UnityEngine.LayerMask.NameToLayer(layer) c.clearFlags = CS.UnityEngine.CameraClearFlags.SolidColor c.orthographic = true local size = math.max(b.extents.x, b.extents.y) c.orthographicSize = size local tex = self:CaptureCamera(c, CS.UnityEngine.Rect(0, 0, b.size.x * coefficientScale, b.size.y * coefficientScale)) CS.UnityEngine.GameObject.Destroy(cameraPhoto) self:ChangeObjLayer(obj, oldLayer) return tex end -- obj: 要截屏的节点 -- width height: 图片大小 function ScreenShotUtil:takeCameraPhoto(obj, width, height, offset) if obj == nil then return nil end local _offset = offset or CS.UnityEngine.Vector3.zero local list = {} local oldLayerList = self:ChangeObjLayer(obj, CS.UnityEngine.LayerMask.NameToLayer(layer)) local b = self:ClacBounds(obj) -- 相机中心世界坐标 local cameraObject = CS.UnityEngine.GameObject.Find("MainCamera") local cameraComponent = CS.UnityEngine.GameObject.Find("MainCamera")[CS.UnityEngine.Camera] -- 相机焦点 local worldPos = cameraComponent:ViewportToWorldPoint(CS.UnityEngine.Vector3(0.5, 0.5, 0)) local cameraPhoto = CS.UnityEngine.GameObject("CameraPhoto") cameraPhoto.hideFlags = CS.UnityEngine.HideFlags.HideAndDontSave cameraPhoto.transform.position = cameraObject.transform.position + _offset cameraPhoto.transform:LookAt(CS.UnityEngine.Vector3(worldPos.x, worldPos.y, 0) + _offset) local c = cameraPhoto:AddComponent(typeof(CS.UnityEngine.Camera)) c.cullingMask = 1 << CS.UnityEngine.LayerMask.NameToLayer(layer) c.clearFlags = CS.UnityEngine.CameraClearFlags.SolidColor c.orthographic = true c.orthographicSize = cameraComponent.orthographicSize local tex = self:CaptureCamera(c, CS.UnityEngine.Rect(0, 0, width or CS.UnityEngine.Screen.width, height or CS.UnityEngine.Screen.height)) CS.UnityEngine.GameObject.Destroy(cameraPhoto) self:ChangeObjLayer(obj, oldLayerList) return tex end -- 更改目标所在层 function ScreenShotUtil:ChangeObjLayer(obj, layer) local s = type(layer) if type(layer) == "table" then for _, value in ipairs(layer) do if value["NODE"] and value["LAYER"] then value["NODE"]:SetLayer(value["LAYER"]) end end else local oldLayerList = {} local recursion recursion = function(gameObject, layer) table.insert( oldLayerList, { NODE = gameObject, LAYER = gameObject.layer } ) gameObject:SetLayer(layer) local transform = gameObject.transform for i = 1, gameObject.transform.childCount do local item = gameObject.transform:GetChild(i - 1) recursion(item.gameObject, layer) end end recursion(obj, layer) return oldLayerList end end -- 拍照 返回Sprite function ScreenShotUtil:TakePhotoSprite(obj) local tex = self:TakePhotoTexture2D(obj) local sprite = CS.UnityEngine.Sprite.Create(tex, CS.UnityEngine.Rect(0, 0, tex.width, tex.height), CS.UnityEngine.Vector2(0.5, 0.5), 100) return sprite end -- 获取整个屏幕的sprite function ScreenShotUtil:TakeScreenSprite(obj) local tex = self:TakeAllScreenImg(obj) local sprite = CS.UnityEngine.Sprite.Create(tex, CS.UnityEngine.Rect(0, 0, tex.width, tex.height), CS.UnityEngine.Vector2(0.5, 0.5), 100) return sprite end -- 对相机截图 function ScreenShotUtil:CaptureCamera(camera, rect) rect.width = math.round(rect.width) rect.height = math.round(rect.height) local rt = CS.UnityEngine.RenderTexture(rect.width, rect.height, 0) camera.targetTexture = rt camera:Render() CS.UnityEngine.RenderTexture.active = rt local screenShot = CS.UnityEngine.Texture2D(rect.width, rect.height, CS.UnityEngine.TextureFormat.RGBA32, false) screenShot:ReadPixels(rect, 0, 0) screenShot:Apply() camera.targetTexture = nil CS.UnityEngine.RenderTexture.active = nil rt:Release() CS.UnityEngine.Object.Destroy(rt) return screenShot end -- 计算目标包围盒 function ScreenShotUtil:ClacBounds(obj) local mesh = obj:GetComponent(typeof(CS.UnityEngine.Renderer)) if mesh ~= nil then local b = mesh.bounds p_max = b.max p_min = b.min center = b.center else end self:RecursionClacBounds(obj.transform) if mesh == nil then self:ClacCenter(p_max, p_min, center) end local size = CS.UnityEngine.Vector3(p_max.x - p_min.x, p_max.y - p_min.y, p_max.z - p_min.z) local bound = CS.UnityEngine.Bounds(center, size) bound.size = size bound.extents = size / 2 return bound end -- 计算包围盒中心坐标 function ScreenShotUtil:ClacCenter(max, min, center) local xc = (p_max.x + p_min.x) / 2 local yc = (p_max.y + p_min.z) / 2 local zc = (p_max.z + p_min.z) / 2 center = CS.UnityEngine.Vector3(xc, yc, zc) end -- 计算包围盒顶点 function ScreenShotUtil:RecursionClacBounds(obj) if obj.transform.childCount <= 0 then return end for i = 1, obj.childCount do local item = obj:GetChild(i - 1) local m = item:GetComponent(typeof(CS.UnityEngine.Renderer)) if m ~= nil then local b = m.bounds if p_max:Equals(CS.UnityEngine.Vector3.zero) and p_min:Equals(CS.UnityEngine.Vector3.zero) then p_max = b.max p_min = b.min end if b.max.x > p_max.x then p_max.x = b.max.x end if b.max.y > p_max.y then p_max.y = b.max.yz end if b.max.z > p_max.z then p_max.z = b.max.z end if b.min.x < p_min.x then p_min.x = b.min.x end if b.min.y < p_min.y then p_min.y = b.min.y end if b.min.z < p_min.z then p_min.z = b.min.z end end self:RecursionClacBounds(item) end end -- ui截图 function ScreenShotUtil:TakeUICameraPhoto(obj, width, height, offset, delayCb) local isDelay = false if delayCb then isDelay = true end -- 创建相机 local camera = CS.UnityEngine.GameObject.Instantiate(UITemplate.camera) local com = camera:GetComponent(typeof(CS.UnityEngine.Camera)) local worldPos = com:ViewportToWorldPoint(CS.UnityEngine.Vector3(0.5, 0.5, 0)) camera.transform:LookAt(CS.UnityEngine.Vector3(worldPos.x, worldPos.y, 0)) local data = CS.UnityEngine.Rendering.Universal.CameraExtensions.GetUniversalAdditionalCameraData(com) data.renderType = CS.UnityEngine.Rendering.Universal.CameraRenderType.Base com.cullingMask = 1 << CS.UnityEngine.LayerMask.NameToLayer(layer) -- 创建画布 local uinode = CS.UnityEngine.GameObject.Instantiate(CS.UnityEngine.Resources.Load("ui/uinode")) local canvas = CS.UnityEngine.GameObject.Instantiate(uinode:Seek("UICanvas")) -- local canvas = CS.UnityEngine.GameObject.Instantiate(UITemplate.canvas) local canvasCom = canvas:GetComponent(typeof(CS.UnityEngine.Canvas)) local oldLayerList = self:ChangeObjLayer(canvas, CS.UnityEngine.LayerMask.NameToLayer(layer)) canvasCom.worldCamera = com canvasCom.scaleFactor = UITemplate.canvas:GetComponent(typeof(CS.UnityEngine.Canvas)).scaleFactor if width then width = width * canvasCom.scaleFactor end if height then height = height * canvasCom.scaleFactor end -- 复制obj local parent = obj:GetParent() local newObj = CS.UnityEngine.GameObject.Instantiate(obj, parent.transform) newObj:SetParent(canvas) if offset then newObj:SetPosition(offset) end -- 截屏 local function capCam() local tex = self:CaptureCamera(com, CS.UnityEngine.Rect(0, 0, width or CS.UnityEngine.Screen.width, height or CS.UnityEngine.Screen.height)) CS.UnityEngine.GameObject.Destroy(camera) CS.UnityEngine.GameObject.Destroy(canvas) CS.UnityEngine.GameObject.Destroy(uinode) return tex end if not isDelay then return capCam() else CS.LuaGlobal.instance:runAtNextFrame( function() local tex = capCam() delayCb(tex) end ) end end --[[ 默认为 1 png类型 2 为 jpg ]] function ScreenShotUtil:saveShareTexture(tex, fileName, fileType) if not tex then printWarn(LOGTAG, "saveShareTexture, 存储图片 tex 不能为空") return end fileName = fileName or tostring(util.time.getTimeStamp()) local File = CS.System.IO.File local Directory = CS.System.IO.Directory local Path = CS.System.IO.Path if not fileType then fileType = 1 end local fileDir = Path.Combine(CS.UnityEngine.Application.persistentDataPath, "capturedata") local bytes local picPath if fileType == 1 then bytes = tex:EncodeToPNG() picPath = Path.Combine(fileDir, string.format("%s.png", fileName)) elseif fileType == 2 then bytes = tex:EncodeToJPG() picPath = Path.Combine(fileDir, string.format("%s.jpg", fileName)) end if not Directory.Exists(fileDir) then Directory.CreateDirectory(fileDir) end File.WriteAllBytes(picPath, bytes) return picPath end return ScreenShotUtil bbqRewardCfg--[[ from file:烤吧奖励表.xlsx --]] local bbqRewardCfg = { [1] = { id = 1, rewardCount = 1, weight = 30, countMin = 20.0, countMax = 50, }, [2] = { id = 2, rewardCount = 2, weight = 40, countMin = 0.1, countMax = 3, }, [3] = { id = 3, rewardCount = 3, weight = 30, countMin = 100.0, countMax = 300, }, } return bbqRewardCfg UITweenDeflocal UITweenDef,_ = defClassStatic("UITweenDef") UITweenDef.TWEEN_TYPE = { SCALE = 0, FADE = 1 } function UITweenDef:init() end UITweenDef:init()PondRemoveStoneUI(-- 池塘 移开石头界面 local PondRemoveStoneUI, super = defClass("PondRemoveStoneUI", UILayer) local TMProUGUI = CS.TMPro.TextMeshProUGUI -- 构造函数 function PondRemoveStoneUI:ctor(buildingId) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/pondui/ponduireslink") self.buildingId = buildingId end -- 当页面加载 function PondRemoveStoneUI:onLoad() self.ui = GameObject.Instantiate(self.R.pond_remove_stone_ui) self:addChild(self.ui) self:initUI() self:showUI() end -- 初始化UI元素 function PondRemoveStoneUI:initUI() self.close_btn = self.ui:Seek("close_btn") self.remove_btn = self.ui:Seek("remove_btn") self.remove_price = self.ui:Seek("remove_price")[TMProUGUI] util.ugui.addClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addClickEvent(self.remove_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:removeStone() end) end -- 展示UI function PondRemoveStoneUI:showUI() local count = StallDeskMgr:getStallDeskCount() local cfg = BoothUnlockCfgParse:getBoothUnlockCfgByOrder(count + 1) if cfg.price <= 0 then self.remove_price.text = "免费" else self.remove_price.text = cfg.price end end -- 点击移除按钮 function PondRemoveStoneUI:removeStone() local desk = StallDeskMgr:getStallDeskById(self.buildingId) desk:startUnlockStall() self:close() end return PondRemoveStoneUImainbrequire("common/core/base/main") require("common/core/app/main") require("common/core/db/main")main*require("modules/help/const/HelpConst")UIDialogSimple%--[[ 只包含一个文字和一个按钮的对话框 author:{zhangpeng} time:2025-04-07 12:26:16 ]] local UIDialogSimple,super= defClass("UIDialogSimple",UILayer) local TMPUGUI = CS.TMPro.TextMeshProUGUI local UnityEngine = CS.UnityEngine local GameObject = UnityEngine.GameObject local LOGTAG = "UIDialogSimple" function UIDialogSimple:ctor() super.ctor(self) self.R = Res.loadResLink("common/ui/uicoms/reslink/uidialogreslink") end function UIDialogSimple:onLoad() self:initView() end function UIDialogSimple:initView() self.ui = GameObject.Instantiate(self.R.dialog_box_simple) self:addChild(self.ui) self:useTweenOnOpen(self.ui) -- ok local close = self.ui:Seek("btn") util.ugui.addButtonClickEvent(close, function() self:close() end) end -- 设置对话框内容 function UIDialogSimple:setContentText(text) self.ui:Seek("content")[TMPUGUI].text = text self.content = text return self end function UIDialogSimple:onExit() super.onExit(self) end return UIDialogSimple BonusConstlocal BonusConst = defClassStatic("BonusConst") function BonusConst:init() end -- 加成类型枚举 BonusConst.BonusType = { -- 提高人气 evaluate = 2001, -- 每分钟增加小费收入 coin_income = 2002, -- 小费收入最大累计时间增加 coin_income_max_time = 2003, -- 客人每次消费可获得收入 coin_income_per_consume = 2004, -- 提高做菜效率 cook_efficiency = 2005, -- 每隔30秒可获得额外收入 coin_income_per_30s = 2006, -- 每小时自助收入 coin_income_per_hour = 2007, -- 每日音符收入 coin_income_per_day = 2008, -- 顾客移动速度 customer_move_speed = 2009, -- 歌曲数量 music_count = 2010, } -- 加成类型字符串映射 BonusConst.BonusTypeString = { ["star"] = 2001, ["tip"] = 2002, ["tipTime"] = 2003, ["consume"] = 2004, ["cook"] = 2005, ["extra30s"] = 2006, ["grill"] = 2007, ["music"] = 2008, ["musiccount"] = 2010, } BonusConst:init()CashierDeskBbq--[[ 烤吧收银台 author:{zhangpeng} time:2025-07-08 16:20:29 ]] local CashierDeskBbq,super = defClass("CashierDeskBbq",BuildingBase) local LOGTAG = "CashierDeskBbq" function CashierDeskBbq:ctor(args) super.ctor(self,args) self.deskId = BuildingConst.buildingType.cashier self.resId = "building_"..self.deskId self.state = BuildingConst.buildingState.used self:initDisplay() end function CashierDeskBbq:initDisplay() self.buildingNode = GameObject.Instantiate(self.scene.R[self.resId]) self.sortBaseNode = self.buildingNode:Seek("base") self:setBuildingParent(self.buildingParent) self:setBuildingNodeName(self.resId) self:addClickEvent() end -- 点击摊位 function CashierDeskBbq:addClickEvent() local restaurantScene = RestaurantMgr:getRestaurantScene() local touchCom = restaurantScene.touchCom touchCom:addListener(self.buildingNode:Seek("img"), TouchCom.LISTENER_TYPE.CLICK, function(p) self:onClickCashierDesk() end ) end function CashierDeskBbq:onClickCashierDesk() printInfo(LOGTAG, "点击烤吧收银台弹窗") end return CashierDeskBbqemployeSkinCfgy --[[ from file:员工皮肤表.xlsx --]] local employeSkinCfg = { [1] = { id = 310101, roleRid = 300001, skinName = "赤", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [2] = { id = 310102, roleRid = 300001, skinName = "橙", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [3] = { id = 310103, roleRid = 300001, skinName = "黄", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [4] = { id = 310104, roleRid = 300001, skinName = "绿", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [5] = { id = 310105, roleRid = 300001, skinName = "青", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [6] = { id = 310301, roleRid = 300003, skinName = "赤", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [7] = { id = 310302, roleRid = 300003, skinName = "橙", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [8] = { id = 310303, roleRid = 300003, skinName = "黄", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [9] = { id = 310304, roleRid = 300003, skinName = "绿", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [10] = { id = 310305, roleRid = 300003, skinName = "青", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [11] = { id = 310501, roleRid = 300005, skinName = "赤", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [12] = { id = 310502, roleRid = 300005, skinName = "橙", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [13] = { id = 310503, roleRid = 300005, skinName = "黄", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [14] = { id = 310504, roleRid = 300005, skinName = "绿", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [15] = { id = 310505, roleRid = 300005, skinName = "青", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [16] = { id = 310601, roleRid = 300006, skinName = "赤", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [17] = { id = 310602, roleRid = 300006, skinName = "橙", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [18] = { id = 310603, roleRid = 300006, skinName = "黄", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [19] = { id = 310604, roleRid = 300006, skinName = "绿", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, [20] = { id = 310605, roleRid = 300006, skinName = "青", resId = "yg_chushi_01", price = 100, attType = 1, value = 5.0, }, } return employeSkinCfg DBMgrE$ local DBMgr = defClassStatic("DBMgr") local Convert = CS.System.Convert local Md5Util = CS.Md5Util function DBMgr:init(isEncrypt, getDeviceIdFunc, getTimeFunc) if self.inited then return end self.inited = true self.isEncrypt = isEncrypt self.dbTableList = {} self.dbTableListDirty = false self.dbTableClsList = {} self.getDeviceIdFunc = getDeviceIdFunc or function () return "111111" end self.getTimeFunc = getTimeFunc or function () return os.time() end self.getVersionFunc = function () return PlatformUtil.getAppVersion() end Msg.add(Msg.EXIT, function() self:closeCurUserDB() end) end ---增加 table 类 ---@param tableCls SqliteTable ---@param isUser boolean 是否是用户表 ---@param isRole boolean 是否是角色表, 都false是设备表 ---@param tableName string 可以用 DBMgr[tableName] 访问 table function DBMgr:addDBTableCls(tableCls, isUser, isRole, tableName) printVerbose(self.logTag,"addDBTableCls") table.insert(self.dbTableClsList, { tableCls = tableCls, isUser = isUser, isRole = isRole, tableName = tableName, }) end ---初始化表。用户表 角色表 设备表 ---@param isUser boolean 是否用户 ---@param isRole boolean 是否角色 function DBMgr:_initTables(isUser, isRole) local db, dbTableList, tableCls if isUser and isRole then db = self.roleDB dbTableList = self.roleDBTableList elseif isUser then db = self.userDB dbTableList = self.userDBTableList else db = self.deviceDB dbTableList = self.deviceDBTableList end if not db then return end local apis = { getTimeFunc = self.getTimeFunc, getVersionFunc = self.getVersionFunc, } for i,v in ipairs(self.dbTableClsList) do if v.isUser == isUser and v.isRole == isRole then local tableCls = v.tableCls local tableVarName = v.tableName or tableCls.__cls_name:gsub("^%l", string.lower) printVerbose(self.logTag,"_initTables %s", tableVarName) self[tableVarName] = self:_addDBTable(db, dbTableList, tableCls, tableVarName, apis) end end end ---获取数据库的路径 function DBMgr:_getDBPath(userId, roleId) local path = CS.UnityEngine.Application.persistentDataPath .. "/db/" if not roleId and not userId then path = path .. "local" .. "/" elseif not roleId then path = path .. userId .. "/" else path = path .. userId .. "/" .. roleId .. "/" end printInfo(self.logTag, "_getDBPath, path:%s", path) return path end ---获取所有的userid function DBMgr:getAllLocalDBUserIdList() local path = CS.UnityEngine.Application.persistentDataPath .. "/db" local list = {} local dirPathList = Directory.GetDirectories(path) for _, dirPath in cs_ipairs(dirPathList) do if CS.LuaHelper.IsFileExists(Path.Combine(dirPath, USER_DB_NAME)) then local userId = string.sub(dirPath, -7) table.insert(list, userId) end end return list end function DBMgr:getDeviceId() local filePath = CS.UnityEngine.Application.persistentDataPath .. "/udid.dat" if CS.LuaHelper.IsFileExists(filePath) then printInfo(self.logTag, "getDeviceId, udid.data exist") local deviceId = CS.LuaHelper.ReadFileText(filePath) if deviceId then return deviceId end end printInfo(self.logTag, "getDeviceId, udid.data not exist") local deviceId = self.getDeviceIdFunc() -- IHumanSDK:getDeviceId() CS.LuaHelper.SaveFileWithText(filePath, deviceId) return deviceId end function DBMgr:getKey() local key = nil if not self.isEncrypt then return key end key = self:getDeviceId() key = Convert.ToBase64String(key) key = Md5Util.GetMd5OfString(key) return key end function DBMgr:_addDBTable(db, dbTableList, tableCls, tableName, apis) local dbTable = db:getTable(tableCls, tableName, apis) table.insert(dbTableList, dbTable) self.dbTableListDirty = true return dbTable end ---打开当前 user db function DBMgr:openCurUserDB(userId) self:closeCurUserDB() self.userDBTableList = {} self.dbTableListDirty = true self.userDB = self:openUserDB(userId) self:_initTables(true, false) end ---打开当前 role db function DBMgr:openCurRoleDB(userId, roleId) self:closeCurRoleDB() self.roleDBTableList = {} self.dbTableListDirty = true self.roleDB = self:openRoleDB(userId, roleId) self:_initTables(true, true) end ---打开当前 device db function DBMgr:openCurDeviceDB() printVerbose(self.logTag, "openCurDeviceDB") self.deviceDBTableList = {} self.dbTableListDirty = true self.deviceDB = self:openDeviceDB() self:_initTables(false, false) end ---关闭当前 user db function DBMgr:closeCurUserDB() if self.userDB then self.userDB:close() end self.userDB = nil self.userDBTableList = {} self.dbTableListDirty = true self:closeCurRoleDB() end ---关闭当前 role db function DBMgr:closeCurRoleDB() if self.roleDB then self.roleDB:close() end self.roleDB = nil self.roleDBTableList = {} self.dbTableListDirty = true end ---打开 user db function DBMgr:openUserDB(userId) local path = self:_getDBPath(userId) .. self.userDBName local db = self.dbCls.new(path) db:open(self:getKey(userId)) return db end ---打开 role db function DBMgr:openRoleDB(userId, roleId) local path = self:_getDBPath(userId, roleId) .. self.roleDBName local db = self.dbCls.new(path) db:open(self:getKey(userId)) return db end ---打开 device db function DBMgr:openDeviceDB() local path = self:_getDBPath(nil, nil) .. self.deviceDBName local db = self.dbCls.new(path) db:open(self:getKey()) return db end ---拷贝 user db. 会把 role 的也拷贝过去,然后删掉原来的文件 function DBMgr:copyUserDB(srcUserId, srcRoleId, desUserId, desRoleId) printInfo(self.logTag, "copyUserDB, from %s %s to %s %s", srcUserId, srcRoleId, desUserId, desRoleId) local srcUserDBPath = self:_getDBPath(srcUserId) local desUserDBPath = self:_getDBPath(desUserId) local srcRoleDBPath = self:_getDBPath(srcUserId, srcRoleId) local desRoleDBPath = self:_getDBPath(desUserId, desRoleId) local srcUserDBFilePath = srcUserDBPath .. self.userDBName local desUserDBFilePath = desUserDBPath .. self.userDBName local srcRoleDBFilePath = srcRoleDBPath .. self.roleDBName local desRoleDBFilePath = desRoleDBPath .. self.roleDBName if not CS.LuaHelper.IsFileExists(srcUserDBFilePath) then printInfo(self.logTag, "copyUserDB, srcUserDBPath is not exist %s", srcUserDBFilePath) return end if not CS.LuaHelper.IsFileExists(desUserDBFilePath) then printInfo(self.logTag, "copyUserDB, desUserDBPath is not exist %s", desUserDBFilePath) return end if not CS.LuaHelper.IsFileExists(srcRoleDBFilePath) then printInfo(self.logTag, "copyRoleDB, srcRoleDBPath is not exist %s", srcRoleDBFilePath) return end if CS.LuaHelper.IsFileExists(desRoleDBFilePath) then printError(self.logTag, "copyRoleDB, desRoleDBPath is exist %s", desRoleDBFilePath) return end local ret, errorMsg = xpcall( function() if not Directory.Exists(desRoleDBPath) then Directory.CreateDirectory(desRoleDBPath) end CS.LuaHelper.CopyFile(srcUserDBFilePath, desUserDBFilePath) CS.LuaHelper.CopyFile(srcRoleDBFilePath, desRoleDBFilePath) Directory.Delete(srcUserDBPath, true) end, function() printInfo(self.logTag, debug.traceback()) end ) if not ret then printError(self.logTag, "copyRoleDB, 数据库拷贝失败 ret:%s errorMsg:%s", ret, errorMsg) return end printInfo(self.logTag, "copyRoleDB, 数据库拷贝成功") end ---获取所有的 table function DBMgr:getDBTableList() if not self.dbTableListDirty then return self.dbTableList end self.dbTableList = {} for i, dbTable in ipairs(self.userDBTableList or {}) do table.insert(self.dbTableList, dbTable) end for i, dbTable in ipairs(self.roleDBTableList or {}) do table.insert(self.dbTableList, dbTable) end for i, dbTable in ipairs(self.deviceDBTableList or {}) do table.insert(self.dbTableList, dbTable) end self.dbTableListDirty = false return self.dbTableList end ---根据 table name 获取 table function DBMgr:getDBTableByName(name) local dbTableList = self:getDBTableList() for _, db in ipairs(dbTableList) do if name == db.name then return db end end end ---删除数据库 function DBMgr:removeCurDB(cb) if self.userDB then self:closeCurUserDB() self.userDB:remove() end if self.roleDB then self:closeCurRoleDB() self.roleDB:remove() end if self.deviceDB then self.deviceDB:remove() end if cb then cb() end end fishingreslink return { --BASIC --ASSET cage = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/Cage.prefab", 0, 0}, mine = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/Mine.prefab", 0, 0}, fisherman_character = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/FishermanCharacter.prefab", 0, 0}, piranha = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/Piranha.prefab", 0, 0}, sword_fish = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/Swordfish.prefab", 0, 0}, fishing_game_map = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/map/fishingGameMap.prefab", 0, 0}, fish = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/Fish.prefab", 0, 0}, fish_600001 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600001.prefab", 0, 0}, fish_600002 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600002.prefab", 0, 0}, fish_600003 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600003.prefab", 0, 0}, fish_600004 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600004.prefab", 0, 0}, fish_600005 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600005.prefab", 0, 0}, fish_600006 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600006.prefab", 0, 0}, fish_600007 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600007.prefab", 0, 0}, fish_600008 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600008.prefab", 0, 0}, fish_600009 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600009.prefab", 0, 0}, fish_600010 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600010.prefab", 0, 0}, fish_600011 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600011.prefab", 0, 0}, fish_600012 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600012.prefab", 0, 0}, fish_600013 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600013.prefab", 0, 0}, fish_600014 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600014.prefab", 0, 0}, fish_600015 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_600015.prefab", 0, 0}, fish_601001 = {"Assets/AssetsPackage/Res/modules/fishing/map/prefab/fishing/fish_601001.prefab", 0, 0}, } GuideCfgParse--[[ 引导配置解析 author:{zhangpeng} time:2025-06-12 12:27:43 ]] local GuideCfgData = require("data/config/guideCfg") local GuideCfgParse = defClassStatic("GuideCfgParse") local LOGTAG = "GuideCfgParse" local guide_list = {} local function initAllIds() for k, v in pairs(GuideCfgData) do table.insert(guide_list, v.guideId) end end function GuideCfgParse:init() initAllIds() end function GuideCfgParse:getData() return GuideCfgData end function GuideCfgParse:getGuideCfg(id) for _, v in pairs(GuideCfgData) do if v.guideId == id then return v end end return nil end -- 重新组装一下数据,根据times字段,把对话内容分在不同的表中 local guide_list_by_times = {} function GuideCfgParse:getGuideCfgList(guideId) local list = {} for _, v in pairs(GuideCfgData) do if v.guideId == guideId then table.insert(list, v) end end return list end -- 获取对话id function GuideCfgParse:getDialogId(guideId) for _, v in pairs(GuideCfgData) do if v.guideId == guideId then return v.dislogNum end end return nil end -- 获取对话内容 function GuideCfgParse:getDialogContent(guideId) for _, v in pairs(guide_list) do if v.guideId == guideId then local dialogNum = v.dislogNum if dialogNum then local content = StorydialogCfgParse:getDialogContent(dialogNum) if content then return content end end end end return nil end GuideCfgParse:init()CuisineUserData--- 用户数据 菜品 ---@class CuisineUserData : LuaClass local CuisineUserData = defClass("CuisineUserData") -- 构造函数 function CuisineUserData:ctor() self:init() end -- 初始化 function CuisineUserData:init() -- 指定菜品的状态 self.cuisine_state = {} -- {id: state} state: 1未解锁 2已解锁 3已学习 -- 指定菜品的等级 self.cuisine_grade = {} -- {id: grade} -- 指定菜品销量 self.cuisine_sales = {} -- {id: sales} end -- 重置数据 function CuisineUserData:resetData() -- 默认学习第一个菜品 self:setCuisineState(200001, 3, true) end -- 加载数据 function CuisineUserData:load() -- 如果是新玩家,重置数据 if UserDataMgr.isNewPlayer then self:resetData() else self:loadFromLocal() end self:onLoadComplete() end -- 从本地加载数据 function CuisineUserData:loadFromLocal() self.cuisine_state = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.CUISINES_STATE, "{}"))) self.cuisine_grade = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.CUISINES_GRADE, "{}"))) self.cuisine_sales = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.CUISINES_SALES, "{}"))) end -- 加载完成回调 function CuisineUserData:onLoadComplete() -- 没有解锁条件的对象默认解锁 local data = CookBookCfgParse:getData() for _, v in pairs(data) do if v.unlockCondId == -1 and (self:getCuisineState(v.id) < 2) then self:setCuisineState(v.id, 2, true) -- 默认解锁 end end for _, v in pairs(data) do if v.buyCond == -1 and (self:getCuisineState(v.id) < 3) then self:setCuisineState(v.id, 3, true) -- 默认已学习 end end self:save() end -- 保存数据 function CuisineUserData:save() self:saveToLocal() end -- 数据保存到本地 function CuisineUserData:saveToLocal() PlayerPrefsMgr:setString(StrogeKeyDef.CUISINES_STATE, json.encode(PlayerPrefsMgr:idToString(self.cuisine_state))) PlayerPrefsMgr:setString(StrogeKeyDef.CUISINES_GRADE, json.encode(PlayerPrefsMgr:idToString(self.cuisine_grade))) PlayerPrefsMgr:setString(StrogeKeyDef.CUISINES_SALES, json.encode(PlayerPrefsMgr:idToString(self.cuisine_sales))) PlayerPrefsMgr:save() end -- 获取菜品状态 function CuisineUserData:getCuisineState(cuisineId) return self.cuisine_state[cuisineId] or 1 end -- 设置菜品状态 function CuisineUserData:setCuisineState(cuisineId, state, refuseSave) self.cuisine_state[cuisineId] = state if not refuseSave then self:save() end return state end -- 获取菜品销量 function CuisineUserData:getCuisineSales(cuisineId) return self.cuisine_sales[cuisineId] or 0 end -- 设置菜品销量 function CuisineUserData:setCuisineSales(cuisineId, value, refuseSave) self.cuisine_sales[cuisineId] = value if not refuseSave then self:save() end return value end -- 增加菜品销量 function CuisineUserData:addCuisineSales(cuisineId, value, refuseSave) local curr = self:getCuisineSales(cuisineId) + value self:setCuisineSales(cuisineId, curr, refuseSave) return curr end -- 获取菜品等级 function CuisineUserData:getCuisineGrade(cuisineId) return self.cuisine_grade[cuisineId] or 1 end -- 设置菜品等级 function CuisineUserData:setCuisineGrade(cuisineId, value, refuseSave) self.cuisine_grade[cuisineId] = value if not refuseSave then self:save() end return value end -- 增加菜品等级 function CuisineUserData:addCuisineGrade(cuisineId, value, refuseSave) local curr = self:getCuisineGrade(cuisineId) + value self:setCuisineGrade(cuisineId, curr, refuseSave) return curr end return CuisineUserDataluaj\  local luaj = {} local callStaticMethod = LuaJavaBridge.callStaticMethod local queue_action = require("common/platform/ext/queue_action") luaj.S = "Ljava/lang/String;" function luaj.getArgsSig(args, retSig) if not Device.isAndroid() then return end local sig = {"("} for i, v in ipairs(args) do local t = type(v) if t == "number" then if v == math.floor(v) then sig[#sig + 1] = "I" else sig[#sig + 1] = "F" end elseif t == "boolean" then sig[#sig + 1] = "Z" elseif t == "function" then sig[#sig + 1] = "I" else sig[#sig + 1] = luaj.S end end sig[#sig + 1] = ")" sig[#sig + 1] = retSig or "V" return table.concat(sig) end local LUAJ_ERR_CODE = { LUAJ_ERR_OK = 0, LUAJ_ERR_TYPE_NOT_SUPPORT = -1, LUAJ_ERR_INVALID_SIGNATURES = -2, LUAJ_ERR_METHOD_NOT_FOUND = -3, LUAJ_ERR_EXCEPTION_OCCURRED = -4, LUAJ_ERR_VM_THREAD_DETACHED = -5, LUAJ_ERR_VM_FAILURE = -6, LUAJ_ERR_CLASS_NOT_FOUND = -7 } local LUAJ_ERR_NAME = {} for k, v in pairs(LUAJ_ERR_CODE) do LUAJ_ERR_NAME[v] = k end -- 调用java类的接口。 -- @function [parent=#luaj] callStaticMethod -- @param string className java类名 -- @param string methodName java类静态方法名 -- @param table args java类静态方法所需要的各种参数 数组 -- @param string sig java类方法的签名 -- @return boolean#boolean ret (return value: bool) ok, mixed ret ok为是否调用成功, ok为true时,ret为java方法的返回值,ok为false时,ret为出错原因 function luaj.callStaticMethod(className, methodName, args, sig) args = args or {} sig = sig or luaj.getArgsSig(args) --需要在java层做QueueAction处理 --这里做QueueAction时机晚了,已经回到了主线程 -- queue_action.table_process(args) -- printInfo("luaj", 'callStaticMethod("%s",\t"%s",\targs,\t"%s"', className, methodName, sig) local ok, ret, javaException = callStaticMethod(className, methodName, args, sig) if not ok then error( string.format("luaj error:%s,class:%s,method:%s,javaException:%s,sig:%s", tostring(LUAJ_ERR_NAME[ret]), tostring(className), tostring(methodName), tostring(javaException), tostring(sig)) ) end return ok, ret end return luaj uActionZ --[[ author:{zhangpeng} time:2022-08-15 17:40:45 ]] local api = CS.wtween.wtweenArgAPI local dynamic_api = CS.wtween.MoveToObjectArgAPI local Vector3 = CS.UnityEngine.Vector3 local SpriteFadeToAPI = CS.wtween.SpriteFadeToAPI local easeType = { Linear = CS.wtween.TweenEase.EaseType.Linear, BounceOut = CS.wtween.TweenEase.EaseType.BounceOut, BounceIn = CS.wtween.TweenEase.EaseType.BounceIn, BounceInOut = CS.wtween.TweenEase.EaseType.BounceInOut, BackIn = CS.wtween.TweenEase.EaseType.BackIn, BackOut = CS.wtween.TweenEase.EaseType.BackOut, BackInOut = CS.wtween.TweenEase.EaseType.BackInOut, Bezier2 = CS.wtween.TweenEase.EaseType.Bezier2, } local function exScaleTo(t,arg1,arg2) local t1 = type(arg1) if t1 == "number" then if arg2 and type(arg2) == "number" then return api.ScaleTo(t,Vector3(arg1,arg2,1)) else return api.ScaleTo(t,Vector3(arg1,arg1,1)) end else return api.ScaleTo(t,arg1) end end local ua = { Delay = api.Delay, MoveTo = api.MoveTo, MoveToObject = dynamic_api.MoveToObject, ScaleTo = exScaleTo, RotateTo = api.RotateTo, BezierTo = CS.wtween.CurveAPI.BezierTo, BezierToNode = CS.wtween.CurveAPI.BezierToNode, cb = api.CallFunc, Destroy = api.Destroy, SetParent = api.SetParent, Sequence = api.Sequence, Spawn = api.Spawn, Step = api.Step, Repeat = api.Repeat, FadeTo = api.FadoTo, RepeatForever = api.RepeatForever, SpriteFadeTo = SpriteFadeToAPI.SpriteFadeTo, Tween = api.Tween,--自定义 ease = { Linear = function(action) return api.Ease(easeType.Linear,action) end, BounceOut = function(action) return api.Ease(easeType.BounceOut,action) end, BounceIn = function(action) return api.Ease(easeType.BounceIn,action) end, BounceInOut = function(action) return api.Ease(easeType.BounceInOut,action) end, BackOut = function(action,s) api.Ease(easeType.BackOut,action) if s then local easeFunc = action.tween.easeFunc easeFunc.s = s end return action end, BackIn = function(action) return api.Ease(easeType.BackIn,action) end, BackInOut = function(action) return api.Ease(easeType.BackInOut,action) end, Bezier2 = function(action,ctrl1,ctrl2) local action = api.Ease(easeType.Bezier2,action) local easeFunc = action.tween.easeFunc return action end, }, Ease = api.Ease, } rawset(_ENV,"ua",ua) FishingMainUI"-- 捕鱼 主界面 local FishingMainUI, super = defClass("FishingMainUI", UILayer) -- view require("modules/ui/fishingui/FishingControlArea") require("modules/ui/fishingui/FishingPropCell") require("modules/ui/fishingui/FishingPauseUI") require("modules/ui/fishingui/FishingFishListUI") -- component local TMProUGUI = CS.TMPro.TextMeshProUGUI local RectTransform = CS.UnityEngine.RectTransform -- 构造函数 function FishingMainUI:ctor() super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/fishingui/fishinguireslink") end -- 当页面加载 function FishingMainUI:onLoad() self.ui = GameObject.Instantiate(self.R.fishing_main_ui) self:addChild(self.ui) self:initUI() self:showUI() end -- 初始化UI元素 function FishingMainUI:initUI() self.start_page = self.ui:Seek("start_page") self.game_page = self.ui:Seek("game_page") self.rawImage = self.ui:Seek("raw_image")[UGUI.RawImage] self.propTime = self.ui:Seek("prop_time") self.propTimeText = self.propTime[TMProUGUI] self.sceneWidth = CS.UnityEngine.Screen.width self.sceneHeight = CS.UnityEngine.Screen.height self.canvasSize = UILayerUtil.canvas.transform.sizeDelta self:setCameraToRawImage(FishingGameMgr.cameraCtl.camera, self.rawImage) self:initStartPage() self:initGamePage() end -- 创建 RenderTexture 并设置到相机和 RawImage function FishingMainUI:setCameraToRawImage(camera, rawImage) -- local width = CS.UnityEngine.Screen.width local height = CS.UnityEngine.Screen.height -- 创建 RenderTexture local renderTexture = CS.UnityEngine.RenderTexture(width, height, 24) -- 设置相机的目标纹理 camera.targetTexture = renderTexture -- 设置 RawImage 的纹理为相机的 RenderTexture rawImage.texture = renderTexture end -- 初始化开始页 function FishingMainUI:initStartPage() self.skin_btn = self.ui:Seek("skin_btn") self.rank_btn = self.ui:Seek("rank_btn") self.play_btn = self.ui:Seek("play_btn") self.play_ad_btn = self.ui:Seek("play_ad_btn") self.play_share_btn = self.ui:Seek("play_share_btn") self.free_value = self.ui:Seek("free_value")[TMProUGUI] self.exit_btn = self.ui:Seek("exit_btn") self.coin_text = self.ui:Seek("coin_ui"):Seek("num") self.star_text = self.ui:Seek("star_ui"):Seek("num") -- 皮肤 util.ugui.addClickEvent(self.skin_btn, function() self:showSkinUI() end) -- 排名 util.ugui.addClickEvent(self.rank_btn, function() self:showRankUI() end) -- 开始游戏 util.ugui.addClickEvent(self.play_btn, function() self:gameStart() end) util.ugui.addClickEvent(self.play_ad_btn, function() self:gameStartAd() end) util.ugui.addClickEvent(self.play_share_btn, function() self:gameStartShare() end) -- 退出游戏 util.ugui.addClickEvent(self.exit_btn, function() FishingGameMgr:gameExit() end) end -- 初始化游戏页 function FishingMainUI:initGamePage() -- 初始化UI元素 local control_area = self.ui:Seek("control_area") self.controlArea = FishingControlArea.new(control_area) -- 能量条 self.energy = self.ui:Seek("energy_bar") self.energyRectTransform = self.energy[RectTransform] self.energy_value = self.energy:Seek("value")[UGUI.Image] self.energy_hazard_value = self.energy:Seek("hazard_value") -- 倒计时 self.time = self.ui:Seek("time")[TMProUGUI] -- 暂停游戏 self.pause_btn = self.ui:Seek("pause_btn") util.ugui.addClickEvent(self.pause_btn, function() FishingGameMgr:gamePause() -- 暂停窗口 FishingPauseUI.new():show():showMask():enableCloseWhenClickMask(function() FishingGameMgr:gameContinue() end) end) -- 眩晕按钮 self.dizzy_btn = FishingPropCell.new(self.ui:Seek("dizzy_btn"), FishingConst.PropType.Dizzy) -- 能量按钮 self.energy_btn = FishingPropCell.new(self.ui:Seek("energy_btn"), FishingConst.PropType.Energy) -- 护盾按钮 self.shield_btn = FishingPropCell.new(self.ui:Seek("shield_btn"), FishingConst.PropType.Shield) -- test self.energy_hazard_value:SetActive(false) end -- 展示UI function FishingMainUI:showUI() -- 显示UI元素 local isPlaying = FishingGameMgr.isPlaying self.start_page:SetActive(not isPlaying) self.game_page:SetActive(isPlaying) if not isPlaying then self:showStartPage() else self:showGamePage() end end -- 游戏开始 function FishingMainUI:start() -- 展示UI元素 self.energy:SetActive(true) -- 开启触摸 self.controlArea:setTouchable(true) self.dizzy_btn:updateUsesRemaining() self.energy_btn:updateUsesRemaining() self.shield_btn:updateUsesRemaining() end -- 游戏暂停 function FishingMainUI:pause() -- 开启触摸 self.controlArea:setTouchable(false) end -- 游戏继续 function FishingMainUI:continue() -- 开启触摸 self.controlArea:setTouchable(true) end -- 游戏结束 function FishingMainUI:over() -- 隐藏UI元素 self.energy:SetActive(false) -- 关闭触摸 self.controlArea:setTouchable(false) end -- 时间更新 function FishingMainUI:timeUpdate(deltaTime) local pos = FishingGameMgr.character.transform.position local sp = FishingGameMgr.cameraCtl.camera:WorldToScreenPoint(pos) sp.x = sp.x * self.canvasSize.x / self.sceneWidth sp.y = sp.y * self.canvasSize.y / self.sceneHeight self.energyRectTransform.anchoredPosition = Vector2(sp.x, sp.y) end -- 展示开始页 function FishingMainUI:showStartPage() self.play_btn:SetActive(FishingGameMgr:getPlayIsFree()) self.play_ad_btn:SetActive(FishingGameMgr:getPlayIsVideo()) self.play_share_btn:SetActive(FishingGameMgr:getPlayIsShare()) local count = FishingGameMgr.userData:getFishingGameCountToday() if count > FishingConst.FreeGameCountLimit then count = 0 else count = FishingConst.FreeGameCountLimit - count end self.free_value.text = string.format("今日免费%d/%d次", count, FishingConst.FreeGameCountLimit) self.coin_text[TMProUGUI].text = CurrencyMgr:getCoin() self.star_text[TMProUGUI].text = CurrencyMgr:getStar() end -- 展示游戏页 function FishingMainUI:showGamePage() end -- 获取ui在世界中的大小 function FishingMainUI:getWorldSize() self.width = self.controlArea.rectTransform.rect.width * self.controlArea.rectTransform.lossyScale.x self.height = self.controlArea.rectTransform.rect.height * self.controlArea.rectTransform.lossyScale.y return {width = self.width, height = self.height} end -- 显示能量条 function FishingMainUI:showEnergy(energy, energyMax) self.energy_value.fillAmount = energy / energyMax end -- 显示倒计时 function FishingMainUI:showCountDown(time) self.time.text = string.format("%02d:%02d", math.floor(time / 60), time % 60) end -- 开始游戏 function FishingMainUI:gameStart() self.start_page:SetActive(false) if FishingGameMgr.openingAnimation then local mapSize = FishingGameMgr.map:getWorldSize() FishingGameMgr.openingAnimation:play(Vector3(0, -mapSize.height + 10, 0), Vector3(0, -mapSize.height / 2, 0)) return end --FishingGameMgr:gameStart() end -- 广告开始游戏 function FishingMainUI:gameStartAd() self:gameStart() end -- 分享开始游戏 function FishingMainUI:gameStartShare() self:gameStart() end -- function FishingMainUI:updateFishingPropCount(propType, count) if propType == FishingConst.PropType.Dizzy then self.dizzy_btn:setCount(count) elseif propType == FishingConst.PropType.Energy then self.energy_btn:setCount(count) elseif propType == FishingConst.PropType.Shield then self.shield_btn:setCount(count) end end -- function FishingMainUI:showSkinUI() end -- function FishingMainUI:showRankUI() end -- 展示道具时间 function FishingMainUI:showPropTime(time) self.propTime:SetActive(true) self.propTimeText.text = string.format("道具剩余时间%d秒", time) end -- 更新道具时间 function FishingMainUI:updatePropTime(time) if not self.propTime.activeSelf then return end self.propTimeText.text = string.format("道具剩余时间%d秒", math.floor(time)) if time <= 0 then self:hidePropTime() end end -- 隐藏道具时间 function FishingMainUI:hidePropTime() self.propTime:SetActive(false) end return FishingMainUIOrderDishesMgrb#--[[ 点菜管理 author:{zhangpeng} time:2025-05-15 20:52:08 ]] local OrderDishesMgr = defClassStatic("OrderDishesMgr") local LOGTAG = "OrderDishesMgr" function OrderDishesMgr:init() printInfo(LOGTAG, "------ 点菜管理 初始化 ------") -- 菜类订单队列 self.dishOrderQueue = {} -- 饮料类订单队列 self.drinkOrderQueue = {} -- 待确认订单队列(需要点击头顶气泡确认) self.pendingOrderQueue = {} -- 订单ID到订单对象的映射 self.orderIdToOrder = {} end -- 添加订单 function OrderDishesMgr:addOrder(orderInfo) printInfo(LOGTAG, string.format("添加订单,订单ID:%s", orderInfo:getOrderId())) -- 保存订单ID到订单对象的映射 self.orderIdToOrder[orderInfo:getOrderId()] = orderInfo local cuisineType = orderInfo:getCuisineType() -- 根据类型分配到不同队列 if cuisineType == CookingConst.CookingType.dish then self:addDishOrder(orderInfo) elseif cuisineType == CookingConst.CookingType.drink then self:addDrinkOrder(orderInfo) else printError(LOGTAG, string.format("未知的菜品类型:%s", cuisineType)) end end -- 添加待确认订单 function OrderDishesMgr:addPendingOrder(orderInfo) printInfo(LOGTAG, string.format("添加待确认订单,订单ID:%s", orderInfo:getOrderId())) -- 保存订单ID到订单对象的映射 (使用类名直接访问静态字段) local orderId = orderInfo:getOrderId() self.orderIdToOrder[orderId] = orderInfo -- 加入待确认队列 table.insert(self.pendingOrderQueue, orderInfo) end -- 确认处理订单(由点击头顶气泡触发) function OrderDishesMgr:confirmProcessOrder(orderId) -- 查找订单 local orderInfo = self.orderIdToOrder[orderId] if not orderInfo then printError(LOGTAG, string.format("找不到订单ID:%s", orderId)) return false end -- 判断所选菜品是否解锁 if not CuisineMgr:isCuisineLearned(orderInfo.cuisineId) then printWarn(LOGTAG, string.format("顾客[%s-%s]的喜爱食物%s未解锁,无法点餐,显示生气气泡", self.customerCfgId, self.roleUniqueId, orderInfo.cuisineId)) -- 显示生气气泡,和手动点餐一样的处理 orderInfo.customer:showNoFoodBubble() -- 从待确认队列中移除 for i, order in ipairs(self.pendingOrderQueue) do if order:getOrderId() == orderId then table.remove(self.pendingOrderQueue, i) break end end return true end -- 确认处理订单 local success = orderInfo:confirmProcess() if success then -- 从待确认队列中移除 for i, order in ipairs(self.pendingOrderQueue) do if order:getOrderId() == orderId then table.remove(self.pendingOrderQueue, i) break end end return true end return false end function OrderDishesMgr:autoOrder() if #self.pendingOrderQueue > 0 then self:confirmProcessOrder(self.pendingOrderQueue[1].orderId) end end -- 根据订单ID获取订单 function OrderDishesMgr:getOrderById(orderId) return self.orderIdToOrder[orderId] end -- 添加菜类订单 function OrderDishesMgr:addDishOrder(orderInfo) printInfo(LOGTAG, string.format("添加菜类订单,订单ID:%s", orderInfo:getOrderId())) -- 查找可用的菜类灶台 local availableBench = self:findAvailableDishBench() if availableBench then -- 有可用灶台,直接开始制作 self:startCooking(orderInfo, availableBench) else -- 无可用灶台,加入队列等待 table.insert(self.dishOrderQueue, orderInfo) printInfo(LOGTAG, string.format("菜类灶台全忙,订单排队等待,队列长度:%s", #self.dishOrderQueue)) end -- 如果有厨师,启动厨师工作 if EmployeMgr:hasChefEmploye() then EmployeMgr:startChefEmployeWork() end end -- 添加饮料类订单 function OrderDishesMgr:addDrinkOrder(orderInfo) printInfo(LOGTAG, string.format("添加饮料类订单,订单ID:%s", orderInfo:getOrderId())) -- 查找可用的饮料类灶台 local availableBench = self:findAvailableDrinkBench() if availableBench then -- 有可用灶台,直接开始制作 self:startCooking(orderInfo, availableBench) else -- 无可用灶台,加入队列等待 table.insert(self.drinkOrderQueue, orderInfo) printInfo(LOGTAG, string.format("饮料类灶台全忙,订单排队等待,队列长度:%s", #self.drinkOrderQueue)) end -- 如果有厨师,启动厨师工作 if EmployeMgr:hasChefEmploye() then EmployeMgr:startChefEmployeWork() end end -- 查找可用的菜类灶台 function OrderDishesMgr:findAvailableDishBench() local cookingBenches = CookingBenckMgr.cookingBenches for _, bench in pairs(cookingBenches) do if bench:getCookingType() == CookingConst.CookingType.dish then if bench:hasIdleStove() then return bench end end end return nil end -- 查找可用的饮料类灶台 function OrderDishesMgr:findAvailableDrinkBench() local cookingBenches = CookingBenckMgr.cookingBenches for _, bench in pairs(cookingBenches) do if bench:getCookingType() == CookingConst.CookingType.drink then if bench:hasIdleStove() then return bench end end end return nil end -- 开始烹饪 function OrderDishesMgr:startCooking(orderInfo, bench) printInfo(LOGTAG, string.format("开始制作订单,订单ID:%s", orderInfo:getOrderId())) -- 获取空闲炉子 local stove = bench:getIdleStove() if stove then -- 设置订单 stove.orderInfo = orderInfo -- 如果顾客还有头顶气泡,则移除气泡 if orderInfo.customer and not orderInfo.bubbleRemoved then orderInfo.customer:removeOrderCuisineBubble() orderInfo.bubbleRemoved = true end -- 开始制作 stove:cook(function() -- 制作完成后回调 self:onCookingFinished(orderInfo, bench, stove) end) else printError(LOGTAG, "找不到空闲炉子,无法开始制作") end end -- 烹饪完成回调 function OrderDishesMgr:onCookingFinished(orderInfo, bench, stove) printInfo(LOGTAG, string.format("订单制作完成,订单ID:%s", orderInfo:getOrderId())) -- 上菜 self:serveOrder(orderInfo) -- 炉子空闲,可以处理下一个订单 stove:setWorking(false) -- 检查对应类型的队列,处理下一个订单 local nextOrder = nil if bench:getCookingType() == CookingConst.CookingType.dish then -- 菜类队列 nextOrder = table.remove(self.dishOrderQueue, 1) else -- 饮料类队列 nextOrder = table.remove(self.drinkOrderQueue, 1) end -- 如果有下一个订单,立即开始制作 if nextOrder then self:startCooking(nextOrder, bench) end end -- 上菜 function OrderDishesMgr:serveOrder(orderInfo) printInfo(LOGTAG, string.format("上菜,订单ID:%s", orderInfo:getOrderId())) -- 获取座位 local seat = orderInfo:getSeat() if seat then -- 确保订单和顾客引用有效 if not orderInfo.customer then printError(LOGTAG, "订单没有关联顾客,无法上菜") return end -- 通知座位上菜 seat:giveCuisine(orderInfo) else printError(LOGTAG, "订单没有关联座位,无法上菜") end -- 设置订单状态为完成 orderInfo:setStatus(OrderConst.Status.completed) -- 记录菜品销量 CuisineMgr:addCuisineSales(orderInfo:getCuisineId(), 1) -- 从映射表中移除订单 self.orderIdToOrder[orderInfo:getOrderId()] = nil end -- 检查等待队列 function OrderDishesMgr:checkWaitingQueues() -- 检查菜类订单队列 if #self.dishOrderQueue > 0 then local availableBench = self:findAvailableDishBench() if availableBench then local orderInfo = table.remove(self.dishOrderQueue, 1) self:startCooking(orderInfo, availableBench) end end -- 检查饮料类订单队列 if #self.drinkOrderQueue > 0 then local availableBench = self:findAvailableDrinkBench() if availableBench then local orderInfo = table.remove(self.drinkOrderQueue, 1) self:startCooking(orderInfo, availableBench) end end end -- 获取待确认订单列表 function OrderDishesMgr:getPendingOrders() return self.pendingOrderQueue end -- 更新 function OrderDishesMgr:update() -- 每帧检查等待队列 self:checkWaitingQueues() end return OrderDishesMgr ExtendScenelocal SceneCls = HackCSharpClass(CS.UnityEngine.SceneManagement.Scene) SceneCls.GetGameRoot = function(self) local rootGos = self:GetRootGameObjects() for i = 0,rootGos.Length - 1,1 do local go = rootGos[i] if go.name == "GameRoot" then return go end end return rootGos[0] end SceneCls.SeekByXPath = function(self,xpath) local rootGos = self:GetRootGameObjects() local splits = string.split(xpath,"/") for i = 0,rootGos.Length - 1,1 do local go = rootGos[i] if go.name == splits[1] then table.remove(splits,1) return go:SeekByXPath(table.concat(splits,"/")) end end endmainrequire("modules/building/restaurant/passivity/PassivityConst") require("modules/building/restaurant/passivity/PassivityDesk") require("modules/building/restaurant/passivity/PassivityDeskMgr") CartoonUI--[[ 四格漫画通用界面 author:{zhangpeng} time:2025-07-23 09:51:10 ]] local CartoonUI, super = defClass("CartoonUI", UILayer) local TMPUGUI = CS.TMPro.TextMeshProUGUI local LOGTAG = "CartoonUI" -- 动画状态名称常量 local ANIM_STATES = { PAGE_1 = "page_1", PAGE_2 = "page_2", PAGE_3 = "page_3", PAGE_4 = "page_4" } local end_page_name = "page_end" function CartoonUI:ctor() super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/cartoonui/cartoonuireslink") self.currentPage = 1 -- 当前页面索引 self.maxPageNum = 4 -- 暂时写死,实际需要根据漫画格子数量 self.auto_play = true end function CartoonUI:onLoad() self.ui = GameObject.Instantiate(self.R.page_1) self:addChild(self.ui) self:initUI() end function CartoonUI:initUI() self.touchMask = self.ui:Seek("mask") util.ugui.addButtonClickEvent(self.touchMask, function() -- 如果全部播完,则关闭界面 if self.currentPage >= self.maxPageNum then self:close() return end self.fastStop = true self.auto_play = false self:playEndingPage() end) self.anim = self.ui:Seek("anim") self.animator = self.anim:GetComponent(typeof(CS.UnityEngine.Animator)) if not self.animator then printInfo(LOGTAG, "Animator component not found on anim object!") return end Event.add(self.anim, Event.OnAnimationEvent, function(event) printInfo(LOGTAG, "Animation event received: " .. tostring(event.stringParameter)) if event.stringParameter == "onPageAnimComplete" then self:onPageAnimationComplete() end end) -- 初始化播放第一页动画 self:playPage(1) end --[[ 播放指定页面的动画 @param pageIndex: 页面索引 (1-4) ]] function CartoonUI:playPage(pageIndex) if not self.animator then return end if pageIndex < 1 or pageIndex > self.maxPageNum then return end local animStateName = string.format("page_%d", pageIndex) if not animStateName then return end self.currentPage = pageIndex self.animator:Play(animStateName) end -- 播放结尾漫画 function CartoonUI:playEndingPage() self.animator:Play(end_page_name) self.currentPage = self.maxPageNum end --[[ 播放下一页动画 ]] function CartoonUI:playNextPage() local nextPage = self.currentPage + 1 if nextPage > 4 then -- 不循环播放,停止动画 self:stopAnimation() return end self:playPage(nextPage) end --[[ 播放上一页动画 ]] function CartoonUI:playPrevPage() local prevPage = self.currentPage - 1 if prevPage < 1 then prevPage = 4 -- 循环播放 end self:playPage(prevPage) end --[[ 获取当前播放的页面索引 @return: 当前页面索引 (1-4) ]] function CartoonUI:getCurrentPage() return self.currentPage end --[[ 检查动画是否正在播放 @param pageIndex: 页面索引 (1-4),可选,不传则检查当前页面 @return: 是否正在播放 ]] function CartoonUI:isPlaying(pageIndex) if not self.animator then return false end local checkPage = pageIndex or self.currentPage local animStateName = ANIM_STATES["PAGE_" .. checkPage] if not animStateName then return false end local currentStateInfo = self.animator:GetCurrentAnimatorStateInfo(0) return currentStateInfo:IsName(animStateName) end --[[ 停止当前动画播放 ]] function CartoonUI:stopAnimation() if self.animator then self.animator.enabled = false end end --[[ 恢复动画播放 ]] function CartoonUI:resumeAnimation() if self.animator then self.animator.enabled = true end end --[[ 页面动画播放完毕回调 ]] function CartoonUI:onPageAnimationComplete() printInfo(LOGTAG, "Page " .. self.currentPage .. " animation completed") -- 如果自动播放,则自动切换到下一页 if self.auto_play then self:playNextPage() end end --[[ 清理资源 ]] function CartoonUI:onDestroy() -- AnimEventListener会自动清理,不需要手动清理 super.onDestroy(self) end return CartoonUI ParticleUtilm--[[ author:{粒子播放工具} time:2025-04-01 10:45:41 ]] local ParticleUtil = {} function ParticleUtil:play(node) local particles = node.transform:GetComponentsInChildren(typeof(CS.UnityEngine.ParticleSystem)) for _, particle in cs_ipairs(particles) do particle:Stop() particle:Play() end end -- 循环播放粒子 function ParticleUtil:playLoop(node) local particles = node.transform:GetComponentsInChildren(typeof(CS.UnityEngine.ParticleSystem)) for _, particle in cs_ipairs(particles) do particle.main.loop = true particle:Stop() particle:Play() end end function ParticleUtil:stop(node) local particles = node.transform:GetComponentsInChildren(typeof(CS.UnityEngine.ParticleSystem)) for _, particle in cs_ipairs(particles) do particle:Stop() end end return ParticleUtil config_main.-- 解析 require("data/config/parse/TextCfgParse") require("data/config/parse/BuildingCfgParse") require("data/config/parse/CustomerCfgParse") require("data/config/parse/EmployeCfgParse") require("data/config/parse/CookBookCfgParse") require("data/config/parse/BuildingTypesCfgParse") require("data/config/parse/BuyCommonCfgParse") require("data/config/parse/EmployeLevelUpCfgParse") require("data/config/parse/EmployeeFunCfgParse") require("data/config/parse/EmployeSkinCfgParse") require("data/config/parse/CuisineUpgradeCfgParse") require("data/config/parse/TaskCfgParse") require("data/config/parse/TaskOrderCfgParse") require("data/config/parse/UnlockCommonCfgParse") require("data/config/parse/StorydialogCfgParse") require("data/config/parse/HelpCfgParse") require("data/config/parse/CustomerSpecialCfgParse") require("data/config/parse/CustomerTagCfgParse") require("data/config/parse/CustomerFunCfgParse") require("data/config/parse/CustomerParamCfgParse") require("data/config/parse/GuideCfgParse") require("data/config/parse/CommonBonusCfgParse") require("data/config/parse/CommonBonusTypeParse") require("data/config/parse/VendorCfgParse") require("data/config/parse/FishCfgParse") require("data/config/parse/MusicBbqCustomerCfgParse") require("data/config/parse/SceneCfgParse") require("data/config/parse/FishingRewardCfgParse") require("data/config/parse/FishingGradeCfgParse") require("data/config/parse/GachaRewardCfgParse") require("data/config/parse/DollCfgParse") require("data/config/parse/BoothUnlockCfgParse") require("data/config/parse/FishSalesCfgParse")unlockCommonCfg\--[[ from file:解锁通用表.xlsx --]] local unlockCommonCfg = { [1] = { id = 130201, limitType = "star", value = 100, }, [2] = { id = 131601, limitType = "star", value = 100, }, [3] = { id = 230001, limitType = "star", value = 100, }, [4] = { id = 300101, limitType = "scene", value = 3, }, [5] = { id = 300102, limitType = "scene", value = 2, }, [6] = { id = 580001, limitType = "scene", value = 2, }, [7] = { id = 580002, limitType = "star", value = 30, }, } return unlockCommonCfg HelpDetailUI--- ---@class HelpDetailUI : UILayer local HelpDetailUI, super = defClass("HelpDetailUI", UILayer) function HelpDetailUI:ctor(helpCfg) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/helpui/helpuireslink") self.config = helpCfg end function HelpDetailUI:onLoad() self.ui = GameObject.Instantiate(self.R.help_detail_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end function HelpDetailUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.help_question = self.ui:Seek("help_question") self.help_answer = self.ui:Seek("help_answer") self.got_it_btn = self.ui:Seek("got_it_btn") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addButtonClickEvent(self.got_it_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) end function HelpDetailUI:showUI() self.help_question:GetComponent("TextMeshProUGUI").text = self.config.question self.help_answer:GetComponent("TextMeshProUGUI").text = self.config.answer end return HelpDetailUIfishingRewardCfg--[[ from file:捞鱼奖励.xlsx --]] local fishingRewardCfg = { [1] = { id = 610001, progress = 50, rewardNums = 1, reward_fish = 0, param_fish = {600005,600006,600007,600008,600009,600010}, count_fish = 5, reward_1 = -1, param_1 = -1, count_1 = -1, }, [2] = { id = 610002, progress = 70, rewardNums = 1, reward_fish = 0, param_fish = {600005,600006,600007,600008,600009,600010}, count_fish = 10, reward_1 = -1, param_1 = -1, count_1 = -1, }, [3] = { id = 610003, progress = 85, rewardNums = 1, reward_fish = 2, param_fish = {600011,600012,600013,600014,600015}, count_fish = 5, reward_1 = -1, param_1 = -1, count_1 = -1, }, [4] = { id = 610004, progress = 100, rewardNums = 2, reward_fish = 2, param_fish = {600011,600012,600013,600014,600015}, count_fish = 20, reward_1 = 1, param_1 = -1, count_1 = 100, }, } return fishingRewardCfg TaskOrderInfo--- ---@class TaskOrderInfo : LuaClass local TaskOrderInfo = defClass("TaskOrderInfo") function TaskOrderInfo:ctor(taskOrderId) self.id = taskOrderId self:init() end function TaskOrderInfo:init() self:initData() self:initStatus() end function TaskOrderInfo:initData() -- 获取订单任务配置 local taskCfg = TaskOrderCfgParse:getTaskOrderCfg(self.id) if not taskCfg then printError("TaskOrderInfo", "订单任务配置不存在: " .. self.id) return end -- 订单任务标题 self.title = taskCfg.title -- 订单任务描述 self.desc = taskCfg.desc -- 等待时间 self.timeLimit = taskCfg.time -- 订单任务奖励种数 self.rewardCount = taskCfg.rewardTypeCount -- 关联顾客id self.customerId = taskCfg.customerId -- 触发条件id列表 self.triggerType = taskCfg.triggerType -- 触发条件参数值列表 self.triggerValues = { [1] = taskCfg.triggerParam_1, [2] = taskCfg.triggerParam_2, } -- 剧情id列表 self.plotIds = { [1] = taskCfg.plot_1, [2] = taskCfg.plot_2, [3] = taskCfg.plot_3, } -- 订单任务奖励id列表 self.rewardIds = { [1] = taskCfg.rewardType_1, [2] = taskCfg.rewardType_2, [3] = taskCfg.rewardType_3, [4] = taskCfg.rewardType_4, } -- 订单任务奖励参数值列表 self.rewardValues = { [1] = taskCfg.rewardParam_1, [2] = taskCfg.rewardParam_2, [3] = taskCfg.rewardParam_3, [4] = taskCfg.rewardParam_4, } end function TaskOrderInfo:initStatus() -- 订单任务创建时间 进度 self.createTime = UserDataMgr.taskUserData.order:getTaskOrderCreateTime(self.id) self.reset = UserDataMgr.taskUserData.order:getTaskOrderIsReset(self.id) ---- 订单任务状态 1:进行中 2:已完成 3:已领取 4:已超时 --self.state = 1 end ----------基础---------- --- 获取订单任务id function TaskOrderInfo:getId() return self.id end --- 获取订单任务标题 function TaskOrderInfo:getTitle() return self.title end --- 获取订单任务描述 function TaskOrderInfo:getDesc() return self.desc end ----------时间---------- --- 设置订单任务剩余时间记录 ---@return number, number, number function TaskOrderInfo:getTimeLift() return self.createTime, self:getTimeLimit(), self:getOutTime() end --- 获取订单任务剩余时间描述 function TaskOrderInfo:getTimeLeftDesc() local now = os.time() return self:timeFormat(self:getTimeLimit() - (now - self:getCreateTime())) end --- 时间格式化 function TaskOrderInfo:timeFormat(num) return string.format("%02d时%02d分", math.floor(num / 3600), math.floor((num % 3600) / 60)) end --- 获取订单任务时间限制 function TaskOrderInfo:getTimeLimit() return self.timeLimit end --- 获取订单任务时间限制描述 function TaskOrderInfo:getTimeLimitDesc() return math.floor(self.timeLimit / 3600) .. "小时后登录领取" end --- 获取订单任务创建时间 function TaskOrderInfo:getCreateTime() return self.createTime end --- 获取订单任务可超时秒数 function TaskOrderInfo:getOutTime() return 86400 -- 超时时间,单位秒 end --- 获取订单百分比进度 function TaskOrderInfo:getFillAmount() local now = os.time() local span = now - self.createTime local timeLimit = self:getTimeLimit() if span > 0 then if (span >= timeLimit) then return 1 else return span / timeLimit end end return 0 end ----------触发---------- --- 根据索引值获取订单任务触发条件类型id function TaskOrderInfo:getTriggerId() return self.triggerType end --- 根据索引值获取订单任务触发条件参数值 function TaskOrderInfo:getTriggerValue(index) return self.triggerValues[index] end --- 获取订单任务触发条件参数值列表 function TaskOrderInfo:getTriggerValues() return self.triggerValues end --- 是否已到达预定时间 function TaskOrderInfo:isTimeArrived() local now = os.time() return (now - self.createTime) >= self:getTimeLimit() end --- 是否已超时 function TaskOrderInfo:isTimeOut() local now = os.time() return (now - self.createTime) >= self:getTimeLimit() + self:getOutTime() end --- 是否已重置 function TaskOrderInfo:isReset() return self.reset end ----------剧情---------- --- 根据索引值获取订单任务剧情id function TaskOrderInfo:getPlotId(index) return self.plotIds[index] end --- 获取订单任务剧情id列表 function TaskOrderInfo:getPlotIds() return self.plotIds end ----------奖励---------- --- 获取订单任务奖励种类数 function TaskOrderInfo:getRewardCount() return self.rewardCount end --- 根据索引值获取订单任务奖励 function TaskOrderInfo:getReward(index) return self.rewardIds[index], self.rewardValues[index] end --- 获取订单任务奖励id function TaskOrderInfo:getRewardId(index) return self.rewardIds[index] end --- 获取订单任务奖励id列表 function TaskOrderInfo:getRewardIds() return self.rewardIds end --- 根据索引值获取订单任务奖励参数值 function TaskOrderInfo:getRewardValue(index) return self.rewardValues[index] end --- 获取订单任务奖励参数值列表 function TaskOrderInfo:getRewardValues() return self.rewardValues end return TaskOrderInfo GameNodeOrder&--[[ 游戏节点默认层级 author:{zhangpeng} time:2025-05-15 14:46:31 ]] local GameNodeOrder = defClassStatic("GameNodeOrder") GameNodeOrder.Order = { -- 角色(顾客,员工等角色) role = 11, } function GameNodeOrder:init() end GameNodeOrder:init() MapBase:--[[ 地图基类, 负责地图的触摸,拖拽 author:zhengnan time:2025-06-03 11:39:25 ]] local MapBase = defClass("MapBase") local Time = UnityEngine.Time -- 运行时显示网格 local debug_gride_runtime = false -- 是否可拖拽 local drag_enable = true -- 拖拽时时候检测边界 local drag_boundary_check = true -- 地图移动边界缓冲区 local BOUNDARY_BUFFER = 0 -- 初始化 function MapBase:ctor(scene, mapRoot) self.scene = scene self.rootNode = scene.rootNode self.mapRoot = mapRoot self.camera = GameUtil:getCamera() self.isDragging = false self.lastTouchPos = nil -- 初始化移动参数 self:initMoveParams() -- 初始化地图参数 self:initMapParams() -- 设置地图边界 self:initMapBoundary() -- 初始化触摸控制 self:initTouch() end -- 初始化移动参数 function MapBase:initMoveParams() self.dragSmoothness = 0.08 -- 增加平滑度,减少抖动 self.currentVelocity = Vector3.zero self.velocityDamping = 0.9 self.minDragDistance = 0.001 -- 提高最小拖拽距离,避免小抖动触发移动 self.dragSensitivity = 1.0 self.moveSpeed = 2.5 -- 中等移动速度,平衡速度和平滑度 end -- 初始化地图参数(子类可重写) function MapBase:initMapParams() -- 基类默认实现为空,子类可以重写 end -- 初始化地图边界 function MapBase:initMapBoundary() if not drag_boundary_check then return end -- 固定地图边界尺寸 local fixedMapWidth = 16.8 local fixedMapHeight = 29.9 -- 打印摄像机位置 local currentCamPos = self.camera.transform.position printInfo(self.__cls_name, string.format("摄像机当前位置: x=%.2f, y=%.2f, z=%.2f", currentCamPos.x, currentCamPos.y, currentCamPos.z)) -- 获取摄像机视口大小 local camera = self.camera local camHeight = 2 * camera.orthographicSize local camWidth = camHeight * camera.aspect local camHalfWidth = camWidth / 2 local camHalfHeight = camHeight / 2 -- 使用固定尺寸计算地图边界(以地图根节点位置为中心) local mapRootPos = self.mapRoot.transform.position local mapCenterX = mapRootPos.x local mapCenterY = mapRootPos.y -- 计算地图边界 local mapMinX = mapCenterX - fixedMapWidth / 2 local mapMaxX = mapCenterX + fixedMapWidth / 2 local mapMinY = mapCenterY - fixedMapHeight / 2 local mapMaxY = mapCenterY + fixedMapHeight / 2 printInfo(self.__cls_name, string.format("固定地图边界: X[%.2f, %.2f], Y[%.2f, %.2f], 尺寸: %.2f x %.2f, 中心: (%.2f, %.2f)", mapMinX, mapMaxX, mapMinY, mapMaxY, fixedMapWidth, fixedMapHeight, mapCenterX, mapCenterY)) -- 首先将摄像机位置设置为查看地图中心(可选) local shouldCenterCamera = false -- 设置为false可保持摄像机原始位置,让MapsMgr控制相机位置 if shouldCenterCamera then -- 计算摄像机居中位置(保持z轴不变,调整x和y) local centerPosX = mapCenterX local centerPosY = mapCenterY -- 设置摄像机位置为居中 local camZ = currentCamPos.z -- 保持原有的Z值(摄像机到场景的距离) self.camera.transform.position = CS.UnityEngine.Vector3(centerPosX, centerPosY, camZ) currentCamPos = self.camera.transform.position printInfo(self.__cls_name, string.format("已将摄像机调整到查看地图中心: x=%.2f, y=%.2f, z=%.2f", currentCamPos.x, currentCamPos.y, currentCamPos.z)) end -- 计算摄像机可移动范围(确保不会超出地图边界太多) -- 水平方向(X轴)边界 self.minCamX = mapMinX + camHalfWidth - BOUNDARY_BUFFER self.maxCamX = mapMaxX - camHalfWidth + BOUNDARY_BUFFER -- 垂直方向(Y轴)边界 self.minCamY = mapMinY + camHalfHeight - BOUNDARY_BUFFER self.maxCamY = mapMaxY - camHalfHeight + BOUNDARY_BUFFER -- 确保边界值合理(如果地图尺寸小于摄像机视口) if self.minCamX > self.maxCamX then local centerX = (self.minCamX + self.maxCamX) / 2 self.minCamX = centerX self.maxCamX = centerX printInfo(self.__cls_name, "地图宽度小于视口,摄像机X轴固定在地图中心") end if self.minCamY > self.maxCamY then local centerY = (self.minCamY + self.maxCamY) / 2 self.minCamY = centerY self.maxCamY = centerY printInfo(self.__cls_name, "地图高度小于视口,摄像机Y轴固定在地图中心") end printInfo(self.__cls_name, string.format("摄像机移动范围: X[%.2f, %.2f], Y[%.2f, %.2f]", self.minCamX, self.maxCamX, self.minCamY, self.maxCamY)) -- 初始边界检查并调整摄像机位置 local updatedPos = self.camera.transform.position local clampedPos = self:clampCameraPosition(updatedPos) -- 如果位置不在边界内,则调整位置 if updatedPos.x ~= clampedPos.x or updatedPos.y ~= clampedPos.y then self.camera.transform.position = clampedPos printInfo(self.__cls_name, string.format("调整摄像机位置到边界内: (%.2f, %.2f, %.2f) -> (%.2f, %.2f, %.2f)", updatedPos.x, updatedPos.y, updatedPos.z, clampedPos.x, clampedPos.y, clampedPos.z)) end end -- 将摄像机位置限制在边界内 function MapBase:clampCameraPosition(position) -- 如果没有边界信息,则直接返回原位置 if not self.minCamX or not self.maxCamX or not self.minCamY or not self.maxCamY then return position end local x = position.x local y = position.y local z = position.z -- 限制摄像机位置 x = math.max(self.minCamX, math.min(x, self.maxCamX)) y = math.max(self.minCamY, math.min(y, self.maxCamY)) -- 检查X轴限制是否起作用 if x ~= position.x then printInfo(self.__cls_name, string.format("摄像机X轴位置被限制: %.2f -> %.2f", position.x, x)) end -- 检查Y轴限制是否起作用 if y ~= position.y then printInfo(self.__cls_name, string.format("摄像机Y轴位置被限制: %.2f -> %.2f", position.y, y)) end return CS.UnityEngine.Vector3(x, y, z) end -- 更新地图边界,使其与当前相机位置匹配 function MapBase:updateMapBoundary() if not drag_boundary_check then return end -- 获取当前相机位置 local currentCamPos = self.camera.transform.position -- 固定地图边界尺寸 local fixedMapWidth = 16.8 local fixedMapHeight = 29.9 -- 获取摄像机视口大小 local camera = self.camera local camHeight = 2 * camera.orthographicSize local camWidth = camHeight * camera.aspect local camHalfWidth = camWidth / 2 local camHalfHeight = camHeight / 2 -- 以当前相机位置为中心计算地图边界 local mapCenterX = currentCamPos.x local mapCenterY = currentCamPos.y -- 计算地图边界 local mapMinX = mapCenterX - fixedMapWidth / 2 local mapMaxX = mapCenterX + fixedMapWidth / 2 local mapMinY = mapCenterY - fixedMapHeight / 2 local mapMaxY = mapCenterY + fixedMapHeight / 2 printInfo(self.__cls_name, string.format("更新地图边界: X[%.2f, %.2f], Y[%.2f, %.2f], 中心: (%.2f, %.2f)", mapMinX, mapMaxX, mapMinY, mapMaxY, mapCenterX, mapCenterY)) -- 计算摄像机可移动范围 self.minCamX = mapMinX + camHalfWidth - BOUNDARY_BUFFER self.maxCamX = mapMaxX - camHalfWidth + BOUNDARY_BUFFER self.minCamY = mapMinY + camHalfHeight - BOUNDARY_BUFFER self.maxCamY = mapMaxY - camHalfHeight + BOUNDARY_BUFFER printInfo(self.__cls_name, string.format("更新摄像机移动范围: X[%.2f, %.2f], Y[%.2f, %.2f]", self.minCamX, self.maxCamX, self.minCamY, self.maxCamY)) end -- 初始化触摸控制 function MapBase:initTouch() printInfo(self.__cls_name, "初始化MapBase触摸事件") -- 获取触摸相关组件,子类需要实现 local touchCom, touchNode = self:getTouchComponents() if not touchCom or not touchNode then -- printError(self.__cls_name, "无法获取触摸组件,触摸功能将不可用") return end touchCom:addListener( touchNode, TouchCom.LISTENER_TYPE.CUSTOM, function(p) self:onTouchBegan(p) return true end, function(p) self:onTouchMoved(p) end, function(p) self:onTouchEnd(p) end ):setSwallow(false) printInfo(self.__cls_name, "地图触摸控制初始化完成") end -- 获取触摸组件(子类必须重写) function MapBase:getTouchComponents() -- printError(self.__cls_name, "子类必须重写getTouchComponents方法") return nil, nil end function MapBase:onTouchBegan(point) printInfo(self.__cls_name, "MapBase-onTouchBegan") self.startPos = point self.isDragging = false self.lastTouchPos = point -- 子类可重写此方法来添加特定的触摸开始逻辑 self:onTouchBeganExtended(point) end -- 子类可重写的触摸开始扩展方法 function MapBase:onTouchBeganExtended(point) -- 默认空实现 end function MapBase:onTouchMoved(point) if not drag_enable then return end local dragThreshold = 0.03 -- 平衡的触发阈值 if not self.isDragging then local dis = (point - self.startPos).magnitude if dis >= dragThreshold then self.isDragging = true self.lastTouchPos = point -- 重置速度向量 self.currentVelocity = Vector3.zero end end if self.isDragging then local delta = point - self.lastTouchPos self.lastTouchPos = point if drag_boundary_check then -- 检查移动量是否太小(避免细微抖动) if delta.magnitude < self.minDragDistance then return end -- 获取当前摄像机位置 local camPos = self.camera.transform.position -- 平衡的移动倍数 local moveDistanceX = -delta.x * self.moveSpeed * 1.2 local moveDistanceY = -delta.y * self.moveSpeed * 1.2 -- 计算目标位置 local targetPos = Vector3( camPos.x + moveDistanceX, camPos.y + moveDistanceY, camPos.z ) -- 根据拖拽速度调整平滑度,但保持一定的平滑效果 local smoothTime = self.dragSmoothness if delta.magnitude > 0.2 then smoothTime = self.dragSmoothness * 0.5 -- 快速拖拽时减少平滑,但不要完全去除 end -- 使用SmoothDamp进行平滑移动,保持稳定的手感 local newPos = Vector3.SmoothDamp( camPos, targetPos, self.currentVelocity, smoothTime, 1000, -- 合理的最大速度限制 Time.deltaTime ) -- 应用边界限制 newPos = self:clampCameraPosition(newPos) -- 设置新的摄像机位置 self.camera.transform.position = newPos end end end function MapBase:onTouchEnd(point) self.isDragging = false -- 保持一定的惯性效果 self.currentVelocity = Vector3.Scale(self.currentVelocity, Vector3(self.velocityDamping, self.velocityDamping, 0)) -- 子类可重写此方法来添加特定的触摸结束逻辑 self:onTouchEndExtended(point) end -- 子类可重写的触摸结束扩展方法 function MapBase:onTouchEndExtended(point) -- 默认空实现 end -- 开始拖拽 function MapBase:onDragBegin(point) self.isDragging = true self.lastTouchPos = point printInfo(self.__cls_name, "开始拖拽地图") end -- 拖拽中 function MapBase:onDrag(point) if not self.isDragging or not self.lastTouchPos then return end -- 计算拖拽偏移量 local deltaX = point.x - self.lastTouchPos.x local deltaY = point.y - self.lastTouchPos.y -- 检查移动量是否太小(避免细微抖动) if math.abs(deltaX) < self.minDragDistance and math.abs(deltaY) < self.minDragDistance then return end -- 获取当前摄像机位置 local camTransform = self.camera.transform local currentPos = camTransform.position -- 平衡的移动倍数 local moveDistanceX = -deltaX * self.moveSpeed * 1.2 local moveDistanceY = -deltaY * self.moveSpeed * 1.2 -- 计算目标位置 local targetPos = Vector3( currentPos.x + moveDistanceX, currentPos.y + moveDistanceY, currentPos.z ) -- 根据拖拽速度调整平滑度 local smoothTime = self.dragSmoothness if math.abs(deltaX) > 0.2 or math.abs(deltaY) > 0.2 then smoothTime = self.dragSmoothness * 0.5 -- 快速拖拽时减少平滑,但不要完全去除 end -- 使用SmoothDamp进行平滑移动 local newPos = Vector3.SmoothDamp( currentPos, targetPos, self.currentVelocity, smoothTime, 1000, -- 合理的最大速度 Time.deltaTime ) -- 应用边界限制 newPos = self:clampCameraPosition(newPos) -- 设置新的摄像机位置 camTransform.position = newPos -- 更新上一次触摸位置 self.lastTouchPos = point end -- 结束拖拽 function MapBase:onDragEnd(point) self.isDragging = false self.lastTouchPos = nil printInfo(self.__cls_name, "结束拖拽地图") end -- 屏幕坐标转世界坐标 function MapBase:screenToWorldPoint(screenPoint) local ray = self.camera:ScreenPointToRay(screenPoint) local plane = CS.UnityEngine.Plane(CS.UnityEngine.Vector3.up, CS.UnityEngine.Vector3.zero) local distance = 0 if plane:Raycast(ray, distance) then return ray:GetPoint(distance) end return nil end -- 获取地图根节点 function MapBase:getMapRoot() return self.mapRoot end -- 设置拖拽速度 function MapBase:setDragSpeed(speed) self.moveSpeed = speed printInfo(self.__cls_name, string.format("拖拽速度已更新为: %.2f", self.moveSpeed)) end -- 退出清理 function MapBase:onExit() -- 基类默认实现,子类可重写 end -- 隐藏 function MapBase:hide() self.mapRoot:SetActive(false) end -- 显示 function MapBase:show() self.mapRoot:SetActive(true) end return MapBasemainrequire("modules/role/customer/data/SpecialCustomerInfo") require("modules/role/customer/data/SpecialCustomerUserData") require("modules/role/customer/data/CustomerInfo") require("modules/role/customer/data/CustomerUserData") CoinOnDesk--[[ 吃完饭扔在桌子上的金币 author:{zhangpeng} time:2025-05-21 18:44:15 ]] local CoinOnDesk = defClass("CoinOnDesk") function CoinOnDesk:ctor(desk, coinNum, mapId) -- 根据菜的id取到菜的真实价格,但是显示只显示一个金币 -- 真实的菜的价格,用来更新货币栏显示的值 self.coinNum = coinNum -- 所在餐桌 self.desk = desk self.coinResLink = CoinDeskMgr:getCoinResLink() self:initView() -- 所在场景 self.mapId = mapId end function CoinOnDesk:initView() self.coinGo = GameObject.Instantiate(self.coinResLink.coin_desk) self:setCoinParent(self.desk:getCoinPosNode()) local randomPos = Vector3(math.random(-10,10), math.random(-10,10), 0) self:setCoinPos(Vector3(randomPos.x*0.01, randomPos.y*0.01, 0)) self:setCoinScale(1.7) self:setCoinOrder(9) end -- 设置金币位置 function CoinOnDesk:setCoinPos(position) self.coinGo:SetPosition(position) end -- 设置金币缩放 function CoinOnDesk:setCoinScale(scale) self.coinGo:SetScalef(scale) end -- 设置金币名字 function CoinOnDesk:setCoinName(name) self.coinGo:SetName(name) end -- 设置金币父节点 function CoinOnDesk:setCoinParent(parent) self.coinGo:SetParent(parent) end -- 设置金币层级 function CoinOnDesk:setCoinOrder(order) local sprite_render = self.coinGo:GetComponent("Renderer") sprite_render.sortingOrder = order end -- 销毁金币 function CoinOnDesk:destroy() if self.coinGo then GameObject.Destroy(self.coinGo) self.coinGo = nil end end -- 飞到货币栏 function CoinOnDesk:flyToCurrencyBar(callback) -- 地图不同不会有金币动画 if self.mapId ~= MapsMgr:getCurrentMapId() then if callback then callback() end return end local mainViewUI = UILayerUtil:findUIByName("MainViewUI") local targetNode = mainViewUI:getCoinUI() --self:flyInWordSpace(targetNode, callback) -- 换个思路:在Canvas坐标系中做动画 self:flyInCanvasSpace(targetNode, callback) end -- 在Canvas坐标系中做飞行动画 function CoinOnDesk:flyInCanvasSpace(targetNode, callback) -- 使用GuideCom中验证过的坐标转换方法 local startPos = self:getGoPositionInCanvasSpace(self.coinGo) --local targetPos = self:getGoPositionInCanvasSpace(targetNode) -- 在Canvas上创建临时的金币UI对象 local flyUI = self:createFlyCoinSpineUI(startPos, targetNode) if not flyUI then if callback then callback() end return end -- 隐藏原始金币 self.coinGo:SetActive(false) -- 使用UI动画 flyUI:RunAction(ua.Sequence({ --ua.MoveTo(0.5, Vector3(targetPos.x, targetPos.y, 0)), ua.MoveTo(1.0, Vector3(0, 0, 0)), ua.cb(function() -- 销毁临时UI对象 GameObject.Destroy(flyUI) -- 销毁原始金币 self:destroy() if callback then callback() end end) })) end -- 借用GuideCom中的坐标转换方法 function CoinOnDesk:getGoPositionInCanvasSpace(goOrPos) if not _IsType(goOrPos, typeof(GameObject)) then return goOrPos end local pos = Vector2.zero local go = goOrPos if CS.LuaHelper.IsNull(go) then printWarn("CoinOnDesk", "getGoPositionInCanvasSpace, go is null") return pos end local isUIGo = util.ugui.isUIGameObject(go) if isUIGo then pos = go.transform:TransformPoint(Vector3.zero) else local uiCamSize = UILayerUtil.cameraCom.orthographicSize local mainCamSize = self.desk.scene.cams.scene.orthographicSize local k = uiCamSize / mainCamSize pos = go.transform:TransformPoint(Vector3.zero) * k end pos = util.ugui.worldSpaceToCanvasSpace(pos, UILayerUtil.canvas) return pos end -- 创建用于飞行的金币UI对象 function CoinOnDesk:createFlyCoinUI(startPos, targetNode) -- 获取原始金币的精灵渲染器 local spriteRenderer = self.coinGo:GetComponent("SpriteRenderer") if not spriteRenderer then printWarn("CoinOnDesk", "createFlyCoinUI, 金币对象没有SpriteRenderer组件") return nil end -- 在UI Canvas上创建一个Image对象 local canvas = UILayerUtil.canvas local flyGo = GameObject("FlyCoin") flyGo.transform:SetParent(canvas.transform, false) -- 添加Image组件 local image = flyGo:AddComponent(typeof(CS.UnityEngine.UI.Image)) image.sprite = spriteRenderer.sprite image.color = spriteRenderer.color -- 设置初始位置和大小 local rectTransform = flyGo.transform rectTransform.anchorMin = Vector2(0.5, 0.5) rectTransform.anchorMax = Vector2(0.5, 0.5) rectTransform.pivot = Vector2(0.5, 0.5) rectTransform.anchoredPosition = startPos rectTransform.sizeDelta = Vector2(50, 50) -- 设置合适的大小 flyGo.transform:SetParent(targetNode.transform, true) return flyGo end -- 创建用于飞行的金币UI对象 function CoinOnDesk:createFlyCoinSpineUI(startPos, targetNode) -- 在UI Canvas上创建一个Spine对象 local canvas = UILayerUtil.canvas local flyGo = GameObject.Instantiate(CoinDeskMgr.commonResLink.coin_fly_spine) flyGo.transform:SetParent(canvas.transform, false) -- 设置初始位置和大小 local rectTransform = flyGo.transform rectTransform.anchoredPosition = startPos flyGo.transform:SetParent(targetNode.transform, true) return flyGo end -- 将UI坐标转换为世界坐标(原方法1) function CoinOnDesk:getUIToWorldPosition(uiGo) -- 方法1:直接使用UI世界坐标(如果UI和场景在同一坐标系) return Vector3(uiGo.transform.position.x, uiGo.transform.position.y, self.coinGo.transform.position.z + 10) end -- 在Canvas坐标系中做飞行动画 function CoinOnDesk:flyInWordSpace(targetNode, callback) -- 使用GuideCom中验证过的坐标转换方法 local startPos = self.coinGo.position local targetPos = self:getWorldPosition(targetNode) local res = ResLoader.loadResLink("modules/currency/currencyreslink") local fly = GameObject.Instantiate(ResLoader.coin_spine) if not fly then if callback then callback() end return end fly.transform.position = startPos -- 隐藏原始金币 self.coinGo:SetActive(false) -- 使用UI动画 fly:RunAction(ua.Sequence({ ua.MoveTo(1.0, targetPos), ua.cb(function() -- 销毁临时UI对象 GameObject.Destroy(fly) -- 销毁原始金币 self:destroy() if callback then callback() end end) })) end function CoinOnDesk:getWorldPosition(targetNode) local camera = UnityEngine.GameObject.Find("MainCamera"):GetComponent(typeof(UnityEngine.Camera)) -- 获取UI元素在屏幕空间的坐标 local screenPos = UnityEngine.RectTransformUtility:WorldToScreenPoint(camera, targetNode.position) -- 将屏幕空间坐标转换为世界空间坐标 --local worldPos = util.ugui.localSpaceToWorldSpace(screenPos, UILayerUtil.canvas) local worldPos = camera.ScreenToWorldPoint(Vector3(screenPos.x, screenPos.y, camera.nearClipPlane)) -- 返回转换后的世界坐标 return worldPos end return CoinOnDesk SceneMgr--[[ @desc: 场景管理 切换,场景栈的管理,场景事件的管理,场景资源的下载和加载 author:{zhangpeng} time:2023-03-21 20:33:25 ]] local SceneMgr = defClassStatic("SceneMgr") local LOGTAG = SceneMgr.__cls_name SceneMgr.State = { idle = 0, dowload = 1, load = 2 } function SceneMgr:init() self.state = SceneMgr.State.idle self.curScene = nil --在场景资源加载完才赋值 self.curSceneInfo = nil --开始进入场景就赋值 self.lastSceneInfo = nil self.sceneInfoStack = {} Msg.add(Msg.SCENE_PREPARE_LOAD, handler(self, self.onScenePrepareLoad)) Msg.add(Msg.SCENE_BEFORE_LOAD, handler(self, self.onSceneBeforeLoad)) Msg.add(Msg.SCENE_AFTER_LOAD_SCENE, handler(self, self.onSceneAfterLoadScene)) end function SceneMgr:isBusy() return self.state ~= SceneMgr.State.idle end function SceneMgr:setCurScene(scene) self.curScene = scene UILayerUtil:setCurScene(scene) end function SceneMgr:getCurScene() return self.curScene end function SceneMgr:getCurSceneName() local scene = self:getCurScene() if not scene then return end return scene:getName() end function SceneMgr:getCurSceneObj() local scene = self:getCurScene() if not scene then return end return scene:getSceneObj() end function SceneMgr:_setCurSceneInfo(sceneInfo) self.lastSceneInfo = self.curSceneInfo self.curSceneInfo = sceneInfo end function SceneMgr:getCurSceneInfo() return self.curSceneInfo end ---进入下一个场景以后,不把自己压入栈,这样就无法返回到自己 ---@param sceneCfg any ---@param args any ---@param callback fun(suc: boolean) function SceneMgr:enter(sceneCfg, args, callback) local sceneInfo = SceneInfo.new(sceneCfg, args) self:_enterWithInfo(sceneInfo, callback) end -- callback 接受一个bool参数 表示是否成功进入场景 function SceneMgr:_enterWithInfo(sceneInfo, callback) self.afterEnterCallback = callback if self:isBusy() then self:_enterFinish(false) return end self:_doEnter(sceneInfo) end ---@private function SceneMgr:_doEnter(sceneInfo) printInfo(LOGTAG,"SceneMgr:_doEnter") self.state = SceneMgr.State.load self:_setCurSceneInfo(sceneInfo) local path = sceneInfo:getPath() local env = Env.new() local cls = env:require(path) local scene = cls.new(sceneInfo) scene:startLoad() end function SceneMgr:afterEnter(scene) self:setCurScene(scene) if self.needPush then self:pushStack(self.lastSceneInfo) self.needPush = false end if self.needPop then self:popStack() self.needPop = false end self.state = SceneMgr.State.idle self:_enterFinish(true) end function SceneMgr:_enterFinish(result) local callback = self.afterEnterCallback or function() end self.afterEnterCallback = nil self.needPop = false self.needPush = false callback(result) end ---不建议使用 ---@param callback fun(suc: boolean) function SceneMgr:reEnter(enterTransCls, callback) -- 有可能重进场景的时候在暂停状态 if App.isPaused() then App.resume() end local sceneInfo = self:getCurSceneInfo() local initEnterTransCls = sceneInfo:getEnterTransCls() sceneInfo:setEnterTransCls(enterTransCls) self:_enterWithInfo( sceneInfo, function(...) sceneInfo:setEnterTransCls(initEnterTransCls) if callback then callback(...) end end ) end ---进入下一个场景以后,把自己压入栈 ---@param callback fun(suc: boolean) function SceneMgr:pushAndEnter(sceneCfg, args, callback) callback = callback or function() end local sceneInfo = self:getCurSceneInfo() if not sceneInfo then callback(false) return end self.needPush = true self:enter(sceneCfg, args, callback) end ---@private ---@param callback fun(suc: boolean) function SceneMgr:_popAndEnter(callback) callback = callback or function() end local sceneInfo = self:getStackTop() if not sceneInfo then callback(false) return end self.needPop = true self:_enterWithInfo(sceneInfo, callback) end function SceneMgr:getStack() return self.sceneInfoStack end function SceneMgr:pushStack(sceneInfo) table.insert(self.sceneInfoStack, sceneInfo) end function SceneMgr:popStack() return table.remove(self.sceneInfoStack, self:getStackCount()) end function SceneMgr:clearStack() self.sceneInfoStack = {} end function SceneMgr:getStackTop() return self.sceneInfoStack[self:getStackCount()] end function SceneMgr:getStackCount() return #self.sceneInfoStack end function SceneMgr:getStackTopSceneName() local sceneInfo = SceneMgr:getStackTop() if not sceneInfo then return nil end return sceneInfo:getName() end function SceneMgr:getStackTopSceneClassName() local sceneInfo = SceneMgr:getStackTop() if not sceneInfo then return nil end return sceneInfo:getSceneClassName() end function SceneMgr:replaceStackTop(sceneInfo) SceneMgr:popStack() SceneMgr:pushStack(sceneInfo) end ---@param sceneInfo SceneInfo ---@param cond fun(sceneInfo: SceneInfo): boolean function SceneMgr:replaceStackByCondition(sceneInfo, cond) for i = #self.sceneInfoStack, 1, -1 do local scene = self.sceneInfoStack[i] if cond(scene) then table.remove(self.sceneInfoStack, i) table.insert(self.sceneInfoStack, i, sceneInfo) break end end end ---@param scenePath string ---@return boolean function SceneMgr:isStackTopScenePathOf(scenePath) local top = SceneMgr:getStackTop() if not top then return false end return scenePath == top.path end function SceneMgr:hasSceneInStack(sceneInfo) for _, _sceneInfo in ipairs(self.sceneInfoStack) do if _sceneInfo:getPath() == sceneInfo[1] then return true end end return false end function SceneMgr:back() if self:getStackCount() <= 0 then self:enterMainScene() return end self:_popAndEnter() end function SceneMgr:enterMainScene() self:clearStack() App.main() end function SceneMgr:isInScene(sceneCls) local scene = self:getCurScene() if scene and IsInstanceOf(scene, sceneCls) then return true end end function SceneMgr:isInSceneByName(sceneName) local scene = self:getCurScene() if not scene then return false end return scene:getName() == sceneName end function SceneMgr:onScenePrepareLoad() UILayerUtil:onAppPause() UILayerUtil:CloseAllLocal(true) --不销毁ui显示对象,因为场景下一帧才出现,会闪一下 App.disableAllTouches() end function SceneMgr:onSceneBeforeLoad() local lastScene = self:getCurScene() if lastScene then lastScene:exit() self:setCurScene() end end function SceneMgr:onSceneAfterLoadScene(msgid, scene) self:afterEnter(scene) end BbqDesk--[[ 烤架建筑 author:{zhangpeng} time:2025-01-01 00:00:00 ]] local BbqDesk,super = defClass("BbqDesk",BuildingBase) local LOGTAG = "BbqDesk" -- 烤架建筑状态 BbqDesk.State = { idle = 1, -- 空闲 used = 2, -- 使用中 } function BbqDesk:ctor(args) super.ctor(self,args) self.deskId = args.deskId self.resId = "building_"..self.buildingInfo.buildingType self.state = BbqDesk.State.idle -- 当前建筑如果有正在使用的顾客,新进来顾客得等待位置 self.stayPoints = {} self.stayPointsOccupancy = {} -- stay_points的占用状态 self:initDeskView() end function BbqDesk:initDeskView() self.buildingNode = GameObject.Instantiate(self.scene.R[self.resId]) self.sortBaseNode = self.buildingNode:Seek("base") self:setBuildingParent(self.buildingParent) self:setBuildingNodeName(self.resId) -- stay_points self.stayPoints = self.buildingNode:SearchPattern("stay_point_\\d", true) -- 初始化stay_points占用状态 for i = 1, table.nums(self.stayPoints) do self.stayPointsOccupancy[i] = nil end printInfo(LOGTAG, string.format("烤架[%s]初始化完成,停留点数量: %d", self.deskId, table.nums(self.stayPoints))) end -- 获取建筑位置 function BbqDesk:getPosition() if self.buildingNode then return self.buildingNode.transform.position end return nil end -- 获取建筑ID function BbqDesk:getDeskId() return self.deskId end -- 获取建筑状态 function BbqDesk:getState() return self.state end -- 设置建筑状态 function BbqDesk:setState(state) self.state = state end -- 获取空闲的stay_point function BbqDesk:getAvailableStayPoint() for i = 1, table.nums(self.stayPoints) do if not self.stayPointsOccupancy[i] then return i, self.stayPoints[i] end end return nil, nil end -- 占用stay_point function BbqDesk:occupyStayPoint(pointIndex, customer) if pointIndex and pointIndex <= table.nums(self.stayPoints) then self.stayPointsOccupancy[pointIndex] = customer printInfo(LOGTAG, string.format("烤架[%s]停留点[%d]被顾客[%s-%s]占用", self.deskId, pointIndex, customer.customerCfgId, customer.roleUniqueId)) return true end return false end -- 释放stay_point function BbqDesk:releaseStayPoint(pointIndex) if pointIndex and pointIndex <= table.nums(self.stayPoints) then local customer = self.stayPointsOccupancy[pointIndex] self.stayPointsOccupancy[pointIndex] = nil if customer then printInfo(LOGTAG, string.format("烤架[%s]停留点[%d]被顾客[%s-%s]释放", self.deskId, pointIndex, customer.customerCfgId, customer.roleUniqueId)) end return true end return false end -- 检查停留点是否被占用 function BbqDesk:isStayPointOccupied(pointIndex) if pointIndex and pointIndex <= table.nums(self.stayPoints) then return self.stayPointsOccupancy[pointIndex] ~= nil end return false end -- 获取停留点总数 function BbqDesk:getStayPointCount() return table.nums(self.stayPoints) end -- 获取可用停留点数量 function BbqDesk:getAvailableStayPointCount() local count = 0 for i = 1, table.nums(self.stayPoints) do if not self.stayPointsOccupancy[i] then count = count + 1 end end return count end -- 更新建筑状态 function BbqDesk:updateBuildingState() local occupiedCount = self:getStayPointCount() - self:getAvailableStayPointCount() if occupiedCount == 0 then self.state = BbqDesk.State.idle else self.state = BbqDesk.State.used end end return BbqDesk AppY--[[ author:{zhangpeng} time:2022-05-11 17:07:11 ]] local App, super = defClassStatic("App") local SceneManagement = CS.UnityEngine.SceneManagement local GameObject = CS.UnityEngine.GameObject local _config = nil local Path = CS.System.IO.Path local File = CS.System.IO.File local LOGTAG = "App" -- 不销毁的节点 local LuaAppMsgGameObjectName = "LuaAppMsgGameObject" function App.init(config) _config = config or {} math.randomseed(os.time()) CS.UnityEngine.Time.timeScale = 1 CS.UnityEngine.Application.targetFrameRate = 60 CS.UnityEngine.Screen.sleepTimeout = CS.UnityEngine.SleepTimeout.NeverSleep App._paused = 0 App._background = false App._exited = false App._initAppMsg() App.onInit() end function App.onInit() printInfo(LOGTAG,"App.onInit()") -- 初始化一些东西 ResLoader.init() SceneMgr:init() App.main() end function App.main(...) if not CS.LuaHelper.UseLocalRes() then print("加载urp_shaders....") -- todo: 加载urp_shaders end if CS.UnityEngine.Application.isEditor then SceneMgr:enter(SceneCfgList.SCENES.MainScene) -- SceneMgr:enter(SceneCfgList.SCENES.TestScene) else SceneMgr:enter(SceneCfgList.SCENES.MainScene) -- SceneMgr:enter(SceneCfgList.SCENES.TestScene) end end function App.enableAllTouches() App.isAllTouchesEnabled = true App._lastEnableAllTouchesLoc = util.lua.getLoc(2) util.ugui.enableAllTouches() printInfo(LOGTAG,"App.enableAllTouches()") end function App.disableAllTouches() App.isAllTouchesEnabled = false App._lastDisbleAllTouchesLoc = util.lua.getLoc(2) util.ugui.disableAllTouches() printInfo(LOGTAG,"App.disableAllTouches()") end function App.exit() Msg.send(Msg.APP_EXIT) --必须清空之前的,尤其可能注册会回调的,闭包会不释放 --这里要求了LuaGlobal上注册的Event.Update只能用来做全局Timer local luaGlobal = CS.LuaGlobal.instance local luaGlobalGo = luaGlobal.gameObject luaGlobal:clearActionQueue() -- --timer必须清除,因为app可能重启,需要卸载干净上次注册的,否则上个_ENV的timer还在跑。 Event.remove(luaGlobalGo,Event.Update) Event.remove(luaGlobalGo,Event.LateUpdate) Event.remove(luaGlobalGo,Event.FixedUpdate) luaGlobalGo:StopAllSounds() luaGlobalGo:StopAllActions() luaGlobalGo:RemoveAllComponentsByType(typeof(CS.AudiosComponent)) luaGlobalGo:RemoveAllComponentsByType(typeof(CS.HttpDownloadCom)) CS.UnityEngine.Input.multiTouchEnabled = false App.enableAllTouches() CS.LuaGlobal.instance:StopAllCoroutines() local SceneManager = SceneManagement.SceneManager for i = 0, SceneManager.sceneCount - 1 do local scene = SceneManager.GetSceneAt(i) local items = scene:GetRootGameObjects() for j = 0, items.Length - 1 do GameObject.Destroy(items[j]) end end end function App.pauseScene() App.pauseAudio() end function App.pause() App._paused = App._paused + 1 CS.UnityEngine.Time.timeScale = 0 App.pauseScene() if App._paused == 1 then Msg.send(Msg.APP_PAUSED) return true else return false end end function App.resumeScene() App.resumeAudio() end function App.resume() --print("[App.resume]", timer, audio, timeline, debug.traceback()) if App._paused == 0 then return false end App._paused = App._paused - 1 if App._paused == 0 then CS.UnityEngine.Time.timeScale = 1 App.resumeScene() Msg.send(Msg.APP_RESUME) return true else return false end end function App.isPaused() return App._paused > 0 end function App.isBackground() return App._background end function App.pauseAudio() local scene = SceneMgr:getCurSceneObj() if scene then local rootGo = scene:GetGameRoot() local result = util.pause.pauseAudio(scene) if rootGo.__pausedAudioList__ then table.insertTo(rootGo.__pausedAudioList__,result) else rootGo.__pausedAudioList__ = result end end end function App.resumeAudio() local scene = SceneMgr:getCurSceneObj() if scene then local rootGo = scene:GetGameRoot() util.pause.resumeAudio(rootGo.__pausedAudioList__) rootGo.__pausedAudioList__ = {} end end function App._onBackground() App._background = true Msg.send(Msg.APP_BACKGROUND) App.pause() printInfo(">App._onBackground") -- print("[APP]_onBackground", Time.realtimeSinceStartup) end function App._onForeground() App._background = false App.resume() Msg.send(Msg.APP_FOREGROUND) printInfo(">App._onForeground") end function App._setLogLvl() if BUILD_ENV == ENV_DEVELOPMENT or BUILD_ENV == ENV_TESTING then else print = function(...) end end local LogType = CS.UnityEngine.LogType local logType = {[LogType.Error] = "LogError", [LogType.Exception] = "LogException", [LogType.Assert] = "LogAssert",} local logHash = {} --上报 Debug.LogError Debug.LogException Debug.LogAssert的日志 local logFunc = function(msg, stb, tt) if logType[tt] then if logHash[msg] then return end local handler = CS.LogHelper.luaLogFileWriteHandler local cs_err = "C# ERR,errorKey = " .. tostring(msg) local cs_stack = "C# ERR,c# traceback = " .. tostring(stb) local lua_stack = "C# ERR,lua traceback = " .. debug.traceback() if handler then handler:WriteLine(cs_err) handler:WriteLine(cs_stack) handler:WriteLine(lua_stack) end logHash[msg] = true if BUILD_ENV ~= ENV_PRODUCTION or CS.LocalDataStorage.Get(StrogeKeyDef.DEVICE_ALWAYS_SHOW_ERR_DIALOG) == "true" then UIComsTool:showDailog(tostring(msg),"ok", function (ui) FirebaseAnalyticsUtil:sendErrorInfo(msg) ui:close() end) end if Device.isIOS() then local param = { name = tostring(msg), reason = tostring(msg), callStack = tostring(stb), } luaoc.callStaticMethod("CustomAppController", "reportLuaErrorToFirebase", param) end end end CS.UnityEngine.Application.logMessageReceived("+", logFunc) Msg.add(Msg.APP_EXIT, function() CS.UnityEngine.Application.logMessageReceived("-", logFunc) end) end function App._initAppMsg() local go = CS.UnityEngine.GameObject.Find(LuaAppMsgGameObjectName) if go then CS.UnityEngine.GameObject.Destroy(go) --必须销毁上一次 end go = CS.UnityEngine.GameObject(LuaAppMsgGameObjectName) CS.UnityEngine.GameObject.DontDestroyOnLoad(go) go:AddEvent(Event.OnApplicationQuit, function() printInfo(LOGTAG, "Event.OnApplicationQuit") App.exit() end) go:AddEvent(Event.OnApplicationUnload, function() printInfo(LOGTAG, "Event.OnApplicationUnload") App.exit() end) if not _config.ignoreApplicationFocus then go:AddEvent(Event.OnApplicationFocus, function(focus) if focus then App._onForeground() else App._onBackground() end end) end Msg.add(Msg.APP_EXIT, function() local go = CS.UnityEngine.GameObject.Find(LuaAppMsgGameObjectName) if go then CS.UnityEngine.GameObject.Destroy(go) end end) endmainm-- mgr require("modules/upgrade/mgr/UpgradeMgr") -- const require("modules/upgrade/const/UpgradeConst")main& -- role require("modules/role/RoleBase") require("modules/role/RoleConst") require("modules/role/RoleMsg") --mgr require("modules/role/RoleMgr") -- 顾客 require("modules/role/customer/main") -- 员工 require("modules/role/employee/main") -- 摊主 require("modules/role/vendor/main") EmployeeListUI -- 员工列表ui ---@class EmployeeListUI:UILayer local EmployeeListUI, super = defClass("EmployeeListUI", UILayer) require("modules/ui/employeeui/EmployeeCell") ---@param pageId number 页签id function EmployeeListUI:ctor(pageId) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/employeeui/employeeuireslink") self.pageId = pageId or 2 self.indexes = {} self.activeIndexes = {} end function EmployeeListUI:onLoad() self.ui = GameObject.Instantiate(self.R.employee_list_ui) self:addChild(self.ui) --self:useTweenOnOpen(self.ui) self:initUI() self:showUI() --self:timer(function() -- local scrollView = self.ui:Seek("Scroll View"):GetComponent("UnityEngine.UI.ScrollRect") -- scrollView.verticalNormalizedPosition = 1 --end, 0.2) end --- 初始化 function EmployeeListUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.toggle_manager = self.ui:Seek("toggle_manager") self.content_manager = self.ui:Seek("content_manager") self.toggle_manager_mask = self.ui:Seek("toggle_manager_mask") self.toggle_employee = self.ui:Seek("toggle_employee") self.content_employee = self.ui:Seek("content_employee") self.toggle_employee_mask = self.ui:Seek("toggle_employee_mask") self.employee_locked = self.ui:Seek("employee_locked") self.scrollView = self.ui:Seek("Scroll View") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addToggleValueChangeEvent(self.toggle_manager, function(isOn) self:managerTab(isOn) end) util.ugui.addToggleValueChangeEvent(self.toggle_employee, function(isOn) self:employeeTab(isOn) end) util.ugui.addButtonClickEvent(self.employee_locked, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) PopUpUI.new("敬请期待"):show():showMask():enableCloseWhenClickMask() end) end --- 展示UI function EmployeeListUI:showUI() if not self.pageId then self.pageId = EmployeConst.PageType.Manager end self:triggerPage(self.pageId) self.scrollView:GetComponent("ScrollRect").normalizedPosition = Vector2(0, 1) -- 滚动到顶部 end function EmployeeListUI:triggerPage(pageId) self:managerTab(pageId == EmployeConst.PageType.Manager) self:employeeTab(pageId == EmployeConst.PageType.Employee) end --- 获取对象 ---@return EmployeeCell function EmployeeListUI:getObj(config, parent) if #self.indexes > 0 then local obj = table.remove(self.indexes) table.insert(self.activeIndexes, obj) obj:reload(config, parent) return obj end local obj = EmployeeCell.new(config, self.R.employee_cell, parent) table.insert(self.activeIndexes, obj) return obj end function EmployeeListUI:clearAll() for _ = 1, #self.activeIndexes do local obj = table.remove(self.activeIndexes) table.insert(self.indexes, obj) obj:hide() end end --- 页签切换 function EmployeeListUI:managerTab(isOn) self.content_manager:SetActive(isOn) self.toggle_manager_mask:SetActive(isOn) printInfo("EmployeeListUI", "managerTab %s", isOn) if isOn then self.pageId = EmployeConst.PageType.Manager self:showAll() end end --- 页签切换 function EmployeeListUI:employeeTab(isOn) self.content_employee:SetActive(isOn) self.toggle_employee_mask:SetActive(isOn) printInfo("EmployeeListUI", "employeeTab %s", isOn) if isOn then self.pageId = EmployeConst.PageType.Employee self:showAll() end end --- 展示所有对象 function EmployeeListUI:showAll() self:clearAll() if self.pageId == EmployeConst.PageType.Manager then local data = EmployeCfgParse:getDataByType(EmployeConst.Posts.Manager) printInfo("EmployeeListUI", "showAll 角色数量%d", #data) for _, value in pairs(data) do self:getObj(value.id, self.content_manager.transform) end elseif self.pageId == EmployeConst.PageType.Employee then local data = EmployeCfgParse:getDataByNotType(EmployeConst.Posts.Manager) printInfo("EmployeeListUI", "showAll 角色数量%d", #data) for _, value in pairs(data) do self:getObj(value.id, self.content_employee.transform) end end end return EmployeeListUImainrequire("modules/role/employee/category/Waiter") require("modules/role/employee/category/Security") require("modules/role/employee/category/Chef") require("modules/role/employee/category/Cashier") require("modules/role/employee/category/Fisherman") require("modules/role/employee/category/Singer") require("modules/role/employee/category/Assistant") require("modules/role/employee/category/BarbecueChef")SingerO--[[ 歌手(音乐烤吧) author:{zhangpeng} time:2025-05-30 15:15:39 ]] local Singer,super = defClass("Singer",Employe) local LOGTAG = "Singer" function Singer:ctor(reslink,cfgId) super.ctor(self, RoleConst.roleType.employee) self.R = reslink self.employeCfgId = cfgId self:init() end function Singer:init() super.init(self) self:playSingAnim() self:setSortingOrder(25) end function Singer:setSortingOrder(sortingOrder) self.sortingOrder = sortingOrder end function Singer:action() if self.state == EmployeConst.State.Idle then self:playSingAnim() end end function Singer:playSingAnim() local actions = EmployeConst.EmployeeAction[self.employeCfgId] if actions and actions.sing then self:playAnimation(actions.sing, true) end end function Singer:getEmployeType() return self.employeCfgId end function Singer:setState(state) self.state = state if self.employeeInfo then self.employeeInfo:setWorkState(state) end end function Singer:getState() return self.state end return SingerStorydialogConstY--[[ 剧情对话常量 author:{zhangpeng} time:2025-05-28 12:13:45 ]] local StorydialogConst = defClassStatic("StorydialogConst") StorydialogConst.TextState = { -- 初始化 Init = 1, -- 播放中 Playing = 2, -- 播放结束 PlayOver = 3, } StorydialogConst.RoleSide = { -- 左侧 Left = "l", -- 右侧 Right = "r", } StorydialogConst.RoleType = { -- 员工 Employee = "3", -- 顾客 Customer = "4", } -- 单字播放时间 StorydialogConst.WordPlayTime = 0.1 function StorydialogConst:init() end StorydialogConst:init() CustomerStoryCell--- 故事单元格 ---@class CustomerStoryCell:LuaClass local CustomerStoryCell = defClass("CustomerStoryCell") function CustomerStoryCell:ctor(go, storyId, isUnlock) local bg = go:Seek("bg") local story_value = go:Seek("story_value") local story_lock = go:Seek("story_lock") local story_btn = go:Seek("story_btn") self.isUnlock = isUnlock local config = StorydialogCfgParse:getDialogCfg(storyId) util.ugui.addClickEvent(bg, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:showStory(storyId) end) story_value:GetComponent("TextMeshProUGUI").text = config.dialogName story_lock:SetActive(not (isUnlock or false)) story_btn:SetActive(isUnlock or false) end function CustomerStoryCell:showStory(storyId) if self.isUnlock then --printWarn("CustomerStoryCell", "CustomerStoryCell:showStory() id = " .. storyId) StoryDialogMgr:showPageStoryDialogUI(storyId, StorydialogConst.RoleType.Customer) else PopUpUI.new("顾客解锁后可查看剧情"):show():showMask():enableCloseWhenClickMask() end end return CustomerStoryCellCustomerCfgParse/ --[[ 顾客配置表解析 author:{zhangpeng} time:2025-05-15 13:44:02 ]] local CustomerCfgData = require("data/config/customerCfg") local CustomerCfgParse = defClassStatic("CustomerCfgParse") local LOG_TAG = "CustomerCfgParse" local customer_list = {} local function initAllIds() for k, v in pairs(CustomerCfgData) do table.insert(customer_list, v.customerId) end end function CustomerCfgParse:init() initAllIds() end function CustomerCfgParse:getData() return CustomerCfgData end function CustomerCfgParse:getCustomerCfg(id) for k,v in pairs(CustomerCfgData) do if v.id == id then return v end end return nil end -- 返回顾客剧情id,顾客暂时只有一个剧情 function CustomerCfgParse:getStoryDialogId(customerId) for k, v in pairs(CustomerCfgData) do -- 找到这个顾客 if v.id == customerId then local dialogId = v.plotId if dialogId then return dialogId end end end return nil end -- 获取星星数量达标能来访的顾客 function CustomerCfgParse:getStarVisitList(starNum) local visitList = {} for _, v in pairs(CustomerCfgData) do if v.visitNeedStar <= starNum then table.insert(visitList, v) end end return visitList end -- 获取指定食物能来访的顾客 function CustomerCfgParse:getCuisineVisitList(cuisineId) local visitList = {} for _, v in pairs(CustomerCfgData) do local value = v.visitItemId[CustomerConst.VisitItemType.c] if value then if type(value) == "number" then if cuisineId == value then table.insert(visitList, v) end elseif type(value) == "table" then for _, cId in ipairs(value) do if cuisineId == cId then table.insert(visitList, v) break end end end end end return visitList end -- 获取指定建筑能来访的顾客 function CustomerCfgParse:getBuildingVisitList(buildingId) local visitList = {} for _, v in pairs(CustomerCfgData) do local value = v.visitItemId[CustomerConst.VisitItemType.b] if value then if type(value) == "number" then if buildingId == value then table.insert(visitList, v) end elseif type(value) == "table" then for _, bId in ipairs(value) do if buildingId == bId then table.insert(visitList, v) break end end end end end return visitList end CustomerCfgParse:init() fishinguireslinkreturn { --BASIC --ASSET fishing_main_ui = {"Assets/AssetsPackage/Res/modules/ui/fishingui/prefabs/fishing_main_ui.prefab", 0, 0}, fishing_pause_ui = {"Assets/AssetsPackage/Res/modules/ui/fishingui/prefabs/fishing_pause_ui.prefab", 0, 0}, fishing_prop_ui = {"Assets/AssetsPackage/Res/modules/ui/fishingui/prefabs/fishing_prop_ui.prefab", 0, 0}, fishing_revive_ui = {"Assets/AssetsPackage/Res/modules/ui/fishingui/prefabs/fishing_revive_ui.prefab", 0, 0}, fishing_failed_ui = {"Assets/AssetsPackage/Res/modules/ui/fishingui/prefabs/fishing_failed_ui.prefab", 0, 0}, fishing_succeed_ui = {"Assets/AssetsPackage/Res/modules/ui/fishingui/prefabs/fishing_succeed_ui.prefab", 0, 0}, fishing_fish_cell = {"Assets/AssetsPackage/Res/modules/ui/fishingui/prefabs/fishing_fish_cell.prefab", 0, 0}, fishing_fish_list_ui = {"Assets/AssetsPackage/Res/modules/ui/fishingui/prefabs/fishing_fish_list_ui.prefab", 0, 0}, } restaurantscenereslinkVreturn { --BASIC --ASSET SCENE = {"Assets/AssetsPackage/Res/modules/scenes/RestaurantScene.unity", 0, 1}, chuan = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/misc/chuan.prefab", 0, 0}, creel = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/misc/creel.prefab", 0, 0}, stalls = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/misc/stalls.prefab", 0, 0}, rg = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/misc/tw_02_rg.prefab", 0, 0}, tw_04_bb = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/misc/tw_04_bb.prefab", 0, 0}, tw_07_yp = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/misc/tw_07_yp.prefab", 0, 0}, tw_08_ht = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/misc/tw_08_ht.prefab", 0, 0}, tw_01_mht = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/misc/tw_01_mht.prefab", 0, 0}, bmh = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/misc/tw_03_bmh.prefab", 0, 0}, xxh = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/misc/tw_05_xxh.prefab", 0, 0}, zys = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/misc/tw_06_zys.prefab", 0, 0}, tw_09_bql = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/misc/tw_09_bql.prefab", 0, 0}, zhaozupai = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/misc/zhaozupai.prefab", 0, 0}, progress = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/progress.prefab", 0, 0}, building_101 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_101.prefab", 0, 0}, building_102 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_102.prefab", 0, 0}, building_103 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_103.prefab", 0, 0}, building_104 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_104.prefab", 0, 0}, building_105 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_105.prefab", 0, 0}, building_106 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_106.prefab", 0, 0}, building_107 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_107.prefab", 0, 0}, building_108 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_108.prefab", 0, 0}, building_110 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_110.prefab", 0, 0}, building_111 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_111.prefab", 0, 0}, building_114 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_114.prefab", 0, 0}, building_115 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_115.prefab", 0, 0}, building_116 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_116.prefab", 0, 0}, building_117 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_117.prefab", 0, 0}, building_119 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_119.prefab", 0, 0}, building_120 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_120.prefab", 0, 0}, building_121 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_121.prefab", 0, 0}, building_122 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_122.prefab", 0, 0}, building_123 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_123.prefab", 0, 0}, building_124 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_124.prefab", 0, 0}, building_151 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_151.prefab", 0, 0}, building_152 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_152.prefab", 0, 0}, building_153 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_153.prefab", 0, 0}, building_154 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_154.prefab", 0, 0}, building_155 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_155.prefab", 0, 0}, building_156 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_156.prefab", 0, 0}, building_157 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_157.prefab", 0, 0}, building_158 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_158.prefab", 0, 0}, building_159 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_159.prefab", 0, 0}, building_171 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_171.prefab", 0, 0}, building_172 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_172.prefab", 0, 0}, building_173 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_173.prefab", 0, 0}, building_174 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_174.prefab", 0, 0}, building_175 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_175.prefab", 0, 0}, building_176 = {"Assets/AssetsPackage/Res/modules/restaurant/map/prefab/buildings/map_buildings/building_176.prefab", 0, 0}, } StringUtil local StringUtil = {} local LOG_TAG = "StringUtil" local unpack = unpack or table.unpack -- 字符串连接 StringUtil.join = function (join_table, joiner) if #join_table == 0 then return "" end local fmt = "%s" for i = 2, #join_table do fmt = fmt .. joiner .. "%s" end return string.format(fmt, unpack(join_table)) end -- 是否包含 -- 注意:plain为true时,关闭模式匹配机制,此时函数仅做直接的 “查找子串”的操作 StringUtil.contains = function (target_string, pattern, plain) plain = plain or true local find_pos_begin, find_pos_end = string.find(target_string, pattern, 1, plain) return find_pos_begin ~= nil end -- 以某个字符串开始 StringUtil.startswith = function (target_string, start_pattern, plain) plain = plain or true local find_pos_begin, find_pos_end = string.find(target_string, start_pattern, 1, plain) return find_pos_begin == 1 end -- 以某个字符串结尾 StringUtil.endswith = function (target_string, start_pattern, plain) plain = plain or true local find_pos_begin, find_pos_end = string.find(target_string, start_pattern, -#start_pattern, plain) return find_pos_end == #target_string end StringUtil.isVersion = function(version) if string.isEmpty(version) then return false end local list = string.split(version, ".") if #list ~= 3 then return false end for i, v in ipairs(list) do local num = tonumber(v) if not num then return false end if num < 0 then return false end end return true end StringUtil.compareVersion = function(versionA, versionB) if not util.string.isVersion(versionA) then printWarn(LOG_TAG, "compareVersion, versionA:%s is not a version", versionA) return 0 end if not util.string.isVersion(versionB) then printWarn(LOG_TAG, "compareVersion, versionB:%s is not a version", versionB) return 0 end local tableA = string.split(versionA, ".") local tableB = string.split(versionB, ".") local maxLength = math.max(#tableA, #tableB) for i = 1, maxLength do local numA = tonumber(tableA[i]) local numB = tonumber(tableB[i]) if numA == nil then return -1 elseif numB == nil then return 1 elseif numA > numB then return 1 elseif numA < numB then return -1 end end return 0 end StringUtil.formatMemorrySize = function(size, unit) if size == nil then return "" end local unitList = {"B", "KB", "MB", "GB", "TB"} local index = 1 if not unit then while size > 1024 and (unitList[index + 1]) do size = size / 1024 index = index + 1 end else for i, v in ipairs(unitList) do if v == unit then index = i break end size = size / 1024 end end return string.format("%.2f%s", size, unitList[index]) end -- 提取其中的数字和字母字符 StringUtil.processNumberAndCharacterText = function(text) local newText = "" local len = #text for i = 1, len do local v = string.sub(text, i, i) if v ~= nil and v ~= "" then local regex = "%w" local re = string.find(v, regex) if re then newText = newText .. tostring(v) end end end return newText end -- 是否CN手机号 StringUtil.isMobile = function(mobile) if not mobile then return false end local len = string.len(mobile) if len ~= 11 then return false end local pattern = "^[1]%d%d%d%d%d%d%d%d%d%d$" return string.match(mobile, pattern) end -- 是否密码 StringUtil.isPwd = function(pwd) if not pwd then return false end local len = string.len(pwd) if len < 8 or len > 12 then return false end for i = 1, len do local v = string.sub(pwd, i, i) if v ~= nil and v ~= "" then local regex = "%w" local re = string.find(v, regex) if not re then return false end end end return true end -- 是否短信验证码 StringUtil.isSmsCode = function(smsCode) if not smsCode then return false end local len = string.len(smsCode) if len ~= 4 then return false end local pattern = "^%d%d%d%d$" return string.match(smsCode, pattern) end -- int转byte StringUtil.formatNumberInBits = function(num, min, gap) min = min or 8 gap = gap or 4 local t = {} -- will contain the bits while num > 0 do local rest = math.fmod(num, 2) t[#t + 1] = rest num = num >> 1 end local t2 = {} for i = 1, math.max(min, #t) do t[i] = t[i] or 0 table.insert(t2, 1, string.format("%d", t[i])) if i % gap == 0 then table.insert(t2, 1, " ") end end local str = table.concat(t2) return str end StringUtil.formatTime = function(time) local day = math.floor(time / 86400) local hour = math.floor(time / 3600) local minute = math.floor((time - hour * 3600) / 60) local second = math.floor(time - hour * 3600 - minute * 60) local str = "" if day > 0 then str = str .. day .. "天" elseif hour > 0 then str = string.format("%02d:%02d:%02d", hour, minute, second) else str = string.format("%02d:%02d", minute, second) end return str end function StringUtil.appendUrlParam(url,key,value) if string.find(url,"?") then return string.format("%s&%s=%s",url,key,value) else return string.format("%s?%s=%s",url,key,value) end end return StringUtil SqliteMgrD ---@class SqliteMgr:DBMgr local SqliteMgr = defClassStatic("SqliteMgr", DBMgr) ---@param isEncrypt boolean 是否加密 ---@param getDeviceIdFunc fun(): string 获取deviceid的方法 ---@param getTimeFunc fun(): number 获取时间戳的方法, 默认 os.time() function SqliteMgr:init(isEncrypt, getDeviceIdFunc, getTimeFunc) SqliteMgr.super.init(self, isEncrypt, getDeviceIdFunc, getTimeFunc) self.logTag = "SqliteMgr" self.userDBName = "user.db" self.roleDBName = "role.db" self.deviceDBName = "device.db" self.dbCls = SqliteDatabase end CustomerDetailUID--- 菜品详情界面 ---@class CustomerDetailUI : UILayer local CustomerDetailUI, super = defClass("CustomerDetailUI", UILayer) -- view require("modules/ui/customerui/CustomerStoryCell") require("modules/ui/customerui/CustomerTagCell") -- UIComponent local UIImage = CS.UnityEngine.UI.Image local TMProUGUI = CS.TMPro.TextMeshProUGUI function CustomerDetailUI:ctor(customerId, isNewCustomer) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/customerui/customeruireslink") self.customerId = customerId self.isNewCustomer = isNewCustomer end function CustomerDetailUI:onLoad() self.ui = GameObject.Instantiate(self.R.customer_detail_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end --- 初始化 function CustomerDetailUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.new_customer = self.ui:Seek("new_customer") self.new_share = self.ui:Seek("new_share") self.share_value = self.ui:Seek("share_value") self.share_btn = self.ui:Seek("share_btn") self:initInfo() self:initToggle() self:initAttribute() self:initPlot() -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addButtonClickEvent(self.share_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:share() end) end --- 展示UI function CustomerDetailUI:showUI() local info = CustomerMgr:getCustomerInfo(self.customerId) local shareCoin = info:getShareCoin() local isVisited = info:isVisited() or self.isNewCustomer local showShareReward = isVisited and (not info:isShared()) and (shareCoin > 0) self.new_customer:SetActive(self.isNewCustomer) self.new_share:SetActive(showShareReward) self.share_value:SetActive(showShareReward) self.share_btn:SetActive(isVisited) if showShareReward then self.share_value[TMProUGUI].text = "+" .. shareCoin end self:showInfo(info) self:showToggle() self:showAttribute(info) self:showPlot(info) end function CustomerDetailUI:initInfo() self.lock = self.ui:Seek("lock") self.info = self.ui:Seek("info") self.customer_img = self.ui:Seek("customer_img") self.customer_spine_root = self.ui:Seek("customer_spine_root") self.customer_name = self.ui:Seek("customer_name") self.customer_desc = self.ui:Seek("customer_desc") end function CustomerDetailUI:showInfo(info) local isVisited = info:isVisited() or self.isNewCustomer self.lock:SetActive(not isVisited) self.info:SetActive(isVisited) if isVisited then --self.customer_img[UIImage].sprite = info:getIcon() --self.customer_img[UIImage]:SetNativeSize() GameObject.Instantiate(info:getSpineUI(), self.customer_spine_root.transform) self.customer_name[TMProUGUI].text = info:getName() self.customer_desc[TMProUGUI].text = info:getDesc() end end function CustomerDetailUI:initToggle() self.toggle_attribute = self.ui:Seek("toggle_attribute") self.toggle_plot = self.ui:Seek("toggle_plot") self.toggle_attribute_mask = self.ui:Seek("toggle_attribute_mask") self.toggle_plot_mask = self.ui:Seek("toggle_plot_mask") self.content_attribute = self.ui:Seek("content_attribute") self.content_plot = self.ui:Seek("content_plot") util.ugui.addToggleValueChangeEvent(self.toggle_attribute, function(isOn) self:attributeTab(isOn) end) util.ugui.addToggleValueChangeEvent(self.toggle_plot, function(isOn) self:plotTab(isOn) end) end function CustomerDetailUI:showToggle() self:attributeTab(true) self:plotTab(false) end function CustomerDetailUI:initAttribute() self.tagCount = 10 self.tags = {} for i = 1, self.tagCount do self.tags[i] = self.ui:Seek("customer_tag_cell_" .. i) end self.line = self.ui:Seek("line") end function CustomerDetailUI:showAttribute(info) local unknown = CustomerMgr:getUnknownIcon() local claw = CustomerMgr:getClawIcon() local dong = CustomerMgr:getDongIcon() local isVisited = info:isVisited() or self.isNewCustomer local idx = 1 if info:getVisitNeedStar() > 0 then idx = self:showTag(idx, dong, info:getVisitNeedStarDesc(), nil) end -- 来访要求 local visitItemIdLists = info:getVisitItemIdLists() for i, v in pairs(visitItemIdLists) do if i == CustomerConst.VisitItemType.c then idx = self:showTag(idx, unknown, info:getVisitCuisineDesc(), function () self:showEntityList(i, v) end) elseif i == CustomerConst.VisitItemType.b then idx = self:showTag(idx, unknown, info:getVisitFacilityDesc(), function () self:showEntityList(i, v) end) end end -- 喜欢的食物 if #info:getFavoriteFoods() > 0 then idx = self:showTag(idx, unknown, info:getFavoriteFoodsDesc(isVisited), function () self:showEntityList(CustomerConst.VisitItemType.c, info:getFavoriteFoods()) end) end -- 空一行 self.line.transform:SetSiblingIndex(idx - 1) -- 招揽条件 if info:getSolicitTagId() ~= -1 then idx = self:showTag(idx, dong, info:getSolicitTagDesc(), nil) end -- 功能 local funList = info:getFunTagIdList() if #funList > 0 then for i, v in ipairs(funList) do idx = self:showTag(idx, claw, info:getFunTagDesc(i), nil) end end for i = idx, self.tagCount do self.tags[i]:SetActive(false) end end function CustomerDetailUI:showTag(idx, icon, desc, clickListener) self.tags[idx]:SetActive(true) CustomerTagCell.new(self.tags[idx], icon, desc, clickListener) idx = idx + 1 return idx end function CustomerDetailUI:showTagList(idx, list, icon, descListener, clickListener) for i, v in ipairs(list) do self.tags[idx]:SetActive(true) CustomerTagCell.new(self.tags[idx], icon, descListener(i), clickListener) idx = idx + 1 return idx end end function CustomerDetailUI:initPlot() self.no_plot = self.ui:Seek("no_plot") self.plot = self.ui:Seek("customer_plot_cell") end function CustomerDetailUI:showPlot(info) local plotId = info:getPlotId() self.plot:SetActive(plotId ~= -1) self.toggle_plot:SetActive(plotId ~= -1) self.no_plot:SetActive(plotId == -1) if plotId ~= -1 then CustomerStoryCell.new(self.plot, plotId, info:isVisited()) end end function CustomerDetailUI:attributeTab(isOn) self.content_attribute:SetActive(isOn) self.toggle_attribute_mask:SetActive(isOn) end function CustomerDetailUI:plotTab(isOn) self.content_plot:SetActive(isOn) self.toggle_plot_mask:SetActive(isOn) end function CustomerDetailUI:showEntityList(type, ids) local tp = nil if type == CustomerConst.VisitItemType.c then tp = 1 elseif type == CustomerConst.VisitItemType.b then tp = 2 end EntityListUI.new(CustomerConst.VisitItemTypeTitle[type], tp, ids):show():showMask():enableCloseWhenClickMask() end function CustomerDetailUI:share() local info = CustomerMgr:getCustomerInfo(self.customerId) local reward = info:getShareCoin() local isReward = info:isShared() and (reward > 0) -- todo 分享 -- 分享回调 if not isReward then return end CurrencyMgr:changeCoin(reward) self:showUI() end return CustomerDetailUICustomerTagCfgParseR--- local CustomerTagCfgData = require("data/config/customerTagCfg") local CustomerTagCfgParse = defClassStatic("CustomerTagCfgParse") function CustomerTagCfgParse:init() end function CustomerTagCfgParse:getData() return CustomerTagCfgData end function CustomerTagCfgParse:getCustomerTagCfg(tagId) return CustomerTagCfgData[tagId] end function CustomerTagCfgParse:getCustomerTagCfgByCuisineId(cuisineId) for _, v in pairs(CustomerTagCfgData) do if not (type(v.favoriteFoods) == "table") then if v.favoriteFoods == cuisineId then return v end else for _, v2 in pairs(v.favoriteFoods) do if v2 == cuisineId then return v end end end end return nil end CustomerTagCfgParse:init() GameNodeAdapt --- 非UI节点屏幕适配 local GameNodeAdapt = {} GameNodeAdapt.layout_type = { top_left = 1, top_center = 2, top_right = 3, center_left = 4, center_center = 5, center_right = 6, bottom_left = 7, bottom_center = 8, bottom_right = 9 } local layout_type = GameNodeAdapt.layout_type -- obj的每条边 靠每个边的 offset function GameNodeAdapt:adaptScreen(obj, layoutType, offset) local lbPoint = GameUtil:getCamera():ScreenToWorldPoint(Vector3(0, 0, 0)) local rtPoint = GameUtil:getCamera():ScreenToWorldPoint(Vector3(Screen.width, Screen.height, 0)) local b = util.BoundUtil:getObjBounds(obj) local x, y = 0, 0 if layoutType == layout_type.top_left then x = lbPoint.x + b.size.x / 2 + offset.x y = rtPoint.y - b.size.y / 2 - offset.y elseif layoutType == layout_type.top_center then x = 0 + offset.x y = rtPoint.y - b.size.y / 2 - offset.y elseif layoutType == layout_type.top_right then x = rtPoint.x - b.size.x / 2 - offset.x y = rtPoint.y - b.size.y / 2 - offset.y elseif layoutType == layout_type.center_left then x = lbPoint.x + b.size.x / 2 + offset.x y = 0 + offset.y elseif layoutType == layout_type.center_center then x = 0 + offset.x y = 0 + offset.y elseif layoutType == layout_type.center_right then x = rtPoint.x - b.size.x / 2 - offset.x y = 0 + offset.y elseif layoutType == layout_type.bottom_left then x = lbPoint.x + b.size.x / 2 + offset.x y = lbPoint.y + b.size.y / 2 + offset.y elseif layoutType == layout_type.bottom_center then x = 0 + offset.x y = lbPoint.y + b.size.y / 2 + offset.y elseif layoutType == layout_type.bottom_right then x = rtPoint.x - b.size.x / 2 - offset.x y = lbPoint.y + b.size.y / 2 + offset.y end local p = Vector3(x, y, 0) obj.transform.position = p end -- 判断 obj 的 bounds 是否在屏幕内 -- isOnlyPos 是否只判断坐标 function GameNodeAdapt:isInScreen(obj, isOnlyPos) if isOnlyPos == nil then isOnlyPos = true end local lbPoint = GameUtil:getCamera():ScreenToWorldPoint(Vector3(0, 0, 0)) local rtPoint = GameUtil:getCamera():ScreenToWorldPoint(Vector3(Screen.width, Screen.height, 0)) local x = obj.transform.position.x local y = obj.transform.position.y local minX, minY, maxX, maxY = x, y, x, y if not isOnlyPos then local b = util.BoundUtil:getObjBounds(obj) minX = minX - b.size.x / 2 maxX = maxX + b.size.x / 2 minY = minY - b.size.y / 2 maxY = maxY + b.size.y / 2 end return x <= rtPoint.x and x >= lbPoint.x and y >= lbPoint.y and y <= rtPoint.y end return GameNodeAdapt ponduireslinkreturn { --BASIC --ASSET pond_remove_stone_ui = {"Assets/AssetsPackage/Res/modules/ui/pondui/prefabs/pond_remove_stone_ui.prefab", 0, 0}, pond_fish_sales_ui = {"Assets/AssetsPackage/Res/modules/ui/pondui/prefabs/pond_fish_sales_ui.prefab", 0, 0}, } Assistant--[[ 助理 author:{zhangpeng} time:2025-05-30 15:16:05 ]] local Assistant,super = defClass("Assistant",Employe) local LOGTAG = "Assistant" function Assistant:ctor(reslink,cfgId) super.ctor(self, RoleConst.roleType.employee) self.R = reslink self.employeCfgId = cfgId self:init() end function Assistant:init() super.init(self) -- 巡逻状态 self.isPatrolling = false -- 当前闲逛点索引 self.currentWanderIndex = 1 -- 标记是否已经开始巡逻(避免重复启动) self.hasStartedPatrol = false printInfo(LOGTAG, string.format("助理[%s]初始化完成", self.employeCfgId)) -- 初始化时播放待机动画 self:playIdleAnim() end -- 员工行为,助理在闲逛点之间走动 function Assistant:action() -- 检查当前状态 if self.state == EmployeConst.State.Idle then -- 开始在闲逛点巡逻 self:startPatrolling() else -- 助理继续工作 self:continueWorking() end end -- 开始在闲逛点巡逻 function Assistant:startPatrolling() -- 如果已经在巡逻,则不重复开始 if self.isPatrolling then return end -- 获取闲逛点 if not self.wanderPoints or #self.wanderPoints == 0 then printWarn(LOGTAG, string.format("助理[%s]没有闲逛点", self.employeCfgId)) -- 如果没有闲逛点,播放待机动画 self:playIdleAnim() return end -- 设置巡逻标志 self.isPatrolling = true -- 如果还没有开始巡逻,延迟1秒开始(避免初始化时立即播放行走动画) if not self.hasStartedPatrol then self.hasStartedPatrol = true self:timer(function() if self.isPatrolling then -- 确保还在巡逻状态 self:moveToNextWanderPoint() end end, 1, 1) else -- 立即开始巡逻 self:moveToNextWanderPoint() end end -- 移动到下一个闲逛点 function Assistant:moveToNextWanderPoint() -- 检查是否还在巡逻状态 if not self.isPatrolling or self.state ~= EmployeConst.State.Idle then self.isPatrolling = false return end -- 前往指定的闲逛点 self:goToWanderPoint(self.currentWanderIndex, function() -- 到达闲逛点后,根据点位播放对应动画 self:playActionAtWanderPoint(self.currentWanderIndex) -- 在当前点停留3-5秒 local stayTime = math.random(3, 5) self:timer(function() if self.isPatrolling then -- 确保还在巡逻状态 -- 移动到下一个闲逛点 self.currentWanderIndex = self.currentWanderIndex + 1 if self.currentWanderIndex > #self.wanderPoints then self.currentWanderIndex = 1 -- 循环回到第一个点 end self:moveToNextWanderPoint() end end, stayTime, 1) end) end -- 前往指定的闲逛点 function Assistant:goToWanderPoint(pointIndex, callback) if not self.wanderPoints or pointIndex < 1 or pointIndex > #self.wanderPoints then if callback then callback() end return end local targetPoint = self.wanderPoints[pointIndex] if not targetPoint or not targetPoint.transform then if callback then callback() end return end local targetPos = targetPoint.transform.position local currentPos = self:getCurPosition() -- 计算距离,如果距离很近(小于0.5),直接执行回调,不需要移动 local distance = (targetPos - currentPos).magnitude if distance < 0.5 then if callback then callback() end return end -- 播放行走动画 self:playWalkAnim() -- 移动到该点 self:rolePathFind(targetPos, function() if callback then callback() end end) end -- 在不同的闲逛点播放不同的动画 function Assistant:playActionAtWanderPoint(pointIndex) if pointIndex == 1 then -- 点1播放调酒动画 self:playSpecialAnim() else -- 其他点播放待机动画 self:playIdleAnim() end end -- 播放特殊动画(调酒) function Assistant:playSpecialAnim() local actions = EmployeConst.EmployeeAction[self.employeCfgId] if actions and actions.special then self:playAnimation(actions.special, true) printInfo(LOGTAG, string.format("助理[%s]播放调酒动画: %s", self.employeCfgId, actions.special)) end end -- 停止巡逻 function Assistant:stopPatrolling() self.isPatrolling = false self.currentWanderIndex = 1 self.hasStartedPatrol = false end -- 继续工作 (预留接口,暂时只做巡逻) function Assistant:continueWorking() -- 如果状态不是空闲,暂停巡逻 if self.isPatrolling then self:stopPatrolling() end end -- 获取员工类型 function Assistant:getEmployeType() return self.employeCfgId end -- 设置状态 function Assistant:setState(state) self.state = state if self.employeeInfo then self.employeeInfo:setWorkState(state) end end -- 获取状态 function Assistant:getState() return self.state end return Assistant UpgradeMgr --- --- local UpgradeMgr = defClassStatic("UpgradeMgr") function UpgradeMgr:init() end --- 菜品是否可升级 function UpgradeMgr:cuisineCanUpgrade(upgradeId) local config = CuisineUpgradeCfgParse:getCuisineUpgradeCfg(upgradeId) if config.limitCond and (config.limitCond ~= -1) then return self:checkLimit(config.limitCond, config.limitArgs) end end --- 获取菜品卖出价格 function UpgradeMgr:getCuisineSaleIncome(upgradeId) local config = CuisineUpgradeCfgParse:getCuisineUpgradeCfg(upgradeId) return config.saleIncom end --- 获取升级条件描述 function UpgradeMgr:getCuisineUpgradeDesc(upgradeId) local config = CuisineUpgradeCfgParse:getCuisineUpgradeCfg(upgradeId) return UpgradeConst.CuisineLimitTypeDesc[config.limitCond]:format(self:getLimitValueName(config.limitCond, config.limitArgs)) end --- 检查购买限制 function UpgradeMgr:checkLimit(limitType, limitValue) if (not limitType) or limitType == -1 then return true end if limitType == UpgradeConst.CuisineLimitType.Star then return CurrencyMgr:getStar() >= limitValue elseif limitType == UpgradeConst.CuisineLimitType.Build then return UserDataMgr.buildingUserData:getBuildingData(limitValue) elseif limitType == UpgradeConst.CuisineLimitType.Task then return TaskMgr:getTaskState(limitValue) == TaskConst.TaskState.claimed elseif limitType == UpgradeConst.CuisineLimitType.Scene then return UserDataMgr:getSceneUnlocked(limitValue) elseif limitType == UpgradeConst.CuisineLimitType.Tag then return false elseif limitType == UpgradeConst.CuisineLimitType.Time then return false elseif limitType == UpgradeConst.CuisineLimitType.Employee then return EmployeMgr:getEmployeeInfo(limitValue):isPurchased() elseif limitType == UpgradeConst.CuisineLimitType.Cuisine then return UserDataMgr.cuisineUserData:getCuisineState(limitValue) >= CuisineConst.State.Purchased elseif limitType == UpgradeConst.CuisineLimitType.Event then return false end return false end --- 获取购买限制描述 function UpgradeMgr:getLimitValueName(limitType, limitValue) local valueName = nil if limitType == UpgradeConst.CuisineLimitType.Star then valueName = limitValue elseif limitType == UpgradeConst.CuisineLimitType.Build then valueName = BuildingMgr:getBuildingInfo(limitValue):getName() elseif limitType == UpgradeConst.CuisineLimitType.Task then valueName = TaskCfgParse:getTaskCfg(limitValue).desc elseif limitType == UpgradeConst.CuisineLimitType.Scene then valueName = SceneCfgParse:getSceneCfgById(limitValue).name elseif limitType == UpgradeConst.CuisineLimitType.Tag then valueName = limitValue elseif limitType == UpgradeConst.CuisineLimitType.Time then valueName = limitValue elseif limitType == UpgradeConst.CuisineLimitType.Employee then valueName = EmployeMgr:getEmployeeInfo(limitValue):getName() elseif limitType == UpgradeConst.CuisineLimitType.Cuisine then valueName = CuisineMgr:getCuisineInfo(limitValue):getName() elseif limitType == UpgradeConst.CuisineLimitType.Event then valueName = limitValue end return valueName end --- 获取升级限制描述 function UpgradeMgr:getLimitDesc(limitType, limitValue) return UpgradeConst.CuisineLimitTypeDesc[limitType]:format(limitValue) end UpgradeMgr:init()receptionreslinkreturn { --BASIC --ASSET reception_ui = {"Assets/AssetsPackage/Res/modules/ui/reception/prefabs/reception_ui.prefab", 0, 0}, } VendorCfgParse--[[ 摊主配置解析 author:{zhangpeng} time:2025-07-05 14:41:12 ]] local VendorCfgData = require("data/config/vendorCfg") local VendorCfgParse = defClassStatic("VendorCfgParse") local LOGTAG = "VendorCfgParse" local vendor_list = {} local function initAllIds() for k, v in pairs(VendorCfgData) do table.insert(vendor_list, v.id) end end function VendorCfgParse:init() initAllIds() end function VendorCfgParse:getVendorCfg(id) for k,v in pairs(VendorCfgData) do if v.id == id then return v end end return nil end function VendorCfgParse:getData() return VendorCfgData end function VendorCfgParse:getVendorCfgList() return vendor_list end -- 获取喜欢指定鱼的摊主配置列表 function VendorCfgParse:getVendorCfgListByFishId(id) local list = {} for _, vendor in ipairs(VendorCfgData) do for _, fishId in pairs(vendor.likefishish) do if fishId == id then table.insert(list, vendor) break end end end return list end -- 获取所有id function VendorCfgParse:getAllIds() return vendor_list end VendorCfgParse:init() OrderInfo--[[ 顾客点餐的订单信息, 包含顾客信息、座位信息、菜品信息、订单状态等 author:{zhangpeng} time:2025-05-16 10:26:59 ]] local OrderInfo = defClass("OrderInfo") local LOGTAG = "OrderInfo" -- 自增订单ID OrderInfo.nextOrderId = 1 function OrderInfo:ctor(args) -- 订单id self.orderId = OrderInfo.nextOrderId OrderInfo.nextOrderId = OrderInfo.nextOrderId + 1 -- 顾客对象 self.customer = args.customer -- 顾客id self.customerId = args.customerId -- 座位对象 self.seat = nil -- 菜品id self.cuisineId = args.cuisineId -- 订单时间 self.orderTime = args.orderTime or os.time() -- 烹饪时间 self.cookingTime = nil -- 订单状态 self.status = OrderConst.Status.created -- 订单处理模式:默认为手动处理 self.processMode = args.processMode or OrderConst.ProcessMode.manual -- 初始化 self:init() -- 如果args中有seat参数,则通过setSeat方法设置 if args.seat then self:setSeat(args.seat) end printInfo(LOGTAG, string.format("创建订单,订单ID:%s,顾客ID:%s,座位ID:%s,菜品ID:%s,处理模式:%s", self.orderId, self.customerId, self.seatId, self.cuisineId, OrderConst.ProcessModeDesc[self.processMode])) end function OrderInfo:init() -- 获取菜品信息 self.cuisineInfo = CuisineInfo.new(self.cuisineId) ---- 根据处理模式设置初始状态 --if self.processMode == OrderConst.ProcessMode.auto then -- -- 自动模式:直接设置为等待制作 -- self:setStatus(OrderConst.Status.waiting) --else -- -- 手动模式:设置为待确认处理 -- self:setStatus(OrderConst.Status.pending) --end self:setStatus(OrderConst.Status.pending) -- 从配置表中获取制作时间 local makeTime = self.cuisineInfo:getMakeTime() / BonusMgr:getCookEfficiencyBonus() self.cookingTime = makeTime end -- 获取订单ID function OrderInfo:getOrderId() return self.orderId end -- 获取菜品ID function OrderInfo:getCuisineId() return self.cuisineId end -- 获取菜品类型 function OrderInfo:getCuisineType() return self.cuisineInfo:getCuisineType() end -- 获取烹饪时间 function OrderInfo:getCookingTime() return self.cuisineInfo:getMakeTime() end -- 获取顾客 function OrderInfo:getCustomer() return self.customer end -- 获取座位 function OrderInfo:getSeat() return self.seat end -- 设置订单状态 function OrderInfo:setStatus(status) local oldStatus = self.status self.status = status printInfo(LOGTAG, string.format("订单[%s]状态变更:%s -> %s", self.orderId, self:getStatusString(oldStatus), self:getStatusString(status))) -- 状态变化时的处理 self:onStatusChanged(oldStatus, status) end -- 获取状态文本 function OrderInfo:getStatusString(status) return OrderConst.StatusDesc[status] end -- 设置订单座位信息 function OrderInfo:setSeat(seat) self.seat = seat -- 记录座位ID用于日志等 if seat then self.seatId = seat.seatIndex end end -- 状态变化时的处理 function OrderInfo:onStatusChanged(oldStatus, newStatus) -- 添加订单到订单管理系统 if oldStatus == OrderConst.Status.created then -- if newStatus == OrderConst.Status.waiting then -- -- 将订单添加到点菜管理器(自动处理模式) -- OrderDishesMgr:addOrder(self) -- -- 如果是自动模式,需要立即清理顾客头顶气泡 -- if self.processMode == OrderConst.ProcessMode.auto and self.customer then -- self.customer:removeOrderCuisineBubble() -- end -- elseif newStatus == OrderConst.Status.pending then if newStatus == OrderConst.Status.pending then -- 手动处理模式:等待点击头顶气泡确认 printInfo(LOGTAG, string.format("订单[%s]等待点击头顶气泡确认处理", self.orderId)) -- 将订单添加到等待确认队列 OrderDishesMgr:addPendingOrder(self) end elseif oldStatus == OrderConst.Status.pending and newStatus == OrderConst.Status.waiting then -- 从待确认状态变为等待制作状态(由点击头顶气泡触发,或者由服务员自动处理) -- 移除顾客头顶气泡 if self.customer then self.customer:removeOrderCuisineBubble() end -- 添加订单到制作队列 OrderDishesMgr:addOrder(self) end end -- 取消订单 function OrderInfo:cancel() self:setStatus(OrderConst.Status.cancelled) -- 通知顾客订单已取消 if self.customer then -- TODO: 实现顾客订单取消处理 end end -- 获取订单处理模式 function OrderInfo:getProcessMode() return self.processMode end -- 设置订单处理模式 function OrderInfo:setProcessMode(mode) local oldMode = self.processMode self.processMode = mode printInfo(LOGTAG, string.format("订单[%s]处理模式变更为:%s", self.orderId, OrderConst.ProcessModeDesc[mode])) -- 如果从手动模式变为自动模式,且订单状态是pending,自动触发订单处理 if oldMode == OrderConst.ProcessMode.manual and mode == OrderConst.ProcessMode.auto and self.status == OrderConst.Status.pending then -- 确认处理订单 self:confirmProcess() end end -- 确认处理订单(由点击头顶气泡触发) function OrderInfo:confirmProcess() -- 只有待确认处理状态才能确认处理 if self.status == OrderConst.Status.pending then printInfo(LOGTAG, string.format("订单[%s]确认处理", self.orderId)) -- 改变状态为等待制作 self:setStatus(OrderConst.Status.waiting) return true else printInfo(LOGTAG, string.format("订单[%s]当前状态[%s],不能确认处理", self.orderId, self:getStatusString(self.status))) return false end end return OrderInfo Scrolleru&--[[ 左右滚动 author:{zhangpeng} time:2022-09-29 17:55:31 ]] local Scroller, super = defClass("Scroller", SceneComponent) local Time = CS.UnityEngine.Time local Vector3 = CS.UnityEngine.Vector3 local Screen = CS.UnityEngine.Screen local Color = CS.UnityEngine.Color local Debug = CS.UnityEngine.Debug local LOG_TAG = "Scroller" local OUTDIR = { LEFT = 1, RIGHT = 2, IN = 3 } function Scroller:ctor(scene) super.ctor(self,scene) self.scene = scene self.scrollEndCbs = {} self.clickEffects = {} self.layerBounds = {} end function Scroller:onLoad() self:initMsg() end function Scroller:initScreenBounds() -- 获取相机视口上的屏幕边界,并用一个3D边界对象表示该边界,以确保滚动的容器不超出屏幕边界 local cam = self.scene.cams.scene local bottom_left = cam:ViewportToWorldPoint(Vector3(0, 0, 0))-- 相机左下角在世界坐标中的位置 local top_right = cam:ViewportToWorldPoint(Vector3(1, 1, 0)) --相机右上角在世界坐标中的位置 bottom_left.z = -10 top_right.z = 10 -- 屏幕在世界坐标中的边界 self.screenBounds = CS.UnityEngine.Bounds(CS.UnityEngine.Vector3.zero, CS.UnityEngine.Vector3.zero) self.screenBounds:SetMinMax(bottom_left, top_right) -- self:drawScreenBounds() end function Scroller:init(range, scrollContainer, touchNode) local touch_node = touchNode or self.scene.rootNode self:initTouch(touch_node) self.canScroll = true self:initScreenBounds() self:initTouch(touchNode) self.container = scrollContainer or touch_node:Seek("ScrollContainer") self.range = range self.rate = 0.6 self.speedFactor = 0.001 -- 容器滚动速度 self.containerScrollSpeed = 0 self.containerAutoScrollSpeed = 0.07 end function Scroller:initTouch(touchNode) self.touchCom = TouchCom.new(touchNode, GameUtil:getCamera()) self.touchCom:addListener( touchNode, TouchCom.LISTENER_TYPE.CUSTOM, function(p) self:onTouchBegan(p) return true end, function(p) self:onTouchMoved(p) end, function(p) self:onTouchEnd(p) end ):setSwallow(false) end function Scroller:initMsg() self:onmsg( { GameSceneMsg.InitScoller, GameSceneMsg.IndoorMapAutoScroll, GameSceneMsg.IndoorMapStopAutoScroll }, function (...) self:listener(...) end ) end function Scroller:listener(msgId, arg) if not self:valid() then return end if msgId == GameSceneMsg.InitScoller then self:init(arg.range, arg.scrollContainer, arg.touchNode) elseif msgId == GameSceneMsg.IndoorMapAutoScroll then self:autoScroll(data) elseif msgId == GameSceneMsg.IndoorMapStopAutoScroll then self:stopAutoScroll() end end function Scroller:addMoveEndCb(cb) table.insert(self.scrollEndCbs, cb) end -- 滚动到左边界回调 function Scroller:addMoveRangeMinCb(cb) self.moveRangMinCb = cb end -- 滚动到右边界的回调 function Scroller:addMoveRangeMaxCb(cb) self.moveRangMaxCb = cb end -- 执行边界回调 function Scroller:doMoveRangeMinCb() if self.moveRangMinCb and self.enableMoveMinCb then self.enableMoveMinCb = false self.moveRangMinCb() end end function Scroller:doMoveRangeMaxCb() if self.moveRangMaxCb and self.enableMoveMaxCb then self.enableMoveMaxCb = false self.moveRangMaxCb() end end function Scroller:doMoveEndCb() for _, f in ipairs(self.scrollEndCbs) do f() end end function Scroller:onTouchBegan(p) -- self:drawScreenBounds() self:enableMoveRangeEdgeCb() self.downWorldX = p.x self.downContainerX = self.container:GetPositionX() if self.moveAction then self.container:StopAction(self.moveAction) self.moveAction = nil end self. containerScrollSpeed = 0 local prevX = self.container:GetPositionX() local prevT = Time.time self.calcVelAct = self.container:Step(function() local x = self.container:GetPositionX() local t = Time.time self.containerScrollSpeed = (x - prevX) / (t - prevT) * 1.1 prevX = x prevT = t end) end function Scroller:onTouchMoved(p) local delta = p.x - self.downWorldX local dstX = self.downContainerX + delta dstX = self:checkRange(dstX) self:checkBounds() if not self.canScroll then return end Msg.send(GameSceneMsg.SceneMapScroll) self:updatePosX(dstX) end function Scroller:onTouchEnd(p) self.isTouchDown = false if math.abs(self.containerScrollSpeed) > 0 then local container = self.container local t = 0 local x0 = container:GetPositionX() local v = self.containerScrollSpeed local log = math.log local rate = self.rate -- 开始惯性滑动 self.moveAction = self.container:Step(function() t = t + Time.deltaTime local factor = rate ^ (10 * t) local dx = v * (factor - 1) * 0.03 / log(rate, 10) if factor > self.speedFactor then local x = self:checkRange(x0 + dx) self:checkBounds() self:updatePosX(x) else -- 结束惯性滚屏 self.moveAction = nil self:doMoveEndCb() self.containerScrollSpeed = 0 return true end end) else self:doMoveEndCb() end if self.calcVelAct then self.container:StopAction(self.calcVelAct) end end -- 强制设置到x(无滚动) function Scroller:focusToX(x) local dstX = self:checkRange(-x) self:checkBounds() printInfo(LOG_TAG, string.format("focusToX: %s", x) ) self:updatePosX(dstX) end -- 强制滚动到x function Scroller:focusScrollToX(x,cb) if self.moveAction then self.container:StopAction(self.moveAction) self.moveAction = nil end if self.focusAct then return end self.touchCom:disable() local srcX = self.container:GetPositionX() local dstX = -x self.focusAct = self.container:RunAction( ua.Sequence({ ua.Tween(0.5,function(r) local _x = srcX * (1 - r) + dstX * r _x = self:checkRange(_x) self:checkBounds() self:updatePosX(_x) end), ua.cb(function() self.focusAct = nil dstX = self:checkRange(dstX) self:checkBounds() self:updatePosX(dstX) self.touchCom:enable() if cb then cb() end end), }) ) end function Scroller:checkBounds() end -- 滚动范围控制在range范围内 function Scroller:checkRange(dstX) local range = self.range if dstX > range.xMin then dstX = range.xMin self:doMoveRangeMinCb() elseif dstX < -range.xMax then self:doMoveRangeMaxCb() dstX = -range.xMax end return dstX end function Scroller:updatePosX(x) local difPosX = x - self.container:GetPositionX() Msg.send(GameSceneMsg.SceneMapRefreshPos, { difPosX = difPosX }) self.container:SetPositionX(x) end -- 开始拖拽物品的时候,禁止滚动 function Scroller:disableScroll() self.canScroll = false end function Scroller:enableScroll() self.canScroll = true end -- 简介检测回调可用(滑动中启用一次,回调之后禁用,再次滑动重新启用) function Scroller:enableMoveRangeEdgeCb() self.enableMoveMinCb = true self.enableMoveMaxCb = true end function Scroller:autoScroll(dir) local posX = self:getPosX() if dir == OUTDIR.LEFT then posX = posX - self.containerAutoScrollSpeed elseif dir == OUTDIR.RIGHT then posX = posX + self.containerAutoScrollSpeed end posX = self:checkRange(posX) self.needAutoScroll = true self:updatePosX(posX) end function Scroller:isScrolling(threshold) threshold = threshold or 0.01 if math.abs(self.containerScrollSpeed) > threshold then return true end return false end function Scroller:getContainer() return self.container end function Scroller:setContainer(container) printInfo(LOG_TAG, string.format("设置容器: %s", container.name)) self.container = container end function Scroller:getPosX() return self.container:GetPositionX() end function Scroller:stopAutoScroll() self.needAutoScroll = false end function Scroller:setRate(rate) if rate > 1 or rate < 0 then self.rate = 0.6 printError(LOG_TAG, string.format("rate值设置应在0-1的范围内")) end self.rate = rate end function Scroller:setSpeedFactor(factor) self.speedFactor = factor end function Scroller:reset() self.canScroll = true end function Scroller:onExit() super.onExit(self) end function Scroller:drawBounds() -- 计算屏幕上的范围线位置 local minBounds = Vector3(self.range.xMin + self.container:GetPositionX(), 0, 0) local maxBounds = Vector3(-self.range.xMax + self.container:GetPositionX(), 0, 0) Debug.DrawLine(minBounds, Vector3(minBounds.x, Screen.height, 0), Color.green) Debug.DrawLine(Vector3(maxBounds.x, 0, 0), Vector3(maxBounds.x, Screen.height, 0), Color.red) end function Scroller:drawScreenBounds() local minBounds = self.screenBounds.min local maxBounds = self.screenBounds.max Debug.DrawLine(minBounds, Vector3(minBounds.x, Screen.height, 0), Color.green) Debug.DrawLine(Vector3(maxBounds.x, 0, 0), Vector3(maxBounds.x, Screen.height, 0), Color.red) end return Scroller CuisineListUI-- 菜品ui ---@class CuisineListUI:UILayer local CuisineListUI, super = defClass("CuisineListUI", UILayer) -- view require("modules/ui/cuisineui/CuisineCell") require("modules/ui/cuisineui/MusicBbqCuisineCell") -- component local Toggle = CS.UnityEngine.UI.Toggle ---@param pageId number 页签id ---@param pageType number 此页内容分类 function CuisineListUI:ctor(pageId, pageType) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/cuisineui/cuisineuireslink") self.pageId = pageId or BuildingConst.buildingCategoryId.restaurant self.pageType = pageType or CuisineConst.CuisineType.default self.cellList = {} self.musicBbqCellList = {} end function CuisineListUI:onLoad() self.ui = GameObject.Instantiate(self.R.cuisine_list_ui) self:addChild(self.ui) --self:useTweenOnOpen(self.ui) self:initUI() self:triggerPage() --local layout = self.ui:Seek("Content"):GetComponent("ContentSizeFitter") --layout:SetLayoutVertical() end --- 初始化 function CuisineListUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.toggle_restaurant = self.ui:Seek("toggle_restaurant") self.content_restaurant = self.ui:Seek("content_restaurant") self.toggle_restaurant_mask = self.ui:Seek("toggle_restaurant_mask") self.toggle_barbecue = self.ui:Seek("toggle_barbecue") self.content_barbecue = self.ui:Seek("content_barbecue") self.toggle_barbecue_mask = self.ui:Seek("toggle_barbecue_mask") self.toggle_type1 = self.ui:Seek("toggle_type1") self.toggle_type1_mask = self.ui:Seek("toggle_type1_mask") self.toggle_type2 = self.ui:Seek("toggle_type2") self.toggle_type2_mask = self.ui:Seek("toggle_type2_mask") self.toggle_type3 = self.ui:Seek("toggle_type3") self.toggle_type3_mask = self.ui:Seek("toggle_type3_mask") self.barbecue_locked = self.ui:Seek("barbecue_locked") self.toggle_sub = self.ui:Seek("toggle_sub") self.scrollView = self.ui:Seek("Scroll View") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addToggleValueChangeEvent(self.toggle_restaurant, function(isOn) self:restaurantTab(isOn) end) util.ugui.addToggleValueChangeEvent(self.toggle_barbecue, function(isOn) self:barbecueTab(isOn) end) util.ugui.addToggleValueChangeEvent(self.toggle_type1, function(isOn) self:type1(isOn) end) util.ugui.addToggleValueChangeEvent(self.toggle_type2, function(isOn) self:type2(isOn) end) util.ugui.addToggleValueChangeEvent(self.toggle_type3, function(isOn) self:type3(isOn) end) util.ugui.addButtonClickEvent(self.barbecue_locked, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) PopUpUI.new("敬请期待"):show():showMask():enableCloseWhenClickMask() end) end function CuisineListUI:triggerPage() if self.pageId == BuildingConst.buildingCategoryId.restaurant then self.toggle_restaurant[Toggle].isOn = true self:barbecueTab(false) elseif self.pageId == BuildingConst.buildingCategoryId.barbecue then self.toggle_barbecue[Toggle].isOn = true self:restaurantTab(false) end end function CuisineListUI:triggerType() if self.pageId == BuildingConst.buildingCategoryId.restaurant then if self.pageType == CuisineConst.CuisineType.default then self.toggle_type1[Toggle].isOn = true elseif self.pageType == CuisineConst.CuisineType.dish then self.toggle_type2[Toggle].isOn = true elseif self.pageType == CuisineConst.CuisineType.drink then self.toggle_type3[Toggle].isOn = true end end if self.pageId == BuildingConst.buildingCategoryId.barbecue then self.toggle_type1[Toggle].isOn = false self.toggle_type2[Toggle].isOn = false self.toggle_type3[Toggle].isOn = false self:type4(self.pageType == CuisineConst.CuisineType.barbecue) end end --- 页签切换 function CuisineListUI:restaurantTab(isOn) self.content_restaurant:SetActive(isOn) self.toggle_restaurant_mask:SetActive(isOn) self.toggle_sub:SetActive(isOn) if isOn then self.pageId = BuildingConst.buildingCategoryId.restaurant self.pageType = CuisineConst.CuisineType.default self:triggerType() printInfo("CuisineListUI", "restaurantTabOn: %s", isOn) end end --- 页签切换 function CuisineListUI:barbecueTab(isOn) self.content_barbecue:SetActive(isOn) self.toggle_barbecue_mask:SetActive(isOn) if isOn then self.pageId = BuildingConst.buildingCategoryId.barbecue self.pageType = CuisineConst.CuisineType.barbecue self:triggerType() printInfo("CuisineListUI", "barbecueTabOn: %s", isOn) end end --- 指定类型展示 function CuisineListUI:type1(isOn) self.toggle_type1_mask:SetActive(isOn) if isOn then self.pageType = CuisineConst.CuisineType.default self:showAll() end end --- 指定类型展示 function CuisineListUI:type2(isOn) self.toggle_type2_mask:SetActive(isOn) if isOn then self.pageType = CuisineConst.CuisineType.dish self:showAll() end end --- 指定类型展示 function CuisineListUI:type3(isOn) self.toggle_type3_mask:SetActive(isOn) if isOn then self.pageType = CuisineConst.CuisineType.drink self:showAll() end end --- 指定类型展示 function CuisineListUI:type4(isOn) if isOn then self.pageType = CuisineConst.CuisineType.barbecue self:showAll() end end --- 创建所有对象 function CuisineListUI:showAll() local data = nil if self.pageType == CuisineConst.CuisineType.default then data = CookBookCfgParse:getDataByTypeList({ CuisineConst.CuisineType.dish, CuisineConst.CuisineType.drink, }) else data = CookBookCfgParse:getDataByType(self.pageType) end printInfo("CuisineListUI", "showAll 数量%d", #data) --self:clearAll() if self.pageId == BuildingConst.buildingCategoryId.restaurant then for i, value in ipairs(data) do self:getCell(value.id, self.content_restaurant.transform, i) end if #self.cellList > #data then for i = #data + 1, #self.cellList do self.cellList[i]:hide() end end elseif self.pageId == BuildingConst.buildingCategoryId.barbecue then for i, value in ipairs(data) do self:getMusicBbqCell(value.id, self.content_barbecue.transform, i) end if #self.musicBbqCellList > #data then for i = #data + 1, #self.musicBbqCellList do self.musicBbqCellList[i]:hide() end end end end function CuisineListUI:getCell(id, parent, idx) if idx < #self.cellList then self.cellList[idx]:reload(id, parent) return self.cellList[idx] else local cell = CuisineCell.new(id, self.R.cuisine_cell, parent) table.insert(self.cellList, cell) return cell end end function CuisineListUI:getMusicBbqCell(id, parent, idx) if idx < #self.musicBbqCellList then self.musicBbqCellList[idx]:reload(id, parent) return self.musicBbqCellList[idx] else local cell = MusicBbqCuisineCell.new(id, self.R.music_bbq_cuisine_cell, self.content_barbecue.transform) table.insert(self.musicBbqCellList, cell) return cell end end return CuisineListUI HelpCfgParselocal HelpCfgData = require("data/config/helpCfg") local HelpCfgParse = defClassStatic("HelpCfgParse") function HelpCfgParse:init() end function HelpCfgParse:getData() return HelpCfgData end function HelpCfgParse:getDataByHelpType(helpType) local list = {} for i, v in pairs(HelpCfgData) do if v.helpType == helpType then table.insert(list, v) end end return list end HelpCfgParse:init() DollConst-- 玩偶常量 local DollConst = defClassStatic("DollConst") function DollConst:init() end -- 玩偶来源 DollConst.Way = { -- 扭蛋 Gacha = 1, -- 其他 Other = 2, } -- 玩偶来源描述 DollConst.WayDesc = { [DollConst.Way.Gacha] = "扭蛋机", [DollConst.Way.Other] = "其他", } -- 玩偶奖励类型 DollConst.RewardType = { -- 星星 Star = 1, -- 音符 MusicalNotes = 2, -- 顾客 Customer = 3, } -- 玩偶状态 DollConst.State = { -- 未领取 None = 1, -- 已领取 Received = 2, } DollConst:init()CustomerNormalVisit--- 顾客来访 ---@class CustomerNormalVisit:LuaClass ---@field isNewPlayer boolean 是否新手 ---@field naturalFlowQueue number[] 自然流顾客队列 ---@field naturalFlowLimit number 自然流顾客上限 ---@field naturalFlowNumberToday number 当日自然流顾客数量 ---@field naturalFlowLastUpdate number 上一个顾客来访时间 ---@field naturalFlowInterval number 间隔来访时间 local CustomerNormalVisit = defClass("CustomerNormalVisit") function CustomerNormalVisit:ctor() self:init() end -- 初始化 function CustomerNormalVisit:init() self.naturalFlowQueue = {} self.naturalFlowLimit = 300 self.naturalFlowInterval = math.random(1, 2) self:initUserData() end -- 用户数据 function CustomerNormalVisit:initUserData() self.naturalFlowLastUpdate = CustomerMgr.time self.naturalFlowNumberToday = CustomerMgr.userData:getTotalCustomerVisitsToday() end -- 更新今天的自然流顾客来访次数 function CustomerNormalVisit:updateVisitsToday() self.naturalFlowNumberToday = CustomerMgr.userData:getTotalCustomerVisitsToday() end -- 开始 function CustomerNormalVisit:onStart() if UserDataMgr.isNewPlayer then self:onEnteringGameNewPlayer() else self:onEnteringGame() end end -- 更新 -- @param time number 在线时长 function CustomerNormalVisit:timeUpdate(time) if UserDataMgr.isNewPlayer then -- 是否为新手 self:addNaturalFlowNewPlayer(time) else self:addNaturalFlow(time) end end -- 刚进入游戏时添加顾客队列 新手 function CustomerNormalVisit:onEnteringGameNewPlayer() for _ = 1, 5 do self:enqueue(CustomerMgr:judgeCustomerId()) end end -- 判断增加自然流顾客 新手 -- @param time number 在线时长 function CustomerNormalVisit:addNaturalFlowNewPlayer(time) if self.naturalFlowNumberToday >= self.naturalFlowLimit then -- 到达上限 return end if time - self.naturalFlowLastUpdate < self.naturalFlowInterval then -- 确定的时间间隔 触发 return end self.naturalFlowLastUpdate = time -- 更新时间 if time <= 300 then -- 重新设置间隔 self.naturalFlowInterval = math.random(30, 40) else self.naturalFlowInterval = math.random(60, 90) end local id = CustomerMgr:judgeCustomerId() -- 获取随机id进入队列 self:enqueue(id) self.naturalFlowNumberToday = self.naturalFlowNumberToday + 1 -- 今日个数增加 end -- 判断增加自然流顾客 -- @param time number 在线时长 function CustomerNormalVisit:addNaturalFlow(time) if self.naturalFlowNumberToday >= self.naturalFlowLimit then -- 到达上限 return end if time - self.naturalFlowLastUpdate < self.naturalFlowInterval then -- 确定的时间间隔 触发 return end self.naturalFlowLastUpdate = time -- 更新时间 if time <= 300 then -- 重新设置间隔 self.naturalFlowInterval = math.random(30, 40) else self.naturalFlowInterval = math.random(60, 90) end local id = CustomerMgr:judgeCustomerId() -- 获取随机id进入队列 self:enqueue(id) self.naturalFlowNumberToday = self.naturalFlowNumberToday + 1 -- 今日个数增加 end -- 入队列 -- @param customerId number 顾客id function CustomerNormalVisit:enqueue(customerId) table.insert(self.naturalFlowQueue, customerId) end -- 出队列 -- @return number 顾客id function CustomerNormalVisit:dequeue() return table.remove(self.naturalFlowQueue, 1) end return CustomerNormalVisitmusicBbqCustomerCfg--[[ from file:烤吧特殊顾客.xlsx --]] local musicBbqCustomerCfg = { [1] = { id = 490001, name = "阿布", moveSpeed = 100, leadId = 400001, teamIds = 400002, chartered = 1, repastMin = 180, repastMax = 240, time = 30, musicMin = 10, musicMax = 20, plot_1 = 649011, plot_2 = 649012, }, [2] = { id = 490002, name = "小伞", moveSpeed = 100, leadId = 400002, teamIds = {400005,400006,400007}, chartered = 1, repastMin = 240, repastMax = 300, time = 20, musicMin = 20, musicMax = 30, plot_1 = 649021, plot_2 = 649022, }, [3] = { id = 490003, name = "豆豆", moveSpeed = 100, leadId = 400003, teamIds = {400010,400011}, chartered = 0, repastMin = 120, repastMax = 150, time = 40, musicMin = 15, musicMax = 25, plot_1 = 649031, plot_2 = 649032, }, [4] = { id = 490004, name = "雪绒", moveSpeed = 100, leadId = 400014, teamIds = {400002,400003,400004}, chartered = 0, repastMin = 300, repastMax = 450, time = 20, musicMin = 5, musicMax = 10, plot_1 = 649041, plot_2 = 649042, }, [5] = { id = 490005, name = "泡芙", moveSpeed = 100, leadId = 400005, teamIds = {400001,400003,400004}, chartered = 1, repastMin = 150, repastMax = 200, time = 15, musicMin = 10, musicMax = 30, plot_1 = 649051, plot_2 = 649052, }, } return musicBbqCustomerCfg FacebookShareUtilK--[[ fb分享 author:{zhangpeng} time:2023-12-26 11:10:34 ]] TimelineUtilX  util.timeline = {} local GameObject = CS.UnityEngine.GameObject local PlayableDirector = CS.UnityEngine.Playables.PlayableDirector local function getTimeline(timelineOrGo) local t = timelineOrGo:GetType() if t == typeof(GameObject) then return timelineOrGo:GetComponent(typeof(PlayableDirector)) elseif t == typeof(PlayableDirector) then return timelineOrGo else CS.UnityEngine.Debug.LogError("TimelineUtil:第一参数不是GameObject或者PlayableDirector,而是" .. tostring(t)); end end function util.timeline.getTimeline( timelineOrGo ) return getTimeline(timelineOrGo) end function util.timeline.play(timelineOrGo,finishCb,isLoop) local timeline = getTimeline(timelineOrGo) local func func = function() if not timeline.gameObject.scene.isLoaded then return--场景销毁时同样会触发stopped事件,时机早于OnDisable和OnDestroy,所以只能靠此判断 end timeline.gameObject.__tlFinishCB__ = nil timeline:stopped("-", func) if finishCb then finishCb() end func = nil end if timeline.gameObject.__tlFinishCB__ then timeline:stopped("-", timeline.gameObject.__tlFinishCB__) timeline.gameObject.__tlFinishCB__ = nil end timeline:stopped("+", func) timeline.gameObject.__tlFinishCB__ = func Event.add(timeline.gameObject,Event.OnDestroy,function() if func then timeline:stopped("-", func) func = nil end end) if isLoop then timeline:Play(timeline.playableAsset, CS.UnityEngine.Playables.DirectorWrapMode.Loop) else timeline:Play() end return timeline end function util.timeline.stop(timelineOrGo) local function resetTl( timeline ) timeline:ClearAllSignalCb() timeline.time = timeline.duration timeline:RebuildGraph() timeline:Evaluate() timeline:Stop() end local timeline = getTimeline(timelineOrGo) -- stop之后,必须清掉回调,要不然还是会调用 if timeline.gameObject.__tlFinishCB__ then timeline:stopped("-", timeline.gameObject.__tlFinishCB__) timeline.gameObject.__tlFinishCB__ = nil end resetTl(timeline) end function util.timeline.reset(timelineOrGo) local function resetTl( timeline ) timeline:ClearAllSignalCb() timeline.time = 0 timeline:RebuildGraph() timeline:Evaluate() timeline:Stop() end local timeline = getTimeline(timelineOrGo) if timeline.gameObject.__tlFinishCB__ then timeline:stopped("-", timeline.gameObject.__tlFinishCB__) timeline.gameObject.__tlFinishCB__ = nil end resetTl(timeline) end function util.timeline.pause(timelineOrGo) local timeline = getTimeline(timelineOrGo) timeline:Pause() end TextCfgParse--[[ 多语言配置表 author:{zhangpeng} time:2024-11-20 11:54:36 ]] local TextCfgData = require("common/data/static_config/config/TextCfg") local TextCfgParse = defClassStatic("TextCfgParse") local LOG_TAG = "TextCfgParse" TextCfgParse.Lan = { cn = 1, en = 2, jp = 3 } local text_list = {} local function initAllIds() for k, v in pairs(TextCfgData) do table.insert(text_list, v.textId) end end -- usage: -- local str = TextCfgParse:getTextStr("login_deviceid_suc",TextCfgParse.Lan.en) -- login_deviceid_suc 在TextCfg中定义 function TextCfgParse:getTextStr(id, language) if language == "cn" then language = TextCfgParse.Lan.cn elseif language == "en" then language = TextCfgParse.Lan.en elseif language == "jp" then language = TextCfgParse.Lan.jp end local result for k,v in pairs(TextCfgData) do if v.textId == id then result = v break end end if language == TextCfgParse.Lan.cn then return result.cn elseif language == TextCfgParse.Lan.en then return result.en elseif language == TextCfgParse.Lan.jp then return result.jp else local str = string.format("not define key:%s", id) printWarn(LOG_TAG, str) return str end end function TextCfgParse:init() initAllIds() end TextCfgParse:init()PassivityDeskMgr --[[ author:{zhangpeng} time:2025-05-16 17:29:50 ]] local PassivityDeskMgr = defClassStatic("PassivityDeskMgr") local LOGTAG = "PassivityDeskMgr" -- init function PassivityDeskMgr:init() printInfo(LOGTAG, "------ 闲逛建筑管理 初始化 ------") self.passivityDesks = {} end -- 根据建筑id创建一个闲逛建筑 function PassivityDeskMgr:createPassivityDesk(buildingInfo) local buildingType = buildingInfo.buildingType local parentNode = RestaurantMgr:getRestaurantScene().rootNode:Seek("building_pos_" .. buildingType) local args = { deskId = buildingInfo.buildingCfgId, parent = parentNode, scene = RestaurantMgr:getRestaurantScene(), } local passivityDesk = nil if not self.passivityDesks then self.passivityDesks = {} end if self:hasSameTypeDesk(buildingType) then self:replaceOldDesk(buildingType, buildingInfo.buildingCfgId) else passivityDesk = PassivityDesk.new(args) table.insert(self.passivityDesks, passivityDesk) end return passivityDesk end -- 是否有同类不同id的闲逛建筑 function PassivityDeskMgr:hasSameTypeDesk(buildingType) for i = 1, table.nums(self.passivityDesks) do local passivityDesk = self.passivityDesks[i] if passivityDesk.buildingInfo.buildingType == buildingType then return true end end return false end -- 查找相同类型建筑的旧闲逛建筑,替换旧的闲逛建筑 -- @param buildingType 建筑类型 -- @param newDeskCfgId 新闲逛建筑配置id function PassivityDeskMgr:replaceOldDesk(buildingType, newDeskCfgId) for i = 1, #self.passivityDesks do local passivityDesk = self.passivityDesks[i] -- 大类相同,配置id不同,则是旧闲逛建筑 if passivityDesk.buildingInfo.buildingType == buildingType and passivityDesk.buildingInfo.buildingCfgId ~= newDeskCfgId then -- 只修改一下prefab的名字为配置id passivityDesk.buildingNode:SetName(newDeskCfgId) break end end end -- 启动逻辑 function PassivityDeskMgr:start() end -- 获取所有消费建筑 function PassivityDeskMgr:getAllPassivityDesks() return self.passivityDesks end -- 获取指定ID的消费建筑 function PassivityDeskMgr:getPassivityDeskById(deskId) for _, desk in ipairs(self.passivityDesks) do if desk.deskId == deskId then return desk end end return nil end return PassivityDeskMgr SingleSqliteTablen --[[ 仅有单条model的sqlite数据库表 不需要指定和使用主键key ]] ---@class SingleSqliteTable:SqliteTable local SingleSqliteTable, super = defClass("SingleSqliteTable", SqliteTable) local LOGTAG = SingleSqliteTable.__cls_name SingleSqliteTable.KEY_NAME = "Singlekey" SingleSqliteTable.KEY_VALUE = 1 function SingleSqliteTable:beforeInit() self:createKeyColumn() end function SingleSqliteTable:createKeyColumn() return self:c(SingleSqliteTable.KEY_NAME, SingleSqliteTable.KEY_VALUE, true) end function SingleSqliteTable:create() return super.create(self, SingleSqliteTable.KEY_VALUE) end function SingleSqliteTable:get() return super.get(self, SingleSqliteTable.KEY_VALUE) end function SingleSqliteTable:getOrCreate() return super.getOrCreate(self, SingleSqliteTable.KEY_VALUE) end function SingleSqliteTable:add(model) if model[SingleSqliteTable.KEY_NAME] ~= SingleSqliteTable.KEY_VALUE then printError(LOGTAG,"add, key错误:%s", model[SingleSqliteTable.KEY_NAME]) return end return super.add(self, model) end function SingleSqliteTable:upd(model) if model[SingleSqliteTable.KEY_NAME] ~= SingleSqliteTable.KEY_VALUE then printError(LOGTAG,"upd, key错误:%s", model[SingleSqliteTable.KEY_NAME]) return end return super.upd(self, model) end function SingleSqliteTable:set(model) if model[SingleSqliteTable.KEY_NAME] ~= SingleSqliteTable.KEY_VALUE then printError(LOGTAG,"set, key错误:%s", model[SingleSqliteTable.KEY_NAME]) return end return super.set(self, model) end return SingleSqliteTable ApplovinMgr --[[ author:{zhangpeng} time:2024-04-25 14:52:46 ]] local ApplovinMgr, super = defClassStatic("ApplovinMgr") local LOG_TAG = "ApplovinMgr" local AdConst = ApplovinConst local IOS_NATIVE_CLASS_NAME = "AppLovinMgr" local JavaADClass = "com/fy/xgame/tilelink/ads/AdManager" function ApplovinMgr:init() self:registLuaCallBack() end function ApplovinMgr:registLuaCallBack() local callback = function(value) if value == "initServer" then return end local decoded_data = json.decode(value) local type = decoded_data.type local code = decoded_data.code local placement = decoded_data.placement printInfo(LOG_TAG, string.format("Ad Type: %s Code: %s", type, code)) if type == AdConst.AdType.EAdTypeRewardedAd then -- 激励视频 elseif type == AdConst.AdType.EAdTypeInterstitialAd then -- 插屏广告 end self:onAdStateCodeChange(code, decoded_data) printInfo(LOG_TAG, "onAdStateCodeCb to lua suc") end if Device.isIOS() then local param = { onAdStateCodeCb = callback, } luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "registLuaCallback", param) elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "registLuaCallback", { callback }) end end function ApplovinMgr:adDidLaunch() if Device.isIOS() then luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "adDidLaunch") elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "adDidLaunch", { }) end end -- 显示一个广告 function ApplovinMgr:showRewardAdByPlacement(placement, showcb) if not Device:isIOS() and not Device:isAndroid() then UIComsTool:showToast(TextCfgParse:getTextStr("ad_not_mobile_device",TextCfgParse.Lan.en), 2) return end local actionNode = SceneMgr:getCurScene().logicNode self.startLoadTime = os.time() actionNode.gameObject:WaitUntil( function () -- condition local ready = self:isRewardedAdReady() self.readyOnce = ready if ready then printInfo(LOG_TAG, string.format("广告就绪:%s",ready)) else local useTime = os.time() - self.startLoadTime if useTime > ApplovinConst.adLoadTimeMax then return true end end UIComsTool:showLoading() return ready end, function () if self.readyOnce then -- show ad if showcb then showcb() end UIComsTool:hideLoading(true) printInfo(LOG_TAG, "开始展示广告") ApplovinMgr:showRewardedAd(placement) else UIComsTool:hideLoading(true) UIComsTool:showToast("no ads available") end end ) end -- 根据广告标识符向服务器请求奖励 function ApplovinMgr:requestAwardByPlacementType(placement) end -- ad状态码发生变化时的回调 function ApplovinMgr:onAdStateCodeChange(code, decodeData) if code == AdConst.AdCode.ESAdCodeLoadSucceeded then local data = json.decode(decodeData.data) if data then printInfo(LOG_TAG,"code::" .. code) printInfo(LOG_TAG,"Ad 单元标识符: " .. data[AdConst.DataEnum.ad_unit_identifier]) printInfo(LOG_TAG,"Ad 国家代码: " .. data[AdConst.DataEnum.country]) printInfo(LOG_TAG,"Ad 创意标识符: " .. data[AdConst.DataEnum.creative_identifier]) printInfo(LOG_TAG,"Ad 网络名称: " .. data[AdConst.DataEnum.network_name]) printInfo(LOG_TAG,"Ad 网络放置位置: " .. data[AdConst.DataEnum.network_placement]) printInfo(LOG_TAG,"Ad 平台: " .. data[AdConst.DataEnum.platform]) printInfo(LOG_TAG,"Ad 收入: " .. data[AdConst.DataEnum.revenue]) printInfo(LOG_TAG,"Ad 收入精度: " .. data[AdConst.DataEnum.revenue_precision]) end UIComsTool:showToast(TextCfgParse:getTextStr("ad_load_suc"),1) -- 通知红点刷新 elseif code == AdConst.AdCode.ESAdCodeDidReward then -- self:updateAdTodayWatchTimes() -- self:updateAllAdWatchTimes() local placement = decodeData.placement -- 发领奖消息 Msg.send(Msg.AD_REWARD_VIEWO_WATCH_SUC, {placeId = placement}) elseif code == AdConst.AdCode.ESAdCodeHide then -- todo:: 恢复之前被暂停的游戏音乐等 end end -- 广告测试模式 function ApplovinMgr:showMediationDebugger() if Device.isIOS() then luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "showMediationDebugger") elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "showMediationDebugger", { }) end end -----------------------------------banner------------------------------------- function ApplovinMgr:loadBannerAd() if Device.isIOS() then elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "loadBannerAd", { }) end end -----------------------------------激励视频------------------------------------- function ApplovinMgr:setNeedLoadRewardedAd(need) local _need if need == true then _need = "true" elseif need == false then _need = "false" end if Device.isIOS() then local param = {need = _need} luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "setNeedLoadRewardedAdWithDictionary", param) elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "setNeedLoadRewardedAd", { need }) end end function ApplovinMgr:loadRewardedAd() if Device.isIOS() then luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "loadRewardedAd") elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "loadRewardedAd", { }) end end -- 主动检测是否有可用的激励视频 function ApplovinMgr:isRewardedAdReady() if Device.isIOS() then local ok,ret = luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "isRewardedAdReady") return ret elseif Device.isAndroid() then local ok, re = luaj.callStaticMethod(JavaADClass, "isRewardedAdReady", {}, "()Z") return re else return false end end function ApplovinMgr:showRewardedAd(placementName) if Device.isIOS() then local param = {placement = placementName} luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "showRewardedAdWithDictionary", param) elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "showRewardedAd", {placementName}) end end ----------------------------------- 广告观看次数统计 ------------------------------------- -- 刷新每日观看次数 function ApplovinMgr:updateAdTodayWatchTimes() local userData = User:getUserTableData() local curtimes = userData:getTodayAdWatchTimes() curtimes = curtimes + 1 userData:setTodayAdWatchTimes(curtimes) userData:save() end -- 刷新总观看次数 function ApplovinMgr:updateAllAdWatchTimes() local userData = User:getUserTableData() local cur_all_times = userData:getAllAdWatchTimes() cur_all_times = cur_all_times + 1 userData:setAllAdWatchTimes(cur_all_times) userData:save() end -- 返回今日观看次数 function ApplovinMgr:getTodayAdWatchTimes() local userData = User:getUserTableData() local todaytimes = userData:getTodayAdWatchTimes() return todaytimes end -- 返回总观看次数 function ApplovinMgr:getAllAdWatchTimes() local userData = User:getUserTableData() local cur_all_times = userData:getAllAdWatchTimes() return cur_all_times end -- 根据广告位名字取奖励内容 function ApplovinMgr:getAwardByPlacementId(placement) end ApplovinMgr:init()HelpCell--- 帮助单元格 ---@class HelpCell : LuaClass local HelpCell = defClass("HelpCell") require("modules/ui/helpui/HelpDetailUI") function HelpCell:ctor(go, helpCfg) self.ui = go self.config = helpCfg local title = self.ui:Seek("title") local button = self.ui:Seek("button") util.ugui.addButtonClickEvent(button, function () AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:click() end) title:GetComponent("TextMeshProUGUI").text = self.config.question end function HelpCell:click() local view = HelpDetailUI.new(self.config):show():showMask():enableCloseWhenClickMask() end return HelpCelltaskcellreslinkreturn { --BASIC --ASSET cell_daily = {"Assets/AssetsPackage/Res/modules/ui/task/prefabs/cell_daily.prefab", 0, 0}, } SettingUserDataV -- 用户数据 设置 local SettingUserData = defClass("SettingUserData") -- 构造函数 function SettingUserData:ctor() self:init() end -- 初始化 function SettingUserData:init() -- 背景音乐是否开启 self.background_music_isOn = true -- 音效是否开启 self.sound_effect_isOn = true -- 如果是新玩家,重置数据 if UserDataMgr.isNewPlayer then self:resetData() -- 新玩家重置数据 end end -- 重置数据 function SettingUserData:resetData() self.background_music_isOn = true self.sound_effect_isOn = true self:save() -- 保存到本地 end -- 从本地或服务器加载数据 function SettingUserData:load() if UserDataMgr.isNewPlayer then return end self:loadFromLocal() end -- 从本地加载数据 function SettingUserData:loadFromLocal() self.background_music_isOn = PlayerPrefsMgr:getInt(StrogeKeyDef.background_music_isOn, 1) == 1 self.sound_effect_isOn = PlayerPrefsMgr:getInt(StrogeKeyDef.sound_effect_isOn, 1) == 1 end -- 从服务器加载数据 function SettingUserData:loadFromServer() end -- 保存数据到本地和服务器 function SettingUserData:save() self:saveToLocal() end -- 保存数据到本地 function SettingUserData:saveToLocal() PlayerPrefsMgr:setInt(StrogeKeyDef.background_music_isOn, self.background_music_isOn and 1 or 0) PlayerPrefsMgr:setInt(StrogeKeyDef.sound_effect_isOn, self.sound_effect_isOn and 1 or 0) PlayerPrefsMgr:save() end -- 保存数据到服务器 function SettingUserData:saveToServer() end -- 背景音乐是否开启 function SettingUserData:isBackgroundMusicIsOn() return self.background_music_isOn end -- 设置背景音乐是否开启 function SettingUserData:setBackgroundMusicIsOn(isOn, refuseSave) self.background_music_isOn = isOn if not refuseSave then self:save() end return self.background_music_isOn end -- 音效是否开启 function SettingUserData:isSoundEffectIsOn() return self.sound_effect_isOn end -- 设置音效是否开启 function SettingUserData:setSoundEffectIsOn(isOn, refuseSave) self.sound_effect_isOn = isOn if not refuseSave then self:save() end return self.sound_effect_isOn end return SettingUserData MapCfgD--[[ 餐厅地图静态配置 author:{zhangpeng} time:2025-05-12 13:53:47 ]] local MapCfg = defClassStatic("MapCfg") local LOGTAG = "MapCfg" -- 地图像素尺寸 MapCfg.pixelSize ={ width = 1974, height = 3280 } -- 地图块尺寸 MapCfg.blockSize = 32 -- ppu(Pixels Per Unit,保持整个游戏统一) MapCfg.ppu = 100 -- astar插件里设置的节点尺寸 MapCfg.nodeSize = MapCfg.blockSize / MapCfg.ppu -- 地图块数量 MapCfg.blockCount = { width = math.ceil(MapCfg.pixelSize.width / MapCfg.blockSize), height = math.ceil(MapCfg.pixelSize.height / MapCfg.blockSize) } function MapCfg:init() -- 打印上面的所有信息 printInfo(LOGTAG, "地图像素尺寸: %s, %s", MapCfg.pixelSize.width, MapCfg.pixelSize.height) printInfo(LOGTAG, "地图块尺寸: %s", MapCfg.blockSize) printInfo(LOGTAG, "ppu: %s", MapCfg.ppu) printInfo(LOGTAG, "节点尺寸: %s", MapCfg.nodeSize) printInfo(LOGTAG, "地图块数量: %s, %s", MapCfg.blockCount.width, MapCfg.blockCount.height) end -- 一些节点名字配置 MapCfg.employeeRootNames = { rootNames = { "male_lead", -- 男主 "female_lead", -- 女主 "waiter", -- 服务员 "chef", -- 厨师 "assistant", -- 助理 "guard", -- 保安 "fisherman", -- 渔民 "singer", -- 歌手 "barbecue_chef" -- 烧烤厨师 }, borthNodeName = "birth_point", wanderRootNodeName = "wander_points", -- 餐厅里的闲逛点 bbqWanderRootNodeName = "bbq_wander_points" -- 烧烤店里的闲逛点 } MapCfg:init() gacharewardreslinkreturn { --BASIC --ASSET reward_ui = {"Assets/AssetsPackage/Res/modules/ui/gacha/prefabs/reward_ui.prefab", 0, 0}, reward_cell = {"Assets/AssetsPackage/Res/modules/ui/gacha/prefabs/reward_cell.prefab", 0, 0}, } GuideConst local GuideConst = defClassStatic("GuideConst") -- 引导类型 GuideConst.guideType = { -- 对话 dialog = 1, -- 点击 click = 2, -- 高亮显示 highlight = 3, -- 展示界面 showUI = 4, } -- 引导交互类型 GuideConst.OptType = { -- 对话 dialog = 1, -- 按钮点击 buttonClick = 2, -- 客人入店 customerEnter = 3, -- 世界场景点击 worldClick = 4, } -- 引导页面类型 GuideConst.ViewName = { -- 主页 main = "MainViewUI", -- 建筑列表 building = "BuildingsListUI", -- 建筑详情列表 building_info = "BuildingInfoUI", -- 视频招揽页面 video_promotion = "VideoPromotionUI", } -- ui按钮名称 GuideConst.UIButtonName = { -- 主页建筑列表按钮 btn_building = "build_btn", -- 主页招揽按钮 btn_flyer = "btn_flyer", -- 主页广告招揽按钮 btn_video = "btn_video", -- 建筑列表页按钮 btn_building_cell = "btn_building_cell", -- 建筑详情页购买按钮 btn_buy = "btn_buy", -- 视频招揽页按钮 btn_video_promotion = "video_btn", } -- ui按钮配置 GuideConst.UIButtonConfig = { -- 主页建筑列表按钮配置 btn_building = { delay = 0.2, r = 40, w = 40, h = 40, dx = 0, dy = 0, interval = 3 }, -- 主页招揽按钮配置 btn_flyer = { delay = 0.2, r = 40, w = 40, h = 40, dx = 0, dy = 0, interval = 3, needClickCount = 8, -- 需要点击次数 }, -- 主页广告招揽按钮配置 btn_video = { delay = 0.2, r = 40, w = 40, h = 40, dx = 0, dy = 0, interval = 3 }, -- 建筑列表页按钮配置 btn_building_cell = { delay = 0.2, r = 0, w = 180, h = 180, dx = 0, dy = 0, interval = 3 }, -- 建筑详情页购买按钮配置 btn_buy = { delay = 0.2, r = 20, w = 180, h = 20, dx = 0, dy = 0, interval = 3 }, -- 视频招揽页按钮配置 btn_video_promotion = { delay = 0.2, r = 20, w = 180, h = 20, dx = 0, dy = 0, interval = 3 }, } -- 世界点击按钮类型 GuideConst.WorldTargetType = { customer = "customer", -- 客人 } -- 世界点击按钮名称 GuideConst.WorldButtonName = { -- 主页建筑列表按钮 bubble = "bubble", } -- 世界点击按钮配置 GuideConst.WorldButtonConfig = { -- 客人气泡按钮配置 bubble = { delay = 0.2, r = 50, w = 100, h = 40, dx = 0, dy = 0, interval = 3 }, } -- 引导步骤状态 GuideConst.stepStatus = { not_started = 0, -- 未开始 in_progress = 1, -- 进行中 completed = 2, -- 已完成 } return GuideConst LuaDebugjit;local debugger_reLoadFile =nil local debugger_xpcall = nil local sethook = debug.sethook local debugger_stackInfo = nil local coro_debugger = nil local require = rawget(_G,"require") local debugger_require = require local debugger_exeLuaString = nil local checkSetVar = nil local loadstring_ = nil local debugger_sendMsg = nil if (loadstring) then loadstring_ = loadstring else loadstring_ = load end local ZZBase64 = {} local LuaDebugTool_ = nil if (LuaDebugTool) then LuaDebugTool_ = LuaDebugTool elseif (CS and CS.LuaDebugTool) then LuaDebugTool_ = CS.LuaDebugTool end local LuaDebugTool = LuaDebugTool_ local loadstring = loadstring_ local getinfo = debug.getinfo local function createSocket() local base = _G local string = require("string") local math = require("math") local socket = require("socket.core") local _M = socket ----------------------------------------------------------------------------- -- Exported auxiliar functions ----------------------------------------------------------------------------- function _M.connect4(address, port, laddress, lport) return socket.connect(address, port, laddress, lport, "inet") end function _M.connect6(address, port, laddress, lport) return socket.connect(address, port, laddress, lport, "inet6") end if (not _M.connect) then function _M.connect(address, port, laddress, lport) local sock, err = socket.tcp() if not sock then return nil, err end if laddress then local res, err = sock:bind(laddress, lport, -1) if not res then return nil, err end end local res, err = sock:connect(address, port) if not res then return nil, err end return sock end end function _M.bind(host, port, backlog) if host == "*" then host = "0.0.0.0" end local addrinfo, err = socket.dns.getaddrinfo(host) if not addrinfo then return nil, err end local sock, res err = "no info on address" for i, alt in base.ipairs(addrinfo) do if alt.family == "inet" then sock, err = socket.tcp4() else sock, err = socket.tcp6() end if not sock then return nil, err end sock:setoption("reuseaddr", true) res, err = sock:bind(alt.addr, port) if not res then sock:close() else res, err = sock:listen(backlog) if not res then sock:close() else return sock end end end return nil, err end _M.try = _M.newtry() function _M.choose(table) return function(name, opt1, opt2) if base.type(name) ~= "string" then name, opt1, opt2 = "default", name, opt1 end local f = table[name or "nil"] if not f then base.error("unknown key (" .. base.tostring(name) .. ")", 3) else return f(opt1, opt2) end end end ----------------------------------------------------------------------------- -- Socket sources and sinks, conforming to LTN12 ----------------------------------------------------------------------------- -- create namespaces inside LuaSocket namespace local sourcet, sinkt = {}, {} _M.sourcet = sourcet _M.sinkt = sinkt _M.BLOCKSIZE = 2048 sinkt["close-when-done"] = function(sock) return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function(self, chunk, err) if not chunk then sock:close() return 1 else return sock:send(chunk) end end } ) end sinkt["keep-open"] = function(sock) return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function(self, chunk, err) if chunk then return sock:send(chunk) else return 1 end end } ) end sinkt["default"] = sinkt["keep-open"] _M.sink = _M.choose(sinkt) sourcet["by-length"] = function(sock, length) return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function() if length <= 0 then return nil end local size = math.min(socket.BLOCKSIZE, length) local chunk, err = sock:receive(size) if err then return nil, err end length = length - string.len(chunk) return chunk end } ) end sourcet["until-closed"] = function(sock) local done return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function() if done then return nil end local chunk, err, partial = sock:receive(socket.BLOCKSIZE) if not err then return chunk elseif err == "closed" then sock:close() done = 1 return partial else return nil, err end end } ) end sourcet["default"] = sourcet["until-closed"] _M.source = _M.choose(sourcet) return _M end local function createJson() local math = require("math") local string = require("string") local table = require("table") local object = nil ----------------------------------------------------------------------------- -- Module declaration ----------------------------------------------------------------------------- local json = {} -- Public namespace local json_private = {} -- Private namespace -- Public constants json.EMPTY_ARRAY = {} json.EMPTY_OBJECT = {} -- Public functions -- Private functions local decode_scanArray local decode_scanComment local decode_scanConstant local decode_scanNumber local decode_scanObject local decode_scanString local decode_scanWhitespace local encodeString local isArray local isEncodable ----------------------------------------------------------------------------- -- PUBLIC FUNCTIONS ----------------------------------------------------------------------------- --- Encodes an arbitrary Lua object / variable. -- @param v The Lua object / variable to be JSON encoded. -- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode) function json.encode(v) -- Handle nil values if v == nil then return "null" end local vtype = type(v) -- Handle strings if vtype == "string" then return '"' .. json_private.encodeString(v) .. '"' -- Need to handle encoding in string end -- Handle booleans if vtype == "number" or vtype == "boolean" then return tostring(v) end -- Handle tables if vtype == "table" then local rval = {} -- Consider arrays separately local bArray, maxCount = isArray(v) if bArray then for i = 1, maxCount do table.insert(rval, json.encode(v[i])) end else -- An object, not an array for i, j in pairs(v) do if isEncodable(i) and isEncodable(j) then table.insert(rval, '"' .. json_private.encodeString(i) .. '":' .. json.encode(j)) end end end if bArray then return "[" .. table.concat(rval, ",") .. "]" else return "{" .. table.concat(rval, ",") .. "}" end end -- Handle null values if vtype == "function" and v == json.null then return "null" end assert(false, "encode attempt to encode unsupported type " .. vtype .. ":" .. tostring(v)) end --- Decodes a JSON string and returns the decoded value as a Lua data structure / value. -- @param s The string to scan. -- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1. -- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil, -- and the position of the first character after -- the scanned JSON object. function json.decode(s, startPos) startPos = startPos and startPos or 1 startPos = decode_scanWhitespace(s, startPos) assert(startPos <= string.len(s), "Unterminated JSON encoded object found at position in [" .. s .. "]") local curChar = string.sub(s, startPos, startPos) -- Object if curChar == "{" then return decode_scanObject(s, startPos) end -- Array if curChar == "[" then return decode_scanArray(s, startPos) end -- Number if string.find("+-0123456789.e", curChar, 1, true) then return decode_scanNumber(s, startPos) end -- String if curChar == '"' or curChar == [[']] then return decode_scanString(s, startPos) end if string.sub(s, startPos, startPos + 1) == "/*" then return json.decode(s, decode_scanComment(s, startPos)) end -- Otherwise, it must be a constant return decode_scanConstant(s, startPos) end --- The null function allows one to specify a null value in an associative array (which is otherwise -- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null } function json.null() return json.null -- so json.null() will also return null ;-) end ----------------------------------------------------------------------------- -- Internal, PRIVATE functions. -- Following a Python-like convention, I have prefixed all these 'PRIVATE' -- functions with an underscore. ----------------------------------------------------------------------------- --- Scans an array from JSON into a Lua object -- startPos begins at the start of the array. -- Returns the array and the next starting position -- @param s The string being scanned. -- @param startPos The starting position for the scan. -- @return table, int The scanned array as a table, and the position of the next character to scan. function decode_scanArray(s, startPos) local array = {} -- The return value local stringLen = string.len(s) assert( string.sub(s, startPos, startPos) == "[", "decode_scanArray called but array does not start at position " .. startPos .. " in string:\n" .. s ) startPos = startPos + 1 -- Infinite loop for array elements repeat startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, "JSON String ended unexpectedly scanning array.") local curChar = string.sub(s, startPos, startPos) if (curChar == "]") then return array, startPos + 1 end if (curChar == ",") then startPos = decode_scanWhitespace(s, startPos + 1) end assert(startPos <= stringLen, "JSON String ended unexpectedly scanning array.") object, startPos = json.decode(s, startPos) table.insert(array, object) until false end --- Scans a comment and discards the comment. -- Returns the position of the next character following the comment. -- @param string s The JSON string to scan. -- @param int startPos The starting position of the comment function decode_scanComment(s, startPos) assert( string.sub(s, startPos, startPos + 1) == "/*", "decode_scanComment called but comment does not start at position " .. startPos ) local endPos = string.find(s, "*/", startPos + 2) assert(endPos ~= nil, "Unterminated comment in string at " .. startPos) return endPos + 2 end --- Scans for given constants: true, false or null -- Returns the appropriate Lua type, and the position of the next character to read. -- @param s The string being scanned. -- @param startPos The position in the string at which to start scanning. -- @return object, int The object (true, false or nil) and the position at which the next character should be -- scanned. function decode_scanConstant(s, startPos) local consts = {["true"] = true, ["false"] = false, ["null"] = nil} local constNames = {"true", "false", "null"} for i, k in pairs(constNames) do if string.sub(s, startPos, startPos + string.len(k) - 1) == k then return consts[k], startPos + string.len(k) end end assert(nil, "Failed to scan constant from string " .. s .. " at starting position " .. startPos) end --- Scans a number from the JSON encoded string. -- (in fact, also is able to scan numeric +- eqns, which is not -- in the JSON spec.) -- Returns the number, and the position of the next character -- after the number. -- @param s The string being scanned. -- @param startPos The position at which to start scanning. -- @return number, int The extracted number and the position of the next character to scan. function decode_scanNumber(s, startPos) local endPos = startPos + 1 local stringLen = string.len(s) local acceptableChars = "+-0123456789.e" while (string.find(acceptableChars, string.sub(s, endPos, endPos), 1, true) and endPos <= stringLen) do endPos = endPos + 1 end local stringValue = "return " .. string.sub(s, startPos, endPos - 1) local stringEval = loadstring(stringValue) assert( stringEval, "Failed to scan number [ " .. stringValue .. "] in JSON string at position " .. startPos .. " : " .. endPos ) return stringEval(), endPos end --- Scans a JSON object into a Lua object. -- startPos begins at the start of the object. -- Returns the object and the next starting position. -- @param s The string being scanned. -- @param startPos The starting position of the scan. -- @return table, int The scanned object as a table and the position of the next character to scan. function decode_scanObject(s, startPos) local object = {} local stringLen = string.len(s) local key, value assert( string.sub(s, startPos, startPos) == "{", "decode_scanObject called but object does not start at position " .. startPos .. " in string:\n" .. s ) startPos = startPos + 1 repeat startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, "JSON string ended unexpectedly while scanning object.") local curChar = string.sub(s, startPos, startPos) if (curChar == "}") then return object, startPos + 1 end if (curChar == ",") then startPos = decode_scanWhitespace(s, startPos + 1) end assert(startPos <= stringLen, "JSON string ended unexpectedly scanning object.") -- Scan the key key, startPos = json.decode(s, startPos) assert(startPos <= stringLen, "JSON string ended unexpectedly searching for value of key " .. key) startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, "JSON string ended unexpectedly searching for value of key " .. key) assert( string.sub(s, startPos, startPos) == ":", "JSON object key-value assignment mal-formed at " .. startPos ) startPos = decode_scanWhitespace(s, startPos + 1) assert(startPos <= stringLen, "JSON string ended unexpectedly searching for value of key " .. key) value, startPos = json.decode(s, startPos) object[key] = value until false -- infinite loop while key-value pairs are found end -- START SoniEx2 -- Initialize some things used by decode_scanString -- You know, for efficiency local escapeSequences = { ["\\t"] = "\t", ["\\f"] = "\f", ["\\r"] = "\r", ["\\n"] = "\n", ["\\b"] = "" } setmetatable( escapeSequences, { __index = function(t, k) -- skip "\" aka strip escape return string.sub(k, 2) end } ) -- END SoniEx2 --- Scans a JSON string from the opening inverted comma or single quote to the -- end of the string. -- Returns the string extracted as a Lua string, -- and the position of the next non-string character -- (after the closing inverted comma or single quote). -- @param s The string being scanned. -- @param startPos The starting position of the scan. -- @return string, int The extracted string as a Lua string, and the next character to parse. function decode_scanString(s, startPos) assert(startPos, "decode_scanString(..) called without start position") local startChar = string.sub(s, startPos, startPos) -- START SoniEx2 -- PS: I don't think single quotes are valid JSON assert(startChar == '"' or startChar == [[']], "decode_scanString called for a non-string") --assert(startPos, "String decoding failed: missing closing " .. startChar .. " for string at position " .. oldStart) local t = {} local i, j = startPos, startPos while string.find(s, startChar, j + 1) ~= j + 1 do local oldj = j i, j = string.find(s, "\\.", j + 1) local x, y = string.find(s, startChar, oldj + 1) if not i or x < i then i, j = x, y - 1 end table.insert(t, string.sub(s, oldj + 1, i - 1)) if string.sub(s, i, j) == "\\u" then local a = string.sub(s, j + 1, j + 4) j = j + 4 local n = tonumber(a, 16) assert(n, "String decoding failed: bad Unicode escape " .. a .. " at position " .. i .. " : " .. j) -- math.floor(x/2^y) == lazy right shift -- a % 2^b == bitwise_and(a, (2^b)-1) -- 64 = 2^6 -- 4096 = 2^12 (or 2^6 * 2^6) local x if n < 128 then x = string.char(n % 128) elseif n < 2048 then -- [110x xxxx] [10xx xxxx] x = string.char(192 + (math.floor(n / 64) % 32), 128 + (n % 64)) else -- [1110 xxxx] [10xx xxxx] [10xx xxxx] x = string.char(224 + (math.floor(n / 4096) % 16), 128 + (math.floor(n / 64) % 64), 128 + (n % 64)) end table.insert(t, x) else table.insert(t, escapeSequences[string.sub(s, i, j)]) end end table.insert(t, string.sub(j, j + 1)) assert( string.find(s, startChar, j + 1), "String decoding failed: missing closing " .. startChar .. " at position " .. j .. "(for string at position " .. startPos .. ")" ) return table.concat(t, ""), j + 2 -- END SoniEx2 end --- Scans a JSON string skipping all whitespace from the current start position. -- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached. -- @param s The string being scanned -- @param startPos The starting position where we should begin removing whitespace. -- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string -- was reached. function decode_scanWhitespace(s, startPos) local whitespace = " \n\r\t" local stringLen = string.len(s) while (string.find(whitespace, string.sub(s, startPos, startPos), 1, true) and startPos <= stringLen) do startPos = startPos + 1 end return startPos end --- Encodes a string to be JSON-compatible. -- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-) -- @param s The string to return as a JSON encoded (i.e. backquoted string) -- @return The string appropriately escaped. local escapeList = { ['"'] = '\\"', ["\\"] = "\\\\", ["/"] = "\\/", [""] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" } function json_private.encodeString(s) local s = tostring(s) return s:gsub( ".", function(c) return escapeList[c] end ) -- SoniEx2: 5.0 compat end -- Determines whether the given Lua type is an array or a table / dictionary. -- We consider any table an array if it has indexes 1..n for its n items, and no -- other data in the table. -- I think this method is currently a little 'flaky', but can't think of a good way around it yet... -- @param t The table to evaluate as an array -- @return boolean, number True if the table can be represented as an array, false otherwise. If true, -- the second returned value is the maximum -- number of indexed elements in the array. function isArray(t) -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable -- (with the possible exception of 'n') if (t == json.EMPTY_ARRAY) then return true, 0 end if (t == json.EMPTY_OBJECT) then return false end local maxIndex = 0 for k, v in pairs(t) do if (type(k) == "number" and math.floor(k) == k and 1 <= k) then -- k,v is an indexed pair if (not isEncodable(v)) then return false end -- All array elements must be encodable maxIndex = math.max(maxIndex, k) else if (k == "n") then if v ~= (t.n or #t) then return false end -- False if n does not hold the number of elements else -- Else of (k=='n') if isEncodable(v) then return false end end -- End of (k~='n') end -- End of k,v not an indexed pair end -- End of loop across all pairs return true, maxIndex end --- Determines whether the given Lua object / table / variable can be JSON encoded. The only -- types that are JSON encodable are: string, boolean, number, nil, table and json.null. -- In this implementation, all other types are ignored. -- @param o The object to examine. -- @return boolean True if the object should be JSON encoded, false if it should be ignored. function isEncodable(o) local t = type(o) return (t == "string" or t == "boolean" or t == "number" or t == "nil" or t == "table") or (t == "function" and o == json.null) end return json end local debugger_print = print local debug_server = nil local breakInfoSocket = nil local json = createJson() local LuaDebugger = { fileMaps = {}, Run = true, --表示正常运行只检测断点 StepIn = false, StepNext = false, StepOut = false, breakInfos = {}, runTimeType = nil, isHook = true, pathCachePaths = {}, isProntToConsole = 1, isDebugPrint = true, hookType = "lrc", stepNextFun = nil, DebugLuaFie = "", runLineCount = 0, --分割字符串缓存 splitFilePaths = {}, version="0.9.3", serVarLevel = 4 } local debug_hook = nil local _resume = coroutine.resume coroutine.resume = function(co, ...) if (LuaDebugger.isHook) then if coroutine.status(co) ~= "dead" then debug.sethook(co, debug_hook, "lrc") end end return _resume(co, ...) end local _wrap = coroutine.wrap coroutine.wrap = function(fun,dd) local newFun =_wrap(function() debug.sethook(debug_hook, "lrc") return fun(); end) return newFun end LuaDebugger.event = { S2C_SetBreakPoints = 1, C2S_SetBreakPoints = 2, S2C_RUN = 3, C2S_HITBreakPoint = 4, S2C_ReqVar = 5, C2S_ReqVar = 6, --单步跳过请求 S2C_NextRequest = 7, --单步跳过反馈 C2S_NextResponse = 8, -- 单步跳过 结束 没有下一步 C2S_NextResponseOver = 9, --单步跳入 S2C_StepInRequest = 10, C2S_StepInResponse = 11, --单步跳出 S2C_StepOutRequest = 12, --单步跳出返回 C2S_StepOutResponse = 13, --打印 C2S_LuaPrint = 14, S2C_LoadLuaScript = 16, C2S_SetSocketName = 17, C2S_LoadLuaScript = 18, C2S_DebugXpCall = 20, S2C_DebugClose = 21, S2C_SerVar = 24, C2S_SerVar = 25, S2C_ReLoadFile = 26, C2S_ReLoadFile = 27, } --@region print function print(...) if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 3) then debugger_print(...) end if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 2) then if (debug_server) then local arg = {...} --这里的...和{}符号中间需要有空格号,否则会出错 local str = "" if (#arg == 0) then arg = {"nil"} end for k, v in pairs(arg) do str = str .. tostring(v) .. "\t" end local sendMsg = { event = LuaDebugger.event.C2S_LuaPrint, data = {msg = ZZBase64.encode(str), type = 1} } local sendStr = json.encode(sendMsg) debug_server:send(sendStr .. "__debugger_k0204__") end end end function luaIdePrintWarn(...) if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 3) then debugger_print(...) end if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 2) then if (debug_server) then local arg = {...} --这里的...和{}符号中间需要有空格号,否则会出错 local str = "" if (#arg == 0) then arg = {"nil"} end for k, v in pairs(arg) do str = str .. tostring(v) .. "\t" end local sendMsg = { event = LuaDebugger.event.C2S_LuaPrint, data = {msg = ZZBase64.encode(str), type = 2} } local sendStr = json.encode(sendMsg) debug_server:send(sendStr .. "__debugger_k0204__") end end end function luaIdePrintErr(...) if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 3) then debugger_print(...) end if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 2) then if (debug_server) then local arg = {...} --这里的...和{}符号中间需要有空格号,否则会出错 local str = "" if (#arg == 0) then arg = {"nil"} end for k, v in pairs(arg) do str = str .. tostring(v) .. "\t" end local sendMsg = { event = LuaDebugger.event.C2S_LuaPrint, data = {msg = ZZBase64.encode(str), type = 3} } local sendStr = json.encode(sendMsg) debug_server:send(sendStr .. "__debugger_k0204__") end end end --@endregion --@region 辅助方法 local function debugger_lastIndex(str, p) local startIndex = string.find(str, p, 1) while startIndex do local findstartIndex = string.find(str, p, startIndex + 1) if (not findstartIndex) then break else startIndex = findstartIndex end end return startIndex end local function debugger_convertParentDir(dir) local index, endindex = string.find(dir, "/%.%./") if (index) then local file1 = string.sub(dir, 1, index - 1) local startIndex = debugger_lastIndex(file1, "/") file1 = string.sub(file1, 1, startIndex - 1) local file2 = string.sub(dir, endindex) dir = file1 .. file2 dir = debugger_convertParentDir(dir) return dir else return dir end end local function debugger_getFilePathInfo(file) local fileName = nil local dir = nil file = file:gsub("/.\\", "/") file = file:gsub("\\", "/") file = file:gsub("//", "/") if file:find("@") == 1 then file = file:sub(2) end local findex = file:find("%./") if (findex == 1) then file = file:sub(3) end file = debugger_convertParentDir(file) local fileLength = string.len(file) local suffixNames = { ".lua", ".lua.txt", ".txt", ".bytes" } table.sort( suffixNames, function(name1, name2) return string.len(name1) > string.len(name2) end ) local suffixLengs = {} for i, suffixName in ipairs(suffixNames) do table.insert(suffixLengs, string.len(suffixName)) end local fileLength = string.len(file) for i, suffix in ipairs(suffixNames) do local suffixName = string.sub(file, fileLength - suffixLengs[i] + 1) if (suffixName == suffix) then file = string.sub(file, 1, fileLength - suffixLengs[i]) break end end local fileNameStartIndex = debugger_lastIndex(file, "/") if (fileNameStartIndex) then fileName = string.sub(file, fileNameStartIndex + 1) dir = string.sub(file, 1, fileNameStartIndex) file = dir .. fileName else fileNameStartIndex = debugger_lastIndex(file, "%.") if (not fileNameStartIndex) then fileName = file dir = "" else dir = string.sub(file, 1, fileNameStartIndex) dir = dir:gsub("%.", "/") fileName = string.sub(file, fileNameStartIndex + 1) file = dir .. fileName end end return file, dir, fileName end --@endregion ----=============================工具方法============================================= --@region 工具方法 local function debugger_strSplit(input, delimiter) input = tostring(input) delimiter = tostring(delimiter) if (delimiter == "") then return false end local pos, arr = 0, {} -- for each divider found for st, sp in function() return string.find(input, delimiter, pos, true) end do table.insert(arr, string.sub(input, pos, st - 1)) pos = sp + 1 end table.insert(arr, string.sub(input, pos)) return arr end local function debugger_strTrim(input) input = string.gsub(input, "^[ \t\n\r]+", "") return string.gsub(input, "[ \t\n\r]+$", "") end local function debugger_dump(value, desciption, nesting) if type(nesting) ~= "number" then nesting = 3 end local lookupTable = {} local result = {} local function _v(v) if type(v) == "string" then v = '"' .. v .. '"' end return tostring(v) end local traceback = debugger_strSplit(debug.traceback("", 2), "\n") print("dump from: " .. debugger_strTrim(traceback[3])) local function _dump(value, desciption, indent, nest, keylen) desciption = desciption or "" local spc = "" if type(keylen) == "number" then spc = string.rep(" ", keylen - string.len(_v(desciption))) end if type(value) ~= "table" then result[#result + 1] = string.format("%s%s%s = %s", indent, _v(desciption), spc, _v(value)) elseif lookupTable[value] then result[#result + 1] = string.format("%s%s%s = *REF*", indent, desciption, spc) else lookupTable[value] = true if nest > nesting then result[#result + 1] = string.format("%s%s = *MAX NESTING*", indent, desciption) else result[#result + 1] = string.format("%s%s = {", indent, _v(desciption)) local indent2 = indent .. " " local keys = {} local keylen = 0 local values = {} for k, v in pairs(value) do keys[#keys + 1] = k local vk = _v(k) local vkl = string.len(vk) if vkl > keylen then keylen = vkl end values[k] = v end table.sort( keys, function(a, b) if type(a) == "number" and type(b) == "number" then return a < b else return tostring(a) < tostring(b) end end ) for i, k in ipairs(keys) do _dump(values[k], k, indent2, nest + 1, keylen) end result[#result + 1] = string.format("%s}", indent) end end end _dump(value, desciption, "- ", 1) for i, line in ipairs(result) do print(line) end end --@endregion local function debugger_valueToString(v) local vtype = type(v) local vstr = nil if (vtype == "userdata") then if (LuaDebugger.isFoxGloryProject ) then return "userdata",vtype else return tostring(v), vtype end elseif (vtype == "table" or vtype == "function" or vtype == "boolean") then local value = vtype xpcall(function() if(LuaDebugger.isFoxGloryProject) then value = vtype else value = tostring(v) end end,function() value = vtype end) return value, vtype elseif (vtype == "number" or vtype == "string" ) then return v, vtype else return tostring(v), vtype end end local function debugger_setVarInfo(name, value) local valueStr, valueType = debugger_valueToString(value) local nameStr,nameType = debugger_valueToString(name) if(valueStr == nil) then valueStr = valueType end local valueInfo = { name =nameStr, valueType = valueType, valueStr = ZZBase64.encode(valueStr) } return valueInfo end local function debugger_getvalue(f) local i = 1 local locals = {} -- get locals while true do local name, value = debug.getlocal(f, i) if not name then break end if (name ~= "(*temporary)") then locals[name] = value end i = i + 1 end local func = getinfo(f, "f").func i = 1 local ups = {} while func do -- check for func as it may be nil for tail calls local name, value = debug.getupvalue(func, i) if not name then break end if (name == "_ENV") then ups["_ENV_"] = value else ups[name] = value end i = i + 1 end return {locals = locals, ups = ups} end --获取堆栈 debugger_stackInfo = function(ignoreCount, event) local datas = {} local stack = {} local varInfos = {} local funcs = {} local index = 0 for i = ignoreCount, 100 do local source = getinfo(i) local isadd = true if (i == ignoreCount) then local file = source.source if (file:find(LuaDebugger.DebugLuaFie)) then return end if (file == "=[C]") then isadd = false end end if not source then break end if (isadd) then local fullName, dir, fileName = debugger_getFilePathInfo(source.source) local info = { src = fullName, scoreName = source.name, currentline = source.currentline, linedefined = source.linedefined, what = source.what, nameWhat = source.namewhat } index = i local vars = debugger_getvalue(i + 1) table.insert(stack, info) table.insert(varInfos, vars) table.insert(funcs, source.func) end if source.what == "main" then break end end local stackInfo = {stack = stack, vars = varInfos, funcs = funcs} local data = { stack = stackInfo.stack, vars = stackInfo.vars, funcs = stackInfo.funcs, event = event, funcsLength = #stackInfo.funcs, upFunc = getinfo(ignoreCount - 3, "f").func } return data end --==============================工具方法 end====================================================== --===========================点断信息================================================== --根据不同的游戏引擎进行定时获取断点信息 --CCDirector:sharedDirector():getScheduler() local debugger_setBreak = nil local function debugger_receiveDebugBreakInfo() if(not jit) then if(_VERSION)then print("当前lua版本为: ".._VERSION.." 请使用 -----LuaDebug.lua----- 进行调试!") else print("当前为lua版本,请使用-----LuaDebug.lua-----进行调试!") end end if (breakInfoSocket) then local msg, status = breakInfoSocket:receive() if(LuaDebugger.isLaunch == true and status == "closed") then os.exit() end if (msg) then local netData = json.decode(msg) if netData.event == LuaDebugger.event.S2C_SetBreakPoints then debugger_setBreak(netData.data) elseif netData.event == LuaDebugger.event.S2C_LoadLuaScript then LuaDebugger.loadScriptBody = netData.data debugger_exeLuaString() debugger_sendMsg(breakInfoSocket,LuaDebugger.event.C2S_LoadLuaScript,LuaDebugger.loadScriptBody) elseif netData.event == LuaDebugger.event.S2C_ReLoadFile then LuaDebugger.reLoadFileBody = netData.data LuaDebugger.isReLoadFile = false LuaDebugger.reLoadFileBody.isReLoad = debugger_reLoadFile(LuaDebugger.reLoadFileBody) LuaDebugger.reLoadFileBody.script = nil debugger_sendMsg( breakInfoSocket, LuaDebugger.event.C2S_ReLoadFile, { stack = LuaDebugger.reLoadFileBody } ) end end end end local function splitFilePath(path) if (LuaDebugger.splitFilePaths[path]) then return LuaDebugger.splitFilePaths[path] end local pos, arr = 0, {} -- for each divider found for st, sp in function() return string.find(path, "/", pos, true) end do local pathStr = string.sub(path, pos, st - 1) table.insert(arr, pathStr) pos = sp + 1 end local pathStr = string.sub(path, pos) table.insert(arr, pathStr) LuaDebugger.splitFilePaths[path] = arr return arr end debugger_setBreak = function(datas) local breakInfos = LuaDebugger.breakInfos for i, data in ipairs(datas) do data.fileName = string.lower(data.fileName) data.serverPath = string.lower(data.serverPath) local breakInfo = breakInfos[data.fileName] if (not breakInfo) then breakInfos[data.fileName] = {} breakInfo = breakInfos[data.fileName] end if (not data.breakDatas or #data.breakDatas == 0) then breakInfo[data.serverPath] = nil else local fileBreakInfo = breakInfo[data.serverPath] if (not fileBreakInfo) then fileBreakInfo = { pathNames = splitFilePath(data.serverPath), --命中次數判斷計數器 hitCounts = {} } breakInfo[data.serverPath] = fileBreakInfo end local lineInfos = {} for li, breakData in ipairs(data.breakDatas) do lineInfos[breakData.line] = breakData if (breakData.hitCondition and breakData.hitCondition ~= "") then breakData.hitCondition = tonumber(breakData.hitCondition) else breakData.hitCondition = 0 end if (not fileBreakInfo.hitCounts[breakData.line]) then fileBreakInfo.hitCounts[breakData.line] = 0 end end fileBreakInfo.lines = lineInfos --這裡添加命中次數判斷 for line, count in pairs(fileBreakInfo.hitCounts) do if (not lineInfos[line]) then fileBreakInfo.hitCounts[line] = nil end end end local count = 0 for i, linesInfo in pairs(breakInfo) do count = count + 1 end if (count == 0) then breakInfos[data.fileName] = nil end end --debugger_dump(breakInfos, "breakInfos", 6) --检查是否需要断点 local isHook = false for k, v in pairs(breakInfos) do isHook = true break end --这样做的原因是为了最大限度的使手机调试更加流畅 注意这里会连续的进行n次 if (isHook) then if (not LuaDebugger.isHook) then debug.sethook(debug_hook, "lrc") end LuaDebugger.isHook = true else if (LuaDebugger.isHook) then debug.sethook() end LuaDebugger.isHook = false end end local function debugger_checkFileIsBreak(fileName) return LuaDebugger.breakInfos[fileName] end --=====================================断点信息 end ---------------------------------------------- local controller_host = "192.168.1.102" local controller_port = 7003 debugger_sendMsg = function(serverSocket, eventName, data) local sendMsg = { event = eventName, data = data } local sendStr = json.encode(sendMsg) serverSocket:send(sendStr .. "__debugger_k0204__") end function debugger_conditionStr(condition, vars, callBack) local function loadScript() local currentTabble = {} local locals = vars[1].locals local ups = vars[1].ups if (ups) then for k, v in pairs(ups) do currentTabble[k] = v end end if (locals) then for k, v in pairs(locals) do currentTabble[k] = v end end setmetatable(currentTabble, {__index = _G}) local fun = loadstring("return " .. condition) setfenv(fun, currentTabble) return fun() end local status, msg = xpcall( loadScript, function(error) print(error) end ) if (status and msg) then callBack() end end --执行lua字符串 debugger_exeLuaString = function() local function loadScript() local script = LuaDebugger.loadScriptBody.script if (LuaDebugger.loadScriptBody.isBreak) then local currentTabble = {_G = _G} local frameId = LuaDebugger.loadScriptBody.frameId frameId = frameId local func = LuaDebugger.currentDebuggerData.funcs[frameId] local vars = LuaDebugger.currentDebuggerData.vars[frameId] local locals = vars.locals local ups = vars.ups for k, v in pairs(ups) do currentTabble[k] = v end for k, v in pairs(locals) do currentTabble[k] = v end setmetatable(currentTabble, {__index = _G}) local fun = loadstring(script) setfenv(fun, currentTabble) fun() else local fun = loadstring(script) fun() end end local status, msg = xpcall( loadScript, function(error) -- debugger_sendMsg(debug_server, LuaDebugger.event.C2S_LoadLuaScript, LuaDebugger.loadScriptBody) end ) LuaDebugger.loadScriptBody.script = nil if (LuaDebugger.loadScriptBody.isBreak) then LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 LuaDebugger.currentDebuggerData = debugger_stackInfo(LuaDebugger.serVarLevel, LuaDebugger.event.C2S_HITBreakPoint) LuaDebugger.loadScriptBody.stack = LuaDebugger.currentDebuggerData.stack end LuaDebugger.loadScriptBody.complete = true end --@region 调试中修改变量值 --根据key 值在 value 查找 local function debugger_getTablekey(key,keyType,value) if(keyType == -1) then return key elseif(keyType == 1) then return tonumber(key) elseif(keyType == 2) then local valueKey = nil for k,v in pairs(value) do local nameType = type(k) if(nameType == "userdata" or nameType == "table") then if (not LuaDebugger.isFoxGloryProject) then valueKey = tostring(k) if(key == valueKey) then return k end break end end end end end local function debugger_setVarValue(server, data) local newValue = nil local level = LuaDebugger.serVarLevel+LuaDebugger.setVarBody.frameId local firstKeyName = data.keys[1] --@region vars check local localValueChangeIndex = -1 local upValueChangeIndex = -1 local upValueFun = nil local oldValue = nil local i = 1 local locals = {} -- get locals while true do local name, value = debug.getlocal(level, i) if not name then break end if(firstKeyName == name) then localValueChangeIndex = i oldValue = value end if (name ~= "(*temporary)") then locals[name] = value end i = i + 1 end local func = getinfo(level, "f").func i = 1 local ups = {} while func do -- check for func as it may be nil for tail calls local name, value = debug.getupvalue(func, i) if not name then break end if(localValueChangeIndex == -1 and firstKeyName == name) then upValueFun = func oldValue = value upValueChangeIndex = i end if (name == "_ENV") then ups["_ENV_"] = value else ups[name] = value end i = i + 1 end --@endregion local vars = {locals = locals, ups = ups} local function loadScript() local currentTabble = {} local locals = vars.locals local ups = vars.ups if (ups) then for k, v in pairs(ups) do currentTabble[k] = v end end if (locals) then for k, v in pairs(locals) do currentTabble[k] = v end end setmetatable(currentTabble, {__index = _G}) local fun = loadstring("return " .. data.value) setfenv(fun, currentTabble) newValue = fun() end local status, msg = xpcall( loadScript, function(error) print(error, "============================") end ) local i = 1 -- local 查找并替换 local keyLength = #data.keys if(keyLength == 1) then if(localValueChangeIndex ~= -1) then debug.setlocal(level, localValueChangeIndex, newValue) elseif(upValueFun ~= nil) then debug.setupvalue( upValueFun, upValueChangeIndex, newValue ) else --全局变量查找 if(_G[firstKeyName]) then _G[firstKeyName] = newValue end end else if(not oldValue) then if(_G[firstKeyName]) then oldValue = _G[firstKeyName] end end local tempValue = oldValue for i=2,keyLength-1 do if(tempValue) then oldValue = oldValue[debugger_getTablekey(data.keys[i],data.numberTypes[i],oldValue)] end end if(tempValue) then oldValue[debugger_getTablekey(data.keys[keyLength],data.numberTypes[keyLength],oldValue)] = newValue end end local varInfo = debugger_setVarInfo(data.varName, newValue) data.varInfo = varInfo LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 LuaDebugger.currentDebuggerData = debugger_stackInfo(LuaDebugger.serVarLevel, LuaDebugger.event.C2S_HITBreakPoint) end --@endregion --调试修改变量值统一的 _resume checkSetVar = function() if (LuaDebugger.isSetVar) then LuaDebugger.isSetVar = false debugger_setVarValue(debug_server,LuaDebugger.setVarBody) LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 _resume(coro_debugger, LuaDebugger.setVarBody) xpcall( checkSetVar, function(error) print("设置变量", error) end ) elseif(LuaDebugger.isLoadLuaScript) then LuaDebugger.isLoadLuaScript = false debugger_exeLuaString() LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 _resume(coro_debugger, LuaDebugger.reLoadFileBody) xpcall( checkSetVar, function(error) print("执行代码", error) end ) elseif(LuaDebugger.isReLoadFile) then LuaDebugger.isReLoadFile = false LuaDebugger.reLoadFileBody.isReLoad = debugger_reLoadFile(LuaDebugger.reLoadFileBody) print("重载结果:",LuaDebugger.reLoadFileBody.isReLoad) LuaDebugger.reLoadFileBody.script = nil LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 _resume(coro_debugger, LuaDebugger.reLoadFileBody) xpcall( checkSetVar, function(error) print("重新加载文件", error) end ) end end local function getSource(source) source = string.lower(source) if (LuaDebugger.pathCachePaths[source]) then LuaDebugger.currentLineFile = LuaDebugger.pathCachePaths[source] return LuaDebugger.pathCachePaths[source] end local fullName, dir, fileName = debugger_getFilePathInfo(source) LuaDebugger.currentLineFile = fullName LuaDebugger.pathCachePaths[source] = fileName return fileName end local function debugger_GeVarInfoBytUserData(server, var) local fileds = LuaDebugTool.getUserDataInfo(var) local varInfos = {} --c# vars for i = 1, fileds.Count do local filed = fileds[i - 1] local valueInfo = { name = filed.name, valueType = filed.valueType, valueStr = ZZBase64.encode(filed.valueStr), isValue = filed.isValue, csharp = true } table.insert(varInfos, valueInfo) end return varInfos end local function debugger_getValueByScript(value, script) local val = nil local status, msg = xpcall( function() local fun = loadstring("return " .. script) setfenv(fun, value) val = fun() end, function(error) print(error, "====>") val = nil end ) return val end local function debugger_getVarByKeys(value, keys, index) local str = "" local keyLength = #keys for i = index, keyLength do local key = keys[i] if (key == "[metatable]") then else if (i == index) then if (string.find(key, "%.")) then if (str == "") then i = index + 1 value = value[key] end if (i >= #keys) then return index, value end return debugger_getVarByKeys(value, keys, i) else str = key end else if (string.find(key, "%[")) then str = str .. key elseif (type(key) == "string") then if (string.find(key, "table:") or string.find(key, "userdata:") or string.find(key, "function:")) then if (str ~= "") then local vl = debugger_getValueByScript(value, str) value = vl if (value) then for k, v in pairs(value) do local ktype = type(k) if (ktype == "userdata" or ktype == "table" or ktype == "function") then local keyName = debugger_valueToString(k) if (keyName == key) then value = v break end end end end str = "" if (i == keyLength) then return #keys, value else return debugger_getVarByKeys(value, keys, i + 1) end else str = str .. '["' .. key .. '"]' end else str = str .. '["' .. key .. '"]' end else str = str .. "[" .. key .. "]" end end end end local v = debugger_getValueByScript(value, str) return #keys, v end --[[ @desc: 查找c# 值 author:k0204 time:2018-04-07 21:32:31 return ]] local function debugger_getCSharpValue(value, searchIndex, keys) local key = keys[searchIndex] local val = LuaDebugTool.getCSharpValue(value, key) if (val) then --1最后一个 直接返回 if (searchIndex == #keys) then return #keys, val else --2再次获得 如果没有找到那么 进行lua 层面查找 local vindex, val1 = debugger_getCSharpValue(val, searchIndex + 1, keys) if (not val1) then --组建新的keys local tempKeys = {} for i = vindex, #keys do table.insert(tempKeys, keys[i]) end local vindx, val1 = debugger_searchVarByKeys(value, searckKeys, 1) return vindx, val1 else return vindex, val1 end end else --3最终这里返回 所以2 中 没有当val1 不为空的处理 return searchIndex, val end end local function debugger_searchVarByKeys(value, keys, searckKeys) local index, val = debugger_getVarByKeys(value, searckKeys, 1) if (not LuaDebugTool or not LuaDebugTool.getCSharpValue or type(LuaDebugTool.getCSharpValue) ~= "function") then return index, val end if (val) then if (index == #keys) then return index, val else local searchStr = "" --进行c# 值查找 local keysLength = #keys local searchIndex = index + 1 local sindex, val = debugger_getCSharpValue(val, searchIndex, keys) return sindex, val end else --进行递减 local tempKeys = {} for i = 1, #searckKeys - 1 do table.insert(tempKeys, keys[i]) end if (#tempKeys == 0) then return #keys, nil end return debugger_searchVarByKeys(value, keys, tempKeys) end end --[[ @desc: 获取metatable 信息 author:k0204 time:2018-04-06 20:27:12 return ]] local function debugger_getmetatable(value, metatable, vinfos, server, variablesReference, debugSpeedIndex, metatables) for i, mtable in ipairs(metatables) do if (metatable == mtable) then return vinfos end end table.insert(metatables, metatable) for k, v in pairs(metatable) do local val = nil if (type(k) == "string") then xpcall( function() val = value[k] end, function(error) val = nil end ) if (val == nil) then xpcall( function() if (string.find(k, "__")) then val = v end end, function(error) val = nil end ) end end if (val) then local vinfo = debugger_setVarInfo(k, val) table.insert(vinfos, vinfo) if (#vinfos > 10) then debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 0 } ) vinfos = {} end end end local m = getmetatable(metatable) if (m) then return debugger_getmetatable(value, m, vinfos, server, variablesReference, debugSpeedIndex, metatables) else return vinfos end end local function debugger_sendTableField(luatable, vinfos, server, variablesReference, debugSpeedIndex, valueType) if (valueType == "userdata") then if (tolua and tolua.getpeer) then luatable = tolua.getpeer(luatable) else return vinfos end end if (luatable == nil) then return vinfos end for k, v in pairs(luatable) do local vinfo = debugger_setVarInfo(k, v) table.insert(vinfos, vinfo) if (#vinfos > 10) then debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 0 } ) vinfos = {} end end return vinfos end local function debugger_sendTableValues(value, server, variablesReference, debugSpeedIndex) local vinfos = {} local luatable = {} local valueType = type(value) local userDataInfos = {} local m = nil if (valueType == "userdata") then m = getmetatable(value) vinfos = debugger_sendTableField(value, vinfos, server, variablesReference, debugSpeedIndex, valueType) if (LuaDebugTool) then local varInfos = debugger_GeVarInfoBytUserData(server, value, variablesReference, debugSpeedIndex) for i, v in ipairs(varInfos) do if (v.valueType == "System.Byte[]" and value[v.name] and type(value[v.name]) == "string") then local valueInfo = { name = v.name, valueType = "string", valueStr = ZZBase64.encode(value[v.name]) } table.insert(vinfos, valueInfo) else table.insert(vinfos, v) end if (#vinfos > 10) then debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 0 } ) vinfos = {} end end end else m = getmetatable(value) vinfos = debugger_sendTableField(value, vinfos, server, variablesReference, debugSpeedIndex, valueType) end if (m) then vinfos = debugger_getmetatable(value, m, vinfos, server, variablesReference, debugSpeedIndex, {}) end debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 1 } ) end --获取lua 变量的方法 local function debugger_getBreakVar(body, server) local variablesReference = body.variablesReference local debugSpeedIndex = body.debugSpeedIndex local vinfos = {} local function exe() local frameId = body.frameId local type_ = body.type local keys = body.keys --找到对应的var local vars = nil if (type_ == 1) then vars = LuaDebugger.currentDebuggerData.vars[frameId + 1] vars = vars.locals elseif (type_ == 2) then vars = LuaDebugger.currentDebuggerData.vars[frameId + 1] vars = vars.ups elseif (type_ == 3) then vars = _G end if (#keys == 0) then debugger_sendTableValues(vars, server, variablesReference, debugSpeedIndex) return end local index, value = debugger_searchVarByKeys(vars, keys, keys) if (value) then local valueType = type(value) if (valueType == "table" or valueType == "userdata") then debugger_sendTableValues(value, server, variablesReference, debugSpeedIndex) else if (valueType == "function") then if(LuaDebugger.isFoxGloryProject) then value = "function" else value = tostring(value) end end debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = ZZBase64.encode(value), isComplete = 1, varType = valueType } ) end else debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = {}, isComplete = 1, varType = "nil" } ) end end xpcall( exe, function(error) -- print("获取变量错误 错误消息-----------------") -- print(error) -- print(debug.traceback("", 2)) debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = { { name = "error", valueType = "string", valueStr = ZZBase64.encode("无法获取属性值:" .. error .. "->" .. debug.traceback("", 2)), isValue = false } }, isComplete = 1 } ) end ) end local function ResetDebugInfo() LuaDebugger.Run = false LuaDebugger.StepIn = false LuaDebugger.StepNext = false LuaDebugger.StepOut = false end local function debugger_loop(server) server = debug_server --命令 local command local eval_env = {} local arg while true do local line, status = server:receive() if (status == "closed") then if(LuaDebugger.isLaunch) then os.exit() else debug.sethook() coroutine.yield() end end if (line) then local netData = json.decode(line) local event = netData.event local body = netData.data if (event == LuaDebugger.event.S2C_DebugClose) then if(LuaDebugger.isLaunch) then os.exit() else debug.sethook() coroutine.yield() end elseif event == LuaDebugger.event.S2C_SetBreakPoints then --设置断点信息 local function setB() debugger_setBreak(body) end xpcall( setB, function(error) print(error) end ) elseif event == LuaDebugger.event.S2C_RUN then --开始运行 LuaDebugger.runTimeType = body.runTimeType LuaDebugger.isProntToConsole = body.isProntToConsole LuaDebugger.isFoxGloryProject = body.isFoxGloryProject LuaDebugger.isLaunch = body.isLaunch ResetDebugInfo() LuaDebugger.currentDebuggerData = nil LuaDebugger.Run = true LuaDebugger.tempRunFlag = true LuaDebugger.currentLine= nil local data = coroutine.yield() LuaDebugger.serVarLevel = 4 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack } ) elseif event == LuaDebugger.event.S2C_ReqVar then -- 获取变量信息 --请求数据信息 debugger_getBreakVar(body, server) elseif event == LuaDebugger.event.S2C_NextRequest then -- 设置单步跳过 ResetDebugInfo() LuaDebugger.StepNext = true --设置当前文件名和当前行数 local data = coroutine.yield() LuaDebugger.serVarLevel = 4 --重置调试信息 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack } ) elseif (event == LuaDebugger.event.S2C_StepInRequest) then --单步跳入 --单步跳入 ResetDebugInfo() LuaDebugger.StepIn = true local data = coroutine.yield() LuaDebugger.serVarLevel = 4 --重置调试信息 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack, eventType = data.eventType } ) elseif (event == LuaDebugger.event.S2C_StepOutRequest) then --单步跳出 ResetDebugInfo() LuaDebugger.StepOut = true local data = coroutine.yield() LuaDebugger.serVarLevel = 4 --重置调试信息 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack, eventType = data.eventType } ) elseif event == LuaDebugger.event.S2C_LoadLuaScript then LuaDebugger.loadScriptBody = body LuaDebugger.isLoadLuaScript = true local data = coroutine.yield() debugger_sendMsg( server, LuaDebugger.event.C2S_LoadLuaScript, LuaDebugger.loadScriptBody ) elseif event == LuaDebugger.event.S2C_SerVar then LuaDebugger.isSetVar = true LuaDebugger.setVarBody = body local data = coroutine.yield() debugger_sendMsg( server, LuaDebugger.event.C2S_SerVar, { stack = data, eventType = data.eventType } ) elseif event == LuaDebugger.event.S2C_ReLoadFile then LuaDebugger.isReLoadFile = true LuaDebugger.reLoadFileBody = body local data = coroutine.yield() debugger_sendMsg( server, LuaDebugger.event.C2S_ReLoadFile, { stack = data, eventType = data.eventType } ) end end end end coro_debugger = coroutine.create(debugger_loop) debug_hook = function(event, line) -- local stepInfo = getinfo(2) -- print(stepInfo.source,stepInfo.currentline) if(not LuaDebugger.isHook) then return end if(LuaDebugger.Run) then if(event == "line") then local isCheck = false for k, breakInfo in pairs(LuaDebugger.breakInfos) do for bk, linesInfo in pairs(breakInfo) do if(linesInfo.lines and linesInfo.lines[line]) then isCheck = true break end end if(isCheck) then break end end if(not isCheck) then return end end end local file = nil if(event == "line") then local funs = nil local funlength =0 if(LuaDebugger.currentDebuggerData) then funs = LuaDebugger.currentDebuggerData.funcs funlength = #funs end local stepInfo = getinfo(2) local tempFunc = stepInfo.func local source = stepInfo.source file = getSource(source); if(source == "=[C]" or source:find(LuaDebugger.DebugLuaFie)) then return end if(funlength > 0 and funs[1] == tempFunc and LuaDebugger.currentLine ~= line) then LuaDebugger.runLineCount = LuaDebugger.runLineCount+1 end local breakInfo = LuaDebugger.breakInfos[file] local breakData = nil local ischeck = false if(breakInfo) then for k, lineInfo in pairs(breakInfo) do local lines = lineInfo.lines if(lines and lines[line]) then ischeck = true break end end end local isHit = false if(ischeck) then --并且在断点中 local info = stepInfo local source = string.lower( info.source ) local fullName,dir,fileName = debugger_getFilePathInfo(source) local hitPathNames = splitFilePath(fullName) local hitCounts = {} local debugHitCounts = nil for k, lineInfo in pairs(breakInfo) do local lines = lineInfo.lines local pathNames = lineInfo.pathNames debugHitCounts = lineInfo.hitCounts if(lines and lines[line]) then breakData = lines[line] --判断路径 hitCounts[k] = 0 local hitPathNamesCount = #hitPathNames local pathNamesCount = #pathNames local checkCount = 0; while(true) do if (pathNames[pathNamesCount] ~= hitPathNames[hitPathNamesCount]) then break else hitCounts[k] = hitCounts[k] + 1 end pathNamesCount = pathNamesCount - 1 hitPathNamesCount = hitPathNamesCount - 1 checkCount = checkCount+1 if(pathNamesCount <= 0 or hitPathNamesCount <= 0) then break end end if(checkCount>0) then break; end if(checkCount==0) then breakData = nil -- break; end else breakData = nil end end if(breakData) then local hitFieName = "" local maxCount = 0 for k, v in pairs(hitCounts) do if(v > maxCount) then maxCount = v hitFieName = k; end end local hitPathNamesLength = #hitPathNames if (hitPathNamesLength == 1 or (hitPathNamesLength > 1 and maxCount > 1)) then if(hitFieName ~= "") then local hitCount = breakData.hitCondition local clientHitCount = debugHitCounts[breakData.line] clientHitCount = clientHitCount + 1 debugHitCounts[breakData.line] = clientHitCount if(funs and funs[1] == tempFunc and LuaDebugger.runLineCount == 0) then LuaDebugger.runLineCount = 0 elseif(LuaDebugger.tempRunFlag and LuaDebugger.currentLine == line) then LuaDebugger.runLineCount = 0 LuaDebugger.tempRunFlag = nil elseif(clientHitCount >= hitCount) then isHit = true end end end end end if(LuaDebugger.StepOut) then if(funlength == 1) then ResetDebugInfo(); LuaDebugger.Run = true return else if(funs[2] == tempFunc) then local data = debugger_stackInfo(3, LuaDebugger.event.C2S_StepInResponse) -- print("StepIn 挂起") --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() return end end end if(LuaDebugger.StepIn) then if(funs[1] == tempFunc and LuaDebugger.runLineCount == 0) then return end local data = debugger_stackInfo(3, LuaDebugger.event.C2S_StepInResponse) -- print("StepIn 挂起") --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() return end if(LuaDebugger.StepNext ) then local isNext = false if(funs) then for i,f in ipairs(funs) do if(tempFunc == f) then if(LuaDebugger.currentLine == line) then return end isNext =true break; end end else isNext =true end if(isNext) then local data = debugger_stackInfo(3, LuaDebugger.event.C2S_NextResponse) LuaDebugger.runLineCount = 0 LuaDebugger.currentLine = line --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() return end end local sevent = nil --断点判断 if(isHit) then LuaDebugger.runLineCount = 0 LuaDebugger.currentLine = line sevent = LuaDebugger.event.C2S_HITBreakPoint --调用 coro_debugger 并传入 参数 local data = debugger_stackInfo(3, sevent) --挂起等待调试器作出反应 if(breakData and breakData.condition) then debugger_conditionStr(breakData.condition,data.vars,function() _resume(coro_debugger, data) checkSetVar() end) else --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() end end end end debugger_xpcall = function() --调用 coro_debugger 并传入 参数 local data = debugger_stackInfo(4, LuaDebugger.event.C2S_HITBreakPoint) if(data.stack and data.stack[1]) then data.stack[1].isXpCall = true end --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() end --调试开始 local function start() local socket = createSocket() print(controller_host) print(controller_port) local fullName,dirName,fileName = debugger_getFilePathInfo(getinfo(1).source) LuaDebugger.DebugLuaFie = fileName local server = socket.connect(controller_host, controller_port) debug_server = server; if server then --创建breakInfo socket socket = createSocket() breakInfoSocket = socket.connect(controller_host, controller_port) if(breakInfoSocket) then breakInfoSocket:settimeout(0) debugger_sendMsg(breakInfoSocket, LuaDebugger.event.C2S_SetSocketName, { name = "breakPointSocket" }) debugger_sendMsg(server, LuaDebugger.event.C2S_SetSocketName, { name = "mainSocket", version = LuaDebugger.version }) xpcall(function() sethook(debug_hook, "lrc") end, function(error) print("error:", error) end) if(not jit) then if(_VERSION)then print("当前lua版本为: ".._VERSION.." 请使用LuaDebug 进行调试!") else print("当前为lua版本,请使用LuaDebug 进行调试!") end end _resume(coro_debugger, server) end end end function StartDebug(host, port,isReLoad) if(not host) then print("error host nil") end if(not port) then print("error prot nil") end if(type(host) ~= "string") then print("error host not string") end if(type(port) ~= "number") then print("error host not number") end controller_host = host controller_port = port xpcall(start, function(error) -- body print(error) end) --代码重载 if(isReLoad) then xpcall(function() debugger_reLoadFile = require("luaideReLoadFile") end,function() print("左侧luaide按钮->打开luaIde最新调试文件所在文件夹->luaideReLoadFile.lua->拷贝到项目中") print("具体使用方式请看luaideReLoadFile中文件注释") debugger_reLoadFile = function() print("未实现代码重载") end end) end return debugger_receiveDebugBreakInfo, debugger_xpcall end --base64 local string = string ZZBase64.__code = { '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', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', }; ZZBase64.__decode = {} for k,v in pairs(ZZBase64.__code) do ZZBase64.__decode[string.byte(v,1)] = k - 1 end function ZZBase64.encode(text) local len = string.len(text) local left = len % 3 len = len - left local res = {} local index = 1 for i = 1, len, 3 do local a = string.byte(text, i ) local b = string.byte(text, i + 1) local c = string.byte(text, i + 2) -- num = a<<16 + b<<8 + c local num = a * 65536 + b * 256 + c for j = 1, 4 do --tmp = num >> ((4 -j) * 6) local tmp = math.floor(num / (2 ^ ((4-j) * 6))) --curPos = tmp&0x3f local curPos = tmp % 64 + 1 res[index] = ZZBase64.__code[curPos] index = index + 1 end end if left == 1 then ZZBase64.__left1(res, index, text, len) elseif left == 2 then ZZBase64.__left2(res, index, text, len) end return table.concat(res) end function ZZBase64.__left2(res, index, text, len) local num1 = string.byte(text, len + 1) num1 = num1 * 1024 --lshift 10 local num2 = string.byte(text, len + 2) num2 = num2 * 4 --lshift 2 local num = num1 + num2 local tmp1 = math.floor(num / 4096) --rShift 12 local curPos = tmp1 % 64 + 1 res[index] = ZZBase64.__code[curPos] local tmp2 = math.floor(num / 64) curPos = tmp2 % 64 + 1 res[index + 1] = ZZBase64.__code[curPos] curPos = num % 64 + 1 res[index + 2] = ZZBase64.__code[curPos] res[index + 3] = "=" end function ZZBase64.__left1(res, index,text, len) local num = string.byte(text, len + 1) num = num * 16 local tmp = math.floor(num / 64) local curPos = tmp % 64 + 1 res[index ] = ZZBase64.__code[curPos] curPos = num % 64 + 1 res[index + 1] = ZZBase64.__code[curPos] res[index + 2] = "=" res[index + 3] = "=" end function ZZBase64.decode(text) local len = string.len(text) local left = 0 if string.sub(text, len - 1) == "==" then left = 2 len = len - 4 elseif string.sub(text, len) == "=" then left = 1 len = len - 4 end local res = {} local index = 1 local decode = ZZBase64.__decode for i =1, len, 4 do local a = decode[string.byte(text,i )] local b = decode[string.byte(text,i + 1)] local c = decode[string.byte(text,i + 2)] local d = decode[string.byte(text,i + 3)] --num = a<<18 + b<<12 + c<<6 + d local num = a * 262144 + b * 4096 + c * 64 + d local e = string.char(num % 256) num = math.floor(num / 256) local f = string.char(num % 256) num = math.floor(num / 256) res[index ] = string.char(num % 256) res[index + 1] = f res[index + 2] = e index = index + 3 end if left == 1 then ZZBase64.__decodeLeft1(res, index, text, len) elseif left == 2 then ZZBase64.__decodeLeft2(res, index, text, len) end return table.concat(res) end function ZZBase64.__decodeLeft1(res, index, text, len) local decode = ZZBase64.__decode local a = decode[string.byte(text, len + 1)] local b = decode[string.byte(text, len + 2)] local c = decode[string.byte(text, len + 3)] local num = a * 4096 + b * 64 + c local num1 = math.floor(num / 1024) % 256 local num2 = math.floor(num / 4) % 256 res[index] = string.char(num1) res[index + 1] = string.char(num2) end function ZZBase64.__decodeLeft2(res, index, text, len) local decode = ZZBase64.__decode local a = decode[string.byte(text, len + 1)] local b = decode[string.byte(text, len + 2)] local num = a * 64 + b num = math.floor(num / 16) res[index] = string.char(num) end return StartDebug TaskOrderCfgParse --[[ 任务配置解析 author:{zhangpeng} time:2025-05-26 11:48:55 ]] local TaskOrderCfgData = require("data/config/taskOrderCfg") local TaskOrderCfgParse = defClassStatic("TaskOrderCfgParse") local LOGTAG = "TaskOrderCfgParse" function TaskOrderCfgParse:init() end function TaskOrderCfgParse:getTaskOrderCfg(id) for _, v in pairs(TaskOrderCfgData) do if v.id == id then return v end end return nil end function TaskOrderCfgParse:getTaskOrderCfgList(ids) local list = {} for i = 1, #ids do table.insert(list, TaskOrderCfgData[ids[i]]) end return list end -- 检查顾客触发订单id function TaskOrderCfgParse:checkCustomerId(customerId) for id, config in pairs(TaskOrderCfgData) do if (config.triggerType == TaskConst.OrderTriggerType.customer) and (config.triggerParam_1 == customerId) then return config end end return nil end -- 获取时间触发订单配置列表 function TaskOrderCfgParse:getOrderCfgListByTriggerType(triggerType) local list = {} for id, config in pairs(TaskOrderCfgData) do if (config.triggerType == triggerType)then list[id] = config end end return list end TaskOrderCfgParse:init() TaskConst--[[ 任务常量 author:{zhangpeng} time:2025-05-27 10:31:31 ]] local TaskConst = defClassStatic("TaskConst") TaskConst.TaskType = { -- 每日任务 daily = 1, -- 成就任务 achievement = 2, -- 订单任务 order = 3, } -- 任务状态 TaskConst.TaskState = { -- 进行中 in_progress = 1, -- 可完成,未领取 success = 2, -- 已领取 claimed = 3, } -- 解锁条件类型 TaskConst.UnlockCondType = { -- 星星数量 star = "star", -- 场景是否解锁 scene = "scene", } -- 订单任务触发方式 TaskConst.OrderTriggerType = { -- 在线时间触发 time = 1, -- 新顾客触发 customer = 2, } --- 奖励类型 TaskConst.RewardType = { -- 金币 Coin = 1, -- 钻石 Diamond = 2, -- 音符 MusicalNotes = 3, -- 鱼饵 FishingBait = 4, -- 进度 Progress = 5, -- 评星 Star = 6, -- 广告券 AdCoupons = 7, } --- 跳转页类型 TaskConst.JumpRef = { -- 建筑购买 Build = "1", -- 菜品学习 Cuisine = "2", -- 宣传按钮 Solicitation = "3", -- 员工雇佣 Employee = "4", } function TaskConst:init() end TaskConst:init()mainrequire("modules/bonus/const/BonusConst") require("modules/bonus/data/BonusUserData") require("modules/bonus/mgr/BonusMgr") CuisineInfo0!--[[ 菜品信息 author:{zhangpeng} time:2025-05-17 11:55:20 ]] ---@class CuisineInfo local CuisineInfo = defClass("CuisineInfo") local LOGTAG = "CuisineInfo" function CuisineInfo:ctor(cuisineCfgId) self.id = cuisineCfgId self:init() end function CuisineInfo:init() self:initData() self:initStatus() end function CuisineInfo:initData() self.config = self:getCuisineCfg() -- 获取菜品配置 local cuisineCfg = self:getCuisineCfg() -- 菜品名称 self.name = cuisineCfg.name -- 菜品描述 self.desc = cuisineCfg.desc -- 菜品类型(1:菜类 2:饮料类) self.cuisineType = cuisineCfg.ctype -- 制作时间 self.makeTime = cuisineCfg.maketime -- 是否可以升级 self.canUp = cuisineCfg.canUp -- 升级id self.upgradeIds = cuisineCfg.levelUpId -- 购买条件 self.buyCond = cuisineCfg.buyCond -- 解锁条件 self.unlockCondId = cuisineCfg.unlockCondId printInfo(LOGTAG, string.format("菜品信息: id:%s, 名字:%s, 类型:%s, 制作时间:%s秒", self.id, self.name, self:getCuisineTypeString(), self.makeTime)) end function CuisineInfo:initStatus() --- 等级 self.grade = UserDataMgr.cuisineUserData:getCuisineGrade(self.id) --- 销量 self.sales = UserDataMgr.cuisineUserData:getCuisineSales(self.id) -- 获取等级配置 self.currGradeCfg = self:getCuisineGradeCfg(self.grade) -- 获取下一等级配置 if not self:isMaxGrade() then self.nextGradeCfg = self:getCuisineGradeCfg(self.grade + 1) else self.nextGradeCfg = nil end -- 状态 self.state = UserDataMgr.cuisineUserData:getCuisineState(self.id) end -- 获取菜品配置 function CuisineInfo:getCuisineCfg() return CookBookCfgParse:getCookBookCfg(self.id) end -- 获取菜品等级配置 function CuisineInfo:getCuisineGradeCfg(num) return CuisineUpgradeCfgParse:getCuisineUpgradeCfg(self.upgradeIds[num]) end --- 获取菜品购买配置 function CuisineInfo:getCuisineBuyCfg() return BuyCommonCfgParse:getBuyCommonCfg(self.buyCond) end -- 获取菜品类型文本 function CuisineInfo:getCuisineTypeString() return CookingConst.CookingTypeName[self:getCuisineType()] end -- 获取菜品ID function CuisineInfo:getCuisineId() return self.id end -- 获取菜品名称 function CuisineInfo:getName() return self.name end -- 获取菜品描述 function CuisineInfo:getDesc() return self.desc end -- 获取菜品类型 (1:菜类 2:饮料类) function CuisineInfo:getCuisineType() return self.cuisineType end -- 获取制作时间 function CuisineInfo:getMakeTime() return self.makeTime end -- 获取是否可以升级 function CuisineInfo:getCanUp() return self.canUp end -- 获取升级条件 function CuisineInfo:getUpgradeCond() return self.upgradeCond end -- 获取购买条件 function CuisineInfo:getBuyCond() return self.buyCond end -- 获取解锁条件 function CuisineInfo:getUnlockCond() return self.unlockCond end -- 获取图标名称 function CuisineInfo:getIconName() return self.config.resId end --- 获取图标 function CuisineInfo:getIcon() return CuisineMgr:getIcon(self.id) end -- 获取当前菜品价格 function CuisineInfo:getCurrPrice() return self.currGradeCfg.saleIncom end -- 获取当前菜品价格描述 function CuisineInfo:getCurrPriceDesc() return string.format("%d", self:getCurrPrice()) end -- 获取菜品价格 function CuisineInfo:getNextPrice() return self.nextGradeCfg.saleIncom end -- 获取下一等级菜品价格描述 function CuisineInfo:getNextPriceDesc() if not self.nextGradeCfg then return "" end return string.format("%d", self:getNextPrice()) end -- 获取历史销量 function CuisineInfo:getSales() return self.sales end -- 设置历史销量 function CuisineInfo:setSales(num) self.sales = num end ----------解锁---------- --- 是否已解锁 function CuisineInfo:isUnlocked() return self.state >= CuisineConst.State.Unlocked end --- 解锁 function CuisineInfo:unlock() self.state = CuisineConst.State.Unlocked -- 更新状态 UserDataMgr.cuisineUserData:setCuisineState(self.id, self.state) end ----------学习---------- --- 是否已学习 function CuisineInfo:isPurchased() return self.state >= CuisineConst.State.Purchased end --- 学习价格 function CuisineInfo:getLearnPrice() return BuyMgr:getPrice(self:getBuyCond()) end --- 学习价格描述 function CuisineInfo:getLearnPriceDesc() return BuyMgr:getPriceDesc(self:getBuyCond()) end --- 学习缺少金币 为负数时不可购买 function CuisineInfo:learnShortage() return BuyMgr:getBuyShortage(self:getBuyCond()) end --- 学习 function CuisineInfo:learn() self.state = CuisineConst.State.Purchased -- 更新状态 UserDataMgr.cuisineUserData:setCuisineState(self.id, self.state) end --- 根据次序获取学习的限制类型id function CuisineInfo:getLearnLimitId(num) return BuyMgr:getLimitIdByIdx(self:getBuyCond(), num) end --- 根据次序获取学习的限制描述 function CuisineInfo:getLearnLimitDesc(num) return BuyMgr:getLimitDescByIdx(self:getBuyCond(), num) end --- 是否满足学习限制 function CuisineInfo:isPushLearnLimit() return BuyMgr:canBuy(self:getBuyCond()) end -- 获取学习限制描述 function CuisineInfo:getLearnDesc() return BuyMgr:getLimitDescByIdx(self:getBuyCond(), 1) end ----------升级---------- --- 获取当前等级 function CuisineInfo:getGrade() return self.grade end --- 获取最大等级 function CuisineInfo:getMaxGrade() return #self.upgradeIds end --- 获取是否可升级 function CuisineInfo:getCanUpgrade() return self:getGrade() < self:getMaxGrade() and self:isPushUpgradeLimit() end --- 是否以达到升级限制 function CuisineInfo:isPushUpgradeLimit() return UpgradeMgr:cuisineCanUpgrade(self.currGradeCfg.id) end -- 获取升级限制描述 function CuisineInfo:getUpgradeDesc() return UpgradeMgr:getCuisineUpgradeDesc(self.currGradeCfg.id) end --- 升级 function CuisineInfo:upgrade() self.grade = self.grade + 1 -- 获取等级配置 self.currGradeCfg = self:getCuisineGradeCfg(self.grade) -- 更新等级 UserDataMgr.cuisineUserData:addCuisineGrade(self.id, 1) -- 获取下一等级配置 if not self:isMaxGrade() then self.nextGradeCfg = self:getCuisineGradeCfg(self.grade + 1) else self.nextGradeCfg = nil end end --- 是否为最大等级 function CuisineInfo:isMaxGrade() return self:getGrade() >= self:getMaxGrade() end function CuisineInfo:getUpgradeLimitId(num) if num > 1 then return -1 end return self.currGradeCfg.limitCond end function CuisineInfo:getUpgradeLimitDesc(num) if num > 1 then return "" end return UpgradeMgr:getCuisineUpgradeDesc(self.currGradeCfg.id) end ----------标签---------- function CuisineInfo:getTagId(num) if num > 1 then return -1 end local data = CustomerTagCfgParse:getCustomerTagCfgByCuisineId(self.id) if not data then return -1 end return data.id end function CuisineInfo:getTagDesc(num) if num > 1 then return "" end return CustomerConst.SolicitationTypeCuisineDesc[self:getTagId(num)] end ----------加成---------- --- 获取加成配置 function CuisineInfo:getBonusId() return self.config.bonusId end --- 获取加成描述 function CuisineInfo:getBonusDesc() return BonusMgr:getBonusDescByIdx(self:getBonusId(), 1) end ----------掉落---------- function CuisineInfo:getDropType() return self.config.dropType end function CuisineInfo:getDropChance() return self.config.dropChance end function CuisineInfo:getDropDesc() if self:getDropType() ~= -1 then return string.format("增加客人音乐烤吧中掉落%s的概率", self:getDropIcon(self:getDropType())) end return "" end function CuisineInfo:getDropIcon(dropType) if dropType == CuisineConst.DropType.coin then return "金币"--"" elseif dropType == CuisineConst.DropType.star then return "星星"--"" elseif dropType == CuisineConst.DropType.note then return "音符"--"" end end return CuisineInfo TaskDailyCell --[[ 日常任务格子 author:{zhangpeng} time:2025-05-26 12:07:53 ]] local TaskDailyCell = defClass("TaskDailyCell") function TaskDailyCell:ctor(cell,taskData) self.cell = cell self.taskData = taskData self:initUI() end function TaskDailyCell:initUI() -- desc self.desc = self.cell:Seek("desc") self.desc:GetComponent("TextMeshProUGUI").text = self.taskData.desc -- 获取当前进度和目标值 local currentValue, targetValue = self:getTaskProgress() -- 计算进度比例(确保不超过1) local progressRatio = math.min(currentValue / targetValue, 1) -- progress self.front = self.cell:Seek("front") if self.front then self.front:GetComponent("Image").fillAmount = progressRatio end -- value self.value = self.cell:Seek("value") if self.value then self.value:GetComponent("TextMeshProUGUI").text = currentValue .. "/" .. targetValue end -- reward self.reward = self.cell:Seek("reward") self.reward_img = self.cell:Seek("reward_img") local sprite = TaskMgr:getTaskRewardIconByIndex(self.taskData.id, 1) self.reward_img:GetComponent("Image").sprite = sprite self.reward:GetComponent("TextMeshProUGUI").text = "+" .. self.taskData.params_1 -- 根据任务状态显示对应按钮(进行中/可领取/已领取) local btn_state_names = {"btn_states_ing","btn_states_get","suc"} for k,v in pairs(btn_state_names) do local btn = self.cell:Seek(v) if btn then btn:SetActive(k == TaskMgr:getTaskState(self.taskData.id)) end end local btn_get = self.cell:Seek("btn_states_get") util.ugui.addButtonClickEvent(btn_get, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) TaskMgr:setTaskClaimed(self.taskData.id) TaskMgr:getTaskRewardById(self.taskData.id, self.reward_img) self:updateUI() local ui = UILayerUtil:findUIByName("TaskUI") ui:updateDailyProgress() end) end -- 根据任务类型获取当前进度和目标值 function TaskDailyCell:getTaskProgress() local taskId = self.taskData.id local currentValue, targetValue = TaskMgr:getTaskProgress(taskId) return currentValue, targetValue end -- 更新UI显示 function TaskDailyCell:updateUI() -- 获取当前进度和目标值 local currentValue, targetValue = self:getTaskProgress() -- 计算进度比例(确保不超过1) local progressRatio = math.min(currentValue / targetValue, 1) -- 更新进度条 if self.front then self.front:GetComponent("Image").fillAmount = progressRatio end -- 更新进度文本 if self.value then self.value:GetComponent("TextMeshProUGUI").text = currentValue .. "/" .. targetValue end -- 根据任务状态显示对应按钮(进行中/可领取/已领取) local btn_state_names = {"btn_states_ing","btn_states_get","suc"} local taskState = TaskMgr:getTaskState(self.taskData.id) for k,v in pairs(btn_state_names) do local btn = self.cell:Seek(v) if btn then btn:SetActive(k == taskState) end end end return TaskDailyCell LuaDebugWlocal debugger_reLoadFile =nil local debugger_xpcall = nil local debugger_stackInfo = nil local coro_debugger = nil local require = rawget(_G,"require") local debugger_require = require local debugger_exeLuaString = nil local checkSetVar = nil local loadstring_ = nil local debugger_sendMsg = nil local _ENV = _G if (loadstring) then loadstring_ = loadstring else loadstring_ = load end --只针对 luadebug 调试 jit版本不存在这个问题 local setfenv = setfenv if (not setfenv) then setfenv = function(fn, env) local i = 1 while true do local name = debug.getupvalue(fn, i) if name == "_ENV" then debug.upvaluejoin( fn, i, (function() return env end), 1 ) break elseif not name then break end i = i + 1 end return fn end end local ZZBase64 = {} local LuaDebugTool_ = nil if (LuaDebugTool) then LuaDebugTool_ = LuaDebugTool elseif (CS and CS.LuaDebugTool) then LuaDebugTool_ = CS.LuaDebugTool end local LuaDebugTool = LuaDebugTool_ local loadstring = loadstring_ local getinfo = debug.getinfo local function createSocket() local base = _G local string = require("string") local math = require("math") local socket = require("socket.core") local _M = socket ----------------------------------------------------------------------------- -- Exported auxiliar functions ----------------------------------------------------------------------------- function _M.connect4(address, port, laddress, lport) return socket.connect(address, port, laddress, lport, "inet") end function _M.connect6(address, port, laddress, lport) return socket.connect(address, port, laddress, lport, "inet6") end if (not _M.connect) then function _M.connect(address, port, laddress, lport) local sock, err = socket.tcp() if not sock then return nil, err end if laddress then local res, err = sock:bind(laddress, lport, -1) if not res then return nil, err end end local res, err = sock:connect(address, port) if not res then return nil, err end return sock end end function _M.bind(host, port, backlog) if host == "*" then host = "0.0.0.0" end local addrinfo, err = socket.dns.getaddrinfo(host) if not addrinfo then return nil, err end local sock, res err = "no info on address" for i, alt in base.ipairs(addrinfo) do if alt.family == "inet" then sock, err = socket.tcp4() else sock, err = socket.tcp6() end if not sock then return nil, err end sock:setoption("reuseaddr", true) res, err = sock:bind(alt.addr, port) if not res then sock:close() else res, err = sock:listen(backlog) if not res then sock:close() else return sock end end end return nil, err end _M.try = _M.newtry() function _M.choose(table) return function(name, opt1, opt2) if base.type(name) ~= "string" then name, opt1, opt2 = "default", name, opt1 end local f = table[name or "nil"] if not f then base.error("unknown key (" .. base.tostring(name) .. ")", 3) else return f(opt1, opt2) end end end ----------------------------------------------------------------------------- -- Socket sources and sinks, conforming to LTN12 ----------------------------------------------------------------------------- -- create namespaces inside LuaSocket namespace local sourcet, sinkt = {}, {} _M.sourcet = sourcet _M.sinkt = sinkt _M.BLOCKSIZE = 2048 sinkt["close-when-done"] = function(sock) return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function(self, chunk, err) if not chunk then sock:close() return 1 else return sock:send(chunk) end end } ) end sinkt["keep-open"] = function(sock) return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function(self, chunk, err) if chunk then return sock:send(chunk) else return 1 end end } ) end sinkt["default"] = sinkt["keep-open"] _M.sink = _M.choose(sinkt) sourcet["by-length"] = function(sock, length) return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function() if length <= 0 then return nil end local size = math.min(socket.BLOCKSIZE, length) local chunk, err = sock:receive(size) if err then return nil, err end length = length - string.len(chunk) return chunk end } ) end sourcet["until-closed"] = function(sock) local done return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function() if done then return nil end local chunk, err, partial = sock:receive(socket.BLOCKSIZE) if not err then return chunk elseif err == "closed" then sock:close() done = 1 return partial else return nil, err end end } ) end sourcet["default"] = sourcet["until-closed"] _M.source = _M.choose(sourcet) return _M end local function createJson() local math = require("math") local string = require("string") local table = require("table") local object = nil ----------------------------------------------------------------------------- -- Module declaration ----------------------------------------------------------------------------- local json = {} -- Public namespace local json_private = {} -- Private namespace -- Public constants json.EMPTY_ARRAY = {} json.EMPTY_OBJECT = {} -- Public functions -- Private functions local decode_scanArray local decode_scanComment local decode_scanConstant local decode_scanNumber local decode_scanObject local decode_scanString local decode_scanWhitespace local encodeString local isArray local isEncodable ----------------------------------------------------------------------------- -- PUBLIC FUNCTIONS ----------------------------------------------------------------------------- --- Encodes an arbitrary Lua object / variable. -- @param v The Lua object / variable to be JSON encoded. -- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode) function json.encode(v) -- Handle nil values if v == nil then return "null" end local vtype = type(v) -- Handle strings if vtype == "string" then return '"' .. json_private.encodeString(v) .. '"' -- Need to handle encoding in string end -- Handle booleans if vtype == "number" or vtype == "boolean" then return tostring(v) end -- Handle tables if vtype == "table" then local rval = {} -- Consider arrays separately local bArray, maxCount = isArray(v) if bArray then for i = 1, maxCount do table.insert(rval, json.encode(v[i])) end else -- An object, not an array for i, j in pairs(v) do if isEncodable(i) and isEncodable(j) then table.insert(rval, '"' .. json_private.encodeString(i) .. '":' .. json.encode(j)) end end end if bArray then return "[" .. table.concat(rval, ",") .. "]" else return "{" .. table.concat(rval, ",") .. "}" end end -- Handle null values if vtype == "function" and v == json.null then return "null" end assert(false, "encode attempt to encode unsupported type " .. vtype .. ":" .. tostring(v)) end --- Decodes a JSON string and returns the decoded value as a Lua data structure / value. -- @param s The string to scan. -- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1. -- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil, -- and the position of the first character after -- the scanned JSON object. function json.decode(s, startPos) startPos = startPos and startPos or 1 startPos = decode_scanWhitespace(s, startPos) assert(startPos <= string.len(s), "Unterminated JSON encoded object found at position in [" .. s .. "]") local curChar = string.sub(s, startPos, startPos) -- Object if curChar == "{" then return decode_scanObject(s, startPos) end -- Array if curChar == "[" then return decode_scanArray(s, startPos) end -- Number if string.find("+-0123456789.e", curChar, 1, true) then return decode_scanNumber(s, startPos) end -- String if curChar == '"' or curChar == [[']] then return decode_scanString(s, startPos) end if string.sub(s, startPos, startPos + 1) == "/*" then return json.decode(s, decode_scanComment(s, startPos)) end -- Otherwise, it must be a constant return decode_scanConstant(s, startPos) end --- The null function allows one to specify a null value in an associative array (which is otherwise -- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null } function json.null() return json.null -- so json.null() will also return null ;-) end ----------------------------------------------------------------------------- -- Internal, PRIVATE functions. -- Following a Python-like convention, I have prefixed all these 'PRIVATE' -- functions with an underscore. ----------------------------------------------------------------------------- --- Scans an array from JSON into a Lua object -- startPos begins at the start of the array. -- Returns the array and the next starting position -- @param s The string being scanned. -- @param startPos The starting position for the scan. -- @return table, int The scanned array as a table, and the position of the next character to scan. function decode_scanArray(s, startPos) local array = {} -- The return value local stringLen = string.len(s) assert( string.sub(s, startPos, startPos) == "[", "decode_scanArray called but array does not start at position " .. startPos .. " in string:\n" .. s ) startPos = startPos + 1 -- Infinite loop for array elements repeat startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, "JSON String ended unexpectedly scanning array.") local curChar = string.sub(s, startPos, startPos) if (curChar == "]") then return array, startPos + 1 end if (curChar == ",") then startPos = decode_scanWhitespace(s, startPos + 1) end assert(startPos <= stringLen, "JSON String ended unexpectedly scanning array.") object, startPos = json.decode(s, startPos) table.insert(array, object) until false end --- Scans a comment and discards the comment. -- Returns the position of the next character following the comment. -- @param string s The JSON string to scan. -- @param int startPos The starting position of the comment function decode_scanComment(s, startPos) assert( string.sub(s, startPos, startPos + 1) == "/*", "decode_scanComment called but comment does not start at position " .. startPos ) local endPos = string.find(s, "*/", startPos + 2) assert(endPos ~= nil, "Unterminated comment in string at " .. startPos) return endPos + 2 end --- Scans for given constants: true, false or null -- Returns the appropriate Lua type, and the position of the next character to read. -- @param s The string being scanned. -- @param startPos The position in the string at which to start scanning. -- @return object, int The object (true, false or nil) and the position at which the next character should be -- scanned. function decode_scanConstant(s, startPos) local consts = {["true"] = true, ["false"] = false, ["null"] = nil} local constNames = {"true", "false", "null"} for i, k in pairs(constNames) do if string.sub(s, startPos, startPos + string.len(k) - 1) == k then return consts[k], startPos + string.len(k) end end assert(nil, "Failed to scan constant from string " .. s .. " at starting position " .. startPos) end --- Scans a number from the JSON encoded string. -- (in fact, also is able to scan numeric +- eqns, which is not -- in the JSON spec.) -- Returns the number, and the position of the next character -- after the number. -- @param s The string being scanned. -- @param startPos The position at which to start scanning. -- @return number, int The extracted number and the position of the next character to scan. function decode_scanNumber(s, startPos) local endPos = startPos + 1 local stringLen = string.len(s) local acceptableChars = "+-0123456789.e" while (string.find(acceptableChars, string.sub(s, endPos, endPos), 1, true) and endPos <= stringLen) do endPos = endPos + 1 end local stringValue = "return " .. string.sub(s, startPos, endPos - 1) local stringEval = loadstring(stringValue) assert( stringEval, "Failed to scan number [ " .. stringValue .. "] in JSON string at position " .. startPos .. " : " .. endPos ) return stringEval(), endPos end --- Scans a JSON object into a Lua object. -- startPos begins at the start of the object. -- Returns the object and the next starting position. -- @param s The string being scanned. -- @param startPos The starting position of the scan. -- @return table, int The scanned object as a table and the position of the next character to scan. function decode_scanObject(s, startPos) local object = {} local stringLen = string.len(s) local key, value assert( string.sub(s, startPos, startPos) == "{", "decode_scanObject called but object does not start at position " .. startPos .. " in string:\n" .. s ) startPos = startPos + 1 repeat startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, "JSON string ended unexpectedly while scanning object.") local curChar = string.sub(s, startPos, startPos) if (curChar == "}") then return object, startPos + 1 end if (curChar == ",") then startPos = decode_scanWhitespace(s, startPos + 1) end assert(startPos <= stringLen, "JSON string ended unexpectedly scanning object.") -- Scan the key key, startPos = json.decode(s, startPos) assert(startPos <= stringLen, "JSON string ended unexpectedly searching for value of key " .. key) startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, "JSON string ended unexpectedly searching for value of key " .. key) assert( string.sub(s, startPos, startPos) == ":", "JSON object key-value assignment mal-formed at " .. startPos ) startPos = decode_scanWhitespace(s, startPos + 1) assert(startPos <= stringLen, "JSON string ended unexpectedly searching for value of key " .. key) value, startPos = json.decode(s, startPos) object[key] = value until false -- infinite loop while key-value pairs are found end -- START SoniEx2 -- Initialize some things used by decode_scanString -- You know, for efficiency local escapeSequences = { ["\\t"] = "\t", ["\\f"] = "\f", ["\\r"] = "\r", ["\\n"] = "\n", ["\\b"] = "" } setmetatable( escapeSequences, { __index = function(t, k) -- skip "\" aka strip escape return string.sub(k, 2) end } ) -- END SoniEx2 --- Scans a JSON string from the opening inverted comma or single quote to the -- end of the string. -- Returns the string extracted as a Lua string, -- and the position of the next non-string character -- (after the closing inverted comma or single quote). -- @param s The string being scanned. -- @param startPos The starting position of the scan. -- @return string, int The extracted string as a Lua string, and the next character to parse. function decode_scanString(s, startPos) assert(startPos, "decode_scanString(..) called without start position") local startChar = string.sub(s, startPos, startPos) -- START SoniEx2 -- PS: I don't think single quotes are valid JSON assert(startChar == '"' or startChar == [[']], "decode_scanString called for a non-string") --assert(startPos, "String decoding failed: missing closing " .. startChar .. " for string at position " .. oldStart) local t = {} local i, j = startPos, startPos while string.find(s, startChar, j + 1) ~= j + 1 do local oldj = j i, j = string.find(s, "\\.", j + 1) local x, y = string.find(s, startChar, oldj + 1) if not i or x < i then i, j = x, y - 1 end table.insert(t, string.sub(s, oldj + 1, i - 1)) if string.sub(s, i, j) == "\\u" then local a = string.sub(s, j + 1, j + 4) j = j + 4 local n = tonumber(a, 16) assert(n, "String decoding failed: bad Unicode escape " .. a .. " at position " .. i .. " : " .. j) -- math.floor(x/2^y) == lazy right shift -- a % 2^b == bitwise_and(a, (2^b)-1) -- 64 = 2^6 -- 4096 = 2^12 (or 2^6 * 2^6) local x if n < 128 then x = string.char(n % 128) elseif n < 2048 then -- [110x xxxx] [10xx xxxx] x = string.char(192 + (math.floor(n / 64) % 32), 128 + (n % 64)) else -- [1110 xxxx] [10xx xxxx] [10xx xxxx] x = string.char(224 + (math.floor(n / 4096) % 16), 128 + (math.floor(n / 64) % 64), 128 + (n % 64)) end table.insert(t, x) else table.insert(t, escapeSequences[string.sub(s, i, j)]) end end table.insert(t, string.sub(j, j + 1)) assert( string.find(s, startChar, j + 1), "String decoding failed: missing closing " .. startChar .. " at position " .. j .. "(for string at position " .. startPos .. ")" ) return table.concat(t, ""), j + 2 -- END SoniEx2 end --- Scans a JSON string skipping all whitespace from the current start position. -- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached. -- @param s The string being scanned -- @param startPos The starting position where we should begin removing whitespace. -- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string -- was reached. function decode_scanWhitespace(s, startPos) local whitespace = " \n\r\t" local stringLen = string.len(s) while (string.find(whitespace, string.sub(s, startPos, startPos), 1, true) and startPos <= stringLen) do startPos = startPos + 1 end return startPos end --- Encodes a string to be JSON-compatible. -- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-) -- @param s The string to return as a JSON encoded (i.e. backquoted string) -- @return The string appropriately escaped. local escapeList = { ['"'] = '\\"', ["\\"] = "\\\\", ["/"] = "\\/", [""] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" } function json_private.encodeString(s) local s = tostring(s) return s:gsub( ".", function(c) return escapeList[c] end ) -- SoniEx2: 5.0 compat end -- Determines whether the given Lua type is an array or a table / dictionary. -- We consider any table an array if it has indexes 1..n for its n items, and no -- other data in the table. -- I think this method is currently a little 'flaky', but can't think of a good way around it yet... -- @param t The table to evaluate as an array -- @return boolean, number True if the table can be represented as an array, false otherwise. If true, -- the second returned value is the maximum -- number of indexed elements in the array. function isArray(t) -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable -- (with the possible exception of 'n') if (t == json.EMPTY_ARRAY) then return true, 0 end if (t == json.EMPTY_OBJECT) then return false end local maxIndex = 0 for k, v in pairs(t) do if (type(k) == "number" and math.floor(k) == k and 1 <= k) then -- k,v is an indexed pair if (not isEncodable(v)) then return false end -- All array elements must be encodable maxIndex = math.max(maxIndex, k) else if (k == "n") then if v ~= (t.n or #t) then return false end -- False if n does not hold the number of elements else -- Else of (k=='n') if isEncodable(v) then return false end end -- End of (k~='n') end -- End of k,v not an indexed pair end -- End of loop across all pairs return true, maxIndex end --- Determines whether the given Lua object / table / variable can be JSON encoded. The only -- types that are JSON encodable are: string, boolean, number, nil, table and json.null. -- In this implementation, all other types are ignored. -- @param o The object to examine. -- @return boolean True if the object should be JSON encoded, false if it should be ignored. function isEncodable(o) local t = type(o) return (t == "string" or t == "boolean" or t == "number" or t == "nil" or t == "table") or (t == "function" and o == json.null) end return json end local debugger_print = print local debug_server = nil local breakInfoSocket = nil local json = createJson() local LuaDebugger = { fileMaps = {}, Run = true, --表示正常运行只检测断点 StepIn = false, StepInLevel = 0, StepNext = false, StepNextLevel = 0, StepOut = false, breakInfos = {}, runTimeType = nil, isHook = true, pathCachePaths = {}, isProntToConsole = 1, isFoxGloryProject = false, isDebugPrint = true, hookType = "lrc", currentFileName = "", currentTempFunc = nil, --分割字符串缓存 splitFilePaths = {}, DebugLuaFie = "", version = "0.9.3", serVarLevel = 4 } local debug_hook = nil local _resume = coroutine.resume coroutine.resume = function(co, ...) if (LuaDebugger.isHook) then if coroutine.status(co) ~= "dead" then debug.sethook(co, debug_hook, "lrc") end end return _resume(co, ...) end local _wrap = coroutine.wrap coroutine.wrap = function(fun,dd) local newFun =_wrap(function() debug.sethook(debug_hook, "lrc") return fun(); end) return newFun end LuaDebugger.event = { S2C_SetBreakPoints = 1, C2S_SetBreakPoints = 2, S2C_RUN = 3, C2S_HITBreakPoint = 4, S2C_ReqVar = 5, C2S_ReqVar = 6, --单步跳过请求 S2C_NextRequest = 7, --单步跳过反馈 C2S_NextResponse = 8, -- 单步跳过 结束 没有下一步 C2S_NextResponseOver = 9, --单步跳入 S2C_StepInRequest = 10, C2S_StepInResponse = 11, --单步跳出 S2C_StepOutRequest = 12, --单步跳出返回 C2S_StepOutResponse = 13, --打印 C2S_LuaPrint = 14, S2C_LoadLuaScript = 16, C2S_SetSocketName = 17, C2S_LoadLuaScript = 18, C2S_DebugXpCall = 20, S2C_DebugClose = 21, S2C_SerVar = 24, C2S_SerVar = 25, S2C_ReLoadFile = 26, C2S_ReLoadFile = 27, } --@region print function print(...) if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 3) then debugger_print(...) end if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 2) then if (debug_server) then local arg = {...} --这里的...和{}符号中间需要有空格号,否则会出错 local str = "" if (#arg == 0) then arg = {"nil"} end for k, v in pairs(arg) do str = str .. tostring(v) .. "\t" end local sendMsg = { event = LuaDebugger.event.C2S_LuaPrint, data = {msg = ZZBase64.encode(str), type = 1} } local sendStr = json.encode(sendMsg) debug_server:send(sendStr .. "__debugger_k0204__") end end end function luaIdePrintWarn(...) if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 3) then debugger_print(...) end if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 2) then if (debug_server) then local arg = {...} --这里的...和{}符号中间需要有空格号,否则会出错 local str = "" if (#arg == 0) then arg = {"nil"} end for k, v in pairs(arg) do str = str .. tostring(v) .. "\t" end local sendMsg = { event = LuaDebugger.event.C2S_LuaPrint, data = {msg = ZZBase64.encode(str), type = 2} } local sendStr = json.encode(sendMsg) debug_server:send(sendStr .. "__debugger_k0204__") end end end function luaIdePrintErr(...) if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 3) then debugger_print(...) end if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 2) then if (debug_server) then local arg = {...} --这里的...和{}符号中间需要有空格号,否则会出错 local str = "" if (#arg == 0) then arg = {"nil"} end for k, v in pairs(arg) do str = str .. tostring(v) .. "\t" end local sendMsg = { event = LuaDebugger.event.C2S_LuaPrint, data = {msg = ZZBase64.encode(str), type = 3} } local sendStr = json.encode(sendMsg) debug_server:send(sendStr .. "__debugger_k0204__") end end end --@endregion --@region 辅助方法 local function debugger_lastIndex(str, p) local startIndex = string.find(str, p, 1) while startIndex do local findstartIndex = string.find(str, p, startIndex + 1) if (not findstartIndex) then break else startIndex = findstartIndex end end return startIndex end local function debugger_convertParentDir(dir) local index, endindex = string.find(dir, "/%.%./") if (index) then local file1 = string.sub(dir, 1, index - 1) local startIndex = debugger_lastIndex(file1, "/") file1 = string.sub(file1, 1, startIndex - 1) local file2 = string.sub(dir, endindex) dir = file1 .. file2 dir = debugger_convertParentDir(dir) return dir else return dir end end local function debugger_getFilePathInfo(file) local fileName = nil local dir = nil file = file:gsub("/.\\", "/") file = file:gsub("\\", "/") file = file:gsub("//", "/") if file:find("@") == 1 then file = file:sub(2) end local findex = file:find("%./") if (findex == 1) then file = file:sub(3) end file = debugger_convertParentDir(file) local fileLength = string.len(file) local suffixNames = { ".lua", ".lua.txt", ".txt", ".bytes" } table.sort( suffixNames, function(name1, name2) return string.len(name1) > string.len(name2) end ) local suffixLengs = {} for i, suffixName in ipairs(suffixNames) do table.insert(suffixLengs, string.len(suffixName)) end local fileLength = string.len(file) for i, suffix in ipairs(suffixNames) do local suffixName = string.sub(file, fileLength - suffixLengs[i] + 1) if (suffixName == suffix) then file = string.sub(file, 1, fileLength - suffixLengs[i]) break end end local fileNameStartIndex = debugger_lastIndex(file, "/") if (fileNameStartIndex) then fileName = string.sub(file, fileNameStartIndex + 1) dir = string.sub(file, 1, fileNameStartIndex) file = dir .. fileName else fileNameStartIndex = debugger_lastIndex(file, "%.") if (not fileNameStartIndex) then fileName = file dir = "" else dir = string.sub(file, 1, fileNameStartIndex) dir = dir:gsub("%.", "/") fileName = string.sub(file, fileNameStartIndex + 1) file = dir .. fileName end end return file, dir, fileName end --@endregion ----=============================工具方法============================================= --@region 工具方法 local function debugger_strSplit(input, delimiter) input = tostring(input) delimiter = tostring(delimiter) if (delimiter == "") then return false end local pos, arr = 0, {} -- for each divider found for st, sp in function() return string.find(input, delimiter, pos, true) end do table.insert(arr, string.sub(input, pos, st - 1)) pos = sp + 1 end table.insert(arr, string.sub(input, pos)) return arr end local function debugger_strTrim(input) input = string.gsub(input, "^[ \t\n\r]+", "") return string.gsub(input, "[ \t\n\r]+$", "") end local function debugger_dump(value, desciption, nesting) if type(nesting) ~= "number" then nesting = 3 end local lookupTable = {} local result = {} local function _v(v) if type(v) == "string" then v = '"' .. v .. '"' end return tostring(v) end local traceback = debugger_strSplit(debug.traceback("", 2), "\n") print("dump from: " .. debugger_strTrim(traceback[3])) local function _dump(value, desciption, indent, nest, keylen) desciption = desciption or "" local spc = "" if type(keylen) == "number" then spc = string.rep(" ", keylen - string.len(_v(desciption))) end if type(value) ~= "table" then result[#result + 1] = string.format("%s%s%s = %s", indent, _v(desciption), spc, _v(value)) elseif lookupTable[value] then result[#result + 1] = string.format("%s%s%s = *REF*", indent, desciption, spc) else lookupTable[value] = true if nest > nesting then result[#result + 1] = string.format("%s%s = *MAX NESTING*", indent, desciption) else result[#result + 1] = string.format("%s%s = {", indent, _v(desciption)) local indent2 = indent .. " " local keys = {} local keylen = 0 local values = {} for k, v in pairs(value) do keys[#keys + 1] = k local vk = _v(k) local vkl = string.len(vk) if vkl > keylen then keylen = vkl end values[k] = v end table.sort( keys, function(a, b) if type(a) == "number" and type(b) == "number" then return a < b else return tostring(a) < tostring(b) end end ) for i, k in ipairs(keys) do _dump(values[k], k, indent2, nest + 1, keylen) end result[#result + 1] = string.format("%s}", indent) end end end _dump(value, desciption, "- ", 1) for i, line in ipairs(result) do print(line) end end --@endregion local function debugger_valueToString(v) local vtype = type(v) local vstr = nil if (vtype == "userdata") then if (LuaDebugger.isFoxGloryProject) then return "userdata",vtype else return tostring(v), vtype end elseif (vtype == "table" or vtype == "function" or vtype == "boolean") then local value = vtype xpcall(function() value = tostring(v) end,function() value = vtype end) return value, vtype elseif (vtype == "number" or vtype == "string" ) then return v, vtype else return tostring(v), vtype end end local function debugger_setVarInfo(name, value) local valueStr, valueType = debugger_valueToString(value) local nameStr,nameType = debugger_valueToString(name) if(valueStr == nil) then valueStr = valueType end local valueInfo = { name =nameStr, valueType = valueType, valueStr = ZZBase64.encode(valueStr) } return valueInfo end local function debugger_getvalue(f) local i = 1 local locals = {} -- get locals while true do local name, value = debug.getlocal(f, i) if not name then break end if (name ~= "(*temporary)") then locals[name] = value end i = i + 1 end local func = getinfo(f, "f").func i = 1 local ups = {} while func do -- check for func as it may be nil for tail calls local name, value = debug.getupvalue(func, i) if not name then break end if (name == "_ENV") then ups["_ENV_"] = value else ups[name] = value end i = i + 1 end return {locals = locals, ups = ups} end --获取堆栈 debugger_stackInfo = function(ignoreCount, event) local datas = {} local stack = {} local varInfos = {} local funcs = {} local index = 0 for i = ignoreCount, 100 do local source = getinfo(i) local isadd = true if (i == ignoreCount) then local file = source.source if (file:find(LuaDebugger.DebugLuaFie)) then return end if (file == "=[C]") then isadd = false end end if not source then break end if (isadd) then local fullName, dir, fileName = debugger_getFilePathInfo(source.source) local info = { src = fullName, scoreName = source.name, currentline = source.currentline, linedefined = source.linedefined, what = source.what, nameWhat = source.namewhat } index = i local vars = debugger_getvalue(i + 1) table.insert(stack, info) table.insert(varInfos, vars) table.insert(funcs, source.func) end if source.what == "main" then break end end local stackInfo = {stack = stack, vars = varInfos, funcs = funcs} local data = { stack = stackInfo.stack, vars = stackInfo.vars, funcs = stackInfo.funcs, event = event, funcsLength = #stackInfo.funcs, upFunc = getinfo(ignoreCount - 3, "f").func } LuaDebugger.currentTempFunc = data.funcs[1] return data end --===========================点断信息================================================== --根据不同的游戏引擎进行定时获取断点信息 --CCDirector:sharedDirector():getScheduler() local debugger_setBreak = nil local function debugger_receiveDebugBreakInfo() if (jit) then if (LuaDebugger.debugLuaType ~= "jit") then local msg = "当前luajit版本为: " .. jit.version .. " 请使用LuaDebugjit 进行调试!" print(msg) end end if (breakInfoSocket) then local msg, status = breakInfoSocket:receive() if(LuaDebugger.isLaunch and status == "closed") then os.exit() end if (msg) then local netData = json.decode(msg) if netData.event == LuaDebugger.event.S2C_SetBreakPoints then debugger_setBreak(netData.data) elseif netData.event == LuaDebugger.event.S2C_LoadLuaScript then LuaDebugger.loadScriptBody = netData.data debugger_exeLuaString() debugger_sendMsg(breakInfoSocket,LuaDebugger.event.C2S_LoadLuaScript,LuaDebugger.loadScriptBody) elseif netData.event == LuaDebugger.event.S2C_ReLoadFile then LuaDebugger.reLoadFileBody = netData.data LuaDebugger.isReLoadFile = false LuaDebugger.reLoadFileBody.isReLoad = debugger_reLoadFile(LuaDebugger.reLoadFileBody) print("重载结果:",LuaDebugger.reLoadFileBody.isReLoad) LuaDebugger.reLoadFileBody.script = nil debugger_sendMsg( breakInfoSocket, LuaDebugger.event.C2S_ReLoadFile, { stack = LuaDebugger.reLoadFileBody } ) end end end end local function splitFilePath(path) if (LuaDebugger.splitFilePaths[path]) then return LuaDebugger.splitFilePaths[path] end local pos, arr = 0, {} -- for each divider found for st, sp in function() return string.find(path, "/", pos, true) end do local pathStr = string.sub(path, pos, st - 1) table.insert(arr, pathStr) pos = sp + 1 end local pathStr = string.sub(path, pos) table.insert(arr, pathStr) LuaDebugger.splitFilePaths[path] = arr return arr end debugger_setBreak = function(datas) local breakInfos = LuaDebugger.breakInfos for i, data in ipairs(datas) do data.fileName = string.lower(data.fileName) data.serverPath = string.lower(data.serverPath) local breakInfo = breakInfos[data.fileName] if (not breakInfo) then breakInfos[data.fileName] = {} breakInfo = breakInfos[data.fileName] end if (not data.breakDatas or #data.breakDatas == 0) then breakInfo[data.serverPath] = nil else local fileBreakInfo = breakInfo[data.serverPath] if (not fileBreakInfo) then fileBreakInfo = { pathNames = splitFilePath(data.serverPath), --命中次數判斷計數器 hitCounts = {} } breakInfo[data.serverPath] = fileBreakInfo end local lineInfos = {} for li, breakData in ipairs(data.breakDatas) do lineInfos[breakData.line] = breakData if (breakData.hitCondition and breakData.hitCondition ~= "") then breakData.hitCondition = tonumber(breakData.hitCondition) else breakData.hitCondition = 0 end if (not fileBreakInfo.hitCounts[breakData.line]) then fileBreakInfo.hitCounts[breakData.line] = 0 end end fileBreakInfo.lines = lineInfos --這裡添加命中次數判斷 for line, count in pairs(fileBreakInfo.hitCounts) do if (not lineInfos[line]) then fileBreakInfo.hitCounts[line] = nil end end end local count = 0 for i, linesInfo in pairs(breakInfo) do count = count + 1 end if (count == 0) then breakInfos[data.fileName] = nil end end --debugger_dump(breakInfos, "breakInfos", 6) --检查是否需要断点 local isHook = false for k, v in pairs(breakInfos) do isHook = true break end --这样做的原因是为了最大限度的使手机调试更加流畅 注意这里会连续的进行n次 if (isHook) then if (not LuaDebugger.isHook) then debug.sethook(debug_hook, "lrc") end LuaDebugger.isHook = true else if (LuaDebugger.isHook) then debug.sethook() end LuaDebugger.isHook = false end end local function debugger_checkFileIsBreak(fileName) return LuaDebugger.breakInfos[fileName] end --=====================================断点信息 end ---------------------------------------------- local controller_host = "192.168.1.102" local controller_port = 7003 debugger_sendMsg = function(serverSocket, eventName, data) local sendMsg = { event = eventName, data = data } local sendStr = json.encode(sendMsg) serverSocket:send(sendStr .. "__debugger_k0204__") end function debugger_conditionStr(condition, vars, callBack) local function loadScript() local currentTabble = {} local locals = vars[1].locals local ups = vars[1].ups if (ups) then for k, v in pairs(ups) do currentTabble[k] = v end end if (locals) then for k, v in pairs(locals) do currentTabble[k] = v end end setmetatable(currentTabble, {__index = _G}) local fun = loadstring("return " .. condition) setfenv(fun, currentTabble) return fun() end local status, msg = xpcall( loadScript, function(error) print(error) end ) if (status and msg) then callBack() end end --执行lua字符串 debugger_exeLuaString = function() local function loadScript() local script = LuaDebugger.loadScriptBody.script if (LuaDebugger.loadScriptBody.isBreak) then local currentTabble = {_G = _G} local frameId = LuaDebugger.loadScriptBody.frameId frameId = frameId local func = LuaDebugger.currentDebuggerData.funcs[frameId] local vars = LuaDebugger.currentDebuggerData.vars[frameId] local locals = vars.locals local ups = vars.ups for k, v in pairs(ups) do currentTabble[k] = v end for k, v in pairs(locals) do currentTabble[k] = v end setmetatable(currentTabble, {__index = _G}) local fun = loadstring(script) setfenv(fun, currentTabble) fun() else local fun = loadstring(script) fun() end end local status, msg = xpcall( loadScript, function(error) -- debugger_sendMsg(debug_server, LuaDebugger.event.C2S_LoadLuaScript, LuaDebugger.loadScriptBody) end ) LuaDebugger.loadScriptBody.script = nil if (LuaDebugger.loadScriptBody.isBreak) then LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 LuaDebugger.currentDebuggerData = debugger_stackInfo(LuaDebugger.serVarLevel, LuaDebugger.event.C2S_HITBreakPoint) LuaDebugger.loadScriptBody.stack = LuaDebugger.currentDebuggerData.stack end LuaDebugger.loadScriptBody.complete = true end --@region 调试中修改变量值 --根据key 值在 value 查找 local function debugger_getTablekey(key,keyType,value) if(keyType == -1) then return key elseif(keyType == 1) then return tonumber(key) elseif(keyType == 2) then local valueKey = nil for k,v in pairs(value) do local nameType = type(k) if(nameType == "userdata" or nameType == "table") then if (not LuaDebugger.isFoxGloryProject) then valueKey = tostring(k) if(key == valueKey) then return k end break end end end end end local function debugger_setVarValue(server, data) local newValue = nil local level = LuaDebugger.serVarLevel+LuaDebugger.setVarBody.frameId local firstKeyName = data.keys[1] --@region vars check local localValueChangeIndex = -1 local upValueChangeIndex = -1 local upValueFun = nil local oldValue = nil local i = 1 local locals = {} -- get locals while true do local name, value = debug.getlocal(level, i) if not name then break end if(firstKeyName == name) then localValueChangeIndex = i oldValue = value end if (name ~= "(*temporary)") then locals[name] = value end i = i + 1 end local func = getinfo(level, "f").func i = 1 local ups = {} while func do -- check for func as it may be nil for tail calls local name, value = debug.getupvalue(func, i) if not name then break end if(localValueChangeIndex == -1 and firstKeyName == name) then upValueFun = func oldValue = value upValueChangeIndex = i end if (name == "_ENV") then ups["_ENV_"] = value else ups[name] = value end i = i + 1 end --@endregion local vars = {locals = locals, ups = ups} local function loadScript() local currentTabble = {} local locals = vars.locals local ups = vars.ups if (ups) then for k, v in pairs(ups) do currentTabble[k] = v end end if (locals) then for k, v in pairs(locals) do currentTabble[k] = v end end setmetatable(currentTabble, {__index = _G}) local fun = loadstring("return " .. data.value) setfenv(fun, currentTabble) newValue = fun() end local status, msg = xpcall( loadScript, function(error) print(error, "============================") end ) local i = 1 -- local 查找并替换 local keyLength = #data.keys if(keyLength == 1) then if(localValueChangeIndex ~= -1) then debug.setlocal(level, localValueChangeIndex, newValue) elseif(upValueFun ~= nil) then debug.setupvalue( upValueFun, upValueChangeIndex, newValue ) else --全局变量查找 if(_G[firstKeyName]) then _G[firstKeyName] = newValue end end else if(not oldValue) then if(_G[firstKeyName]) then oldValue = _G[firstKeyName] end end local tempValue = oldValue for i=2,keyLength-1 do if(tempValue) then oldValue = oldValue[debugger_getTablekey(data.keys[i],data.numberTypes[i],oldValue)] end end if(tempValue) then oldValue[debugger_getTablekey(data.keys[keyLength],data.numberTypes[keyLength],oldValue)] = newValue end end local varInfo = debugger_setVarInfo(data.varName, newValue) data.varInfo = varInfo LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 LuaDebugger.currentDebuggerData = debugger_stackInfo(LuaDebugger.serVarLevel, LuaDebugger.event.C2S_HITBreakPoint) end --@endregion --调试修改变量值统一的 _resume checkSetVar = function() if (LuaDebugger.isSetVar) then LuaDebugger.isSetVar = false debugger_setVarValue(debug_server,LuaDebugger.setVarBody) LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 _resume(coro_debugger, LuaDebugger.setVarBody) xpcall( checkSetVar, function(error) print("设置变量", error) end ) elseif(LuaDebugger.isLoadLuaScript) then LuaDebugger.isLoadLuaScript = false debugger_exeLuaString() LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 _resume(coro_debugger, LuaDebugger.reLoadFileBody) xpcall( checkSetVar, function(error) print("执行代码", error) end ) elseif(LuaDebugger.isReLoadFile) then LuaDebugger.isReLoadFile = false LuaDebugger.reLoadFileBody.isReLoad = debugger_reLoadFile(LuaDebugger.reLoadFileBody) print("重载结果:",LuaDebugger.reLoadFileBody.isReLoad) LuaDebugger.reLoadFileBody.script = nil LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 _resume(coro_debugger, LuaDebugger.reLoadFileBody) xpcall( checkSetVar, function(error) print("重新加载文件", error) end ) end end local function getSource(source) source = string.lower(source) if (LuaDebugger.pathCachePaths[source]) then LuaDebugger.currentLineFile = LuaDebugger.pathCachePaths[source] return LuaDebugger.pathCachePaths[source] end local fullName, dir, fileName = debugger_getFilePathInfo(source) LuaDebugger.currentLineFile = fullName LuaDebugger.pathCachePaths[source] = fileName return fileName end local function debugger_GeVarInfoBytUserData(server, var) local fileds = LuaDebugTool.getUserDataInfo(var) local varInfos = {} --c# vars for i = 1, fileds.Count do local filed = fileds[i - 1] local valueInfo = { name = filed.name, valueType = filed.valueType, valueStr = ZZBase64.encode(filed.valueStr), isValue = filed.isValue, csharp = true } table.insert(varInfos, valueInfo) end return varInfos end local function debugger_getValueByScript(value, script) local val = nil local status, msg = xpcall( function() local fun = loadstring("return " .. script) setfenv(fun, value) val = fun() end, function(error) print(error, "====>") val = nil end ) return val end local function debugger_getVarByKeys(value, keys, index) local str = "" local keyLength = #keys for i = index, keyLength do local key = keys[i] if (key == "[metatable]") then else if (i == index) then if (string.find(key, "%.")) then if (str == "") then i = index + 1 value = value[key] end if (i >= #keys) then return index, value end return debugger_getVarByKeys(value, keys, i) else str = key end else if (string.find(key, "%[")) then str = str .. key elseif (type(key) == "string") then if (string.find(key, "table:") or string.find(key, "userdata:") or string.find(key, "function:")) then if (str ~= "") then local vl = debugger_getValueByScript(value, str) value = vl if (value) then for k, v in pairs(value) do local ktype = type(k) if (ktype == "userdata" or ktype == "table" or ktype == "function") then local keyName = debugger_valueToString(k) if (keyName == key) then value = v break end end end end str = "" if (i == keyLength) then return #keys, value else return debugger_getVarByKeys(value, keys, i + 1) end else str = str .. '["' .. key .. '"]' end else str = str .. '["' .. key .. '"]' end else str = str .. "[" .. key .. "]" end end end end local v = debugger_getValueByScript(value, str) return #keys, v end --[[ @desc: 查找c# 值 author:k0204 time:2018-04-07 21:32:31 return ]] local function debugger_getCSharpValue(value, searchIndex, keys) local key = keys[searchIndex] local val = LuaDebugTool.getCSharpValue(value, key) if (val) then --1最后一个 直接返回 if (searchIndex == #keys) then return #keys, val else --2再次获得 如果没有找到那么 进行lua 层面查找 local vindex, val1 = debugger_getCSharpValue(val, searchIndex + 1, keys) if (not val1) then --组建新的keys local tempKeys = {} for i = vindex, #keys do table.insert(tempKeys, keys[i]) end local vindx, val1 = debugger_searchVarByKeys(value, searckKeys, 1) return vindx, val1 else return vindex, val1 end end else --3最终这里返回 所以2 中 没有当val1 不为空的处理 return searchIndex, val end end local function debugger_searchVarByKeys(value, keys, searckKeys) local index, val = debugger_getVarByKeys(value, searckKeys, 1) if (not LuaDebugTool or not LuaDebugTool.getCSharpValue or type(LuaDebugTool.getCSharpValue) ~= "function") then return index, val end if (val) then if (index == #keys) then return index, val else local searchStr = "" --进行c# 值查找 local keysLength = #keys local searchIndex = index + 1 local sindex, val = debugger_getCSharpValue(val, searchIndex, keys) return sindex, val end else --进行递减 local tempKeys = {} for i = 1, #searckKeys - 1 do table.insert(tempKeys, keys[i]) end if (#tempKeys == 0) then return #keys, nil end return debugger_searchVarByKeys(value, keys, tempKeys) end end --[[ @desc: 获取metatable 信息 author:k0204 time:2018-04-06 20:27:12 return ]] local function debugger_getmetatable(value, metatable, vinfos, server, variablesReference, debugSpeedIndex, metatables) for i, mtable in ipairs(metatables) do if (metatable == mtable) then return vinfos end end table.insert(metatables, metatable) for k, v in pairs(metatable) do local val = nil if (type(k) == "string") then xpcall( function() val = value[k] end, function(error) val = nil end ) if (val == nil) then xpcall( function() if (string.find(k, "__")) then val = v end end, function(error) val = nil end ) end end if (val) then local vinfo = debugger_setVarInfo(k, val) table.insert(vinfos, vinfo) if (#vinfos > 10) then debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 0 } ) vinfos = {} end end end local m = getmetatable(metatable) if (m) then return debugger_getmetatable(value, m, vinfos, server, variablesReference, debugSpeedIndex, metatables) else return vinfos end end local function debugger_sendTableField(luatable, vinfos, server, variablesReference, debugSpeedIndex, valueType) if (valueType == "userdata") then if (tolua and tolua.getpeer) then luatable = tolua.getpeer(luatable) else return vinfos end end if (luatable == nil) then return vinfos end for k, v in pairs(luatable) do local vinfo = debugger_setVarInfo(k, v) table.insert(vinfos, vinfo) if (#vinfos > 10) then debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 0 } ) vinfos = {} end end return vinfos end local function debugger_sendTableValues(value, server, variablesReference, debugSpeedIndex) local vinfos = {} local luatable = {} local valueType = type(value) local userDataInfos = {} local m = nil if (valueType == "userdata") then m = getmetatable(value) vinfos = debugger_sendTableField(value, vinfos, server, variablesReference, debugSpeedIndex, valueType) if (LuaDebugTool) then local varInfos = debugger_GeVarInfoBytUserData(server, value, variablesReference, debugSpeedIndex) for i, v in ipairs(varInfos) do if (v.valueType == "System.Byte[]" and value[v.name] and type(value[v.name]) == "string") then local valueInfo = { name = v.name, valueType = "string", valueStr = ZZBase64.encode(value[v.name]) } table.insert(vinfos, valueInfo) else table.insert(vinfos, v) end if (#vinfos > 10) then debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 0 } ) vinfos = {} end end end else m = getmetatable(value) vinfos = debugger_sendTableField(value, vinfos, server, variablesReference, debugSpeedIndex, valueType) end if (m) then vinfos = debugger_getmetatable(value, m, vinfos, server, variablesReference, debugSpeedIndex, {}) end debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 1 } ) end --获取lua 变量的方法 local function debugger_getBreakVar(body, server) local variablesReference = body.variablesReference local debugSpeedIndex = body.debugSpeedIndex local vinfos = {} local function exe() local frameId = body.frameId local type_ = body.type local keys = body.keys --找到对应的var local vars = nil if (type_ == 1) then vars = LuaDebugger.currentDebuggerData.vars[frameId + 1] vars = vars.locals elseif (type_ == 2) then vars = LuaDebugger.currentDebuggerData.vars[frameId + 1] vars = vars.ups elseif (type_ == 3) then vars = _G end if (#keys == 0) then debugger_sendTableValues(vars, server, variablesReference, debugSpeedIndex) return end local index, value = debugger_searchVarByKeys(vars, keys, keys) if (value) then local valueType = type(value) if (valueType == "table" or valueType == "userdata") then debugger_sendTableValues(value, server, variablesReference, debugSpeedIndex) else if (valueType == "function") then value = tostring(value) end debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = ZZBase64.encode(value), isComplete = 1, varType = valueType } ) end else debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = {}, isComplete = 1, varType = "nil" } ) end end xpcall( exe, function(error) -- print("获取变量错误 错误消息-----------------") -- print(error) -- print(debug.traceback("", 2)) debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = { { name = "error", valueType = "string", valueStr = ZZBase64.encode("无法获取属性值:" .. error .. "->" .. debug.traceback("", 2)), isValue = false } }, isComplete = 1 } ) end ) end local function ResetDebugInfo() LuaDebugger.Run = false LuaDebugger.StepIn = false LuaDebugger.StepNext = false LuaDebugger.StepOut = false LuaDebugger.StepNextLevel = 0 end local function debugger_loop(server) server = debug_server --命令 local command local eval_env = {} local arg while true do local line, status = server:receive() if (status == "closed") then if(LuaDebugger.isLaunch) then os.exit() else debug.sethook() coroutine.yield() end end if (line) then local netData = json.decode(line) local event = netData.event local body = netData.data if (event == LuaDebugger.event.S2C_DebugClose) then if(LuaDebugger.isLaunch) then os.exit() else debug.sethook() coroutine.yield() end elseif event == LuaDebugger.event.S2C_SetBreakPoints then --设置断点信息 local function setB() debugger_setBreak(body) end xpcall( setB, function(error) print(error) end ) elseif event == LuaDebugger.event.S2C_RUN then --开始运行 LuaDebugger.runTimeType = body.runTimeType LuaDebugger.isProntToConsole = body.isProntToConsole LuaDebugger.isFoxGloryProject = body.isFoxGloryProject LuaDebugger.isLaunch = body.isLaunch ResetDebugInfo() LuaDebugger.Run = true local data = coroutine.yield() LuaDebugger.serVarLevel = 4 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack } ) elseif event == LuaDebugger.event.S2C_ReqVar then -- 获取变量信息 --请求数据信息 debugger_getBreakVar(body, server) elseif event == LuaDebugger.event.S2C_NextRequest then -- 设置单步跳过 ResetDebugInfo() LuaDebugger.StepNext = true LuaDebugger.StepNextLevel = 0 --设置当前文件名和当前行数 local data = coroutine.yield() LuaDebugger.serVarLevel = 4 --重置调试信息 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack } ) elseif (event == LuaDebugger.event.S2C_StepInRequest) then --单步跳入 --单步跳入 ResetDebugInfo() LuaDebugger.StepIn = true local data = coroutine.yield() LuaDebugger.serVarLevel = 4 --重置调试信息 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack, eventType = data.eventType } ) elseif (event == LuaDebugger.event.S2C_StepOutRequest) then --单步跳出 ResetDebugInfo() LuaDebugger.StepOut = true local data = coroutine.yield() LuaDebugger.serVarLevel = 4 --重置调试信息 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack, eventType = data.eventType } ) elseif event == LuaDebugger.event.S2C_LoadLuaScript then LuaDebugger.loadScriptBody = body LuaDebugger.isLoadLuaScript = true local data = coroutine.yield() debugger_sendMsg( server, LuaDebugger.event.C2S_LoadLuaScript, LuaDebugger.loadScriptBody ) elseif event == LuaDebugger.event.S2C_SerVar then LuaDebugger.isSetVar = true LuaDebugger.setVarBody = body local data = coroutine.yield() debugger_sendMsg( server, LuaDebugger.event.C2S_SerVar, { stack = data, eventType = data.eventType } ) elseif event == LuaDebugger.event.S2C_ReLoadFile then LuaDebugger.isReLoadFile = true LuaDebugger.reLoadFileBody = body local data = coroutine.yield() debugger_sendMsg( server, LuaDebugger.event.C2S_ReLoadFile, { stack = data, eventType = data.eventType } ) end end end end coro_debugger = coroutine.create(debugger_loop) debug_hook = function(event, line) if(not LuaDebugger.isHook) then return end if(LuaDebugger.Run) then if(event == "line") then local isCheck = false for k, breakInfo in pairs(LuaDebugger.breakInfos) do for bk, linesInfo in pairs(breakInfo) do if(linesInfo.lines and linesInfo.lines[line]) then isCheck = true break end end if(isCheck) then break end end if(not isCheck) then return end else LuaDebugger.currentFileName = nil LuaDebugger.currentTempFunc = nil return end end --跳出 if (LuaDebugger.StepOut) then if (event == "line" or event == "call") then return end local tempFun = getinfo(2, "f").func if (LuaDebugger.currentDebuggerData.funcsLength == 1) then ResetDebugInfo() LuaDebugger.Run = true else if (LuaDebugger.currentDebuggerData.funcs[2] == tempFun) then local data = debugger_stackInfo(3, LuaDebugger.event.C2S_StepInResponse) --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() end end return end -- debugger_dump(LuaDebugger,"LuaDebugger") -- print(LuaDebugger.StepNextLevel,"LuaDebugger.StepNextLevel") local file = nil if (event == "call") then -- end -- if(not LuaDebugger.StepOut) then if (not LuaDebugger.Run) then LuaDebugger.StepNextLevel = LuaDebugger.StepNextLevel + 1 end -- print("stepIn",LuaDebugger.StepNextLevel) local stepInfo = getinfo(2, "S") local source = stepInfo.source if (source:find(LuaDebugger.DebugLuaFie) or source == "=[C]") then return end file = getSource(source) LuaDebugger.currentFileName = file elseif (event == "return" or event == "tail return") then -- end -- if(not LuaDebugger.StepOut) then if (not LuaDebugger.Run) then LuaDebugger.StepNextLevel = LuaDebugger.StepNextLevel - 1 end LuaDebugger.currentFileName = nil elseif (event == "line") then --@region 判断命中断点 --判断命中断点 --判断命中断点 --判断命中断点 --判断命中断点 local isHit = false local stepInfo = nil if (not LuaDebugger.currentFileName) then stepInfo = getinfo(2, "S") local source = stepInfo.source if (source == "=[C]" or source:find(LuaDebugger.DebugLuaFie)) then return end file = getSource(source) LuaDebugger.currentFileName = file end file = LuaDebugger.currentFileName --判断断点 local breakInfo = LuaDebugger.breakInfos[file] local breakData = nil if (breakInfo) then local ischeck = false for k, lineInfo in pairs(breakInfo) do local lines = lineInfo.lines if (lines and lines[line]) then ischeck = true break end end if (ischeck) then --并且在断点中 -- local info = stepInfo -- if (not info) then -- print("info ---------------") -- info = getinfo(2) -- end local hitPathNames = splitFilePath(LuaDebugger.currentLineFile) local hitCounts = {} local debugHitCounts = nil for k, lineInfo in pairs(breakInfo) do local lines = lineInfo.lines local pathNames = lineInfo.pathNames debugHitCounts = lineInfo.hitCounts if (lines and lines[line]) then breakData = lines[line] --判断路径 hitCounts[k] = 0 local hitPathNamesCount = #hitPathNames local pathNamesCount = #pathNames local checkCount = 0; while (true) do if (pathNames[pathNamesCount] ~= hitPathNames[hitPathNamesCount]) then break else hitCounts[k] = hitCounts[k] + 1 end pathNamesCount = pathNamesCount - 1 hitPathNamesCount = hitPathNamesCount - 1 checkCount = checkCount+1 if (pathNamesCount <= 0 or hitPathNamesCount <= 0) then break end end if(checkCount>0) then break; end if(checkCount==0) then breakData = nil -- break; end else breakData = nil end end if (breakData) then local hitFieName = "" local maxCount = 0 for k, v in pairs(hitCounts) do if (v > maxCount) then maxCount = v hitFieName = k end end local hitPathNamesLength = #hitPathNames if (hitPathNamesLength == 1 or (hitPathNamesLength > 1 and maxCount > 1)) then if (hitFieName ~= "") then local hitCount = breakData.hitCondition local clientHitCount = debugHitCounts[breakData.line] clientHitCount = clientHitCount + 1 debugHitCounts[breakData.line] = clientHitCount if (clientHitCount >= hitCount) then isHit = true end end end end end end --@endregion if (LuaDebugger.StepIn) then local data = debugger_stackInfo(3, LuaDebugger.event.C2S_NextResponse) --挂起等待调试器作出反应 if (data) then LuaDebugger.currentTempFunc = data.funcs[1] _resume(coro_debugger, data) checkSetVar() return end end if (LuaDebugger.StepNext) then if (LuaDebugger.StepNextLevel <= 0) then local data = debugger_stackInfo(3, LuaDebugger.event.C2S_NextResponse) -- 挂起等待调试器作出反应 if (data) then LuaDebugger.currentTempFunc = data.funcs[1] _resume(coro_debugger, data) checkSetVar() return end end end if (isHit) then local data = debugger_stackInfo(3, LuaDebugger.event.C2S_HITBreakPoint) if (breakData and breakData.condition) then debugger_conditionStr( breakData.condition, data.vars, function() _resume(coro_debugger, data) checkSetVar() end ) else --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() end end end end debugger_xpcall = function() --调用 coro_debugger 并传入 参数 local data = debugger_stackInfo(4, LuaDebugger.event.C2S_HITBreakPoint) if(data.stack and data.stack[1]) then data.stack[1].isXpCall = true end --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() end --调试开始 local function start() local fullName, dirName, fileName = debugger_getFilePathInfo(getinfo(1).source) LuaDebugger.DebugLuaFie = fileName local socket = createSocket() print(controller_host) print(controller_port) local server = socket.connect(controller_host, controller_port) debug_server = server if server then --创建breakInfo socket socket = createSocket() breakInfoSocket = socket.connect(controller_host, controller_port) if (breakInfoSocket) then breakInfoSocket:settimeout(0) debugger_sendMsg( breakInfoSocket, LuaDebugger.event.C2S_SetSocketName, { name = "breakPointSocket" } ) debugger_sendMsg( server, LuaDebugger.event.C2S_SetSocketName, { name = "mainSocket", version = LuaDebugger.version } ) xpcall( function() debug.sethook(debug_hook, "lrc") end, function(error) print("error:", error) end ) if (jit) then if (LuaDebugger.debugLuaType ~= "jit") then print("error======================================================") local msg = "当前luajit版本为: " .. jit.version .. " 请使用LuaDebugjit 进行调试!" print(msg) end end _resume(coro_debugger, server) end end end function StartDebug(host, port,reLoad) if (not host) then print("error host nil") end if (not port) then print("error prot nil") end if (type(host) ~= "string") then print("error host not string") end if (type(port) ~= "number") then print("error host not number") end controller_host = host controller_port = port xpcall( start, function(error) -- body print(error) end ) --代码重载 if(isReLoad) then xpcall(function() debugger_reLoadFile = require("luaideReLoadFile") end,function() print("左侧luaide按钮->打开luaIde最新调试文件所在文件夹->luaideReLoadFile.lua->拷贝到项目中") print("具体使用方式请看luaideReLoadFile中文件注释") debugger_reLoadFile = function() print("未实现代码重载") end end) end return debugger_receiveDebugBreakInfo, debugger_xpcall end --base64 local string = string ZZBase64.__code = { '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', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', }; ZZBase64.__decode = {} for k,v in pairs(ZZBase64.__code) do ZZBase64.__decode[string.byte(v,1)] = k - 1 end function ZZBase64.encode(text) local len = string.len(text) local left = len % 3 len = len - left local res = {} local index = 1 for i = 1, len, 3 do local a = string.byte(text, i ) local b = string.byte(text, i + 1) local c = string.byte(text, i + 2) -- num = a<<16 + b<<8 + c local num = a * 65536 + b * 256 + c for j = 1, 4 do --tmp = num >> ((4 -j) * 6) local tmp = math.floor(num / (2 ^ ((4-j) * 6))) --curPos = tmp&0x3f local curPos = tmp % 64 + 1 res[index] = ZZBase64.__code[curPos] index = index + 1 end end if left == 1 then ZZBase64.__left1(res, index, text, len) elseif left == 2 then ZZBase64.__left2(res, index, text, len) end return table.concat(res) end function ZZBase64.__left2(res, index, text, len) local num1 = string.byte(text, len + 1) num1 = num1 * 1024 --lshift 10 local num2 = string.byte(text, len + 2) num2 = num2 * 4 --lshift 2 local num = num1 + num2 local tmp1 = math.floor(num / 4096) --rShift 12 local curPos = tmp1 % 64 + 1 res[index] = ZZBase64.__code[curPos] local tmp2 = math.floor(num / 64) curPos = tmp2 % 64 + 1 res[index + 1] = ZZBase64.__code[curPos] curPos = num % 64 + 1 res[index + 2] = ZZBase64.__code[curPos] res[index + 3] = "=" end function ZZBase64.__left1(res, index,text, len) local num = string.byte(text, len + 1) num = num * 16 local tmp = math.floor(num / 64) local curPos = tmp % 64 + 1 res[index ] = ZZBase64.__code[curPos] curPos = num % 64 + 1 res[index + 1] = ZZBase64.__code[curPos] res[index + 2] = "=" res[index + 3] = "=" end function ZZBase64.decode(text) local len = string.len(text) local left = 0 if string.sub(text, len - 1) == "==" then left = 2 len = len - 4 elseif string.sub(text, len) == "=" then left = 1 len = len - 4 end local res = {} local index = 1 local decode = ZZBase64.__decode for i =1, len, 4 do local a = decode[string.byte(text,i )] local b = decode[string.byte(text,i + 1)] local c = decode[string.byte(text,i + 2)] local d = decode[string.byte(text,i + 3)] --num = a<<18 + b<<12 + c<<6 + d local num = a * 262144 + b * 4096 + c * 64 + d local e = string.char(num % 256) num = math.floor(num / 256) local f = string.char(num % 256) num = math.floor(num / 256) res[index ] = string.char(num % 256) res[index + 1] = f res[index + 2] = e index = index + 3 end if left == 1 then ZZBase64.__decodeLeft1(res, index, text, len) elseif left == 2 then ZZBase64.__decodeLeft2(res, index, text, len) end return table.concat(res) end function ZZBase64.__decodeLeft1(res, index, text, len) local decode = ZZBase64.__decode local a = decode[string.byte(text, len + 1)] local b = decode[string.byte(text, len + 2)] local c = decode[string.byte(text, len + 3)] local num = a * 4096 + b * 64 + c local num1 = math.floor(num / 1024) % 256 local num2 = math.floor(num / 4) % 256 res[index] = string.char(num1) res[index + 1] = string.char(num2) end function ZZBase64.__decodeLeft2(res, index, text, len) local decode = ZZBase64.__decode local a = decode[string.byte(text, len + 1)] local b = decode[string.byte(text, len + 2)] local num = a * 64 + b num = math.floor(num / 16) res[index] = string.char(num) end return StartDebug main require("common/network/NetworkStateUtil") if not Device.isWebGL() then -- socket -- require("common/network/socket/CmdDef") -- require("common/network/socket/SocketCmdMgr") -- require("common/network/socket/PBSocketPack") -- require("common/network/socket/SocketMgr") end -- http require("common/network/http/HttpCmdDef") require("common/network/http/HttpCmdMgr") Swordfish-- 剑鱼 local Swordfish = defClass("Swordfish") -- 构造函数 function Swordfish:ctor(worldPos, parent) self.id = 601002 self.go = GameObject.Instantiate(FishingGameMgr.resLink.sword_fish) self.transform = self.go.transform self.transform:SetParent(parent, false) self.transform.localPosition = worldPos or Vector3.zero self:init() end -- 初始化 function Swordfish:init() self:initData() self:initEvent() end -- 重载剑鱼 function Swordfish:reload(worldPos) self.go:SetActive(true) self.transform.localPosition = worldPos or Vector3.zero self.time = 2 self.active = true end -- 隐藏剑鱼 function Swordfish:hide() self.active = false self.go:SetActive(false) end -- 初始化数据 function Swordfish:initData() local config = FishCfgParse:getFishCfgById(self.id) self.speed = config.speed self.grade = config.grade self.time = 2 self.active = true end -- 初始化事件 function Swordfish:initEvent() self.collider = self.go:Seek("collider") Event.add(self.collider, Event.OnTriggerEnter2D, function(other) self:onTriggerEnter(other) end) end -- 时间更新 function Swordfish:timeUpdate(deltaTime) if not self.active then return end -- 等待两秒向玩家发起攻击 if self.time > 0 then self.time = self.time - deltaTime else -- 向左移动 local pos = self.transform.localPosition pos.x = pos.x - self.speed / 100 * deltaTime self.transform.localPosition = pos -- 如果剑鱼超出屏幕范围,则隐藏 if pos.x < -FishingGameMgr.map:getWorldSize().width / 2 then self:hide() end end end -- 当玩家触发碰撞时 function Swordfish:onTriggerEnter(other) if (not FishingGameMgr.isPlaying) or FishingGameMgr.isPaused then return end if other.gameObject:CompareTag("Player") then if self.grade > FishingGameMgr:getCharacterGrade() then FishingGameMgr:hurt() -- 改为减少能量 end end end return SwordfishOpeningAnimation!-- 捕鱼开场动画 local OpeningAnimation = defClass("OpeningAnimation") function OpeningAnimation:ctor(map) self:init(map) end function OpeningAnimation:init(map) self.capybara = map:Seek("capybara") self.capybaraAmin = self.capybara:Seek("spine") self.spray = map:Seek("spray") self.sprayAnim = self.spray:Seek("spine") self.startPoint = map:Seek("start_point") self.jumpPoint = map:Seek("jump_point") self.jumpDownPoint = map:Seek("jump_down_point") self.jumpEndPoint = map:Seek("jump_end_point") self.cameraDownPoint = map:Seek("camera_down_point") self.cagePoint = map:Seek("cage_point") self.cagePointSpine = self.cagePoint:Seek("spine") end function OpeningAnimation:reset() FishingGameMgr.map.anims:SetActive(false) self.cagePos = nil self.centerPos = nil self.capybara.transform.localPosition = self.startPoint.transform.localPosition self.capybara.transform.localEulerAngles = Vector3.zero self:playCapybaraStand() end function OpeningAnimation:play(cagePos, centerPos) self.cagePos = cagePos self.centerPos = centerPos self.cagePoint.transform.localPosition = cagePos self:playCageAnim() local delayTime = 1.27 local moveTime = 0 local jumpTime = 0.45 local jumpDownTime = 0.46 local swimTime = 2 local downTime = 1.4 local cageTime = 0.8 local backTime = 1 self.capybara.transform.localPosition = self.startPoint.transform.localPosition self.capybara.transform.localEulerAngles = Vector3.zero FishingGameMgr.cameraCtl:setCameraPosition(Vector3.zero) FishingGameMgr.map.anims:SetActive(true) self:playCapybaraAnim(delayTime, moveTime, jumpTime, swimTime) self:playCapybaraJumpAnim(delayTime + moveTime, jumpDownTime) self:playCameraMoveAnim(delayTime + moveTime + jumpTime, swimTime, downTime, cageTime, backTime) end -- 播放水獺移动到跳跃点的动画 function OpeningAnimation:playCapybaraAnim(delayTime, moveTime, jumpTime, swimTime) self:playCapybaraJump() self.capybara:RunAction(ua.Sequence({ -- 等待拿起鱼叉 ua.Delay(delayTime), -- 移动到跳跃点 ua.MoveTo(moveTime, self.jumpPoint.transform.localPosition), -- 等待跳跃完成 ua.Delay(jumpTime), -- 播放水花动画 ua.cb(function() self.capybara.transform.localPosition = self.jumpEndPoint.transform.localPosition self.capybara.transform.localEulerAngles = self.jumpEndPoint.transform.localEulerAngles self:playSpray() self:playCapybaraSwim() end), -- 游泳到相机开始下移的位置 ua.MoveTo(swimTime, self.cameraDownPoint.transform.localPosition) })) end -- 跳水时平移一段距离(看起来跳得更远) function OpeningAnimation:playCapybaraJumpAnim(delayTime, jumpDownTime) self.capybara:RunAction(ua.Sequence({ -- 等待跳跃开始 ua.Delay(delayTime), -- 平移到跳跃位置 ua.MoveTo(jumpDownTime, self.jumpDownPoint.transform.localPosition), ua.cb(function() --self:playCapybaraJump() end) })) end -- 播放相机移动动画 function OpeningAnimation:playCameraMoveAnim(delayTime, swimTime, downTime, cageTime, backTime) local cameraDownPos = self.cameraDownPoint.transform.localPosition cameraDownPos.z = -10 local cagePos = self.cagePos cagePos.z = -10 local centerPos = self.centerPos centerPos.z = -10 FishingGameMgr.cameraCtl.go:RunAction(ua.Sequence({ -- 等待水獭开始游泳 ua.Delay(delayTime), -- 和水獭同步移动到相机开始下移的位置 ua.MoveTo(swimTime * 0.85, cameraDownPos), -- 单独移动到牢笼位置 ua.MoveTo(downTime, cagePos), -- 等待一点时间观察牢笼 ua.Delay(cageTime), ua.cb(function() -- 主角放置在地图中心 FishingGameMgr.map:runCharacter() end), -- 单独移动回中心位置 ua.MoveTo(backTime, centerPos), ua.cb(function() self:reset() -- 隐藏牢笼点 self.cagePoint:SetActive(false) -- 游戏开始 FishingGameMgr:gameStart() end) })) end -- 播放水獺跳跃动画 function OpeningAnimation:playCapybaraJump() self.capybara:SetActive(true) util.spine.play(self.capybaraAmin, "kapibala_dive", false,nil,2.2) end -- 播放水獺游泳动画 function OpeningAnimation:playCapybaraSwim() util.spine.play(self.capybaraAmin, "kapibala_swim", true,nil,1) end -- 播放水獭站立动画 function OpeningAnimation:playCapybaraStand() self.capybara:SetActive(true) util.spine.play(self.capybaraAmin, "kapibala_stand_1", true) end -- 播放水花动画 function OpeningAnimation:playSpray() self.spray:SetActive(true) util.spine.play(self.sprayAnim, "stand_1", false, function() self.spray:SetActive(false) end) end -- 播放牢笼动画 function OpeningAnimation:playCageAnim() self.cagePoint:SetActive(true) util.spine.play(self.cagePointSpine, "longzi_yangtuo_stand_1", true) end return OpeningAnimation TableUtil-- -- table扩展工具类,对table不支持的功能执行扩展 -- 注意: -- 1、所有参数带hashtable的函数,将把table当做哈希表对待 -- 2、所有参数带array的函数,将把table当做可空值数组对待 -- 3、所有参数带tb的函数,对表通用,不管是哈希表还是数组 -- -- 计算哈希表长度 local function count(hashtable) local count = 0 for _,_ in pairs(hashtable) do count = count + 1 end return count end -- 计算数据长度 local function length(array) if array.n ~= nil then return array.n end local count = 0 for i,_ in pairs(array) do if count < i then count = i end end return count end -- 设置数组长度 local function setlen(array, n) array.n = n end -- 获取哈希表所有键 local function keys(hashtable) local keys = {} for k, v in pairs(hashtable) do keys[#keys + 1] = k end return keys end -- 获取哈希表所有值 local function values(hashtable) local values = {} for k, v in pairs(hashtable) do values[#values + 1] = v end return values end -- 合并哈希表:将src_hashtable表合并到dest_hashtable表,相同键值执行覆盖 local function merge(dest_hashtable, src_hashtable) for k, v in pairs(src_hashtable) do dest_hashtable[k] = v end end -- 合并数组:将src_array数组从begin位置开始插入到dest_array数组 -- 注意:begin <= 0被认为没有指定起始位置,则将两个数组执行拼接 local function insertto(dest_array, src_array, begin) assert(begin == nil or type(begin) == "number") if begin == nil or begin <= 0 then begin = #dest_array + 1 end local src_len = #src_array for i = 0, src_len - 1 do dest_array[i + begin] = src_array[i + 1] end end -- 从数组中查找指定值,返回其索引,没找到返回false local function indexof(array, value, begin) for i = begin or 1, #array do if array[i] == value then return i end end return false end -- 从哈希表查找指定值,返回其键,没找到返回nil -- 注意: -- 1、containskey用hashtable[key] ~= nil快速判断 -- 2、containsvalue由本函数返回结果是否为nil判断 local function keyof(hashtable, value) for k, v in pairs(hashtable) do if v == value then return k end end return nil end -- 从数组中删除指定值,返回删除的值的个数 function table.removebyvalue(array, value, removeall) local remove_count = 0 for i = #array, 1, -1 do if array[i] == value then table.remove(array, i) remove_count = remove_count + 1 if not removeall then break end end end return remove_count end -- 遍历写:用函数返回值更新表格内容 local function map(tb, func) for k, v in pairs(tb) do tb[k] = func(k, v) end end -- 遍历读:不修改表格 local function walk(tb, func) for k,v in pairs(tb) do func(k, v) end end -- 按指定的排序方式遍历:不修改表格 local function walksort(tb, sort_func, walk_func) local keys = table.keys(tb) table.sort(keys, function(lkey, rkey) return sort_func(lkey, rkey) end) for i = 1, table.length(keys) do walk_func(keys[i], tb[keys[i]]) end end -- 过滤掉不符合条件的项:不对原表执行操作 local function filter(tb, func) local filter = {} for k, v in pairs(tb) do if not func(k, v) then filter[k] = v end end return filter end -- 筛选出符合条件的项:不对原表执行操作 local function choose(tb, func) local choose = {} for k, v in pairs(tb) do if func(k, v) then choose[k] = v end end return choose end -- 获取数据循环器:用于循环数组遍历,每次调用走一步,到数组末尾从新从头开始 local function circulator(array) local i = 1 local iter = function() i = i >= #array and 1 or i + 1 return array[i] end return iter end local function dump(tb, dump_metatable, max_level) local lookup_table = {} local level = 0 local rep = string.rep local dump_metatable = dump_metatable local max_level = max_level or 1 local function _dump(tb, level) local str = "\n" .. rep("\t", level) .. "{\n" for k,v in pairs(tb) do local k_is_str = type(k) == "string" and 1 or 0 local v_is_str = type(v) == "string" and 1 or 0 str = str..rep("\t", level + 1).."["..rep("\"", k_is_str)..(tostring(k) or type(k))..rep("\"", k_is_str).."]".." = " if type(v) == "table" then if not lookup_table[v] and ((not max_level) or level < max_level) then lookup_table[v] = true str = str.._dump(v, level + 1, dump_metatable).."\n" else str = str..(tostring(v) or type(v))..",\n" end else str = str..rep("\"", v_is_str)..(tostring(v) or type(v))..rep("\"", v_is_str)..",\n" end end if dump_metatable then local mt = getmetatable(tb) if mt ~= nil and type(mt) == "table" then str = str..rep("\t", level + 1).."[\"__metatable\"]".." = " if not lookup_table[mt] and ((not max_level) or level < max_level) then lookup_table[mt] = true str = str.._dump(mt, level + 1, dump_metatable).."\n" else str = str..(tostring(mt) or type(mt))..",\n" end end end str = str..rep("\t", level) .. "}," return str end return _dump(tb, level) end table.count = count table.length = length table.setlen = setlen table.keys = keys table.values = values table.merge = merge table.insertto = insertto table.indexof = indexof table.keyof = keyof table.map = map table.walk = walk table.walksort = walksort table.filter = filter table.choose = choose table.circulator = circulator table.dump = dump FishingGradeCfgParse-- 捕鱼等级配置解析 local FishingGradeCfgParse = defClassStatic("FishingGradeCfgParse") local FishingGradeCfgData = require("data/config/fishingGradeCfg") local LOGTAG = "FishingGradeCfgParse" -- 初始化 function FishingGradeCfgParse:init() self.gradeDic = {} for _, v in pairs(FishingGradeCfgData) do self.gradeDic[v.grade] = v end end -- 获取所有捕鱼等级配置 function FishingGradeCfgParse:getAllFishingGradeCfg() return FishingGradeCfgData end -- 获取捕鱼等级配置 function FishingGradeCfgParse:getFishingGradeCfgByGrade(grade) return self.gradeDic[grade] end FishingGradeCfgParse:init() GachaUI&--[[ 扭蛋机弹窗 author:{zhangpeng} time:2025-07-08 16:13:25 ]] local GachaUI, super = defClass("GachaUI", UILayer) require("modules/ui/gacha/GachaRewardUI") require("modules/ui/dollui/DollMainUI") local TMPUGUI = CS.TMPro.TextMeshProUGUI local LOGTAG = "GachaUI" function GachaUI:ctor() super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/gacha/gachareslink") end function GachaUI:onLoad() self.ui = GameObject.Instantiate(self.R.gacha_ui) self:addChild(self.ui) -- self:useTweenOnOpen(self.ui) self:initUI() self:updateCostDisplay() end function GachaUI:initUI() self.onceBtn = self.ui:Seek("once_btn") self.tenBtn = self.ui:Seek("ten_btn") self.closeBtn = self.ui:Seek("close_btn") self.star_ui = self.ui:Seek("star_ui") self.star_num = self.star_ui:Seek("num") self.star_icon = self.star_ui:Seek("star") self:updateStar(CurrencyMgr:getStar()) self.coin_ui = self.ui:Seek("coin_ui") self.coin_num = self.coin_ui:Seek("num") self.coin_icon = self.coin_ui:Seek("coin") self:updateCoin(CurrencyMgr:getCoin()) self.music_ui = self.ui:Seek("music_ui") self.musical_notes_num = self.music_ui:Seek("num") self.musical_notes_icon = self.music_ui:Seek("icon") self:updateMusicalNotes(CurrencyMgr:getMusicalNotes()) self.desc = self.ui:Seek("desc") self.nextDesc = self.ui:Seek("next_desc") self:updateDesc() self:timer(function() self:updateDesc() end, 60, 0) -- 每60秒更新一次描述 self.ball = self.ui:Seek("ball") self.ball.initpos = self.ball.transform.position -- 获取成本显示UI元素(如果存在) self.onceCostText = self.ui:Seek("once_text") self.tenCostText = self.ui:Seek("ten_text") util.ugui.addButtonClickEvent(self.onceBtn, function() -- 检查是否可以进行扭蛋(至少单抽) if GachaDeskMgr:canGacha(1) then self:onceGacha() else PopUpUI.new("资源不足,无法进行扭蛋"):show():showMask():enableCloseWhenClickMask() end end) util.ugui.addButtonClickEvent(self.tenBtn, function() if GachaDeskMgr:canGacha(10) then self:tenGacha() else PopUpUI.new("资源不足,无法进行扭蛋"):show():showMask():enableCloseWhenClickMask() end end) -- 如果有关闭按钮,添加关闭事件 if self.closeBtn then util.ugui.addButtonClickEvent(self.closeBtn, function() self:close() end) end -- 关闭按钮 local closeBtn = self.ui:Seek("close_btn") util.ugui.addButtonClickEvent(closeBtn, function() self:close() end) -- 切换页面按钮 self.to_doll_ui = self.ui:Seek("to_doll_ui") util.ugui.addClickEvent(self.to_doll_ui, function() -- 切换到玩偶页面 DollMainUI.new():show() end) end -- 更新按钮上当前剩余货币数量 function GachaUI:updateCostDisplay() local onceCost = GachaDeskMgr:getGachaCost(1) local tenCost = GachaDeskMgr:getGachaCost(10) -- 更新单抽成本显示 if self.onceCostText then self.onceCostText[TMPUGUI].text = string.format(" %s", GachaConst.DrawCost[GachaConst.DrawType.SingleDraw]) end -- 更新十连抽成本显示 if self.tenCostText then self.tenCostText[TMPUGUI].text = string.format(" %s", GachaConst.DrawCost[GachaConst.DrawType.TenDraws]) end end -- 单抽 function GachaUI:onceGacha() -- 播放扭蛋动画 local anim = self.ui:Seek("anim") util.spine.play(anim, "stand_2",true) self.ui:Delay(2,function () util.spine.play(anim, "stand_1",false) -- 显示球 self.ball:SetActive(true) -- 随机显示一个颜色 local color = math.random(1, 5) for i = 1, 5 do local ball_img = self.ball:Seek("ball_" .. i) ball_img:SetActive(i == color) end -- 球向下移动 self.ball:RunAction(ua.Sequence({ ua.MoveTo(0.6, Vector3(self.ball.transform.position.x, self.ball:GetComponent(typeof(CS.UnityEngine.RectTransform)).anchoredPosition.y - 100, 0)), ua.cb(function() self.ball:SetActive(false) -- 重置一下ball的坐标 self.ball.transform.position = self.ball.initpos -- print("执行单抽") self.gachaType = GachaConst.GachaType.single local success, result = GachaDeskMgr:doGacha(1) if success then self:showGachaResult(result) self:updateCostDisplay() -- 更新成本显示(更新当前金币) self:updateDesc() else self:showInsufficientResourcesTip(result) end end) })) end) end -- 十连抽 function GachaUI:tenGacha() print("执行十连抽") local anim = self.ui:Seek("anim") util.spine.play(anim, "stand_2",true) self.ui:Delay(5,function () util.spine.play(anim, "stand_1",false) -- 显示球 self.ball:SetActive(true) -- 随机显示一个颜色 local color = math.random(1, 5) for i = 1, 5 do local ball_img = self.ball:Seek("ball_" .. i) ball_img:SetActive(i == color) end -- 球向下移动 self.ball:RunAction(ua.Sequence({ ua.MoveTo(0.6, Vector3(self.ball.transform.position.x, self.ball:GetComponent(typeof(CS.UnityEngine.RectTransform)).anchoredPosition.y - 100, 0)), ua.cb(function() self.ball:SetActive(false) -- 重置一下ball的坐标 self.ball.transform.position = self.ball.initpos -- print("执行单抽") self.gachaType = GachaConst.GachaType.ten local success, result = GachaDeskMgr:doGacha(10) if success then self:showGachaResult(result) self:updateCostDisplay() -- 更新成本显示(更新当前金币) else self:showInsufficientResourcesTip(result) end end) })) end) end -- 展示抽奖结果 function GachaUI:showGachaResult(rewards) for i, reward in ipairs(rewards) do if reward.type == 3 and reward.itemId > 0 then -- 玩偶显示名称 local dollCfg = DollCfgParse:getDollCfg(reward.itemId) printInfo(LOGTAG, "抽到了:%s", dollCfg.name) else -- 金币/音符 printInfo(LOGTAG, "抽到了:%s 数量:%d", reward.typeName, reward.count) end end GachaRewardUI.new(rewards, self.gachaType):show():showMask(0.85):enableCloseWhenClickMask() self:updateStar(CurrencyMgr:getStar()) self:updateCoin(CurrencyMgr:getCoin()) self:updateMusicalNotes(CurrencyMgr:getMusicalNotes()) end -- 显示资源不足提示 function GachaUI:showInsufficientResourcesTip(errorMsg) local tipMsg = errorMsg or "金币不足,无法进行扭蛋!" printInfo(LOGTAG, tipMsg) end -- 更新描述 function GachaUI:updateDesc() local count = GachaDeskMgr.userData:getFreeTodayCount() self.desc[TMPUGUI].text = string.format("今日剩余次数:%d", count) self.nextDesc:SetActive(count <= 0) if count <= 0 then local secondsToNextDay = self:getSecondsToNextDay() if secondsToNextDay > 3600 then self.nextDesc[TMPUGUI].text = string.format("距离下次刷新:%d小时%02d分钟", secondsToNextDay / 3600, (secondsToNextDay % 3600) / 60) end end end function GachaUI:updateStar(num, animate) self.star_num:GetComponent("TextMeshProUGUI").text = tostring(num) if animate then if self.star_anim then return --self.star_icon:StopAction(self.star_anim) end self.star_anim = self.star_icon:RunAction(ua.Sequence({ ua.ScaleTo(0.2, 0.8), ua.ScaleTo(0.1, 1.2), ua.ScaleTo(0.3, 1), ua.cb(function() self.star_anim = nil end) })) end end function GachaUI:updateMusicalNotes(num, animate) self.musical_notes_num:GetComponent("TextMeshProUGUI").text = tostring(num) if animate then self.musical_notes_icon:RunAction(ua.Sequence({ ua.ScaleTo(0.2, 0.8), ua.ScaleTo(0.1, 1.2), ua.ScaleTo(0.3, 1), ua.cb(function() self.musical_notes_anim = nil end) })) end end function GachaUI:updateCoin(num, animate) self.coin_num:GetComponent("TextMeshProUGUI").text = tostring(num) if animate then if self.coin_anim then return --self.coin_icon:StopAction(self.coin_anim) end self.coin_anim = self.coin_icon:RunAction(ua.Sequence({ ua.ScaleTo(0.2, 0.8), ua.ScaleTo(0.1, 1.2), ua.ScaleTo(0.3, 1), ua.cb(function() self.coin_anim = nil end) })) end end function GachaUI:getSecondsToNextDay() -- 获取当前时间戳(秒) local currentTime = os.time() -- 将时间戳转换为日期表(包含年、月、日、时、分、秒等信息) local currentDate = os.date("*t", currentTime) -- 计算当前时间距离当天结束的秒数(24小时 = 86400秒) local currentTotalSeconds = currentDate.hour * 3600 + currentDate.min * 60 + currentDate.sec local secondsToMidnight = 86400 - currentTotalSeconds return secondsToMidnight end return GachaUI Fisherman--[[ 渔夫 author:{zhangpeng} time:2025-05-30 15:15:18 ]] local Fisherman,super = defClass("Fisherman",Employe) function Fisherman:ctor(reslink,cfgId) super.ctor(self, RoleConst.roleType.employee) self.R = reslink self.employeCfgId = cfgId self:init() end function Fisherman:init() super.init(self) -- self:initEmployeCfg() -- self:initDisplay() -- 初始化状态为空闲 self.state = EmployeConst.State.Idle end return Fisherman DingConst4--[[ 餐桌常量 author:{zhangpeng} time:2025-05-16 15:38:02 ]] local DiningConst = defClassStatic("DiningConst") -- 餐桌相关消息 DiningConst.Msg = { -- 通知顾客有空闲餐桌 notifyCustomerIdleDiningDesk = "notifyCustomerIdleDiningDesk", -- 通知顾客订单完成 notifyCustomerOrderFinished = "notifyCustomerOrderFinished", -- 通知顾客离开 notifyCustomerLeave = "notifyCustomerLeave", } -- 默认餐桌配置id DiningConst.diningDeskIds = { BuildingConst.buildingType.diningdesk1, BuildingConst.buildingType.diningdesk2, BuildingConst.buildingType.diningdesk3, BuildingConst.buildingType.diningdesk4, } -- 桌子序号 DiningConst.deskIndex = { [BuildingConst.buildingType.diningdesk1] = 1, [BuildingConst.buildingType.diningdesk2] = 2, [BuildingConst.buildingType.diningdesk3] = 3, [BuildingConst.buildingType.diningdesk4] = 4, } -- 餐桌状态 DiningConst.State = { -- 空闲 idle = 1, -- 占用 used = 2, -- 脏 dirty = 3, } -- 座位状态 DiningConst.SeatState = { -- 空闲 idle = 1, -- 有顾客正在前往该座位 approaching = 2, -- 占用 used = 3, -- 等待上菜 waitingFood = 4, -- 进餐中 eating = 5, -- 脏 dirty = 6, } return DiningConst TaskAchCell--[[ 成就任务cell 作者:{zhangpeng} 时间:2025-05-26 13:39:50 ]] local TaskAchCell = defClass("TaskAchCell") function TaskAchCell:ctor(cell,taskData) self.cell = cell self.taskData = taskData self:initUI() self:showUI() end -- 重新加载 function TaskAchCell:reload(taskData) self.taskData = taskData self:showUI() end function TaskAchCell:initUI() -- desc self.desc = self.cell:Seek("desc") -- progress self.front = self.cell:Seek("front") -- value self.value = self.cell:Seek("value") -- reward self.reward = self.cell:Seek("reward") self.reward_img = self.cell:Seek("reward_img") local btn_suc = self.cell:Seek("btn_states_suc") util.ugui.addButtonClickEvent(btn_suc, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:getReward() end) end -- 展示ui function TaskAchCell:showUI() -- desc self.desc:GetComponent("TextMeshProUGUI").text = self.taskData.desc -- 获取当前进度和目标值 local currentValue, targetValue = self:getTaskProgress() -- 计算进度比例(确保不超过1) local progressRatio = math.min(currentValue / targetValue, 1) -- progress self.front:GetComponent("Image").fillAmount = progressRatio -- value self.value:GetComponent("TextMeshProUGUI").text = currentValue .. "/" .. targetValue -- reward local sprite = TaskMgr:getTaskRewardIconByIndex(self.taskData.id, 1) self.reward_img:GetComponent("Image").sprite = sprite self.reward:GetComponent("TextMeshProUGUI").text = "+" .. self.taskData.params_1 self:updateUI() end -- 根据任务类型获取当前进度和目标值 function TaskAchCell:getTaskProgress() local taskId = self.taskData.id local currentValue, targetValue = TaskMgr:getTaskProgress(taskId) return currentValue, targetValue end -- 更新UI显示 function TaskAchCell:updateUI() -- 根据任务状态显示对应按钮(进行中/可领取/已领取) local taskState = TaskMgr:getTaskState(self.taskData.id) --if taskState == TaskConst.TaskState.claimed then -- local next = TaskAchMgr:getAchNextMissions(self.taskData.id) -- if next then -- self.taskData = next -- self:showUI() -- taskState = TaskMgr:getTaskState(self.taskData.id) -- --else -- -- -- 如果没有下一个任务,隐藏当前cell -- -- self.cell:SetActive(false) -- end --end -- 获取当前进度和目标值 local currentValue, targetValue = self:getTaskProgress() -- 计算进度比例(确保不超过1) local progressRatio = math.min(currentValue / targetValue, 1) -- 更新进度条 if self.front then self.front:GetComponent("Image").fillAmount = progressRatio end -- 更新进度文本 if self.value then self.value:GetComponent("TextMeshProUGUI").text = currentValue .. "/" .. targetValue end ---- 根据任务状态显示对应按钮(进行中/可领取/已领取) --local taskState = TaskMgr:getTaskState(self.taskData.id) -- 更新按钮状态 local btn_ing = self.cell:Seek("btn_states_ing") local btn_suc = self.cell:Seek("btn_states_suc") local btn_claimed = self.cell:Seek("suc") if btn_ing then btn_ing:SetActive(taskState == TaskConst.TaskState.in_progress) end if btn_suc then btn_suc:SetActive(taskState == TaskConst.TaskState.success) end if btn_claimed then btn_claimed:SetActive(taskState == TaskConst.TaskState.claimed) end end -- 获取完成条件类型 function TaskAchCell:getTaskConditionType() return self.taskData.completeCond end -- 获取任务奖励 function TaskAchCell:getReward() TaskMgr:setTaskClaimed(self.taskData.id) TaskMgr:getTaskRewardById(self.taskData.id, self.reward_img) self:updateUI() local ui = UILayerUtil:findUIByName("TaskUI") ui:updateStateAndAchTackView() end return TaskAchCellmain-- 通用组件 require("modules/common/component/main") require("modules/common/util/main") require("modules/common/mgr/main") require("modules/common/const/main") require("modules/common/data/main") require("modules/common/event/main") .bundles_assets_assetspackage_luascripts.bundle6+} @VM$Hӵ}_^0Zcqs&8U9PoR@=vV41want{DuC æ!ph 32n2myX첓.6xpW9hm|wZOظXJ1O >bY^J<%!aM"z9 @Ki|æS[ SkO\HKi=rzkΓ3c%V%B͎`n _t{DZrϔgPsWTPP.( a\N,şM6,h5T?BBK}Ck¥򲥮Ud.(E(4iΐ!LӅlh=ws'\k0Ģ|lkf_܍OǶ niSiP/ @4KkQMN aiN9lLE-+n>ojrgǴC^Nv#H`PJ!XrE!5<.nX0n҅{us .?ݟ 6}],3qUWPDkSi+b3QY#(amkjRhZɇ7UΜr@&qG]{^2/'8l/u$-2HrD{5Og h XcF< Aun;#ȡܹujH0{YWL@^+x\ETfa'sk [QrzH|0:~N>6 i5ЏK0JN5 lpr؏oN)(u" HqSc POrY%z,9mMAҌ!eЙ1umio$QElsL>O-2z9 \ݰ}*u`";}~$+yw\0sלjt" xZ7ZR_:0NoET(~:nOLsq/$h!glsk??{gaNoLK1ǼxZe׹ &*=dk/lgiu2a,5^k_? \Tx$H{D$:tNs ͤbU3Ó% 'mCJ7G9k{,vSeSdܬ ؟wXodkƫvN}6hǀkW$LKa <5HA)݁$S[KbˊӜG\\_`z^SHVoRL-DKE˴Duca𙭕/ஐչHW}x?Q95;Jwu4JTvśb/h+q}X\Yb\v9 {f'Kfֽ֙Nuxv/ \ GH9^x/@6'fi| 5=;fѦ)v1[^ ɰ:AD$Rԝkˀn9?=P|p:$8%VVbq&kHt[ O2h%BY}\kaՔċ=݁Ea"b-s@4Yd~k4/Di5]QQ%W$0œȝԹD:1@ZR;<ی*)gfE J"T nj4-QO/t (m^=-]<Thdni\k;Y'Q ccCj"|BRқh Dy`:EL`](vtx $T5ۂ^8k%g.T4M[$DV+p;:p(:3R(K{.BAR_Ŷ6ysI=/~7x"|[Iu? X#wSFxa&MI)?z6}>q@\'N(aYt ~Z|0GN9`[LM<bZ~VZ:!aa]҈Ta'r7\ ^tLRm(.`AnC yH]l}{9En7TAߏFzM2ȸ ?\]:J G?V#CXE6G/oO06<]{S9wD?BnܸLS%N 7uєdKMA\ bx^J9w'.2ֻd$.oNE`_,;(TMG1biw/1* J*%ζ%Ѣ} HqNE& t3?/^r:g>q BEr ])KVjtc/w K:8fxGЋ> sT;_߾Q!7;f8 C k o\J ؔ0^J9 Ui|a=PA&@e2.^3Q/# 5q#*֋r/B7KK-hrߜ#`{Ȏs黀V7ZxhVS0N2BX >DO.f5Y蚂$d5 "ʥM30r07g,msWQXn;atkwO7_N+@,5[4B~P-{D6db S}򮷴cCּ먦9̛xYdμ%~SXJiҢ"VKB@>h=67Assets/AssetsPackage/LuaScripts/boot/build_config.bytesL?}5~.5Assets/AssetsPackage/LuaScripts/boot/debug/main.byteso/Assets/AssetsPackage/LuaScripts/boot/main.bytesZ aiN96Assets/AssetsPackage/LuaScripts/boot/main_editor.bytesFa"b-5Assets/AssetsPackage/LuaScripts/boot/main_webgl.bytes9w'.?Assets/AssetsPackage/LuaScripts/common/const/StrogeKeyDef.bytes5ЏK07Assets/AssetsPackage/LuaScripts/common/const/main.bytesjtc9Assets/AssetsPackage/LuaScripts/common/core/app/App.bytes[lLE-+n9Assets/AssetsPackage/LuaScripts/common/core/app/Msg.bytesViSiP:Assets/AssetsPackage/LuaScripts/common/core/app/main.bytes9uE㹲 AAssets/AssetsPackage/LuaScripts/common/core/app/scene/Scene.bytes];Y'Q DAssets/AssetsPackage/LuaScripts/common/core/app/scene/SceneCfg.bytesyH]HAssets/AssetsPackage/LuaScripts/common/core/app/scene/SceneCfgList.bytes h 32nJAssets/AssetsPackage/LuaScripts/common/core/app/scene/SceneComponent.bytes {,vSEAssets/AssetsPackage/LuaScripts/common/core/app/scene/SceneInfo.bytesa DyDAssets/AssetsPackage/LuaScripts/common/core/app/scene/SceneMgr.bytes,򮷴c@Assets/AssetsPackage/LuaScripts/common/core/app/scene/main.bytess:Assets/AssetsPackage/LuaScripts/common/core/base/Def.bytes!,msW;Assets/AssetsPackage/LuaScripts/common/core/base/Enum.bytesdvtx:Assets/AssetsPackage/LuaScripts/common/core/base/Env.bytesְ)<Assets/AssetsPackage/LuaScripts/common/core/base/Event.bytes(\Y@Assets/AssetsPackage/LuaScripts/common/core/base/ExtendLua.bytesl+p;::Assets/AssetsPackage/LuaScripts/common/core/base/Log.bytes m:Assets/AssetsPackage/LuaScripts/common/core/base/Msg.bytesnΜrBAssets/AssetsPackage/LuaScripts/common/core/base/ReslinkLoad.bytes:!aa]?Assets/AssetsPackage/LuaScripts/common/core/base/TimerMgr.bytes"͎`n ;Assets/AssetsPackage/LuaScripts/common/core/base/Util.bytes4AAssets/AssetsPackage/LuaScripts/common/core/base/extend/Ext.bytesJAssets/AssetsPackage/LuaScripts/common/core/base/extend/ExtendBounds.bytes^ЗNAssets/AssetsPackage/LuaScripts/common/core/base/extend/ExtendGameObject.bytesz9 \TAssets/AssetsPackage/LuaScripts/common/core/base/extend/ExtendPlayableDirector.bytes ؔ0HAssets/AssetsPackage/LuaScripts/common/core/base/extend/ExtendRect.bytes#Q9IAssets/AssetsPackage/LuaScripts/common/core/base/extend/ExtendScene.bytesJBAssets/AssetsPackage/LuaScripts/common/core/base/extend/main.bytes@ |+;Assets/AssetsPackage/LuaScripts/common/core/base/main.bytesHqSc GAssets/AssetsPackage/LuaScripts/common/core/base/resmgr/ResLoader.bytesSuMAssets/AssetsPackage/LuaScripts/common/core/base/resmgr/YooAssetAdapter.bytesa'GxBAssets/AssetsPackage/LuaScripts/common/core/base/resmgr/main.bytes+h*oOAssets/AssetsPackage/LuaScripts/common/core/base/touch/TouchClickListener.bytesy4>sEAssets/AssetsPackage/LuaScripts/common/core/base/touch/TouchCom.bytes6 iPAssets/AssetsPackage/LuaScripts/common/core/base/touch/TouchCustomListener.bytesO*JAssets/AssetsPackage/LuaScripts/common/core/base/touch/TouchListener.bytesKƁjxAAssets/AssetsPackage/LuaScripts/common/core/base/touch/main.bytesO IAssets/AssetsPackage/LuaScripts/common/core/base/utils/AnimatorUtil.bytes3i| 5FAssets/AssetsPackage/LuaScripts/common/core/base/utils/BoundUtil.bytesVFGAssets/AssetsPackage/LuaScripts/common/core/base/utils/CSharpUtil.bytes\kHAssets/AssetsPackage/LuaScripts/common/core/base/utils/CommonUtils.bytesĦ9$FAssets/AssetsPackage/LuaScripts/common/core/base/utils/CoordUtil.bytesQیEAssets/AssetsPackage/LuaScripts/common/core/base/utils/FileUtil.bytes ^J9 JAssets/AssetsPackage/LuaScripts/common/core/base/utils/GameNodeAdapt.bytesrl/u$DAssets/AssetsPackage/LuaScripts/common/core/base/utils/LuaUtil.bytesi.TIAssets/AssetsPackage/LuaScripts/common/core/base/utils/ParticleUtil.bytesV/FAssets/AssetsPackage/LuaScripts/common/core/base/utils/PauseUtil.bytesv? XEAssets/AssetsPackage/LuaScripts/common/core/base/utils/PerfUtil.bytesDKLAssets/AssetsPackage/LuaScripts/common/core/base/utils/PositionConvert.bytes/39IAssets/AssetsPackage/LuaScripts/common/core/base/utils/RichTextUtil.bytes`[DAssets/AssetsPackage/LuaScripts/common/core/db/dbkv/KVDatabase.bytes]ǴC^?Assets/AssetsPackage/LuaScripts/common/core/db/dbkv/KVMgr.bytes_j"|AAssets/AssetsPackage/LuaScripts/common/core/db/dbkv/KVTable.bytesZDAssets/AssetsPackage/LuaScripts/common/core/db/dbsql/BaseModel.bytesS|lkfLAssets/AssetsPackage/LuaScripts/common/core/db/dbsql/SingleSqliteTable.bytesLRm(.GAssets/AssetsPackage/LuaScripts/common/core/db/dbsql/SqliteColumn.bytesKiJAssets/AssetsPackage/LuaScripts/common/core/db/dbsql/SqliteCondition.bytes<:IAssets/AssetsPackage/LuaScripts/common/core/db/dbsql/SqliteDatabase.bytesh%)EAssets/AssetsPackage/LuaScripts/common/core/db/dbsql/SqliteFunc.bytes/lgDAssets/AssetsPackage/LuaScripts/common/core/db/dbsql/SqliteMgr.bytes/9̛FAssets/AssetsPackage/LuaScripts/common/core/db/dbsql/SqliteModel.bytes`AnFAssets/AssetsPackage/LuaScripts/common/core/db/dbsql/SqliteQuery.bytes6hǀFAssets/AssetsPackage/LuaScripts/common/core/db/dbsql/SqliteTable.bytes7ZEAssets/AssetsPackage/LuaScripts/common/core/db/dbsql/SqliteUtil.bytes90BAssets/AssetsPackage/LuaScripts/common/core/db/dbsql/SyncMgr.bytesb`:EL9Assets/AssetsPackage/LuaScripts/common/core/db/main.bytesTa'r6Assets/AssetsPackage/LuaScripts/common/core/main.bytes& FAssets/AssetsPackage/LuaScripts/common/cos/CosLuaBatchUploadTask.byteseЙ1EAssets/AssetsPackage/LuaScripts/common/cos/CosLuaCredentialBean.bytesQ\k0;Assets/AssetsPackage/LuaScripts/common/cos/CosLuaEnum.bytesӜG\\:Assets/AssetsPackage/LuaScripts/common/cos/CosLuaMgr.bytesJJ*5>Assets/AssetsPackage/LuaScripts/common/cos/CosLuaTagTask.bytesJ<JAssets/AssetsPackage/LuaScripts/common/cos/CosLuaTemporaryCredential.bytesG/AAssets/AssetsPackage/LuaScripts/common/cos/CosLuaUploadTask.bytes4V65Assets/AssetsPackage/LuaScripts/common/cos/main.bytesK,aL6Assets/AssetsPackage/LuaScripts/common/data/main.bytes fLEAssets/AssetsPackage/LuaScripts/common/network/socket/pbreslink.bytesLMAssets/AssetsPackage/LuaScripts/common/platform/ext/luaj.bytesE˴Du?Assets/AssetsPackage/LuaScripts/common/platform/ext/luaoc.byteswcF< >Assets/AssetsPackage/LuaScripts/common/platform/ext/main.bytes6v1[^FAssets/AssetsPackage/LuaScripts/common/platform/ext/queue_action.bytes Ui>Assets/AssetsPackage/LuaScripts/common/platform/ios/XSdk.bytesz,9mM=Assets/AssetsPackage/LuaScripts/common/platform/pc/XSdk.bytesĭrd;Assets/AssetsPackage/LuaScripts/common/ui/UITypeEnums.byteswԈ|"4Assets/AssetsPackage/LuaScripts/common/ui/main.bytes|L@^>Assets/AssetsPackage/LuaScripts/common/ui/uibase/UILayer.bytesRĢBAssets/AssetsPackage/LuaScripts/common/ui/uibase/UILayerUtil.bytes-uxAAssets/AssetsPackage/LuaScripts/common/ui/uibase/UIOrderDef.byteslZ=Assets/AssetsPackage/LuaScripts/common/ui/uibase/UIRoot.bytesKa;Assets/AssetsPackage/LuaScripts/common/ui/uibase/main.byteshQIAAssets/AssetsPackage/LuaScripts/common/ui/uicoms/UIComsTool.bytesr?Assets/AssetsPackage/LuaScripts/common/ui/uicoms/UIDialog.bytesl}{Assets/AssetsPackage/LuaScripts/common/ui/uicoms/UIToast.bytes)[4BUAssets/AssetsPackage/LuaScripts/common/ui/uicoms/reslink/uicircleloadingreslink.bytes: 3NAssets/AssetsPackage/LuaScripts/common/ui/uicoms/reslink/uidialogreslink.bytes 5qMAssets/AssetsPackage/LuaScripts/common/ui/uicoms/reslink/uitoastreslink.bytes*v9 {EAssets/AssetsPackage/LuaScripts/common/ui/uitween/UITweenAction.bytesVZBAssets/AssetsPackage/LuaScripts/common/ui/uitween/UITweenDef.bytesN}9Assets/AssetsPackage/LuaScripts/data/config/TextCfg.byteshPDkS>Assets/AssetsPackage/LuaScripts/data/config/bbqRewardCfg.bytes25}5%#@Assets/AssetsPackage/LuaScripts/data/config/boothUnlockCfg.bytesk$DV=Assets/AssetsPackage/LuaScripts/data/config/buildingCfg.bytes o\JBAssets/AssetsPackage/LuaScripts/data/config/buildingTypesCfg.bytesCXE6>Assets/AssetsPackage/LuaScripts/data/config/buyCommonCfg.bytes_`K&/@Assets/AssetsPackage/LuaScripts/data/config/commonBonusCfg.bytesR_AAssets/AssetsPackage/LuaScripts/data/config/commonBonusType.bytesՀE1C=Assets/AssetsPackage/LuaScripts/data/config/config_main.bytesXJ1=Assets/AssetsPackage/LuaScripts/data/config/cookBookCfg.bytesy#ȡܹCAssets/AssetsPackage/LuaScripts/data/config/cuisineUpgradeCfg.bytes+=Assets/AssetsPackage/LuaScripts/data/config/customerCfg.bytes{S9wD@Assets/AssetsPackage/LuaScripts/data/config/customerFunCfg.bytes-CBAssets/AssetsPackage/LuaScripts/data/config/customerParamCfg.bytesC @Assets/AssetsPackage/LuaScripts/data/config/customerTagCfg.bytes&8U99Assets/AssetsPackage/LuaScripts/data/config/dollCfg.bytes <<Assets/AssetsPackage/LuaScripts/data/config/employeCfg.bytes1* ?Assets/AssetsPackage/LuaScripts/data/config/employeFunCfg.bytesIVWCAssets/AssetsPackage/LuaScripts/data/config/employeLevelUpCfg.bytesRǘ@Assets/AssetsPackage/LuaScripts/data/config/employeSkinCfg.bytesAssets/AssetsPackage/LuaScripts/data/config/fishSalesCfg.bytesaNoLAAssets/AssetsPackage/LuaScripts/data/config/fishingGradeCfg.bytes$ <ΞABAssets/AssetsPackage/LuaScripts/data/config/fishingRewardCfg.bytesœ\@Assets/AssetsPackage/LuaScripts/data/config/gachaRewardCfg.bytes5HA):Assets/AssetsPackage/LuaScripts/data/config/guideCfg.bytes>DO9Assets/AssetsPackage/LuaScripts/data/config/helpCfg.bytesۯp!5*=Assets/AssetsPackage/LuaScripts/data/config/helpTypeCfg.bytes&/hEAssets/AssetsPackage/LuaScripts/data/config/musicBbqCustomerCfg.bytesCaKAssets/AssetsPackage/LuaScripts/data/config/parse/BoothUnlockCfgParse.bytesq0~WHAssets/AssetsPackage/LuaScripts/data/config/parse/BuildingCfgParse.bytesjY#(amMAssets/AssetsPackage/LuaScripts/data/config/parse/BuildingTypesCfgParse.bytes+f'KIAssets/AssetsPackage/LuaScripts/data/config/parse/BuyCommonCfgParse.bytes6xpKAssets/AssetsPackage/LuaScripts/data/config/parse/CommonBonusCfgParse.bytesG60-$LAssets/AssetsPackage/LuaScripts/data/config/parse/CommonBonusTypeParse.bytes\uHAssets/AssetsPackage/LuaScripts/data/config/parse/CookBookCfgParse.bytes}+x\ENAssets/AssetsPackage/LuaScripts/data/config/parse/CuisineUpgradeCfgParse.bytes%ЁŖ HAssets/AssetsPackage/LuaScripts/data/config/parse/CustomerCfgParse.bytes/h  %KAssets/AssetsPackage/LuaScripts/data/config/parse/CustomerFunCfgParse.bytes_H`PMAssets/AssetsPackage/LuaScripts/data/config/parse/CustomerParamCfgParse.bytes&*=dkOAssets/AssetsPackage/LuaScripts/data/config/parse/CustomerSpecialCfgParse.bytes$5;Jwu4JKAssets/AssetsPackage/LuaScripts/data/config/parse/CustomerTagCfgParse.bytesDAssets/AssetsPackage/LuaScripts/data/config/parse/DollCfgParse.bytesr=/~GAssets/AssetsPackage/LuaScripts/data/config/parse/EmployeCfgParse.bytes,[tNAssets/AssetsPackage/LuaScripts/data/config/parse/EmployeLevelUpCfgParse.bytesr:g>qKAssets/AssetsPackage/LuaScripts/data/config/parse/EmployeSkinCfgParse.bytes|@\'KAssets/AssetsPackage/LuaScripts/data/config/parse/EmployeeFunCfgParse.bytesHqNEDAssets/AssetsPackage/LuaScripts/data/config/parse/FishCfgParse.bytes2Qy~$>eIAssets/AssetsPackage/LuaScripts/data/config/parse/FishSalesCfgParse.bytes$DZrϔLAssets/AssetsPackage/LuaScripts/data/config/parse/FishingGradeCfgParse.bytes4nMAssets/AssetsPackage/LuaScripts/data/config/parse/FishingRewardCfgParse.bytesF'!KAssets/AssetsPackage/LuaScripts/data/config/parse/GachaRewardCfgParse.bytesq/'8EAssets/AssetsPackage/LuaScripts/data/config/parse/GuideCfgParse.bytes+෡DAssets/AssetsPackage/LuaScripts/data/config/parse/HelpCfgParse.bytesQPAssets/AssetsPackage/LuaScripts/data/config/parse/MusicBbqCustomerCfgParse.bytes.WK`!EAssets/AssetsPackage/LuaScripts/data/config/parse/SceneCfgParse.bytes:0NOAssets/AssetsPackage/LuaScripts/data/config/parse/SpecialCustomerCfgParse.bytes06<]KAssets/AssetsPackage/LuaScripts/data/config/parse/StorydialogCfgParse.bytesZJDAssets/AssetsPackage/LuaScripts/data/config/parse/TaskCfgParse.bytesWIAssets/AssetsPackage/LuaScripts/data/config/parse/TaskOrderCfgParse.bytes!DAssets/AssetsPackage/LuaScripts/data/config/parse/TextCfgParse.bytesxSFxa&LAssets/AssetsPackage/LuaScripts/data/config/parse/UnlockCommonCfgParse.byteszujH0FAssets/AssetsPackage/LuaScripts/data/config/parse/VendorCfgParse.bytesa΀:Assets/AssetsPackage/LuaScripts/data/config/sceneCfg.bytes"˾DAssets/AssetsPackage/LuaScripts/data/config/specialCustomerCfg.bytesf]@Assets/AssetsPackage/LuaScripts/data/config/storydialogCfg.bytes5Ѧ)9Assets/AssetsPackage/LuaScripts/data/config/taskCfg.bytes[Thdni\>Assets/AssetsPackage/LuaScripts/data/config/taskOrderCfg.bytesM2ȸAAssets/AssetsPackage/LuaScripts/data/config/unlockCommonCfg.bytesn3R(K{;Assets/AssetsPackage/LuaScripts/data/config/vendorCfg.bytesbU3>Assets/AssetsPackage/LuaScripts/data/db/models/UserModel.bytes#>Assets/AssetsPackage/LuaScripts/data/db/tables/UserTable.bytesj4M[/Assets/AssetsPackage/LuaScripts/data/main.bytes |a=P@Assets/AssetsPackage/LuaScripts/main/YooAssetLoaderExample.bytes^Bn/Assets/AssetsPackage/LuaScripts/main/main.bytes"W}x?DAssets/AssetsPackage/LuaScripts/modules/bonus/const/BonusConst.bytes7 ɰ:AFAssets/AssetsPackage/LuaScripts/modules/bonus/data/BonusUserData.bytes3 ҿB8Assets/AssetsPackage/LuaScripts/modules/bonus/main.bytes?\]@Assets/AssetsPackage/LuaScripts/modules/bonus/mgr/BonusMgr.bytesq^qro$CAssets/AssetsPackage/LuaScripts/modules/building/BuildingBase.bytesTV(?-BDAssets/AssetsPackage/LuaScripts/modules/building/BuildingConst.bytesU-QOCAssets/AssetsPackage/LuaScripts/modules/building/BuildingInfo.bytesq7BAssets/AssetsPackage/LuaScripts/modules/building/BuildingMgr.bytes1biw/GAssets/AssetsPackage/LuaScripts/modules/building/BuildingUserData.bytesYQMNKAssets/AssetsPackage/LuaScripts/modules/building/barbecue/bbq/BbqDesk.bytes7?[3GNAssets/AssetsPackage/LuaScripts/modules/building/barbecue/bbq/BbqDeskMgr.bytes;8HAssets/AssetsPackage/LuaScripts/modules/building/barbecue/bbq/main.bytes`ojrgOAssets/AssetsPackage/LuaScripts/modules/building/barbecue/gacha/GachaDesk.bytesR*)gfE SAssets/AssetsPackage/LuaScripts/modules/building/barbecue/gacha/GachaDeskInfo.bytesGN9RAssets/AssetsPackage/LuaScripts/modules/building/barbecue/gacha/GachaDeskMgr.bytesC&~n']CSAssets/AssetsPackage/LuaScripts/modules/building/barbecue/gacha/GachaUserData.bytes ^tJAssets/AssetsPackage/LuaScripts/modules/building/barbecue/gacha/main.bytes"QXnOAssets/AssetsPackage/LuaScripts/modules/building/barbecue/stage/StageDesk.bytes1x/@TAssets/AssetsPackage/LuaScripts/modules/building/barbecue/stage/StageDeskConst.bytesPws'RAssets/AssetsPackage/LuaScripts/modules/building/barbecue/stage/StageDeskMgr.bytes??{gSAssets/AssetsPackage/LuaScripts/modules/building/barbecue/stage/StageDeskSeat.bytes:tNJAssets/AssetsPackage/LuaScripts/modules/building/barbecue/stage/main.bytesJ}Ck¥UAssets/AssetsPackage/LuaScripts/modules/building/fishingcenter/stall/StallConst.bytes4"TAssets/AssetsPackage/LuaScripts/modules/building/fishingcenter/stall/StallDesk.bytes:~N>WAssets/AssetsPackage/LuaScripts/modules/building/fishingcenter/stall/StallDeskMgr.bytes0MJTAssets/AssetsPackage/LuaScripts/modules/building/fishingcenter/stall/StallInfo.bytes\R[ pOAssets/AssetsPackage/LuaScripts/modules/building/fishingcenter/stall/main.bytes$;atk;Assets/AssetsPackage/LuaScripts/modules/building/main.bytes~t ~VAssets/AssetsPackage/LuaScripts/modules/building/restaurant/cooking/CookingBench.bytes)(uYAssets/AssetsPackage/LuaScripts/modules/building/restaurant/cooking/CookingBenchMgr.byteszH|0[Assets/AssetsPackage/LuaScripts/modules/building/restaurant/cooking/CookingBenchStove.bytes\Tx$VAssets/AssetsPackage/LuaScripts/modules/building/restaurant/cooking/CookingConst.bytes uC æ!pNAssets/AssetsPackage/LuaScripts/modules/building/restaurant/cooking/main.bytesHӵ}OAssets/AssetsPackage/LuaScripts/modules/building/restaurant/eat/DingConst.bytestuPAssets/AssetsPackage/LuaScripts/modules/building/restaurant/eat/DiningDesk.bytes_ύ͟SAssets/AssetsPackage/LuaScripts/modules/building/restaurant/eat/DiningDeskMgr.bytesIBBKTAssets/AssetsPackage/LuaScripts/modules/building/restaurant/eat/DiningDeskSeat.bytes4"JAssets/AssetsPackage/LuaScripts/modules/building/restaurant/eat/main.bytesGЋ>RAssets/AssetsPackage/LuaScripts/modules/building/restaurant/order/OrderConst.bytes+]QAssets/AssetsPackage/LuaScripts/modules/building/restaurant/order/OrderInfo.bytes:J GLAssets/AssetsPackage/LuaScripts/modules/building/restaurant/order/main.bytes ܬ ؟wdAssets/AssetsPackage/LuaScripts/modules/building/restaurant/passiveincome/PassivityIncomeConst.bytesO!LӅlh=cAssets/AssetsPackage/LuaScripts/modules/building/restaurant/passiveincome/PassivityIncomeDesk.bytes[fAssets/AssetsPackage/LuaScripts/modules/building/restaurant/passiveincome/PassivityIncomeDeskMgr.bytes1TAssets/AssetsPackage/LuaScripts/modules/building/restaurant/passiveincome/main.bytesDw&pZAssets/AssetsPackage/LuaScripts/modules/building/restaurant/passivity/PassivityConst.bytes-F6yxYAssets/AssetsPackage/LuaScripts/modules/building/restaurant/passivity/PassivityDesk.bytes\Assets/AssetsPackage/LuaScripts/modules/building/restaurant/passivity/PassivityDeskMgr.bytesf}s6PAssets/AssetsPackage/LuaScripts/modules/building/restaurant/passivity/main.bytes@Ht[ OZAssets/AssetsPackage/LuaScripts/modules/building/restaurant/reception/ReceptionConst.bytes;0HOaYAssets/AssetsPackage/LuaScripts/modules/building/restaurant/reception/ReceptionDesk.bytesXAssets/AssetsPackage/LuaScripts/modules/building/restaurant/reception/ReceptionMgr.bytesmp(:PAssets/AssetsPackage/LuaScripts/modules/building/restaurant/reception/main.bytes eSd@Assets/AssetsPackage/LuaScripts/modules/buy/const/BuyConst.bytesM0œȝ6Assets/AssetsPackage/LuaScripts/modules/buy/main.bytesD<9<Assets/AssetsPackage/LuaScripts/modules/buy/mgr/BuyMgr.bytes8D$GAssets/AssetsPackage/LuaScripts/modules/common/component/Scroller.bytesNiΐNAssets/AssetsPackage/LuaScripts/modules/common/component/ScrollerTopDown.bytesݰ}*uCAssets/AssetsPackage/LuaScripts/modules/common/component/main.bytes}N(aYEAssets/AssetsPackage/LuaScripts/modules/common/const/AudioConst.bytesDAssets/AssetsPackage/LuaScripts/modules/common/const/GameConst.bytes{{YWHAssets/AssetsPackage/LuaScripts/modules/common/const/GameNodeOrder.bytesu{5Og ?Assets/AssetsPackage/LuaScripts/modules/common/const/main.bytesֈ \:HAssets/AssetsPackage/LuaScripts/modules/common/data/CommonUserData.bytesq6ysI>Assets/AssetsPackage/LuaScripts/modules/common/data/main.bytesGʮ/̃FAssets/AssetsPackage/LuaScripts/modules/common/event/SimpleEvent.bytesu0Q|y?Assets/AssetsPackage/LuaScripts/modules/common/event/main.bytes< pMlK]9Assets/AssetsPackage/LuaScripts/modules/common/main.bytes";AAssets/AssetsPackage/LuaScripts/modules/common/mgr/AudioMgr.bytes(ϬQZ@GAssets/AssetsPackage/LuaScripts/modules/common/mgr/PlayerPrefsMgr.bytesKbˊGAssets/AssetsPackage/LuaScripts/modules/common/mgr/StoryDialogMgr.bytesAS=Assets/AssetsPackage/LuaScripts/modules/common/mgr/main.bytesq% l(tBAssets/AssetsPackage/LuaScripts/modules/common/util/GameUtil.bytes祵Y=>Assets/AssetsPackage/LuaScripts/modules/common/util/main.byteswZOظCAssets/AssetsPackage/LuaScripts/modules/constant/SceneMsgEnum.bytes38DAssets/AssetsPackage/LuaScripts/modules/cuisine/cuisinereslink.bytes'( a\GAssets/AssetsPackage/LuaScripts/modules/cuisine/data/CuisineConst.bytes6)8LղhFAssets/AssetsPackage/LuaScripts/modules/cuisine/data/CuisineInfo.bytesLW$JAssets/AssetsPackage/LuaScripts/modules/cuisine/data/CuisineUserData.bytesZ<:Assets/AssetsPackage/LuaScripts/modules/cuisine/main.bytes7uDAssets/AssetsPackage/LuaScripts/modules/cuisine/mgr/CuisineMgr.bytesZvf0RAssets/AssetsPackage/LuaScripts/modules/cuisine/mgr/viewmgr/CuisineBubbleMgr.bytesN5 lpPAssets/AssetsPackage/LuaScripts/modules/cuisine/mgr/viewmgr/CuisineIconMgr.bytesGHAssets/AssetsPackage/LuaScripts/modules/cuisine/view/CuisineBubble.bytes>VDAssets/AssetsPackage/LuaScripts/modules/currency/CurrencyConst.bytes5LBAssets/AssetsPackage/LuaScripts/modules/currency/CurrencyMgr.bytes)şM6,GAssets/AssetsPackage/LuaScripts/modules/currency/CurrencyUserData.bytes xZ@Assets/AssetsPackage/LuaScripts/modules/currency/coin/main.bytes=$8%VKAssets/AssetsPackage/LuaScripts/modules/currency/coin/mgr/CoinDeskMgr.bytesuIuKAssets/AssetsPackage/LuaScripts/modules/currency/coin/view/CoinOnDesk.bytes(,5FAssets/AssetsPackage/LuaScripts/modules/currency/currencyreslink.bytes[ Sk;Assets/AssetsPackage/LuaScripts/modules/currency/main.bytesBBY}\k<Assets/AssetsPackage/LuaScripts/modules/doll/DollConst.bytesw#w:Assets/AssetsPackage/LuaScripts/modules/doll/DollMgr.bytes:$LX?Assets/AssetsPackage/LuaScripts/modules/doll/DollUserData.bytes>(hk9Y7Assets/AssetsPackage/LuaScripts/modules/doll/main.bytesWĀHAssets/AssetsPackage/LuaScripts/modules/fishing/const/FishingConst.bytes!c%V%BHAssets/AssetsPackage/LuaScripts/modules/fishing/data/FishermanInfo.bytesAОJAssets/AssetsPackage/LuaScripts/modules/fishing/data/FishingUserData.bytesj#@kDAssets/AssetsPackage/LuaScripts/modules/fishing/fishingreslink.bytes҈:Assets/AssetsPackage/LuaScripts/modules/fishing/main.bytes?VDAssets/AssetsPackage/LuaScripts/modules/fishing/map/FishingMap.bytes9sXűDAssets/AssetsPackage/LuaScripts/modules/fishing/mgr/FishingMgr.bytes Ó% 'mCCAssets/AssetsPackage/LuaScripts/modules/fishing/minigame/Cage.bytes~TfaCAssets/AssetsPackage/LuaScripts/modules/fishing/minigame/Fish.bytesDՔċQAssets/AssetsPackage/LuaScripts/modules/fishing/minigame/FishermanCharacter.bytessq/$SAssets/AssetsPackage/LuaScripts/modules/fishing/minigame/FishingGameCameraCtl.bytes@VMAssets/AssetsPackage/LuaScripts/modules/fishing/minigame/FishingGameMap.bytesAҌ!MAssets/AssetsPackage/LuaScripts/modules/fishing/minigame/FishingGameMgr.bytes!ஐչHCAssets/AssetsPackage/LuaScripts/modules/fishing/minigame/Mine.bytesh!gOAssets/AssetsPackage/LuaScripts/modules/fishing/minigame/OpeningAnimation.bytesEX8j]FAssets/AssetsPackage/LuaScripts/modules/fishing/minigame/Piranha.bytesj,EHAssets/AssetsPackage/LuaScripts/modules/fishing/minigame/Swordfish.bytes8 c9OAssets/AssetsPackage/LuaScripts/modules/fishing/minigame/VictoryAnimation.bytestCAssets/AssetsPackage/LuaScripts/modules/fishing/minigame/main.bytes5>h=FAssets/AssetsPackage/LuaScripts/modules/fishing/tool/BezierMover.bytes|0<Assets/AssetsPackage/LuaScripts/modules/guide/GuideCom.bytes폌 ]>Assets/AssetsPackage/LuaScripts/modules/guide/GuideConst.bytes~F<Assets/AssetsPackage/LuaScripts/modules/guide/GuideMgr.bytes?+o;Assets/AssetsPackage/LuaScripts/modules/guide/GuideUI.bytesC kAAssets/AssetsPackage/LuaScripts/modules/guide/GuideUserData.bytes^ccC@Assets/AssetsPackage/LuaScripts/modules/guide/guidereslink.bytes BE8Assets/AssetsPackage/LuaScripts/modules/guide/main.bytesO@ZR;BAssets/AssetsPackage/LuaScripts/modules/help/const/HelpConst.bytes// \7Assets/AssetsPackage/LuaScripts/modules/help/main.bytes'@>Assets/AssetsPackage/LuaScripts/modules/login/LoginScene.bytes/HR;Assets/AssetsPackage/LuaScripts/modules/login/LoginUI.bytes>O-EAssets/AssetsPackage/LuaScripts/modules/login/loginscenereslink.bytes`BRқhBAssets/AssetsPackage/LuaScripts/modules/login/loginuireslink.bytes蚂$d8Assets/AssetsPackage/LuaScripts/modules/login/main.bytes?bq&k2Assets/AssetsPackage/LuaScripts/modules/main.bytesa  AAssets/AssetsPackage/LuaScripts/modules/mainscene/MainScene.bytesݿ0:Assets/AssetsPackage/LuaScripts/modules/maps/MapBase.bytesmQ(<Assets/AssetsPackage/LuaScripts/modules/maps/MapsConst.bytes-\֔&2:Assets/AssetsPackage/LuaScripts/modules/maps/MapsMgr.bytesN7Assets/AssetsPackage/LuaScripts/modules/maps/main.bytesET(;Assets/AssetsPackage/LuaScripts/modules/musicbbq/main.bytes:kˀn9?=FAssets/AssetsPackage/LuaScripts/modules/musicbbq/map/MusicbbqMap.bytesQ~2FAssets/AssetsPackage/LuaScripts/modules/musicbbq/mgr/MusicbbqMgr.bytes~:nOLHAssets/AssetsPackage/LuaScripts/modules/restaurant/RestaurantConst.bytes5 "=Assets/AssetsPackage/LuaScripts/modules/restaurant/main.bytese CAssets/AssetsPackage/LuaScripts/modules/restaurant/map/MapCfg.bytesTG?uJAssets/AssetsPackage/LuaScripts/modules/restaurant/map/RestaurantMap.byteseznrGAssets/AssetsPackage/LuaScripts/modules/restaurant/map/TilemapCfg.bytesLS%N KAssets/AssetsPackage/LuaScripts/modules/restaurant/mgr/CustomerQueMgr.bytesK(HFKAssets/AssetsPackage/LuaScripts/modules/restaurant/mgr/OrderDishesMgr.bytesIAssets/AssetsPackage/LuaScripts/modules/role/vendor/data/VendorInfo.bytesצ'GMAssets/AssetsPackage/LuaScripts/modules/role/vendor/data/VendorUserData.bytes/w K:>Assets/AssetsPackage/LuaScripts/modules/role/vendor/main.bytes*N{OJAssets/AssetsPackage/LuaScripts/modules/setting/data/SettingUserData.bytes߾Q!:Assets/AssetsPackage/LuaScripts/modules/setting/main.bytesbnX0nFAssets/AssetsPackage/LuaScripts/modules/task/const/TaskBootConst.bytes?BAssets/AssetsPackage/LuaScripts/modules/task/const/TaskConst.bytesNԹD:1HAssets/AssetsPackage/LuaScripts/modules/task/data/TaskBootUserData.bytes,fֽ֙NEAssets/AssetsPackage/LuaScripts/modules/task/data/TaskOrderInfo.bytes])KVIAssets/AssetsPackage/LuaScripts/modules/task/data/TaskOrderUserData.bytesAߏFzDAssets/AssetsPackage/LuaScripts/modules/task/data/TaskUserData.bytes%w7Assets/AssetsPackage/LuaScripts/modules/task/main.bytes{ȎsAAssets/AssetsPackage/LuaScripts/modules/task/mgr/TaskAchMgr.bytes9RԝBAssets/AssetsPackage/LuaScripts/modules/task/mgr/TaskBootMgr.bytes4=;fCAssets/AssetsPackage/LuaScripts/modules/task/mgr/TaskDailyMgr.bytesHs@4Y>Assets/AssetsPackage/LuaScripts/modules/task/mgr/TaskMgr.bytesT nj4CAssets/AssetsPackage/LuaScripts/modules/task/mgr/TaskOrderMgr.bytes<Assets/AssetsPackage/LuaScripts/modules/test/TestScene.bytesii+b3QCAssets/AssetsPackage/LuaScripts/modules/test/testscenereslink.bytes-BsMLAssets/AssetsPackage/LuaScripts/modules/ui/buildinglist/BuildingInfoUI.bytesW/ @QAssets/AssetsPackage/LuaScripts/modules/ui/buildinglist/buildinginforeslink.bytes=hSAssets/AssetsPackage/LuaScripts/modules/ui/buildinglist/buildinglistuireslink.bytestZ@X^VAssets/AssetsPackage/LuaScripts/modules/ui/buildinglist/buildingsui/BuildingCell.bytesƫvYAssets/AssetsPackage/LuaScripts/modules/ui/buildinglist/buildingsui/BuildingsListUI.bytes)b\^Assets/AssetsPackage/LuaScripts/modules/ui/buildinglist/buildingsui/buildingslistreslink.bytes2JVAssets/AssetsPackage/LuaScripts/modules/ui/buildinglist/themeui/BuildingThemesUI.bytesM(E(4OAssets/AssetsPackage/LuaScripts/modules/ui/buildinglist/themeui/ThemeCell.bytes%gPsWRAssets/AssetsPackage/LuaScripts/modules/ui/buildinglist/themeui/ThemeUIConst.byteskkjRh[Assets/AssetsPackage/LuaScripts/modules/ui/buildinglist/themeui/buildingthemesreslink.bytest3?/^DAssets/AssetsPackage/LuaScripts/modules/ui/cartoonui/CartoonUI.bytes ȹKAssets/AssetsPackage/LuaScripts/modules/ui/cartoonui/cartoonuireslink.bytes݁$SDAssets/AssetsPackage/LuaScripts/modules/ui/commonui/EntityCell.bytes7;HAssets/AssetsPackage/LuaScripts/modules/ui/commonui/EntityDetailUI.bytesf$T5ۂFAssets/AssetsPackage/LuaScripts/modules/ui/commonui/EntityListUI.bytes3vAAssets/AssetsPackage/LuaScripts/modules/ui/commonui/PopUpUI.bytes0xYdμIAssets/AssetsPackage/LuaScripts/modules/ui/commonui/commonuireslink.bytes\ b>Assets/AssetsPackage/LuaScripts/modules/ui/commonui/main.bytesim|FAssets/AssetsPackage/LuaScripts/modules/ui/cuisineui/CuisineCell.bytessaJAssets/AssetsPackage/LuaScripts/modules/ui/cuisineui/CuisineDetailUI.bytesHX>5T?HAssets/AssetsPackage/LuaScripts/modules/ui/cuisineui/CuisineListUI.bytescq) NAssets/AssetsPackage/LuaScripts/modules/ui/cuisineui/MusicBbqCuisineCell.bytespyK RAssets/AssetsPackage/LuaScripts/modules/ui/cuisineui/MusicBbqCuisineDetailUI.bytes ͤKAssets/AssetsPackage/LuaScripts/modules/ui/cuisineui/cuisineuireslink.bytesZHAssets/AssetsPackage/LuaScripts/modules/ui/customerui/AdCustomerUI.bytes+} NAssets/AssetsPackage/LuaScripts/modules/ui/customerui/BoothOwnerDetailUI.bytes wanHAssets/AssetsPackage/LuaScripts/modules/ui/customerui/CustomerCell.bytesN=f7LAssets/AssetsPackage/LuaScripts/modules/ui/customerui/CustomerDetailUI.bytesx^JJAssets/AssetsPackage/LuaScripts/modules/ui/customerui/CustomerListUI.bytes"2MAssets/AssetsPackage/LuaScripts/modules/ui/customerui/CustomerStoryCell.bytes@=vKAssets/AssetsPackage/LuaScripts/modules/ui/customerui/CustomerTagCell.bytes9En7TOAssets/AssetsPackage/LuaScripts/modules/ui/customerui/SpecialCustomerCell.bytes`J!XrE!SAssets/AssetsPackage/LuaScripts/modules/ui/customerui/SpecialCustomerDetailUI.bytes%TvśbFAssets/AssetsPackage/LuaScripts/modules/ui/customerui/VendorCell.bytesBXDaGJAssets/AssetsPackage/LuaScripts/modules/ui/customerui/VendorDetailUI.bytes+Ϳ1WoLAssets/AssetsPackage/LuaScripts/modules/ui/customerui/VideoPromotionUI.bytes=ALFsMAssets/AssetsPackage/LuaScripts/modules/ui/customerui/customeruireslink.byteses-4T@Assets/AssetsPackage/LuaScripts/modules/ui/dollui/DollCell.bytesId~kDAssets/AssetsPackage/LuaScripts/modules/ui/dollui/DollDetailUI.byteso.BARBAssets/AssetsPackage/LuaScripts/modules/ui/dollui/DollMainUI.bytesUǶ nCAssets/AssetsPackage/LuaScripts/modules/ui/dollui/DollRowCell.bytes5<nߔEAssets/AssetsPackage/LuaScripts/modules/ui/dollui/dolluireslink.bytes>HAssets/AssetsPackage/LuaScripts/modules/ui/employeeui/EmployeeCell.bytesK1ǼxLAssets/AssetsPackage/LuaScripts/modules/ui/employeeui/EmployeeDetailUI.bytesH{D$JAssets/AssetsPackage/LuaScripts/modules/ui/employeeui/EmployeeListUI.bytes.vHAssets/AssetsPackage/LuaScripts/modules/ui/employeeui/EmployeeSkin.bytes A&@LAssets/AssetsPackage/LuaScripts/modules/ui/employeeui/EmployeeSkinCell.bytes%!aM"zMAssets/AssetsPackage/LuaScripts/modules/ui/employeeui/EmployeeStoryCell.bytes׹ MAssets/AssetsPackage/LuaScripts/modules/ui/employeeui/employeeuireslink.bytesVoRL-MAssets/AssetsPackage/LuaScripts/modules/ui/fishingui/FishingControlArea.bytesHk8ª9JAssets/AssetsPackage/LuaScripts/modules/ui/fishingui/FishingFailedUI.bytesʥM3JAssets/AssetsPackage/LuaScripts/modules/ui/fishingui/FishingFishCell.bytesB7KK-LAssets/AssetsPackage/LuaScripts/modules/ui/fishingui/FishingFishListUI.bytesxAun;HAssets/AssetsPackage/LuaScripts/modules/ui/fishingui/FishingMainUI.bytes.f5YIAssets/AssetsPackage/LuaScripts/modules/ui/fishingui/FishingPauseUI.bytesE=݁EJAssets/AssetsPackage/LuaScripts/modules/ui/fishingui/FishingPropCell.bytesBX HAssets/AssetsPackage/LuaScripts/modules/ui/fishingui/FishingPropUI.bytesElsLJAssets/AssetsPackage/LuaScripts/modules/ui/fishingui/FishingReviveUI.bytesNKAssets/AssetsPackage/LuaScripts/modules/ui/fishingui/FishingSucceedUI.bytes>bY^KAssets/AssetsPackage/LuaScripts/modules/ui/fishingui/fishinguireslink.bytesl^ȀEDAssets/AssetsPackage/LuaScripts/modules/ui/gacha/GachaRewardUI.bytes2>Assets/AssetsPackage/LuaScripts/modules/ui/gacha/GachaUI.bytesvhcCCAssets/AssetsPackage/LuaScripts/modules/ui/gacha/gachareslink.bytesp,IAssets/AssetsPackage/LuaScripts/modules/ui/gacha/gacharewardreslink.bytesyMIHAssets/AssetsPackage/LuaScripts/modules/ui/helpui/HelpCategoryCell.bytes t{DJAssets/AssetsPackage/LuaScripts/modules/ui/helpui/HelpCategoryListUI.bytesJ4/Di@Assets/AssetsPackage/LuaScripts/modules/ui/helpui/HelpCell.bytes%DAssets/AssetsPackage/LuaScripts/modules/ui/helpui/HelpDetailUI.bytesSHBAssets/AssetsPackage/LuaScripts/modules/ui/helpui/HelpListUI.bytesK5]QQ%EAssets/AssetsPackage/LuaScripts/modules/ui/helpui/helpuireslink.bytes첓.BAssets/AssetsPackage/LuaScripts/modules/ui/mainui/MainViewUI.bytes,iIAssets/AssetsPackage/LuaScripts/modules/ui/mainui/mainviewuireslink.bytes؏oIAssets/AssetsPackage/LuaScripts/modules/ui/pondui/PondFishSalesCell.byteshGAssets/AssetsPackage/LuaScripts/modules/ui/pondui/PondFishSalesUI.bytesbXձIAssets/AssetsPackage/LuaScripts/modules/ui/pondui/PondRemoveStoneUI.bytess-2HrDEAssets/AssetsPackage/LuaScripts/modules/ui/pondui/ponduireslink.bytesV~}`kFAssets/AssetsPackage/LuaScripts/modules/ui/reception/ReceptionUI.bytesIFETKAssets/AssetsPackage/LuaScripts/modules/ui/reception/receptionreslink.bytes HAssets/AssetsPackage/LuaScripts/modules/ui/settingui/SettingMainUI.bytes'*KAssets/AssetsPackage/LuaScripts/modules/ui/settingui/settinguireslink.bytescqsLAssets/AssetsPackage/LuaScripts/modules/ui/storydialogui/StoryDialogUI.bytesєdKMAOAssets/AssetsPackage/LuaScripts/modules/ui/storydialogui/StorydialogConst.bytes{}>qCAssets/AssetsPackage/LuaScripts/modules/ui/storydialogui/main.bytes3iҢ"VSAssets/AssetsPackage/LuaScripts/modules/ui/storydialogui/storydialoguireslink.bytesh8k%g>Assets/AssetsPackage/LuaScripts/modules/ui/taskui/TaskUI.bytes9 @CAssets/AssetsPackage/LuaScripts/modules/ui/taskui/TaskUIConst.bytese?ݟ 6}OAssets/AssetsPackage/LuaScripts/modules/ui/taskui/achievement/TaskAchCell.bytes~3^6MAssets/AssetsPackage/LuaScripts/modules/ui/taskui/boot/TaskBootDetailUI.bytesLUd.KAssets/AssetsPackage/LuaScripts/modules/ui/taskui/daily/TaskDailyCell.bytes1+ kMAssets/AssetsPackage/LuaScripts/modules/ui/taskui/daily/taskcellreslink.bytes~@KAssets/AssetsPackage/LuaScripts/modules/ui/taskui/order/TaskOrderCell.bytesoNE`_OAssets/AssetsPackage/LuaScripts/modules/ui/taskui/order/TaskOrderDetailUI.bytes" PAssets/AssetsPackage/LuaScripts/modules/ui/taskui/order/taskorderuireslink.byteskW$LEAssets/AssetsPackage/LuaScripts/modules/ui/taskui/taskuireslink.bytes[QrFAssets/AssetsPackage/LuaScripts/modules/ui/worldmapui/WorldMapUI.bytes;P|pMAssets/AssetsPackage/LuaScripts/modules/ui/worldmapui/worldmapuireslink.bytesiu2aRAssets/AssetsPackage/LuaScripts/modules/unlock/component/UnlockHintComponent.bytesr FAssets/AssetsPackage/LuaScripts/modules/unlock/const/UnlockConst.bytesHtJz9Assets/AssetsPackage/LuaScripts/modules/unlock/main.byteslskBAssets/AssetsPackage/LuaScripts/modules/unlock/mgr/UnlockMgr.bytesPoRHAssets/AssetsPackage/LuaScripts/modules/upgrade/const/UpgradeConst.bytes8vYyq:Assets/AssetsPackage/LuaScripts/modules/upgrade/main.bytesp_ŶDAssets/AssetsPackage/LuaScripts/modules/upgrade/mgr/UpgradeMgr.bytes!zz.bundles_assets_assetspackage_luascripts.bundleExtendGameObject<local gameObjetsAttrsMap = {} local SpriteRenderer = CS.UnityEngine.SpriteRenderer local LuaHelper = CS.LuaHelper local RectTransform = CS.UnityEngine.RectTransform local GameObject = CS.UnityEngine.GameObject local Object = CS.UnityEngine.Object local GameObjectCls,GameObjectCls__index = HackCSharpClass(CS.UnityEngine.GameObject) ---@diagnostic disable-next-line: duplicate-set-field GameObjectCls.__index = function(ud, k) local v = rawget(GameObjectCls, k) -- lua扩展的方法或者属性 if v ~= nil then return v end local v = GameObjectCls__index(ud, k) -- c#的方法或者属性 if v ~= nil then return v end local ktype = type(k) if ktype == "table" then local meta = getmetatable(k) if meta and meta.__call then --Find Component 传入c#类的时候获取应对组件 local comp = ud:GetComponent(typeof(k)) if comp then return comp end end end --GameObject的lua userdata对象上挂的lua属性 local attrs = gameObjetsAttrsMap[ud] if not attrs then attrs = {} gameObjetsAttrsMap[ud] = attrs -- local name = ud.name -- print("luaGC GameObject的lua关联添加"..name) if Event then Event.add( ud, Event.OnDestroy, function() -- print("luaGC GameObject的lua关联析构"..name) gameObjetsAttrsMap[ud] = nil end ) end end return attrs[k] end local GameObjectCls__newindex = GameObjectCls.__newindex GameObjectCls.__newindex = function(ud, k, v) -- GameObjectCls__newindex(ud,k,v) -- 如果k是c#的属性,会失效,get不影响,set影响。相当于GameObject的属性setter都不可用,GameObject的setter在GameObjectExtend.cs里都有等价函数(GameObject能挂属性的代价)。 local attrs = gameObjetsAttrsMap[ud] if not attrs then attrs = {} gameObjetsAttrsMap[ud] = attrs -- local name = ud.name -- print("luaGC GameObject的lua关联添加"..name) if Event then --Event不一定存在 Event.add( ud, Event.OnDestroy, function() -- print("luaGC GameObject的lua关联析构"..name) gameObjetsAttrsMap[ud] = nil end ) end end attrs[k] = v end local function recur(go, cb) cb(go) for i = 0, go.transform.childCount - 1 do local child = go.transform:GetChild(i).gameObject recur(child, cb) end end GameObjectCls.TraverseChildrenRecusivly = function(self, cb) recur(self, cb) end --Unity的坑,如果节点A.activeSelf == false,删除父节点,节点A的OnDestroy可能不触发 local originDestroy = rawget(GameObject, "__originDestroy") or CS.UnityEngine.GameObject.Destroy rawset(GameObject, "__originDestroy", originDestroy) rawset( GameObject, "Destroy", function(go, time) if not CS.LuaHelper.IsNull(go) then local TraverseChildrenRecusivly = GameObjectCls.TraverseChildrenRecusivly if TraverseChildrenRecusivly then TraverseChildrenRecusivly( go, function(child) if not child.activeSelf and child.SetActive then child:SetActive(true) end end ) if time then originDestroy(go, time) else originDestroy(go) end else print("Failed To Destroy!") print(typeof(go), debug.traceback()) print(go.name) end end end ) GameObjectCls.SetVisible = function(self, visible, recursively) local coms if not recursively then coms = self:GetComponents(typeof(CS.UnityEngine.Renderer)) else coms = self:GetComponentsInChildren(typeof(CS.UnityEngine.Renderer)) end for _,com in cs_ipairs(coms)do com.enabled = visible end end GameObjectCls.AddEvent = function(self, ev, fn, obj) Event.add(self, ev, fn, obj) end GameObjectCls.RmEvent = function(self, ev, fn, obj) Event.rm(self, ev, fn, obj) end -- GameObjectCls.Clone = function(self, transform, meshlist) -- local copy = transform and GameObject.Instantiate(self, transform) or GameObject.Instantiate(self) -- local list1 = meshlist or self:GetComponentsInChildren(typeof(MeshRenderer)) -- local list2 = copy:GetComponentsInChildren(typeof(MeshRenderer)) -- for i = 0, list2.Length - 1 do -- local m1, m2 = list1[i], list2[i] -- if m1.name == m2.name then -- m2.lightmapIndex = m1.lightmapIndex -- m2.lightmapScaleOffset = m1.lightmapScaleOffset -- else -- print("[GameObject][InstantiateWithLightmap]lightmap dismatch", debug.traceback()) -- end -- end -- return copy -- end GameObjectCls.SearchPattern = function(self,name, recurse) recurse = recurse or false local result = self:_SearchPattern(name, recurse) local tbl = {} for i = 0,result.Count - 1 do table.insert(tbl,result[i]) end return tbl end GameObjectCls.ChildPattern = function(self,name) local result = self:_ChildPattern(name) local tbl = {} for i = 0,result.Count - 1 do table.insert(tbl,result[i]) end return tbl end -- 返回子节点,非递归 GameObjectCls.getChildrens = function (self) local childrens = {} for i = 1, self.transform.childCount do local item = self.transform:GetChild(i - 1).gameObject table.insert(childrens,item) end return childrens end GameObjectCls.Delay = function(self,time,cb) return self:RunAction(ua.Sequence({ ua.Delay(time), ua.cb(cb), })) end GameObjectCls.Step = function(self,cb,interval) if interval then return self:RunAction(ua.RepeatForever(ua.Sequence({ ua.Delay(interval), ua.cb(cb) }))) else return self:RunAction(ua.Step(cb)) end end GameObjectCls.__Sound__ = function(self) local audiosCom = self:GetComponent(typeof(CS.AudiosComponent)) if not audiosCom then audiosCom = self:AddComponent(typeof(CS.AudiosComponent)) end return audiosCom end GameObjectCls.PlaySound = function(self,assetInfo,cb) local com = self:__Sound__() local audioClip local t = type(assetInfo) if t == "table" then audioClip = Res.loadAsset(assetInfo[1]) elseif t == "string" then audioClip = Res.loadAsset(assetInfo) else-- is AudioClip audioClip = assetInfo end if audioClip then return com:Play(audioClip,cb) end end GameObjectCls.StopSound = function (self, audioClip) if audioClip then audioClip:Stop() end end GameObjectCls.PlayVoice = function(self,assetInfo,cb) if self.__playVoiceRet then self.__playVoiceRet:Stop() end self.__playVoiceRet = GameObjectCls.PlaySound(self,assetInfo,cb) end GameObjectCls.PlaySoundLoop = function(self,assetInfo,isBgMusic) local com = self:__Sound__() local audioClip local t = type(assetInfo) if t == "table" then audioClip = Res.loadAsset(assetInfo[1]) elseif t == "string" then audioClip = Res.loadAsset(assetInfo) else-- is AudioClip audioClip = assetInfo end if audioClip then if isBgMusic == nil then isBgMusic = false end return com:PlayLoop(audioClip,isBgMusic) end end GameObjectCls.PlayBgMusic = function(self,assetInfo) return self:PlaySoundLoop(assetInfo, true) end GameObjectCls.PlaySoundSeq = function(self,...) local args = {...} local cur local audios = {} local proxy = { audios = audios, stop = function(self) if self.current then self.current:Stop() self.current = nil end if self.action then self.gameObject:StopAction(self.action) self.action = nil end end, pause = function(self) if self.current then self.current:Pause() end end, resume = function(self) if self.current then self.current:Resume() end end, current = nil, action = nil, } local com = self:__Sound__() for _,arg in ipairs(args)do local t = type(arg) if t == "table" then cur = {clip = Res.loadAsset(arg[1])} table.insert(audios,cur) elseif t == "function" then if cur then if not cur.cb then cur.cb = arg else table.insert(audios,{cb = arg}) end else table.insert(audios,{cb = arg}) end elseif t == "number" then cur.delay = arg elseif t == "string" then cur = {clip = Res.loadAsset(arg)} table.insert(audios,cur) else error("GameObject.PlaySoundSeq arg type error:" .. t) end end local com = self:__Sound__() local function playNext() local audio = table.remove(audios,1) if audio then if audio.clip then proxy.current = com:Play(audio.clip,function() proxy.current = nil if audio.delay then proxy.action = self.gameObject:Delay(audio.delay,function() proxy.action = nil if audio.cb then audio.cb() end playNext() end) else if audio.cb then audio.cb() end playNext() end end) else if audio.cb then audio.cb() end playNext() end end end playNext() return proxy end GameObjectCls.StopAllSounds = function(self,ignoreLoop) if ignoreLoop == nil then ignoreLoop = false end local com = self:__Sound__() com:StopAll(ignoreLoop) end GameObjectCls.RemoveAllChildren = function(self) local trans = self.transform local Destroy = CS.UnityEngine.GameObject.Destroy for i = 0,trans.childCount - 1 do local child = trans:GetChild(i) Destroy(child.gameObject) end end GameObjectCls.WaitUntil = function(self,condition,cb) if condition() then--保证当前帧也判断一次 if cb then cb() end else self:RunAction(ua.Step(function() if condition() then if cb then cb() end return true else return false end end)) end end -- 设置到摄像机中心位置 GameObjectCls.SetPosInCameraCenter = function(self) local camera = CS.UnityEngine.GameObject.Find("MainCamera") if camera then local pos local parent = self:GetParent() if parent then pos = parent.transform:InverseTransformPoint(camera:GetWorldPosition()) else pos = camera:GetWorldPosition() end pos.z = 0 self:SetPosition(pos) end end GameObjectCls.GetOpacity = function(self) local spr = self:GetComponent(typeof(SpriteRenderer)) --这里兼容下ugui local maskableGraphic = self:GetComponent(typeof(CS.UnityEngine.UI.MaskableGraphic)) if spr then local c = spr.color return c.a elseif maskableGraphic then local c = maskableGraphic.color return c.a end end -- alpha range: [0,1] GameObjectCls.SetOpacity = function(self,alpha) local spr = self:GetComponent(typeof(SpriteRenderer)) --这里兼容下ugui local maskableGraphic = self:GetComponent(typeof(CS.UnityEngine.UI.MaskableGraphic)) -- 兼容spine local spineAnimation = self:GetComponent(typeof(CS.Spine.Unity.SkeletonAnimation)); if spr then local c = spr.color c.a = alpha spr.color = c elseif maskableGraphic then local c = maskableGraphic.color c.a = alpha maskableGraphic.color = c elseif spineAnimation then -- local spineMaterial = spineAnimation.material -- local spineMater = spineAnimation:GetMaterial(CS.Spine.Unity.MaterialType.Default) -- local material = spineAnimation.skeletonDataAsset.atlasAssets[0].materials[0].material -- local c = spineMaterial.color -- c.a = alpha -- spineMaterial.color = c end end local IsNull = CS.LuaHelper.IsNull GameObjectCls.RunAtNextFrame = function(self,cb) CS.LuaGlobal.instance:runAtNextFrame(function() if not IsNull(self) and self.activeSelf then cb() end end) end GameObjectCls.RunAtNextNFrame = function(self,N,cb) local n = 0 local function run() n = n + 1 if n == N then cb() else self:RunAtNextFrame(run) end end run() end GameObjectCls.RunTimer = function(self,dt,func) return self:RunAction(ua.RepeatForever(ua.Sequence({ ua.Delay(dt), ua.cb(func) }))) end GameObjectCls.GetAnchoredPositionX = function(self,x) local rectTrans = self:GetComponent(typeof(RectTransform)) if rectTrans then return rectTrans.anchoredPosition3D.x end end GameObjectCls.GetAnchoredPositionY = function(self,x) local rectTrans = self:GetComponent(typeof(RectTransform)) if rectTrans then return rectTrans.anchoredPosition3D.y end end GameObjectCls.SetAnchoredPositionX = function(self,x) local rectTrans = self:GetComponent(typeof(RectTransform)) if rectTrans then local p = rectTrans.anchoredPosition3D p.x = x rectTrans.anchoredPosition3D = p end end GameObjectCls.SetAnchoredPositionY = function(self,y) local rectTrans = self:GetComponent(typeof(RectTransform)) if rectTrans then local p = rectTrans.anchoredPosition3D p.y = y rectTrans.anchoredPosition3D = p end end GameObjectCls.GetXPath = function(self) local curr = self.transform local xpath = nil while curr ~= nil do if xpath then xpath = curr.gameObject.name .. "/" .. xpath else xpath = curr.gameObject.name end curr = curr.parent end return xpath end GameObjectCls.SeekByXPath = function(self, xpath) local splits = string.split(xpath, "/") local curr = self ---@diagnostic disable-next-line: param-type-mismatch for _, split in ipairs(splits) do curr = curr:Child(split) if not curr then break end end return curr end GameObjectCls.RemoveAllComponentsByType = function(self, comType) local coms = self:GetComponents(comType) for _, com in cs_ipairs(coms) do Object.Destroy(com) end end GameObjectCls.SeekInParentHierarchy = function(self, name) local function func(go) local parent = go.transform.parent if not parent then return false end local parentGo = go.transform.parent.gameObject if parentGo.name == name then return true end return func(parentGo) end return func(self) endmainviewuireslinkreturn { --BASIC --ASSET mainview_ui = {"Assets/AssetsPackage/Res/modules/ui/mainui/prefab/mainview_ui.prefab", 0, 0}, } build_config  local ue = CS.UnityEngine APP_NAME = "Tile Link" local LOGTAG = "[build_config] " print(LOGTAG.."BUILD_ENV:", BUILD_ENV) DEBUG = ue.Debug.isDebugBuild print(LOGTAG.."DEBUG:", DEBUG) SKIP_INPKG_HOTUPDATE = true print(LOGTAG.."跳过热更新:", SKIP_INPKG_HOTUPDATE) -- sqlite是否需要加密 电脑上不加密 SQLITE_ENCRYPT = true if ue.Application.platform == ue.RuntimePlatform.WindowsEditor or ue.Application.platform == ue.RuntimePlatform.OSXEditor then SQLITE_ENCRYPT = false end print(LOGTAG.."SQLITE_ENCRYPT:", SQLITE_ENCRYPT) ENV_DEVELOPMENT = "dev" ENV_PRODUCTION = "production" if BUILD_ENV == ENV_DEVELOPMENT then RES_URL_LAN = "http://192.144.239.125:55068/dev/"--开发用文件服务器地址 RES_URL = "http://192.144.239.125:55068/" -- cdn 资源服务器 WEAK_RES_URL = "" PACKAGE_NAME = "com.fy.xgame.tilelink.dev" elseif BUILD_ENV == ENV_PRODUCTION then RES_URL_LAN = "http://192.144.239.125:55068/production/"--开发用文件服务器地址 RES_URL = "http://192.144.239.125:55068/" -- cdn 资源服务器 WEAK_RES_URL = "" PACKAGE_NAME = "com.fy.xgame.tilelink" else error("BUILD_ENV:" .. BUILD_ENV) end local branchFile = "svnBranch.txt" SVN_BRANCH_NAME = "linkmatchClient"-- CS.LuaHelper.ReadTextFileInPackage(branchFile) or "trunk" CosLuaTagTask---@class CosLuaTagTask:LuaClass local CosLuaTagTask = defClass("CosLuaTagTask") local LOGTAG = "CosLuaTagTask" local CosSDKAPI = CS.iHuman.UnitySz.Framework.COS.CosSDKAPI CosLuaTagTask.STATUS = { NONE = 0, RUNNING = 1, SUC = 2, FAIL = 3, } ---@enum CosLuaTagTaskOpt CosLuaTagTask.OPT = { SET = 0, GET = 1, DELETE = 2, } ---@param cosUrl string cosurl ---@param opt CosLuaTagTaskOpt 操作类型 ---@param serviceType CosLuaServiceType 服务类型 ---@param completeCallback fun(result:boolean, data:{errorCode:integer, errorMsg:string, tags:table}, tagTask:CosLuaTagTask) function CosLuaTagTask:ctor(cosUrl, opt, tags, serviceType, completeCallback) self.cosUrl = cosUrl self.opt = opt self.tags = tags self.serviceType = serviceType self.completeCallback = completeCallback self.status = self.STATUS.NONE end function CosLuaTagTask:start() printInfo(LOGTAG, "start srcPath:%s", self.srcPath) if self.status ~= self.STATUS.NONE then printWarn(LOGTAG, "CosLuaTagTask start error. status(%s) is not none.", tostring(self.status)) return end self.status = self.STATUS.RUNNING local cosXmlServer = CosLuaTemporaryCredential:getCosXmlServer(self.serviceType) if not cosXmlServer then self:onComplete(false, {errorCode = -4, errorMsg = "cosXmlServer is nil"}) return end local cosLuaCredentialBean = CosLuaTemporaryCredential:getCredential(self.serviceType) if not cosLuaCredentialBean then self:onComplete(false, {errorCode = -5, errorMsg = "cosLuaCredentialBean is nil"}) return end local rPath = CosLuaTemporaryCredential:getRelativePath(self.cosUrl, self.serviceType) printInfo(LOGTAG, "cosUrl:%s, rPath:%s", self.cosUrl, rPath) if self.opt == self.OPT.GET then CosSDKAPI.GetObjectTagging(cosXmlServer, cosLuaCredentialBean.bucket_name, rPath, handler(self, self.onComplete)) elseif self.opt == self.OPT.SET then CosSDKAPI.PutObjectTagging(cosXmlServer, cosLuaCredentialBean.bucket_name, rPath, self.tags, handler(self, self.onComplete)) elseif self.opt == self.OPT.DELETE then CosSDKAPI.DeleteObjectTagging(cosXmlServer, cosLuaCredentialBean.bucket_name, rPath, handler(self, self.onComplete)) end end function CosLuaTagTask:isFinished() return (self.status == self.STATUS.SUC or self.status == self.STATUS.FAIL) end function CosLuaTagTask:isSuc() return self.status == self.STATUS.SUC end function CosLuaTagTask:canStart() return self.status == self.STATUS.NONE end ---获取失败,上层调用 ---@param service_type CosLuaServiceType function CosLuaTagTask:failWithErrorCredential(service_type) if self.status == self.STATUS.NONE and self.service_type == service_type then self:onComplete(false, -3, "fetch credential error") end end function CosLuaTagTask:getStatus() return self.status end ---@private ---@param result boolean ---@param code integer ---@param message string ---@param tags table|nil function CosLuaTagTask:onComplete(result, code, message, tags) local luaTags = {} for key, value in pairs(tags or {}) do luaTags[key] = value end tags = nil local data = {errorCode = code, errorMsg = message, tags = luaTags} printInfo(LOGTAG, "onComplete result:%s, data:%s", result, table.toString(data)) self.status = result and self.STATUS.SUC or self.STATUS.FAIL if self.completeCallback then self.completeCallback(result, data, self) end end return CosLuaTagTask BuyMgr~-- 购买管理器 local BuyMgr = defClassStatic("BuyMgr") -- 初始化 function BuyMgr:init() end --- 获取购买配置 function BuyMgr:getConfig(buyId) return BuyCommonCfgParse:getBuyCommonCfg(buyId) end --- 检查购买限制 function BuyMgr:canBuy(buyId) if (not buyId) or (buyId == -1) then return true end local config = self:getConfig(buyId) if config.limitType_1 then return self:checkLimit(config.limitType_1, config.value_1) end end -- 获取购买货币类型 function BuyMgr:getBuyCurrencyType(buyId) if (not buyId) or (buyId == -1) then return nil end local config = self:getConfig(buyId) local currencyType = config.currencyType if currencyType == BuyConst.BuyCurrencyType.Coin then return CurrencyConst.CurrencyType.Coin elseif currencyType == BuyConst.BuyCurrencyType.MusicalNotes then return CurrencyConst.CurrencyType.MusicalNotes end return nil end --- 获取购买价格 function BuyMgr:getPrice(buyId) if (not buyId) or (buyId == -1) then return 0 end local config = self:getConfig(buyId) return config.price end --- 获取购买价格 function BuyMgr:getBuyShortage(buyId) if (not buyId) or (buyId == -1) then return 0 end local config = self:getConfig(buyId) local currencyType = config.currencyType if currencyType == BuyConst.BuyCurrencyType.Coin then return config.price - CurrencyMgr:getCoin() elseif currencyType == BuyConst.BuyCurrencyType.MusicalNotes then return config.price - CurrencyMgr:getMusicalNotes() end return 0 end -- 购买 function BuyMgr:buy(buyId) if (not buyId) or (buyId == -1) then return true, "购买成功" end local config = self:getConfig(buyId) if config.price <= 0 then return true, "购买成功" end local currencyType = config.currencyType if currencyType == BuyConst.BuyCurrencyType.Coin then if CurrencyMgr:getCoin() < config.price then return false, "金币不足" end CurrencyMgr:changeCoin(-config.price) elseif currencyType == BuyConst.BuyCurrencyType.MusicalNotes then if CurrencyMgr:getMusicalNotes() < config.price then return false, "音符不足" end CurrencyMgr:changeMusicalNotes(-config.price) else return false, "未知货币类型" end return true, "购买成功" end -- 视频购买 function BuyMgr:buyByVideo(buyId, shortage) if (not buyId) or (buyId == -1) then return true, "购买成功" end local config = self:getConfig(buyId) if config.price <= 0 then return true, "购买成功" end local currencyType = config.currencyType if currencyType == BuyConst.BuyCurrencyType.Coin then CurrencyMgr:changeCoin(-config.price + shortage) elseif currencyType == BuyConst.BuyCurrencyType.MusicalNotes then CurrencyMgr:changeMusicalNotes(-config.price + shortage) else return false, "未知货币类型" end return true, "购买成功" end -- 获取价格描述 function BuyMgr:getPriceDesc(buyId) if (not buyId) or (buyId == -1) then return "Error默认已购买" end local config = self:getConfig(buyId) local currencyType = config.currencyType if config.price <= 0 then return "免费" end if currencyType == BuyConst.BuyCurrencyType.Coin then return string.format("%d", config.price) elseif currencyType == BuyConst.BuyCurrencyType.MusicalNotes then return string.format("%d", config.price) end return "None:" .. config.price end -- 是否满足购买价格 function BuyMgr:isMeetPrice(buyId) if (not buyId) or (buyId == -1) then return true end local config = self:getConfig(buyId) local currencyType = config.currencyType if currencyType == BuyConst.BuyCurrencyType.Coin then return CurrencyConst.CurrencyType.Coin elseif currencyType == BuyConst.BuyCurrencyType.Music then return CurrencyConst.CurrencyType.MusicalNotes end return false end --- 检查购买限制 function BuyMgr:checkLimit(limitType, limitValue) if (not limitType) or limitType == -1 then return true end if limitType == BuyConst.BuyLimitType.Star then return CurrencyMgr:getStar() >= limitValue elseif limitType == BuyConst.BuyLimitType.Build then return UserDataMgr.buildingUserData:getBuildingData(limitValue) elseif limitType == BuyConst.BuyLimitType.Tag then return false elseif limitType == BuyConst.BuyLimitType.Cuisine then return UserDataMgr.cuisineUserData:getCuisineState(limitValue) >= CuisineConst.State.Purchased elseif limitType == BuyConst.BuyLimitType.Event then return false end return false end --- 获取购买限制描述 function BuyMgr:getLimitDescById(buyId) local config = self:getConfig(buyId) return BuyConst.BuyLimitTypeDesc[config.limitType_1]:format(config.value_1) end --- 获取购买限制类型id function BuyMgr:getLimitIdByIdx(buyId, idx) if (not buyId) or (buyId == -1) then return -1 end local config = self:getConfig(buyId) if idx == 1 then return config.limitType_1 or -1 elseif idx == 2 then return config.limitType_2 or -1 elseif idx == 3 then return config.limitType_3 or -1 elseif idx == 4 then return config.limitType_4 or -1 elseif idx == 5 then return config.limitType_5 or -1 end return -1 end --- 获取购买限制类型id function BuyMgr:getLimitValueByIdx(buyId, idx) if (not buyId) or (buyId == -1) then return 0 end local config = self:getConfig(buyId) if idx == 1 then return config.value_1 elseif idx == 2 then return config.value_2 elseif idx == 3 then return config.value_3 elseif idx == 4 then return config.value_4 elseif idx == 5 then return config.value_5 end return -1 end --- 获取购买限制描述 function BuyMgr:getLimitValueNameByType(limitType, limitValue) local valueName if limitType == BuyConst.BuyLimitType.Star then valueName = limitValue elseif limitType == BuyConst.BuyLimitType.Build then valueName = BuildingMgr:getBuildingInfo(limitValue):getName() elseif limitType == BuyConst.BuyLimitType.Tag then valueName = limitValue elseif limitType == BuyConst.BuyLimitType.Cuisine then valueName = CuisineMgr:getCuisineInfo(limitValue):getName() elseif limitType == BuyConst.BuyLimitType.Event then valueName = limitValue end return valueName end --- 获取购买限制描述 function BuyMgr:getLimitDescByIdx(buyId, idx) local limitType = self:getLimitIdByIdx(buyId, idx) local value = self:getLimitValueByIdx(buyId, idx) return BuyConst.BuyLimitTypeDesc[limitType]:format(self:getLimitValueNameByType(limitType, value)) end --- 获取购买限制描述 function BuyMgr:getLimitDesc(limitType, limitValue) return BuyConst.BuyLimitTypeDesc[limitType]:format(self:getLimitValueNameByType(limitType, limitValue)) end BuyMgr:init() ApplovinConstj --[[ author:{zhangpeng} time:2024-04-25 18:00:11 ]] local ApplovinConst,_ = defClassStatic("ApplovinConst") -- 激励视频播放位置标识符 -- ApplovinConst.RewardPlacement = { -- SkinPart = "SkinPartItem", -- 换装界面的皮肤部件 -- SkinSuit = "SkinSuit", -- 换装界面的套装 -- CatItem = "CatItem", -- 猫咪道具 -- Test = "TestPlacement" -- 测试位置 -- } -- 广告加载超时时间 ApplovinConst.adLoadTimeMax = 3 -- 广告位 ApplovinConst.placement = { FreeCoins = "free_coins", -- 看5次广告免费得金币 Victory = "victory", -- 胜利界面 Fail = "fail", -- 失败看广告 ItemGetAd = "item_get_ad" -- 获得道具看广告 } -- 广告类型 ApplovinConst.AdType = { EAdTypeRewardedAd = 100, -- 激励视频 EAdTypeInterstitialAd = 200 -- 插屏广告 }; -- sdk的回调结果类型 ApplovinConst.AdCode = { ESAdCodeLoadSucceeded = 0, -- 加载成功 ESAdCodeLoadFailed = 1, -- 加载失败 ESAdCodeShowSucceeded = 10, -- 显示成功,此时应该暂停游戏音频 ESAdCodeShowFailed = 11, -- 显示失败 ESAdCodeHide = 12, -- 显示完毕,关闭全屏广告,恢复背景音乐等,自动预加载下一个广告 ESAdCodeDidReward = 20, -- 关闭视频,给用户奖励 ESAdCodeClick = 30, -- 点击了广告 ESAdCodeImpression = 40, }; -- 加载的广告数据详情 ApplovinConst.DataEnum = { ad_unit_identifier = "ad_unit_identifier", -- 广告单元标识符,用于标识特定的广告单元 country = "country", -- 国家代码,表示广告请求的国家 creative_identifier = "creative_identifier",-- 创意标识符,用于标识特定的广告创意 network_name = "network_name", -- 网络名称,表示提供广告的广告网络 network_placement = "network_placement", -- 网络放置位置,表示广告在网络上的具体位置或类型 platform = "platform", -- 平台,表示广告平台 revenue = "revenue", -- 收入,表示广告的收入金额 revenue_precision = "revenue_precision" -- 收入精度,表示收入金额的精确度 } function ApplovinConst:init() end ApplovinConst:init()PositionConvert?--[[ 世界坐标,屏幕坐标,UGUI坐标相互转换 可转换路径: UI 坐标 -> 屏幕坐标 UI 坐标 -> ViewPort 屏幕坐标 -> 世界坐标 屏幕坐标 -> UI 坐标 世界坐标 -> 屏幕坐标 1:屏幕坐标: 相对于屏幕坐标系 从屏幕左下角开始 坐标为 Vector2 (0, 0),右上角结束坐标为 Vector2(Screen.width, Screen.height) 2:ViewPort坐标: 相对于摄像机视口坐标系 从视口左下角开始 坐标为 Vector2 (0, 0),右上角结束坐标为 Vector2(1, 1) 3:UGUI坐标: 相对于Canvas坐标系,是一种基于屏幕坐标系的特殊坐标系,原点位于Canvas的中心点 从Canvas的中心点开始 x 轴向右延伸,y 轴向上延伸 Canvas的中心有两种情况: Screen Space - Overlay(此项目默认全都使用Overlay) 以屏幕为参考,覆盖在屏幕上方,此时,Canvas中心位于屏幕中心 Screen Space - Camera 以相机为参考,覆盖在相机渲染的区域上方,此时,Canvas中心位于相机视口(ViewPort)的中心 因此,Canvas的中心在屏幕的位置取决于Canvas的渲染模式和Canvas的大小设置 4:世界坐标: 相对于世界坐标系,以米为单位,使用Transform类的position属性获得 注意: UGUI坐标和世界坐标之间不能直接转换,需要通过屏幕坐标中转 author:{zhangpeng} time:2024-03-11 11:30:57 ]] local Screen = CS.UnityEngine.Screen local Vector2 = CS.UnityEngine.Vector2 local Vector3 = CS.UnityEngine.Vector3 local Rect = CS.UnityEngine.Rect local Bounds = CS.UnityEngine.Bounds local PositionConvert = {} -- 通用:将任意UI节点的世界坐标转换为UIRoot为根的世界坐标(适配UI节点不在Canvas根节点的情况) -- @param uiNode 需要转换的UI节点(GameObject或RectTransform) -- @return uirootWorldPos 以UIRoot为根的世界坐标 function PositionConvert.UIPosToWorldPos(uiNode) local uiRoot = CS.UnityEngine.GameObject.Find("UIRoot") if not uiRoot then printWarn("PositionConvert", "找不到UIRoot") return uiNode.transform.position end local uiRootRectTransform = uiRoot:GetComponent(typeof(CS.UnityEngine.RectTransform)) local nodeRectTransform = uiNode:GetComponent(typeof(CS.UnityEngine.RectTransform)) local uiCamera = UILayerUtil:getCamera() local worldPos = nil -- 先把uiNode的位置转换到UIRoot下的局部坐标 local localPos = uiRootRectTransform:InverseTransformPoint(uiNode.transform.position) -- 再转成UIRoot的世界坐标 local uiRootWorldPos = uiRootRectTransform:TransformPoint(localPos) -- 转到屏幕坐标 local screenPos = uiCamera:WorldToScreenPoint(uiRootWorldPos) -- 屏幕坐标转主摄像机世界坐标 local mainCamera = CS.UnityEngine.GameObject.Find("MainCamera") if mainCamera then worldPos = mainCamera:GetComponent("Camera"):ScreenToWorldPoint(screenPos) end return worldPos end -----------------------------------UI------------------------------------- --[[ @desc: UI--->屏幕坐标 time:2024-03-11 11:32:19 ]] function PositionConvert.UIToScreen(uipos) local uiCamera = UILayerUtil:getCamera() local screenPoint = CS.UnityEngine.RectTransformUtility.WorldToScreenPoint(uiCamera, uipos); return screenPoint end --[[ @desc: UI-->ViewPort坐标 author:{author} time:2024-03-11 11:52:53 @return: ]] function PositionConvert.UIToViewpoint() end ----------------------------------屏幕-------------------------------------- --[[ @desc: 屏幕 -> 世界 time:2024-03-11 12:11:55 ]] function PositionConvert.ScreenToWorld(screenPos) local screen_pos = CS.UnityEngine.Vector3(screenPos.x, screenPos.y, 0) local worldPostion = UILayerUtil:getCamera():ScreenToWorldPoint(screen_pos) return worldPostion end --[[ @desc: 屏幕 -> ViewPort time:2024-03-11 12:12:55 ]] function PositionConvert.ScreenToViewPort(screenPos) local screen_pos = CS.UnityEngine.Vector3(screenPos.x, screenPos.y, 0) local viewPortPostion = UILayerUtil:getCamera():ScreenToViewportPoint(screen_pos) return viewPortPostion end --[[ @desc: 屏幕 -> UI @canvas: 为UI所在的Canvas。 time:2024-03-11 11:35:29 ]] function PositionConvert.ScreenToUI(screenPos, canvas) -- local worldPos = PositionConvert.ScreenToWorld(screenPos) -- local uiPos = Vector2.zero -- RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform, screenPos, nil, uiPos); -- -- img的锚点需要在屏幕中间,此时就可正确的设置其坐标了。 -- img.rectTransform.anchoredPosition = uiPos; end --------------------------------------ViewPort---------------------------------- function PositionConvert.ViewportToWorld(screenPos) local screen_pos = CS.UnityEngine.Vector3(screenPos.x, screenPos.y, 0) local viewPortPostion = UILayerUtil:getCamera():ViewportToWorldPoint(screen_pos) return viewPortPostion end --------------------------------------世界---------------------------------- --[[ @desc: 世界 -> 屏幕 time:2024-03-11 12:00:42 ]] function PositionConvert.WorldToScreen(worldPosition) local cam = UILayerUtil:getCamera() local worldpos = CS.UnityEngine.Vector3(worldPosition.x, worldPosition.y, 0) local screenpos = cam:GetComponent("Camera"):WorldToScreenPoint(worldpos) return screenpos end --[[ @desc: 世界 -> ViewPort time:2024-03-11 12:07:19 ]] function PositionConvert.WorldToViewPort(worldPosition) local cam = UILayerUtil:getCamera() local worldpos = CS.UnityEngine.Vector3(worldPosition.x, worldPosition.y, 0) local viewport = cam:GetComponent("Camera"):WorldToViewportPoint(worldpos) return viewport end --[[ @desc: 世界坐标转换为RectTransform 对象的本地坐标 time:2024-03-11 14:43:42 ]] function PositionConvert.WorldToRectTrancLocalPos(gameObject, rectTransform) local localPos = rectTransform.transform:InverseTransformPoint(gameObject.transform.position) return localPos end --[[ @desc: 世界坐标转UGUI坐标(角色头上顶UI,取的角色头上坐标之后,转换为UGUI坐标,把UI设置到该位置) author:{author} time:2024-03-11 22:34:37 ]] function PositionConvert.WorldToUI(position, canvasRectTransform) local scene = SceneMgr:getCurScene() local mainCamera = scene.cams.scene local screenPoint = mainCamera.WorldToScreenPoint(position) local screenSize = Vector2(Screen.width, Screen.height); screenPoint = screenPoint - screenSize/2 -- 将屏幕坐标变换为以屏幕中心为原点 local anchorPos = screenPoint / screenSize * canvasRectTransform.sizeDelta -- 缩放得到UGUI坐标 return anchorPos; end --[[ @desc: author:{author} time:2024-03-11 15:08:26 ]] function PositionConvert.getSafeScreenRectInCoord(camera,coord,isIgnoreBottom) local safeArea = CS.UnityEngine.Screen.safeArea if isIgnoreBottom then safeArea.yMin = 0 end local bl = safeArea.min local tr = safeArea.max bl = CS.UnityEngine.Vector3(bl.x, bl.y, 0) tr = CS.UnityEngine.Vector3(tr.x, tr.y, 0) bl = camera:ScreenToWorldPoint(bl) tr = camera:ScreenToWorldPoint(tr) bl = coord.transform:InverseTransformPoint(bl) tr = coord.transform:InverseTransformPoint(tr) return CS.UnityEngine.Rect(bl.x, bl.y, tr.x - bl.x, tr.y - bl.y) end return PositionConvertCustomerFunCfgParseLlocal CustomerFunCfgData = require("data/config/customerFunCfg") local CustomerFunCfgParse = defClassStatic("CustomerFunCfgParse") function CustomerFunCfgParse:init() end function CustomerFunCfgParse:getData() return CustomerFunCfgData end function CustomerFunCfgParse:getCustomerFunCfg(customerFunCfgId) for _, v in pairs(CustomerFunCfgData) do if v.id == customerFunCfgId then return v end end printError("CustomerFunCfgParse", string.format("没有找到顾客功能配置[%s]", id)) return nil end CustomerFunCfgParse:init() BuildingConst--[[ 建筑常量 author:{zhangpeng} time:2025-05-15 19:39:26 ]] ---@class BuildingConst local BuildingConst = defClassStatic("BuildingConst") -- 建筑分类id(餐厅/烤吧) BuildingConst.buildingCategoryId = { -- 餐厅 restaurant = 1, -- 烤吧 barbecue = 2, -- 池塘 pond = 3, } -- 建筑分类名称 BuildingConst.buildingCategoryName = { [BuildingConst.buildingCategoryId.restaurant] = "餐厅", [BuildingConst.buildingCategoryId.barbecue] = "烤吧", [BuildingConst.buildingCategoryId.pond] = "池塘", } -- 建筑状态 BuildingConst.buildingState = { -- 未解锁 locked = 0, -- 解锁未购买 unlocked = 1, -- 已购买未使用 purchased = 2, -- 使用了 used = 3, } -- 建筑类型分类(无重复类型,包含餐厅和烤吧的所有建筑) BuildingConst.buildingCategory = { -- 餐桌 diningtable = 11, -- 长餐桌 diningtablelong = 12, -- 迎宾台 reception = 13, -- 酒桶 barrel = 14, -- 露天电影 outdoormovie = 15, -- 水果台 fruitstand = 16, -- 咖啡机 coffee = 17, -- 鲜花 flower = 18, -- 帐篷 tent = 19, -- 主灶台 mainstove = 20, -- 小灶台 smallstove = 21, -- 备菜区 preparea = 22, -- 原木碗柜 cupboard = 23, -- 冰桶 icebucket = 24, -- 货柜 cabinet = 25, -- 椅子 chair = 26, -- 烤炉 oven = 27, -- 烧烤架(音乐烤吧) barbecue = 28, -- 舞台(音乐烤吧) stage = 29, -- 自助酒水(音乐烤吧) selfservice = 30, -- 钢琴(音乐烤吧) instrument = 31, -- 音箱(音乐烤吧) speaker = 32, -- 扭蛋机(音乐烤吧) gashapon = 33, -- 摊位(池塘) booth = 34, } -- 主餐厅建筑 BuildingConst.mainDiningBuilding = { -- 帐篷 [1] = BuildingConst.buildingCategory.tent, -- 主灶台 [2] = BuildingConst.buildingCategory.mainstove, -- 小灶台 [3] = BuildingConst.buildingCategory.smallstove, -- 餐桌 [4] = BuildingConst.buildingCategory.diningtable, -- 长餐桌 [5] = BuildingConst.buildingCategory.diningtablelong, -- 迎宾台 [6] = BuildingConst.buildingCategory.reception, -- 酒桶 [7] = BuildingConst.buildingCategory.barrel, -- 露天电影 [8] = BuildingConst.buildingCategory.outdoormovie, -- 水果台 [9] = BuildingConst.buildingCategory.fruitstand, -- 咖啡机 [10] = BuildingConst.buildingCategory.coffee, -- 鲜花 [11] = BuildingConst.buildingCategory.flower, -- 烤炉 [12] = BuildingConst.buildingCategory.oven, -- 烧烤架 [13] = BuildingConst.buildingCategory.barbecue, -- 扭蛋机 [14] = BuildingConst.buildingCategory.gashapon, } -- 烤吧建筑 BuildingConst.barbecueBuilding = { -- 舞台 [1] = BuildingConst.buildingCategory.stage, -- 自助酒水 [2] = BuildingConst.buildingCategory.selfservice, -- 乐器 [3] = BuildingConst.buildingCategory.instrument, -- 音箱 [4] = BuildingConst.buildingCategory.speaker, -- 扭蛋机 [5] = BuildingConst.buildingCategory.gashapon, } -- 建筑分类类型配置 BuildingConst.buildingCategoryConfig = { [BuildingConst.buildingCategoryId.restaurant] = BuildingConst.mainDiningBuilding, [BuildingConst.buildingCategoryId.barbecue] = BuildingConst.barbecueBuilding, } -- 建筑功能类型 BuildingConst.buildingFunType = { -- 消费建筑 consume = 1, -- 生产建筑 produce = 2, -- 装饰建筑 decoration = 3, -- 其他建筑 other = 4, -- 闲逛类 wander = 5, } -- 建筑类型 BuildingConst.buildingType = { -- 1号餐桌 diningdesk1 = 104, --ok -- 1号炉灶 cookingbench1 = 102, --ok -- 2号餐桌 diningdesk2 = 106, --ok -- 迎宾台 reception = 105, --ok -- 2号炉灶 cookingbench2 = 103, --ok -- 水吧台 waterbar = 120, -- ok -- 备菜区 preparea = 110, -- 被动收益,每隔30秒加金币,直接扔地上,玩家需要点击收取到货币栏 -- 3号餐桌 diningdesk3 = 107, --ok -- 4号餐桌 diningdesk4 = 108, --ok -- 水池 waterpool = 123, -- 每分钟加金币,自动进收银台 -- 3号炉灶 cookingbench3 = 124, --ok -- 水果台 fruitstand = 119, --ok -- 地毯 carpet = 101, -- 被动收益,每隔N秒加金币,自动进收银台 -- 碗柜 cupboard = 111, -- 被动收益,每隔N秒加金币,自动进收银台 -- 露天电影 outdoormovie = 115, --ok -- 酒桶 barrel = 114, --ok -- 冰箱 refrigerator = 116, -- 被动收益,每隔N秒加金币,自动进收银台 -- 货柜 cabinet = 117, -- 被动收益,每隔N秒加金币,自动进收银台 -- 烤炉 oven = 122, -- 被动收益,每隔N秒加金币,直接扔地上,玩家需要点击收取到货币栏 -- 花架 flowerstand = 121, -- 被动收益,每隔N秒加金币,自动进收银台 -- 音乐烤吧 -- 1号烤架 bbqgrill1 = 151, -- 被动收益, -- 2号烤架 bbqgrill2 = 153, -- 被动收益 -- 3号烤架 bbqgrill3 = 154, -- 被动收益 -- 舞台 stage = 152, -- -- 自助酒水 selfservice = 155, -- 钢琴 instrument = 156, -- 音箱 speaker = 157, -- 扭蛋机 gashapon = 158, -- 收银台 cashier = 159, -- 池塘 -- 1号摊位 booth1 = 171, -- 2号摊位 booth2 = 172, -- 3号摊位 booth3 = 173, -- 4号摊位 booth4 = 174, -- 5号摊位 booth5 = 175, -- 6号摊位 booth6 = 176, } -- 不参与排序的建筑 BuildingConst.noSortingBuilding = { -- 地毯 [BuildingConst.buildingType.carpet] = true, -- 碗柜 [BuildingConst.buildingType.cupboard] = true, -- 冰箱 [BuildingConst.buildingType.refrigerator] = true, -- 货柜 [BuildingConst.buildingType.cabinet] = true, -- 烤炉 [BuildingConst.buildingType.oven] = true, -- 水池 [BuildingConst.buildingType.waterpool] = true, } -- 烤吧的闲逛建筑 BuildingConst.bbqWanderBuilding = { -- 舞台 BuildingConst.buildingType.stage, -- 1号烤架 BuildingConst.buildingType.bbqgrill1, -- 2号烤架 BuildingConst.buildingType.bbqgrill2, -- 3号烤架 BuildingConst.buildingType.bbqgrill3, } function BuildingConst:ctor() end return BuildingConst VendorDetailUI9 -- 池塘 摊主详情界面 local VendorDetailUI, super = defClass("VendorDetailUI", UILayer) -- component local TMProUGUI = CS.TMPro.TextMeshProUGUI -- 构造函数 function VendorDetailUI:ctor(vendorId, isNewCustomer) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/customerui/customeruireslink") self.vendorId = vendorId self.isNewCustomer = isNewCustomer or false end -- 当页面加载 function VendorDetailUI:onLoad() self.ui = GameObject.Instantiate(self.R.vendor_detail_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end -- 初始化UI function VendorDetailUI:initUI() self.closeBtn = self.ui:Seek("close_btn") self.lockNode = self.ui:Seek("lock") self.infoNode = self.ui:Seek("info") self.share_btn = self.ui:Seek("share_btn") self.story_btn = self.ui:Seek("story_btn") self.new_customer = self.ui:Seek("new_customer") self:initInfo() self:initIncome() util.ugui.addClickEvent(self.closeBtn, function() self:close() end) util.ugui.addClickEvent(self.share_btn, function() self:share() end) util.ugui.addClickEvent(self.story_btn, function() self:showStory() end) end -- 展示UI function VendorDetailUI:showUI() local info = VendorMgr:getVendorInfo(self.vendorId) local isVisited = info:isVisited() or self.isNewCustomer local storyId = info:getStoryId() self.lockNode:SetActive(not isVisited) self.infoNode:SetActive(isVisited) self.share_btn:SetActive(isVisited) self.new_customer:SetActive(self.isNewCustomer) self.story_btn:SetActive(storyId and storyId ~= -1) self:showInfo(info) self:showIncome(info) end function VendorDetailUI:initInfo() self.iconNode = self.infoNode:Seek("icon") self.spineNode = self.infoNode:Seek("spine_root") self.nameNode = self.infoNode:Seek("name") self.descNode = self.infoNode:Seek("desc") self.favoriteFish = self.ui:Seek("favorite_fish") self.visiteStar = self.ui:Seek("visite_star") end function VendorDetailUI:showInfo(info) local isVisited = info:isVisited() or self.isNewCustomer if isVisited then --GameObject.Instantiate(info:getSpineUI(), self.spineNode.transform) self.nameNode[TMProUGUI].text = info:getName() self.descNode[TMProUGUI].text = info:getDesc() end local fish = info:getFavoriteFish() self.favoriteFish:SetActive(#fish > 0) if #fish > 0 then self.favoriteFish[TMProUGUI].text = info:getFavoriteFishDesc(isVisited) end local star = info:getVisitNeedStar() self.visiteStar:SetActive(star > 0) if star > 0 then self.visiteStar[TMProUGUI].text = info:getVisitNeedStarDesc() end end function VendorDetailUI:initIncome() self.income = self.ui:Seek("income") end function VendorDetailUI:showIncome(info) --self.income[TMProUGUI].text = info:getIncomeDesc() end function VendorDetailUI:share() local info = VendorMgr:getVendorInfo(self.vendorId) if info:isShared() then -- todo else -- todo end end function VendorDetailUI:showStory() local info = VendorMgr:getVendorInfo(self.vendorId) --local storyId = info:getStoryId() --StoryDialogMgr:showPageStoryDialogUI() end return VendorDetailUIFishingGameMgr3-local FishingGameMgr = defClassStatic("FishingGameMgr") -- log local LOGTAG = "FishingGameMgr" -- view require("modules/ui/fishingui/FishingMainUI") require("modules/ui/fishingui/FishingReviveUI") require("modules/ui/fishingui/FishingFailedUI") require("modules/ui/fishingui/FishingSucceedUI") function FishingGameMgr:init() self:initUserData() self.resLink = ResLoader.loadResLink("modules/fishing/fishingreslink") end function FishingGameMgr:initUserData() -- 初始化用户数据 self.userData = UserDataMgr.fishingUserData end -- 进入游戏 function FishingGameMgr:gameEnter() printInfo(LOGTAG, "进入游戏") -- 加载地图 self.map = FishingGameMap.new() self.cameraCtl = self.map.cameraCtl -- 开场动画初始化 self.openingAnimation = OpeningAnimation.new(self.map.go) -- 胜利动画初始化 self.victoryAnimation = VictoryAnimation.new(self.map.go) -- 其他初始化逻辑 self.mainUI = FishingMainUI.new():show() -- 更新游戏人数 self.map:updateGameNum(0, 0) -- todo end -- 退出游戏 function FishingGameMgr:gameExit() printInfo(LOGTAG, "离开游戏") GameObject.Destroy(self.map.go) self.map = nil self.cameraCtl = nil UILayerUtil:closeUIByName("FishingMainUI") end -- 游戏开始 function FishingGameMgr:gameStart() printInfo(LOGTAG, "游戏开始") self.isPlaying = true self.isRevive = false self.isPaused = false self.energyMax = 0 self.time = FishingConst.GameTimeLimit -- 记录游戏次数 self.userData:addFishingGameCountToday(1) -- 重置道具使用次数 self:updateFishingPropUsesRemaining() -- 生成普通鱼 self.map:start() -- 初始化游戏角色 self.character = self.map.character -- 数值 self.mainUI.controlArea:setCharacter(self.character) -- 展示ui变动 self.mainUI:showUI() self.mainUI:start() -- 显示能量 self:updateEnergy() -- 开始计时 self:startTimer() end -- 游戏重新开始 function FishingGameMgr:gameRestart() self:gameStart() end -- 游戏更新 function FishingGameMgr:gameUpdate(deltaTime) if self.isPaused then return end self.map:timeUpdate(deltaTime) self.mainUI:timeUpdate(deltaTime) -- 每秒触发判断 if self.inSecondTime < 1 then self.inSecondTime = self.inSecondTime + deltaTime return end self.inSecondTime = 0 self.time = self.time - 1 self.mainUI:showCountDown(self.time) self.character:reduceEnergy() if self.time <= 0 then self:die() return end -- 每分触发判断 if self.inMinuteTime < 60 then self.inMinuteTime = self.inMinuteTime + 1 return end self.inMinuteTime = 0 end -- 游戏暂停 function FishingGameMgr:gamePause() self.isPaused = true self.mainUI:pause() end -- 游戏继续 function FishingGameMgr:gameContinue() self.isPaused = false self.mainUI:continue() end -- 游戏结束 function FishingGameMgr:gameOver() printInfo(LOGTAG, "游戏结束") self.isPlaying = false self:stopTimer() self.map:over() self.mainUI:over() self.character = nil end -- 游戏胜利 function FishingGameMgr:gameVictory() self.character:win() self:gameOver() -- 展示胜利界面 FishingSucceedUI.new():show():showMask() end -- 游戏失败 function FishingGameMgr:gameFailed() local grade = self:getCharacterGrade() self:gameOver() -- 展示失败界面 FishingFailedUI.new(grade, self.energyMax):show():showMask() end -- 返回开始界面 function FishingGameMgr:backStartPage() self:gameOver() self.mainUI:showUI() self.cameraCtl:setCameraSize(10) self.cameraCtl:setCameraPosition(Vector3.zero) self.cameraCtl:updateGradientSize() self.map.decorate:SetActive(false) self.map.anims:SetActive(true) -- 更新游戏人数 self.map:updateGameNum(0, 0) -- todo end -- 开始计时器 function FishingGameMgr:startTimer() self.inSecondTime = 0 -- 秒内计时 self.inMinuteTime = 0 -- 分内计时 -- 启动游戏计时器 self.tmr = TimerMgr:add( function(deltaTime) self:gameUpdate(deltaTime) end , 0.02, 0) end -- 停止计时器 function FishingGameMgr:stopTimer() -- 停止游戏计时器 if self.tmr then TimerMgr:rem(self.tmr) self.tmr = nil end end -- 信息 -- 获取角色当前能量值 function FishingGameMgr:getCharacterEnergy() return self.character:getEnergy() end -- 获取角色当前等级 function FishingGameMgr:getCharacterGrade() return self.character:getGrade() end -- 获取角色无敌状态 function FishingGameMgr:getCharacterInvincible() return self.character:getInvincible() end -- 获取角色信息 function FishingGameMgr:getCharacterInfo(id) return FishermanInfo.new(id) end -- 是否已达到目标等级 function FishingGameMgr:isReachTargetLevel() return self.character:getGrade() >= FishingConst.MaxGrade end -- 开始游戏时是否为免费模式 function FishingGameMgr:getPlayIsFree() local count = self.userData:getFishingGameCountToday() if count <= 3 then return true end return false end -- 开始游戏时是否为视频模式 function FishingGameMgr:getPlayIsVideo() local count = self.userData:getFishingGameCountToday() if count <= 3 then return false end return count % 2 == 0 end -- 开始游戏时是否为分享模式 function FishingGameMgr:getPlayIsShare() local count = self.userData:getFishingGameCountToday() if count <= 3 then return false end return count % 2 ~= 0 end -- 复活时是否为视频模式 function FishingGameMgr:getReviveIsVideo() return self.userData:getFishingReviveCountToday() % 2 == 0 end -- 复活时是否为分享模式 function FishingGameMgr:getReviveIsShare() return self.userData:getFishingReviveCountToday() % 2 ~= 0 end function FishingGameMgr:updateEnergy() local energy = self:getCharacterEnergy() if self.energyMax < energy then self.energyMax = energy end self.mainUI:showEnergy(energy, self.character.info:getMaxEnergy()) end -- 行为 -- 吃 function FishingGameMgr:eat(fish) self.character:eat(fish) end -- 升级 function FishingGameMgr:upgrade() if self:getCharacterGrade() + 1 >= FishingConst.MaxGrade then self.mainUI.controlArea:setTouchable(false) self.character:setDisplay(Vector3.right) self.character:clearDisplay() self.map:setCameraUpdate(false) self.map:stopFishes() self.map:clearHideFish() self.victoryAnimation:play(self.character.transform.localPosition) return end -- 升级角色 self.character:upgrade() -- 相机尺寸 self.map:upgrade(self:getCharacterGrade()) -- 更新能量 self:updateEnergy() end -- 受伤 function FishingGameMgr:hurt() if self.character:getInvincible() then return end if self.character:getShield() then -- 如果有护盾,护盾消失 self.character:setShield(false) return end self.character:hurt() -- 如果能量小于等于0,死亡 if self:getCharacterEnergy() > 0 then return end self:die() end -- 死亡 function FishingGameMgr:die(isEnergyDepletion) printInfo(LOGTAG, "角色死亡") self.character:die() -- 复活 if not self.isRevive then -- 游戏暂停 self:gamePause() -- 显示复活界面 FishingReviveUI.new(isEnergyDepletion):show():showMask():enableCloseWhenClickMask(function() self:gameFailed() end) return end -- 游戏结束 self:gameFailed() end -- 复活 function FishingGameMgr:revive() self.userData:addFishingReviveCountToday(1) self.isRevive = true self.character:revive() -- 更新能量 self:updateEnergy() -- 游戏继续 self:gameContinue() end -- 道具 -- 获取道具数量 function FishingGameMgr:getFishingPropCount(propType) return self.userData:getFishingPropCount(propType) end -- 更新 function FishingGameMgr:updateFishingPropUsesRemaining() self.propUseCount = {} -- 更新道具使用次数 for propType, _ in pairs(FishingConst.PropCountLimit) do self.propUseCount[propType] = FishingConst.PropCountLimit[propType] end end -- 获取本局剩余使用次数 function FishingGameMgr:getFishingPropUsesRemaining(propType) return self.propUseCount[propType] end -- 减少本局道具使用次数 function FishingGameMgr:reduceFishingPropUsesRemaining(propType) local curr = self:getFishingPropUsesRemaining(propType) - 1 if curr < 0 then curr = 0 end self.propUseCount[propType] = curr return curr end -- 增加道具数量 function FishingGameMgr:addFishingProp(propType, count) local currCount = self.userData:addFishingPropCount(propType, count, true) self.userData:addFishingGetPropCount(1, true) self.userData:save() -- 更新UI if self.mainUI then self.mainUI:updateFishingPropCount(propType, self:getFishingPropCount(propType)) end return currCount end -- 减少道具数量 function FishingGameMgr:reduceFishingProp(propType, count) local currCount = self.userData:reduceFishingPropCount(propType, count) -- 更新UI if self.mainUI then self.mainUI:updateFishingPropCount(propType, currCount) end return currCount end -- 使用道具 function FishingGameMgr:useFishingProp(propType) -- 根据道具ID执行相应的逻辑 if propType == FishingConst.PropType.Dizzy then -- 使用眩晕道具 self:useDizzyProp() return self:getFishingPropUsesRemaining(propType) elseif propType == FishingConst.PropType.Energy then -- 使用能量道具 self:useEnergyProp() return self:getFishingPropUsesRemaining(propType) elseif propType == FishingConst.PropType.Shield then -- 使用护盾道具 self:useShieldProp() return self:getFishingPropUsesRemaining(propType) else printError(LOGTAG, "无效的道具类型: " .. tostring(propType)) return -1 end end -- 使用眩晕道具 function FishingGameMgr:useDizzyProp() -- 减少道具数量 self:reduceFishingProp(FishingConst.PropType.Dizzy, 1) self:reduceFishingPropUsesRemaining(FishingConst.PropType.Dizzy) -- 执行眩晕逻辑 self.map:setDizzy(true, FishingConst.DizzyTime) end -- 使用能量道具 function FishingGameMgr:useEnergyProp() -- 减少道具数量 self:reduceFishingProp(FishingConst.PropType.Energy, 1) self:reduceFishingPropUsesRemaining(FishingConst.PropType.Energy) -- 执行能量增加逻辑 self.character:setDoubleEnergy(true, FishingConst.DoubleEnergyTime) self.mainUI:showPropTime(FishingConst.DoubleEnergyTime) end -- 使用护盾道具 function FishingGameMgr:useShieldProp() -- 减少道具数量 self:reduceFishingProp(FishingConst.PropType.Shield, 1) self:reduceFishingPropUsesRemaining(FishingConst.PropType.Shield) -- 执行护盾逻辑 self.character:setShield(true) end -- 是否是视频模式 function FishingGameMgr:getPropIsVideo() return self.userData:getFishingGetPropCount() % 2 == 0 end -- 是否为分享模式 function FishingGameMgr:getPropIsShare() return self.userData:getFishingGetPropCount() % 2 ~= 0 end return FishingGameMgr TaskBootMgrQ!--[[ 引导任务管理 author:{zhangpeng} time:2025-05-27 11:11:34 ]] local TaskBootMgr = defClassStatic("TaskBootMgr") function TaskBootMgr:init() printInfo("TaskBootMgr", "------ 引导任务管理 初始化 ------") self:initEvent() self:initUserData() self:setEvent() end -- 初始化用户数据 function TaskBootMgr:initUserData() self.userData = UserDataMgr.taskUserData.boot self.currentTaskId = self.userData:getCurrentBootTaskId() -- 当前引导任务ID end -- 初始化事件 function TaskBootMgr:initEvent() self.updateBootTask = SimpleEvent.new("UpdateBootTask") -- 更新引导任务事件 end -- 设置事件 function TaskBootMgr:setEvent() if self:isAllDone() then return end CuisineMgr.cuisineLearnedEvent:addEvent(function (eventId, cuisineId) self:onCuisineLearnedEvent(eventId, cuisineId) end) BuildingMgr.buildBuyEvent:addEvent(function (eventId, buildingId) self:onBuildingPurchasedEvent(eventId, buildingId) end) EmployeMgr.employeeHireEvent:addEvent(function (eventId, employeeId) self:onEmployeeHiredEvent(eventId, employeeId) end) CustomerMgr.customerSolicitation.solicitationClickEvent:addEvent(function (eventId, clickCount) self:onPromotionClickEvent(eventId, clickCount) end) end -- 获取引导任务配置 function TaskBootMgr:getConfig(taskId) return TaskCfgParse:getTaskCfg(taskId) end -- 前往按钮 function TaskBootMgr:gotoTargetPage(taskId) -- 这里可以添加跳转到指定引导任务的逻辑 printInfo("TaskBootMgr", "跳转到引导任务索引: %s", taskId) local data = self:getConfig(taskId) if data.jumpRef == TaskConst.JumpRef.Build then -- 购买设施 BuildingInfoUI.new(BuildingMgr:getConfig(data.refId)):show():showMask():enableCloseWhenClickMask() elseif data.jumpRef == TaskConst.JumpRef.Cuisine then -- 学习菜品 CuisineDetailUI.new(data.refId):show():showMask():enableCloseWhenClickMask() elseif data.jumpRef == TaskConst.JumpRef.Solicitation then -- 主页按钮提示 local restaurantScene = RestaurantMgr:getRestaurantScene() restaurantScene.mainViewUI:showSolicitButtonHint() elseif data.jumpRef == TaskConst.JumpRef.Employee then -- 雇佣员工 EmployeeDetailUI.new(data.refId):show():showMask():enableCloseWhenClickMask() end end -- 获取所有奖励 function TaskBootMgr:getRewardAll(startNode, taskId) local data = self:getConfig(taskId) for i = 1, 2 do local rewardType = data["rewardType_" .. i] local rewardValue = data["params_" .. i] self:getReward(startNode, rewardType, rewardValue) end end -- 获取奖励 function TaskBootMgr:getReward(startNode, rewardType, rewardValue) if (not rewardType) or rewardType == -1 then return end if rewardType == TaskConst.RewardType.Coin then -- 金币 CurrencyMgr:changeCoin(rewardValue, true) CoinDeskMgr:flyToCurrencyBar(startNode, function () CurrencyMgr:refreshCoin(true) end) elseif rewardType == TaskConst.RewardType.Diamond then -- 钻石 CurrencyMgr:changeDiamond(rewardValue) elseif rewardType == TaskConst.RewardType.MusicalNotes then -- 音符 CurrencyMgr:changeMusicalNotes(rewardValue) elseif rewardType == TaskConst.RewardType.FishingBait then -- 鱼饵 CurrencyMgr:changeFishingBait(rewardValue) elseif rewardType == TaskConst.RewardType.Progress then -- 进度 --UserDataMgr:setProgress(UserDataMgr.progress + rewardValue) elseif rewardType == TaskConst.RewardType.Star then -- 星星 CurrencyMgr:changeStar(rewardValue) elseif rewardType == TaskConst.RewardType.AdCoupons then -- 广告券 CurrencyMgr:changeAdCoupons(rewardValue) else printError("TaskBootMgr:getReward - 未知奖励类型: %s", rewardType) end end --- 获取当前任务ID function TaskBootMgr:getCurrentTaskId() return self.currentTaskId end --- 获取下一个任务ID function TaskBootMgr:getNextTaskId() --local data = self:getConfig(self.currentTaskId) --if not data then -- return -1 --end --return data.nextTaskId local data = self:getConfig(self.currentTaskId + 1) if not data then return -1 end return data.id end -- 触发条件 --- 在主页展示当前任务 function TaskBootMgr:showCurrentTask() local ui = UILayerUtil:findUIByName("MainViewUI") if ui then ui:updateBootTask(self.currentTaskId) end end function TaskBootMgr:nextTask() self.currentTaskId = self:getNextTaskId() -- 存档 self.userData:setCurrentBootTaskId(self.currentTaskId) self.userData:resetPromotionClickCount() -- 更新任务ui self:showCurrentTask() end -- 当菜品被学习时触发 function TaskBootMgr:onCuisineLearnedEvent(eventId, cuisineId) if (not self.currentTaskId) or (self.currentTaskId == -1) then CuisineMgr.cuisineLearnedEvent:removeEventById(eventId) return end if self:checkGetTaskClear(cuisineId) then self:showCurrentTask() end end -- 当建筑被购买时触发 function TaskBootMgr:onBuildingPurchasedEvent(eventId, buildingId) if (not self.currentTaskId) or (self.currentTaskId == -1) then BuildingMgr.buildBuyEvent:removeEventById(eventId) return end if self:checkGetTaskClear(buildingId) then self:showCurrentTask() end end -- 当员工被雇佣时触发 function TaskBootMgr:onEmployeeHiredEvent(eventId, employeeId) if (not self.currentTaskId) or (self.currentTaskId == -1) then EmployeMgr.employeeHireEvent:removeEventById(eventId) return end if self:checkGetTaskClear(employeeId) then self:showCurrentTask() end end -- 当招揽点击次数增加时触发 function TaskBootMgr:onPromotionClickEvent(eventId, clickCount) if (not self.currentTaskId) or (self.currentTaskId == -1) then CustomerMgr.customerSolicitation.solicitationClickEvent:removeEventById(eventId) return end local count = self.userData:addPromotionClickCount(clickCount) if count > self:getTaskTotalValue(self.currentTaskId) then return end self:showCurrentTask() end -- 检查任务是否完成 function TaskBootMgr:checkTaskClear(taskId) return self:getTaskCurrentValue(taskId) >= self:getTaskTotalValue(taskId) end -- 获取任务的当前进度 function TaskBootMgr:getTaskCurrentValue(taskId) local config = self:getConfig(taskId) local type = config.completeCond if type == TaskCfgParse.TaskReachType.unlock_cuisine then local info = CuisineMgr:getCuisineInfo(config.refId) if info:isPurchased() then return 1 end elseif type == TaskCfgParse.TaskReachType.unlock_building then local info = BuildingMgr:getBuildingInfo(config.refId) if info:isPurchased() then return 1 end elseif type == TaskCfgParse.TaskReachType.unlock_employee_count then local info = EmployeMgr:getEmployeeInfo(config.refId) if info:isPurchased() then return 1 end elseif type == TaskCfgParse.TaskReachType.promote_count then local count = self.userData:getPromotionClickCount() return count end return 0 end -- 获取任务的总值 function TaskBootMgr:getTaskTotalValue(taskId) local config = self:getConfig(taskId) return config.count end -- 检查获取物品类型的任务是否完成 function TaskBootMgr:checkGetTaskClear(targetId) local id = self:getConfig(self.currentTaskId).refId if id == targetId then return true end return false end -- 检查点击类型的任务是否完成 function TaskBootMgr:checkClickTaskClear(currCount) local count = self:getTaskTotalValue(self.currentTaskId) if currCount > count then return true end return false end -- 检查任务是否全部完成 function TaskBootMgr:isAllDone() if self.currentTaskId == -1 then return true end return false end return TaskBootMgr FacebookLoginUtil --[[ fb登录管理 author:{zhangpeng} time:2023-12-26 10:47:05 ]] local FacebookLoginUtil, super = defClassStatic("FacebookLoginUtil") local LOG_TAG = "FacebookLoginUtil" local IOC_FB_UTIL_CLASS_NAME = "FBLoginUtil" local JavaFacebookClass = "com/fy/xgame/tilelink/util/FacebookUtil" function FacebookLoginUtil:init() FacebookLoginUtil:registLuaCallBack() end function FacebookLoginUtil:login() if Device.isIOS() then local param = {} luaoc.callStaticMethod(IOC_FB_UTIL_CLASS_NAME, "loginWithFacebook") elseif Device.isAndroid() then local function cb(code, msg) if code == 0 then printInfo(LOG_TAG, msg) -- 根据不同发起原因,发送不同消息 if User:getLoginOrigin() == LoginConst.Origin.login then printInfo(LOG_TAG,"fb登录成功回调") Msg.send(Msg.USER_LOGIN_FB_SUC, json.decode(msg)) elseif User:getLoginOrigin() == LoginConst.Origin.bind then printInfo(LOG_TAG,"fb绑定登录成功回调") Msg.send(Msg.USER_LOGIN_FB_BIND_SUC, json.decode(msg)) end elseif code == 1 then Msg.send(Msg.USER_LOGIN_FB_FAILED) elseif code == 2 then Msg.send(Msg.USER_LOGIN_FB_CANCLE) end end luaj.callStaticMethod(JavaFacebookClass, "loginWithFacebook", {cb}) end end function FacebookLoginUtil:registLuaCallBack() if Device.isIOS() then local param = { loginErrorCb = function() printInfo(LOG_TAG,"FB 登录失败回调") Msg.send(Msg.USER_LOGIN_FB_FAILED) end, loginCancleCb = function () printInfo(LOG_TAG,"FB 取消登录回调") Msg.send(Msg.USER_LOGIN_FB_CANCLE) end, loginSucCb = function (name, email, fid) local data = {id = fid, name = name, email = email} table.print_r(data,"Facebook ID 登录成功回调数据") Msg.send(Msg.USER_LOGIN_FB_SUC,data) end } luaoc.callStaticMethod(IOC_FB_UTIL_CLASS_NAME, "registLuaCallback", param) end end function FacebookLoginUtil:shareLink(url) if Device.isIOS() then local param = {} luaoc.callStaticMethod(IOC_FB_UTIL_CLASS_NAME, "loginWithFacebook") elseif Device.isAndroid() then local function cb(code, msg) if code == 0 then printInfo(LOG_TAG,"分享成功回调") elseif code == 1 then -- Msg.send(Msg.USER_LOGIN_FB_FAILED) elseif code == 2 then -- Msg.send(Msg.USER_LOGIN_FB_CANCLE) end end luaj.callStaticMethod(JavaFacebookClass, "shareLink", {url, cb}) end end FacebookLoginUtil:init() GachaConst--[[ 扭蛋机常量 author:{zhangpeng} time:2025-07-09 11:13:26 ]] local GachaConst = defClassStatic("GachaConst") GachaConst.CostType = CurrencyConst.CurrencyType.MusicalNotes -- 扭蛋机抽法 GachaConst.DrawType = { -- 单抽 SingleDraw = 1, -- 十连抽 TenDraws = 10, } -- 扭蛋机抽法消费 GachaConst.DrawCost = { [GachaConst.DrawType.SingleDraw] = 300, -- 单抽消耗300个音符 [GachaConst.DrawType.TenDraws] = 2800, -- 十连抽消耗2800个音符 } GachaConst.State = { idle = 1, used = 2, } GachaConst.GachaType = { single = 1, ten = 2, } -- 奖励物品类型 GachaConst.RewardType = { star = 1, -- 星星 music = 2, -- 音符 doll = 3, -- 玩偶 } return GachaConstRestaurantConst--[[ 餐厅常量 author:{zhangpeng} time:2025-05-16 13:35:31 ]] local RestaurantConst = defClassStatic("RestaurantConst") function RestaurantConst:init() end RestaurantConst:init() main4-- static cfg require("data/config/config_main") luaoc local luaoc = {} local callStaticMethod = LuaObjcBridge.callStaticMethod local queue_action = require("main/ext/queue_action") function luaoc.callStaticMethod(className, methodName, args) if not Device.isIOS() then return end queue_action.table_process(args) local ok, ret, errMsg = callStaticMethod(className, methodName, args) if not ok then local msg = string.format('luaoc.callStaticMethod("%s", "%s", "%s") - error: [%s] %s', className, methodName, tostring(args), tostring(ret), tostring(errMsg)) if ret == -1 then error(msg .. "INVALID PARAMETERS") elseif ret == -2 then error(msg .. "CLASS NOT FOUND") elseif ret == -3 then error(msg .. "METHOD NOT FOUND") elseif ret == -4 then error(msg .. "EXCEPTION OCCURRED") elseif ret == -5 then error(msg .. "INVALID METHOD SIGNATURE") else error(msg .. "UNKNOWN") end end return ok, ret end return luaoc GachaDesk--[[ 扭蛋机 author:{zhangpeng} time:2025-07-09 11:11:23 ]] require("modules/ui/gacha/GachaUI") local GachaDesk,super = defClass("GachaDesk",BuildingBase) local LOGTAG = "GachaDesk" local isClick = false function GachaDesk:ctor(args) super.ctor(self,args) self.deskId = BuildingConst.buildingType.gashapon self.resId = "building_"..self.deskId self:initDisplay() end function GachaDesk:initDisplay() self.buildingNode = GameObject.Instantiate(self.scene.R[self.resId]) self.sortBaseNode = self.buildingNode:Seek("base") self.dong = self.buildingNode:Seek("dong") self:setBuildingParent(self.buildingParent) self:setBuildingNodeName(self.resId) self:addClickEvent() -- 展示提示 self.dong:SetActive((not isClick) and (GachaDeskMgr.userData:getFreeTodayCount() > 0)) -- 初始状态不显示动画 end -- 点击摊位 function GachaDesk:addClickEvent() local restaurantScene = RestaurantMgr:getRestaurantScene() local touchCom = restaurantScene.touchCom local touchNode = self.buildingNode:Seek("building_anim") touchCom:addListener(touchNode, TouchCom.LISTENER_TYPE.CLICK, function(p) self:onClickGachaDesk() end ) end function GachaDesk:onClickGachaDesk() printInfo(LOGTAG, "点击扭蛋机") GachaUI.new():show() isClick = true self.dong:SetActive(false) -- 点击后隐藏动画 end return GachaDeskmain:-- common require("modules/common/main") -- common ui require("modules/ui/commonui/main") -- modules require("modules/guide/main") -- 引导 require("modules/ui/storydialogui/main") -- 剧情 require("modules/currency/main") -- 货币 require("modules/task/main") -- 任务 require("modules/role/main") -- 角色 require("modules/building/main") -- 建筑 require("modules/cuisine/main") -- 菜品 require("modules/maps/main") -- 地图 require("modules/restaurant/main") -- 餐厅 require("modules/musicbbq/main") -- 音乐烧烤 require("modules/help/main") -- 帮助 require("modules/unlock/main") -- 解锁 require("modules/buy/main") -- 购买 require("modules/upgrade/main") -- 升级 require("modules/bonus/main") -- 加成 require("modules/setting/main") -- 设置 require("modules/fishing/main") -- 捕鱼 require("modules/doll/main") -- 玩偶 -- other require("modules/constant/SceneMsgEnum")UnlockHintComponent-- 解锁需求提示组件 local UnlockHintComponent = defClass("UnlockHintComponent") -- 构造函数 function UnlockHintComponent:ctor(node, hint) self.lock_obj = node self.hint = hint or "" self:init() end -- 初始化组件 function UnlockHintComponent:init() local isUnlocked = false self.lock_obj:SetActive(not isUnlocked) if not isUnlocked then util.ugui.addButtonClickEvent(self.lock_obj, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:click() end) end end -- 解锁 function UnlockHintComponent:unlock() self.lock_obj:SetActive(false) end -- 点击事件 function UnlockHintComponent:click() PopUpUI.new(self.hint):show():showMask():enableCloseWhenClickMask() -- test --CurrencyMgr:changeStar(30) end return UnlockHintComponentCuisineUpgradeCfgParselocal CuisineUpgradeCfg = require("data/config/cuisineUpgradeCfg") ---@class CuisineUpgradeCfgParse local CuisineUpgradeCfgParse = defClassStatic("CuisineUpgradeCfgParse") -- init function CuisineUpgradeCfgParse:init() end -- 根据升级配置id取一个升级配置的数据 function CuisineUpgradeCfgParse:getCuisineUpgradeCfg(cuisineUpgradeId) for _, v in pairs(CuisineUpgradeCfg) do if v.id == cuisineUpgradeId then return v end end end CuisineUpgradeCfgParse:init()Scenej# ---@class Scene:LuaClass local Scene = defClass("Scene") local LOGTAG = "Scene" local LogicNodeName = "LogicNode" function Scene:ctor(sceneInfo) printInfo(LOGTAG, "ctor, name:%s", self.__cls_name) self.sceneInfo = sceneInfo self:onmsg(Msg.PAUSE, function (msgid, ...) self:onAppPause() end) self:onmsg(Msg.RESUME, function (msgid, ...) self:onAppResume() end) self:__initCom() end function Scene:startLoad() ---@type ReslinkLoad local sceneLoad = ReslinkLoad.new() local info = self:info() sceneLoad:addResLink(info.asset) self._sceneLoad = sceneLoad local sceneInfo = self.sceneInfo if sceneInfo:getEnterTransCls() then self:loadSceneAsync(sceneInfo:getEnterTransCls()) else self:loadSceneSync() end end function Scene:getSceneArgs() return self.sceneInfo:getArgs() end ---comment ---@return SceneInfoValue function Scene:info() printError(LOGTAG,"info未重写") return { asset = nil } end function Scene:getSceneObj() if not self._scene then return end if CS.LuaHelper.IsNull(self._scene) then return end return self._scene end -- 子类自己的加载 function Scene:getCustomSceneLoad() return nil end function Scene:_doLoadScene(finishCb, progressCb, isAsync) self:prepareLoad() TimerMgr:runAtEndOfFrame( function() self:beforeLoad() local customSceneLoad = self:getCustomSceneLoad() if customSceneLoad ~= nil then -- 如果有自定义的load, 进度和完成以自定义的load为准 self._sceneLoad:load(customSceneLoad, nil, isAsync) else self._sceneLoad:load(finishCb, progressCb, isAsync) end end ) end function Scene:loadSceneSync() self:_doLoadScene(function (sceneList) self:onLoadComplete(sceneList[1]) end, nil, false) end function Scene:loadSceneAsync(transCls) local transUI = transCls.new() self._transUI = transUI self:doTransIn( transUI, function() self:_doLoadScene(function (sceneList) self:onLoadComplete(sceneList[1]) end, function (progress) self:onLoadProgress(progress) end, true) end ) end ---场景加载的进度 function Scene:onLoadProgress(progress) if not self._transUI then return end self._transUI:setProgress(progress) end ---场景加载完成 ---@param scene CS.UnityEngine.SceneManagement.Scene function Scene:onLoadComplete(scene) if not self._transUI then self:afterLoad(scene) self:afterTrans() return end self:doTransOut( self._transUI, function() self:afterLoad(scene) self:afterTrans() end ) end function Scene:doTransIn(transUI, callback) transUI:transIn( function() if self.__exited then return end callback() end ):show(true) end function Scene:doTransOut(transUI, callback) transUI:transOut( function() if self.__exited then return end callback() end ) end --#region 加载时的生命周期函数 function Scene:prepareLoad() printInfo(LOGTAG, "prepareLoad, name:%s", self.__cls_name) -- 暂停上一个场景的 self:onAppPause() local go = self:getSceneObj() if go then local rootGo = go:GetGameRoot() util.pause.stopAll(rootGo) end Msg.send(Msg.SCENE_PREPARE_LOAD, self) end function Scene:beforeLoad() printInfo(LOGTAG, "beforeLoad, name:%s", self.__cls_name) Msg.send(Msg.SCENE_BEFORE_LOAD, self) end ---@param scene CS.UnityEngine.SceneManagement.Scene function Scene:afterLoad(scene) printInfo(LOGTAG, "afterLoad, name:%s", self.__cls_name) self._scene = scene Msg.send(Msg.SCENE_AFTER_LOAD_SCENE, self) self:beforeOnLoad(scene) self:onLoad() util.ugui.enableAllTouches() ResLoader.gcAfterLoadScene() self:afterOnLoad() self:initDebug() end --[[ ---@param scene CS.UnityEngine.SceneManagement.Scene function Scene:afterLoadDep(scene) printInfo(LOGTAG, "afterLoadDep, name:%s", self.__cls_name) self:beforeOnLoad(scene) self:onLoad() util.ugui.enableAllTouches() ResLoader.gcAfterLoadScene() self:afterOnLoad() end ]] function Scene:afterTrans() printInfo(LOGTAG, "afterTrans, name:%s", self.__cls_name) self:onSceneTransitionOver() end --#endregion function Scene:beforeOnLoad(scene) --#region 初始化 rootNode 和 cams local rootGameObjects = scene:GetRootGameObjects() ---@type CS.UnityEngine.GameObject, CS.UnityEngine.Camera local rootNode, camera = rootGameObjects[0], nil for _, go in cs_ipairs(rootGameObjects) do if go.name == "GameRoot" then rootNode = go elseif go.name == "MainCamera" then camera = go:GetComponent("Camera") end end if camera == nil then local camera = UnityEngine.GameObject.FindFirstObjectByType(typeof(UnityEngine.Camera)) end ---@type CS.UnityEngine.GameObject self.rootNode = rootNode self.cams = { scene = camera } self:initLogicNode() local data = CS.UnityEngine.Rendering.Universal.CameraExtensions.GetUniversalAdditionalCameraData(self.cams.scene) data.cameraStack:Add(UILayerUtil:getCamera()) self:defineComs() for _, com in pairs(self.__coms__) do com:onLoad() end end function Scene:afterOnLoad() for _, com in pairs(self.__coms__) do com:afterOnLoad() end end function Scene:initLogicNode() local go = CS.UnityEngine.GameObject.Find(LogicNodeName) if not go then go = CS.UnityEngine.GameObject(LogicNodeName) CS.UnityEngine.GameObject.DontDestroyOnLoad(go) end self.logicNode = go end --#region 子类重写 function Scene:onLoad() printInfo(LOGTAG, "onLoad, name:%s", self.__cls_name) Msg.send(Msg.SCENE_ON_LOAD, self.__cls_name) end function Scene:onSceneTransitionOver() for _, com in pairs(self.__coms__) do com:onSceneTransitionOver() end end function Scene:onExit() self.sceneInfo = nil Scene.super.onExit(self) printInfo(LOGTAG, "onExit, name:%s", self.__cls_name) Msg.send(Msg.SCENE_EXIT, self.__cls_name) end --#endregion --#region 组件相关 --初始化组件 -- 防止self.coms被重写 -- fake的作用是为了能够直接访问组件,而不用判断 hasCom function Scene:__initCom() local emptyFunc = function() end local fake = {} setmetatable(fake, { __index = function(t, k) return emptyFunc end }) self.coms = {} self.__coms__ = {} local __coms__ = self.__coms__ setmetatable(self.coms, { __index = function(t, k) if k == "__cls_inst" then return false end local com = __coms__[k] if com then return com else printWarn(LOGTAG, "index, 组件不存在:%s", k) return fake end end, __newindex = function(table, key, value) __coms__[key] = value end }) end -- 废弃 -- function Scene:hasCom(comName) -- return self.__coms__[comName] -- end --声明组件 function Scene:defineComs() end function Scene:addCom(comCls, ...) local com = comCls.new(self, ...) self.coms[com.__cls_name] = com end --#endregion --#region pause resume function Scene:onAppPause() printVerbose(LOGTAG, "onAppPause %s", tostring(self)) local go = self:getSceneObj() if go == nil then return end local rootGo = go:GetGameRoot() if rootGo.__pausedList__ then return end local pauseList = {audio = {}, timeline = {}, videoplayer = {}, action = {} } pauseList.audio = util.pause.pauseAudio(rootGo) pauseList.timeline = util.pause.pauseTimeline(rootGo) pauseList.videoplayer = util.pause.pauseVideoPlayer(rootGo) pauseList.action = util.pause.pauseAction(rootGo) rootGo.__pausedList__ = pauseList end function Scene:onAppResume() local go = self:getSceneObj() local rootGo = go and go:GetGameRoot() or {} if not rootGo.__pausedList__ then return end local pauseList = rootGo.__pausedList__ rootGo.__pausedList__ = nil util.pause.resumeAudio(pauseList.audio) util.pause.resumeTimeline(pauseList.timeline) util.pause.resumeVideoPlayer(pauseList.videoplayer) util.pause.resumeAction(pauseList.action) end function Scene:getName() return self.__cls_name end function Scene:initDebug() if Application.isEditor and self.rootNode then self.rootNode:Step(function() if Input.GetKeyDown("space") then SceneMgr:reEnter() end end) end end --#endregionboot_debug_mainlocal LOG_TAG = "debug_main" -- luaide 调试 local debugXpCall local breakSocketHandle local platform = CS.UnityEngine.Application.platform local can_debug = (platform == CS.UnityEngine.RuntimePlatform.WindowsEditor or platform == CS.UnityEngine.RuntimePlatform.WindowsPlayer or false) local DEBUG_LUA = true--false and can_debug if DEBUG_LUA then breakSocketHandle,debugXpCall = require("Assets.LuaScripts.common.debug.luaidedebug.LuaDebug")("localhost",7003) print(LOG_TAG,"init lua debug :",platform) endTaskOrderCustomer--- ---@class TaskOrderCustomer: CustomerSpecial local TaskOrderCustomer, super = defClass("TaskOrderCustomer", CustomerSpecial) -- require("modules/ui/taskui/order/TaskOrderDetailUI") --- 构造函数 function TaskOrderCustomer:ctor(resLink, customerId) super.ctor(resLink, customerId) self.customerId = customerId end --- function TaskOrderCustomer:start() local pos = self:getCurPosition() pos.y = pos.y + 1 self:walkToPos(pos, 100, function() self:callback() end ) end function TaskOrderCustomer:callback() self:createDongBubble() self:showDongBubble() end --- 点击 function TaskOrderCustomer:click() self:hideDongBubble() --TaskOrderMgr:triggerTargetOrder(self.customerId) local pos = self:getCurPosition() pos.y = pos.y - 1 self:walkToPos(pos, 100, function() end ) end -- 创建惊叹号 function TaskOrderCustomer:createDongBubble() end -- 显示惊叹号 function TaskOrderCustomer:showDongBubble() self.dong:SetActive(true) end -- 隐藏惊叹号 function TaskOrderCustomer:hideDongBubble() self.dong:SetActive(false) end return TaskOrderCustomer employeCfg--[[ from file:员工表.xlsx --]] local employeCfg = { [1] = { id = 300001, name = "店长", tabType = 1, scene = 1, desc = "动物营地的店长,热爱露营与旅行", movespeed = "100", res = "yg_chushi_01", price = 1000, storyNum = 0, storyid_1 = -1, storyid_2 = -1, storyid_3 = -1, unlockCondId = -1, levelupIds = {310101,310102,310103,310104,310105}, artBustRes = "employe_boy", }, [2] = { id = 300002, name = "店长", tabType = 1, scene = 1, desc = "动物营地的店长,热爱露营与旅行", movespeed = "100", res = "yg_chushi_01", price = 1000, storyNum = 0, storyid_1 = -1, storyid_2 = -1, storyid_3 = -1, unlockCondId = -1, levelupIds = {310201,310202,310203,310204,310205}, artBustRes = "employe_gril", }, [3] = { id = 300003, name = "布丁", tabType = 2, scene = 1, desc = "活力满满,但也经常因为粗心大意摔坏盘子", movespeed = "100", res = "yg_chushi_01", price = 20000, storyNum = 3, storyid_1 = 630301, storyid_2 = -1, storyid_3 = -1, unlockCondId = -1, levelupIds = {310301,310302,310303,310304,310305,310306,310307,310308,310309,310310,310311,310312,310313,310314,310315}, artBustRes = "employe_fuwuyuan", }, [4] = { id = 300004, name = "贝尔", tabType = 4, scene = 1, desc = "淡淡的伤疤透露着忧郁,待在厨房不爱出去", movespeed = "100", res = "yg_chushi_01", price = 60000, storyNum = 3, storyid_1 = 630401, storyid_2 = -1, storyid_3 = -1, unlockCondId = -1, levelupIds = {310501,310502,310503,310504,310505,310506,310507,310508,310509,310510,310511,310512,310513,310514,310515}, artBustRes = "employe_chushi", }, [5] = { id = 300005, name = "闪电", tabType = 9, scene = 1, desc = "这只树懒堪称树懒界的“闪电侠”,毕竟青苔还没在它身上找到立足之地", movespeed = "100", res = "yg_chushi_01", price = 20000, storyNum = 3, storyid_1 = 630501, storyid_2 = -1, storyid_3 = -1, unlockCondId = -1, levelupIds = {310301,310302,310303,310304,310305,310306,310307,310308,310309,310310,310311,310312,310313,310314,310315}, artBustRes = "employe_shulan", }, [6] = { id = 300006, name = "旺财", tabType = 3, scene = 1, desc = "埋头苦干,干饭积极,发言佛系", movespeed = "100", res = "yg_chushi_01", price = 60000, storyNum = 3, storyid_1 = 630601, storyid_2 = -1, storyid_3 = -1, unlockCondId = -1, levelupIds = {310501,310502,310503,310504,310505,310506,310507,310508,310509,310510,310511,310512,310513,310514,310515}, artBustRes = "employe_baoan", }, [7] = { id = 300007, name = "卡皮巴拉", tabType = 6, scene = 3, desc = "圆滚滚身材驮着慢灵魂,主打一个随波逐流的日常", movespeed = "100", res = "yg_chushi_01", price = 60000, storyNum = 3, storyid_1 = 630701, storyid_2 = -1, storyid_3 = -1, unlockCondId = 300101, levelupIds = {310501,310502,310503,310504,310505,310506,310507,310508,310509,310510,310511,310512,310513,310514,310515}, artBustRes = "employe_yufu", }, [8] = { id = 300008, name = "铃铃", tabType = 7, scene = 2, desc = "当她打开歌喉,那里就是只属于她的舞台", movespeed = "100", res = "yg_chushi_01", price = 60000, storyNum = 3, storyid_1 = 630801, storyid_2 = -1, storyid_3 = -1, unlockCondId = 300102, levelupIds = {310501,310502,310503,310504,310505,310506,310507,310508,310509,310510,310511,310512,310513,310514,310515}, artBustRes = "employe_geshou", }, [9] = { id = 300009, name = "奇奇", tabType = 8, scene = 2, desc = "阳光开朗,喜欢发觉生活中的乐趣", movespeed = "100", res = "yg_chushi_01", price = 60000, storyNum = 3, storyid_1 = 630901, storyid_2 = -1, storyid_3 = -1, unlockCondId = 300102, levelupIds = {310501,310502,310503,310504,310505,310506,310507,310508,310509,310510,310511,310512,310513,310514,310515}, artBustRes = "employe_shaokao", }, } return employeCfg customeruireslink<return { --BASIC --ASSET video_promotion_ui = {"Assets/AssetsPackage/Res/modules/ui/roles/customer/prefab/video_promotion_ui.prefab", 0, 0}, ad_customer_ui = {"Assets/AssetsPackage/Res/modules/ui/roles/customer/prefab/customer_ad_ui.prefab", 0, 0}, customer_list_ui = {"Assets/AssetsPackage/Res/modules/ui/roles/customer/prefab/customer_list_ui.prefab", 0, 0}, customer_detail_ui = {"Assets/AssetsPackage/Res/modules/ui/roles/customer/prefab/customer_detail_ui.prefab", 0, 0}, customer_cell = {"Assets/AssetsPackage/Res/modules/ui/roles/customer/prefab/customer_cell.prefab", 0, 0}, customer_plot_cell = {"Assets/AssetsPackage/Res/modules/ui/roles/customer/prefab/customer_plot_cell.prefab", 0, 0}, customer_tag_cell = {"Assets/AssetsPackage/Res/modules/ui/roles/customer/prefab/customer_tag_cell.prefab", 0, 0}, special_customer_detail_ui = {"Assets/AssetsPackage/Res/modules/ui/roles/customer/prefab/special_customer_detail_ui.prefab", 0, 0}, special_customer_cell = {"Assets/AssetsPackage/Res/modules/ui/roles/customer/prefab/special_customer_cell.prefab", 0, 0}, vendor_cell = {"Assets/AssetsPackage/Res/modules/ui/roles/vendor/prefabs/vendor_cell.prefab", 0, 0}, vendor_detail_ui = {"Assets/AssetsPackage/Res/modules/ui/roles/vendor/prefabs/vendor_detail_ui.prefab", 0, 0}, } TouchCustomListener---@class TouchCustomListener:TouchListener local TouchCustomListener = defClass("TouchCustomListener", TouchListener) function TouchCustomListener:onInit() end ---@param point CS.UnityEngine.Vector3 function TouchCustomListener:onBegan(point) if self._beganCb then self._actived = self._beganCb(point) end end ---@param point CS.UnityEngine.Vector3 function TouchCustomListener:onMoved(point) if self._movedCb then self._movedCb(point) end end ---@param point CS.UnityEngine.Vector3 function TouchCustomListener:onEnd(point) if self._endCb then self._endCb(point) end self._actived = false end buildinginforeslinkreturn { --BASIC --ASSET building_info = {"Assets/AssetsPackage/Res/modules/ui/buildbuildinglist/prefabs/building_info.prefab", 0, 0}, } Ext@ local UnityEngine = CS.UnityEngine local GameObject = UnityEngine.GameObject local Quaternion = UnityEngine.Quaternion local LuaHelperReadFile = CS.LuaHelper.ReadFile local LuaHelperDeleteFile = CS.LuaHelper.DeleteFile local type = type local rawget = rawget local rawset = rawset --Ext UnityClass Quaternion local QuaternionCls = xlua.metatable_operation(typeof(Quaternion)) local QuaternionClsMul = HackFunc(QuaternionCls, "__mul") QuaternionCls.__mul = function(q, v) if type(v) == "number" then return Quaternion(q.x*v, q.y*v, q.z*v, q.w*v) else return QuaternionClsMul(q, v) end end QuaternionCls.__add = function(q, v) return Quaternion(q.x+v.x, q.y+v.y, q.z+v.z, q.w+v.w) end QuaternionCls.__sub = function(q, v) return Quaternion(q.x-v.x, q.y-v.y, q.z-v.z, q.w-v.w) end local ButtonClickedEventCls = xlua.metatable_operation(typeof(UnityEngine.UI.Button.ButtonClickedEvent)) rawset(ButtonClickedEventCls, "AddListener", ButtonClickedEventCls.__index(nil, "AddListener")) local ButtonClickedEventClsIndex = HackFunc(ButtonClickedEventCls, "__index") ButtonClickedEventCls.__index = function(ud, k) local v = rawget(ButtonClickedEventCls, k) if v then return v end v = ButtonClickedEventClsIndex(ud, k) if v then return v end end RoleConst--[[ author:{zhangpeng} time:2025-05-13 10:25:19 ]] local RoleConst = defClassStatic("RoleConst") RoleConst.roleIdPrefix = { customer = "customer_", employee = "employee_", } -- 朝向 RoleConst.direction = { -- 左 left = "left", -- 右 right = "right", } -- 角色大类 RoleConst.roleType = { -- 顾客 customer = 1, -- 员工 employee = 2, } -- 员工类型 RoleConst.employeeType = { -- 男主角 hero = 1, -- 女主角 heroine = 2, -- 服务员 waiter = 3, -- 保安 security = 4, -- 大厨 chef = 5, -- 收银员 cashier = 6, -- 渔夫 fisherman = 7, -- 歌手 singer = 8, -- 助理 assistant = 9, } function RoleConst:init() end RoleConst:init() Event--@func Event.add(go, ev, fn, obj=nil) --@desc 给go绑定一个事件回调,同GameObject.AddEvent --@arg ev:int/string,事件名称或事件id(两种都支持),见Event --@arg fn:function,回调函数,参数见unity定义 --@func Event.remove(go, ev=nil, fn=nil, obj=nil) --@desc 删除给go绑定的事件回调,条件判断 (ev && fn && obj),同GameObject.RmEvent --@arg ev:int/string,事件名称或事件id(两种都支持),见Event,ev==nil则表示所有事件ev==* --@arg fn:function,回调函数,参数见unity定义,fn==nil则表示所有函数fn==* ------------------------------------ --Event = nil--ignore UnityEngine.Event -- local GameObject = CS.UnityEngine.GameObject local Object = CS.UnityEngine.Object local LOGTAT = "Event" ---@class Event:LuaStaticClass local Event = defClassStatic("Event") Event.Awake = 1 Event.Start = 2 Event.Reset = 3 Event.Update = 4 Event.LateUpdate = 5 Event.FixedUpdate = 6 Event.OnDisable = 7 Event.OnEnable = 8 Event.OnDestroy = 9 Event.OnApplicationFocus = 10 Event.OnApplicationPause = 11 Event.OnApplicationQuit = 12 Event.OnPreCull = 13 Event.OnPreRender = 14 Event.OnWillRenderObject = 15 Event.OnRenderObject = 16 Event.OnPostRender = 17 Event.OnRenderImage = 18 Event.OnBecameInvisible = 19 Event.OnBecameVisible = 20 Event.OnMouseDown = 21 Event.OnMouseDrag = 22 Event.OnMouseEnter = 23 Event.OnMouseExit = 24 Event.OnMouseOver = 25 Event.OnMouseUp = 26 Event.OnMouseUpAsButton = 27 Event.OnCollisionEnter = 28 Event.OnCollisionExit = 29 Event.OnCollisionStay = 30 Event.OnCollisionEnter2D = 31 Event.OnCollisionExit2D = 32 Event.OnCollisionStay2D = 33 Event.OnTriggerEnter = 34 Event.OnTriggerExit = 35 Event.OnTriggerStay = 36 Event.OnTriggerEnter2D = 37 Event.OnTriggerExit2D = 38 Event.OnTriggerStay2D = 39 Event.OnJointBreak = 40 Event.OnJointBreak2D = 41 Event.OnParticleCollision = 42 Event.OnParticleTrigger = 43 Event.OnTransformChildrenChanged = 44 Event.OnTransformParentChanged = 45 Event.OnControllerColliderHit = 46 Event.OnAnimationEvent = 47 Event.OnGUI = 48 Event.OnAnimatorMove = 49 Event.OnAnimatorIK = 50 Event.OnAudioFilterRead = 51 Event.OnBeginDrag = 52 Event.OnEndDrag = 53 Event.OnDrag = 54 Event.OnApplicationUnload = 55 Event.cslist = { CS.LuaEventAwake, CS.LuaEventStart, CS.LuaEventReset, CS.LuaEventUpdate, CS.LuaEventLateUpdate, CS.LuaEventFixedUpdate, CS.LuaEventOnDisable, CS.LuaEventOnEnable, CS.LuaEventOnDestroy, CS.LuaEventOnApplicationFocus, CS.LuaEventOnApplicationPause, CS.LuaEventOnApplicationQuit, CS.LuaEventOnPreCull, CS.LuaEventOnPreRender, CS.LuaEventOnWillRenderObject, CS.LuaEventOnRenderObject, CS.LuaEventOnPostRender, CS.LuaEventOnRenderImage, CS.LuaEventOnBecameInvisible, CS.LuaEventOnBecameVisible, CS.LuaEventOnMouseDown, CS.LuaEventOnMouseDrag, CS.LuaEventOnMouseEnter, CS.LuaEventOnMouseExit, CS.LuaEventOnMouseOver, CS.LuaEventOnMouseUp, CS.LuaEventOnMouseUpAsButton, CS.LuaEventOnCollisionEnter, CS.LuaEventOnCollisionExit, CS.LuaEventOnCollisionStay, CS.LuaEventOnCollisionEnter2D, CS.LuaEventOnCollisionExit2D, CS.LuaEventOnCollisionStay2D, CS.LuaEventOnTriggerEnter, CS.LuaEventOnTriggerExit, CS.LuaEventOnTriggerStay, CS.LuaEventOnTriggerEnter2D, CS.LuaEventOnTriggerExit2D, CS.LuaEventOnTriggerStay2D, CS.LuaEventOnJointBreak, CS.LuaEventOnJointBreak2D, CS.LuaEventOnParticleCollision, CS.LuaEventOnParticleTrigger, CS.LuaEventOnTransformChildrenChanged, CS.LuaEventOnTransformParentChanged, CS.LuaEventOnControllerColliderHit, CS.LuaEventOnAnimationEvent, CS.LuaEventOnGUI, CS.LuaEventOnAnimatorMove, CS.LuaEventOnAnimatorIK, CS.LuaEventOnAudioFilterRead, CS.LuaEventOnBeginDrag, CS.LuaEventOnEndDrag, CS.LuaEventOnDrag, CS.LuaEventOnApplicationUnload, } Event.goEvents = {} Event.__exited = false Event.add = function(go, eventId, cb) if Event.__exited then return end local events = Event.goEvents[go] if events == nil then events = {} Event.goEvents[go] = events end local cb_list = events[eventId] if not cb_list then cb_list = {} events[eventId] = cb_list local comp = go:AddComponent(typeof(Event.cslist[eventId])) comp:Bind(function(...) for _,cb in ipairs(cb_list) do cb(...) end end) end table.insert(cb_list,cb) return { remove = function() for i = #cb_list,1,-1 do if cb_list[i] == cb then table.remove(cb_list,i) end end end } end Event.remove = function(go, eventId) local events = Event.goEvents[go] or {} if not events[eventId] then return end if not CS.LuaHelper.IsNull(go) then local comp = go:GetComponent(typeof(Event.cslist[eventId])) if comp then if eventId == Event.OnDestroy then comp:UnBind() else comp:Bind(nil) end Object.Destroy(comp) end end events[eventId] = nil end Event.exit = function () print("Event exit") --把所有绑定了 Event.OnDestroy 的go都触发一下回调,并移除 Event.OnDestroy 事件. 不然在 gameObject 真正 OnDestroy 的时候,拿不到 luaenv for go, events in pairs(Event.goEvents or {}) do for eventId, cb_list in pairs(events) do if eventId == Event.OnDestroy then for _,cb in ipairs(cb_list or {}) do cb(false) end end Event.remove(go, eventId) end end Event.goEvents = {} Event.__exited = true end --protect Event setmetatable(Event, { __newindex = function(t, k, v) error("Event modify limited"..t..k..v) end, __index = function(t, k) error("Event define missing"..t..k) end }) StageDeskMgrg--[[ 舞台管理器 author:{zhangpeng} time:2025-07-07 17:38:11 ]] local StageDeskMgr = defClassStatic("StageDeskMgr") local LOGTAG = "StageDeskMgr" -- init function StageDeskMgr:init() printInfo(LOGTAG, "------ 舞台建筑管理 初始化 ------") self.stageDesks = {} end -- 根据建筑id创建一个舞台 function StageDeskMgr:createStageDesk(buildingInfo) local parentNode = RestaurantMgr:getRestaurantScene().rootNode:Seek("building_pos_" .. buildingInfo.buildingType) local args = { deskId = buildingInfo.buildingCfgId, parent = parentNode, scene = RestaurantMgr:getRestaurantScene(), } local stageDesk = StageDesk.new(args) table.insert(self.stageDesks, stageDesk) return stageDesk end -- 获取所有舞台实例 function StageDeskMgr:getAllStageDesks() return self.stageDesks end -- 根据ID获取特定舞台实例 function StageDeskMgr:getStageDeskById(deskId) for _, desk in ipairs(self.stageDesks) do if desk.deskCfgId == deskId then return desk end end return nil end return StageDeskMgr ThemeCell --[[ 建筑主体的单个格子 author:{zhangpeng} time:2025-05-27 17:06:27 ]] local ThemeCell = defClass("ThemeCell") local LOGTAG = "ThemeCell" local TMPUGUI = CS.TMPro.TextMeshProUGUI local Image = CS.UnityEngine.UI.Image function ThemeCell:ctor(cell, data) self.cell = cell self.data = data self.isSelect = false self.isPurchase = false self.isUnlock = false self:initCellInfo() end function ThemeCell:initCellInfo() local info = BuildingMgr:getBuildingInfo(self.data.uid) self.price = info:getBuyPrice() self.isPurchase = info:isPurchased() self.isUnlock = info:isUnlocked() self.inUse = info:isInUse() self.cell:Seek("bg_lock"):SetActive(not self.isUnlock) self.cell:Seek("bg_unlock"):SetActive(self.isUnlock) self.cell:Seek("mask_lock"):SetActive(not self.isUnlock) -- name local name = self.cell:Seek("name") if self.isUnlock then name[TMPUGUI].text = self.data.name else name[TMPUGUI].text = "?" end -- 价格 local price = self.cell:Seek("price_num") price:SetActive(self.isUnlock and (not self.isPurchase)) if self.isUnlock and (not self.isPurchase) then local priceNum = info:getBuyPrice() price[TMPUGUI].text = priceNum end -- img local imgObj = self.cell:Seek("building_img") local img = imgObj[Image] img.sprite = info:getUiIcon() util.ugui:setImageTileSize(img, 260) util.ugui.addButtonClickEvent(imgObj, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) if (not self.isSelect) and (not self.isUnlock) then PopUpUI.new("有设施未达到购买条件,不能购买") return end self:setSelect(not self.isSelect, false) --if self.selectCallback then -- self.selectCallback(self, info:getBuyPrice(), self.isSelect) --end end) self.cell:Seek("purchase"):SetActive(self.isPurchase) self.cell:Seek("unlock"):SetActive(not self.isUnlock) self.cell:Seek("select"):SetActive(false) self.cell:Seek("state_str"):SetActive(self.isPurchase and self.inUse) self.cell:Seek("state_str2"):SetActive(self.isPurchase and (not self.inUse)) end -- 设置选中状态 function ThemeCell:setSelect(isSelect, isSelectAll) if (not self.isUnlock) or self.isPurchase then return false end if self.isSelect == isSelect then return false end self.isSelect = isSelect self.cell:Seek("select"):SetActive(isSelect) if self.selectCallback then self.selectCallback(self, self.price, self.isSelect, isSelectAll) end return true end -- 是否被选中 function ThemeCell:getIsSelect() return self.isSelect end -- 是否已购买 function ThemeCell:getIsPurchase() return self.isPurchase end -- 是否可选 function ThemeCell:canSelect() return self.isUnlock and (not self.isPurchase) end function ThemeCell:getId() return self.data.uid end function ThemeCell:getPrice() return self.price end function ThemeCell:isInUse() return self.inUse end function ThemeCell:isUnlocked() return self.isUnlock end function ThemeCell:buy() self.isPurchase = true self.cell:Seek("purchase"):SetActive(self.isPurchase) self.cell:Seek("select"):SetActive(false) end -- 设置选中回调 function ThemeCell:setSelectCallback(callback) self.selectCallback = callback end return ThemeCell CSharpUtil_G.cs_ipairs = function(cs_array) local enumerator = cs_array:GetEnumerator() local i = 0 return function() if enumerator:MoveNext() then i = i + 1 return i,enumerator.Current end end end _G.cs_pairs = function(cs_dict) local enumerator = cs_dict:GetEnumerator() return function() if enumerator:MoveNext() then local Current = enumerator.Current return Current.Key,Current.Value end end endDollMgrW-- 玩偶常量 local DollMgr = defClassStatic("DollMgr") -- 初始化 function DollMgr:init() self:initUserData() end -- 初始化用户数据 function DollMgr:initUserData() self.userData = UserDataMgr.dollUserData end function DollMgr:getConfig(dollId) return DollCfgParse:getDollCfg(dollId) end -- 获取玩偶数量 function DollMgr:getDollCount(id) return self.userData:getDollCount(id) end -- 增加玩偶数量 function DollMgr:addDollCount(id, count) return self.userData:addDollCount(id, count) end function DollMgr:getDollState(id) return self.userData:getDollState(id) end function DollMgr:setDollState(id, state) return self.userData:setDollState(id, state) end -- 获取玩偶奖励 function DollMgr:getRewardAll(dollId) local config = self:getConfig(dollId) if not config then return end for i = 1, 2 do self:getReward(config["rewardId_" .. i], config["param_" .. i]) end -- 设置玩偶状态为已领取 self:setDollState(dollId, DollConst.State.Received) end -- 获取玩偶奖励 function DollMgr:getReward(rewardType, rewardValue) if (not rewardType) or (rewardType == -1) then return end if rewardType == DollConst.RewardType.Star then CurrencyMgr:changeStar(rewardValue) elseif rewardType == DollConst.RewardType.MusicalNotes then CurrencyMgr:changeMusicalNotes(rewardValue) elseif rewardType == DollConst.RewardType.Customer then CustomerMgr:enterTheRestaurant(rewardValue) end end return DollMgr SceneCfgParse -- local SceneCfgData = require("data/config/sceneCfg") local SceneCfgParse = defClassStatic("SceneCfgParse") local LOGTAG = "SceneCfgParse" -- 初始化 function SceneCfgParse:init() self.idDic = {} for _, v in pairs(SceneCfgData) do self.idDic[v.id] = v end end -- 获取所有场景配置 function SceneCfgParse:getData() return SceneCfgData end -- 获取场景配置 function SceneCfgParse:getSceneCfgById(id) return self.idDic[id] end -- 获取场景配置列表 function SceneCfgParse:getSceneCfgByName(name) for _, v in pairs(SceneCfgData) do if v.name == name then return v end end return nil end -- 获取所有场景配置 function SceneCfgParse:getAllSceneCfg() return SceneCfgData end SceneCfgParse:init()cuisineuireslinknreturn { --BASIC --ASSET cuisine_cell = {"Assets/AssetsPackage/Res/modules/ui/cuisineui/prefabs/cuisine_cell.prefab", 0, 0}, cuisine_list_ui = {"Assets/AssetsPackage/Res/modules/ui/cuisineui/prefabs/cuisine_list_ui.prefab", 0, 0}, cuisine_detail_ui = {"Assets/AssetsPackage/Res/modules/ui/cuisineui/prefabs/cuisine_detail_ui.prefab", 0, 0}, music_bbq_cuisine_cell = {"Assets/AssetsPackage/Res/modules/ui/cuisineui/prefabs/music_bbq_cuisine_cell.prefab", 0, 0}, music_bbq_cuisine_detail_ui = {"Assets/AssetsPackage/Res/modules/ui/cuisineui/prefabs/music_bbq_cuisine_detail_ui.prefab", 0, 0}, } main} require("modules/role/customer/generated/CustomerFirstVisit") require("modules/role/customer/generated/CustomerNormalVisit") require("modules/role/customer/generated/CustomerSolicitation") require("modules/role/customer/generated/CustomerSpecialVisit") require("modules/role/customer/generated/MusicGrillBarVisit") require("modules/role/customer/generated/FishingVisits") UILoading--[[ author:{zhangpeng} time:2023-08-01 16:33:43 ]] local UILoading, super = defClass("UILoading", UILayer) local GameObject = CS.UnityEngine.GameObject function UILoading:ctor() super.ctor(self) self.R = Res.loadResLink("common/ui/uicoms/reslink/uicircleloadingreslink") end function UILoading:onLoad() self.ui = GameObject.Instantiate(self.R.circle_loading_ui) self:addChild(self.ui) self:setPriority(UILayer.UI_ORDER.LOADING) end function UILoading:onExit() super.onExit(self) end return UILoadingKVTable ---@class KVTable:LuaClass local KVTable = defClass("KVTable") local LOGTAG = KVTable.__cls_name --[[ ]] function KVTable:ctor(databaseApis, name) self.databaseApis = databaseApis self.name = name self:init() end function KVTable:init() self.columnList = {} end function KVTable:getName() return self.name end --------------------------------------------------------------------------------------------- --数据库 增删改查 相关接口 --------------------------------------------------------------------------------------------- ---获取, key 可以是列表 ---@param key string|string[] ---@param defaultValue any ---@return any function KVTable:get(key, defaultValue) return self.databaseApis.get(key, defaultValue) end ---设置 ---@param key string|string[] ---@param value any function KVTable:set(key, value) return self.databaseApis.set(key, value) end -- 获取所有的key ---@return any[] function KVTable:getKeys() return self.databaseApis.getKeys() end return KVTable SettingMainUI---设置主界面 ---@class SettingMainUI : UILayer local SettingMainUI, super = defClass("SettingMainUI", UILayer) require("modules/ui/helpui/HelpCategoryListUI") --- 构造函数 function SettingMainUI:ctor() super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/settingui/settinguireslink") end --- 加载UI function SettingMainUI:onLoad() self.ui = GameObject.Instantiate(self.R.setting_main_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end --- 初始化UI function SettingMainUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.toggle_m = self.ui:Seek("toggle_m") self.toggle_s = self.ui:Seek("toggle_s") self.toggle_s_on = self.ui:Seek("toggle_s_on") self.toggle_s_off = self.ui:Seek("toggle_s_off") self.toggle_m_on = self.ui:Seek("toggle_m_on") self.toggle_m_off = self.ui:Seek("toggle_m_off") self.help_btn = self.ui:Seek("help_btn") self.feedback_btn = self.ui:Seek("feedback_btn") self.weixin_btn = self.ui:Seek("weixin_btn") self.weibo_btn = self.ui:Seek("weibo_btn") self.id = self.ui:Seek("id") self.id_copy_btn = self.ui:Seek("id_copy_btn") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addToggleValueChangeEvent(self.toggle_m, function(isOn) self:toggleBackgroundMusicSwitch(isOn) AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) end) util.ugui.addToggleValueChangeEvent(self.toggle_s, function(isOn) self:toggleSoundEffectSwitch(isOn) AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) end) util.ugui.addButtonClickEvent(self.help_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:help() end) util.ugui.addButtonClickEvent(self.feedback_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:feedback() end) util.ugui.addButtonClickEvent(self.weixin_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:weixin() end) util.ugui.addButtonClickEvent(self.weibo_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:weibo() end) util.ugui.addButtonClickEvent(self.id_copy_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:copy() end) end --- 展示UI function SettingMainUI:showUI() self.toggle_m:GetComponent("Toggle").isOn = AudioMgr:isBackgroundMusicEnabled() self.toggle_s:GetComponent("Toggle").isOn = AudioMgr:isSoundEffectsEnabled() self.id:GetComponent("TextMeshProUGUI").text = "nil" end --- 切换背景音乐开关 function SettingMainUI:toggleBackgroundMusicSwitch(isOn) self.toggle_m_on:SetActive(isOn) self.toggle_m_off:SetActive(not isOn) AudioMgr:setBackgroundMusicEnabled(isOn) end --- 切换音效开关 function SettingMainUI:toggleSoundEffectSwitch(isOn) self.toggle_s_on:SetActive(isOn) self.toggle_s_off:SetActive(not isOn) AudioMgr:setSoundEffectsEnabled(isOn) end --- 帮助 function SettingMainUI:help() local view = HelpCategoryListUI.new():show():showMask():enableCloseWhenClickMask() end --- 反馈 function SettingMainUI:feedback() -- todo 反馈链接 end --- 微信 function SettingMainUI:weixin() -- todo 打开微信 end --- 微博 function SettingMainUI:weibo() -- todo 打开微博 end --- 复制ID function SettingMainUI:copy() local value = self.id:GetComponent("TextMeshProUGUI").text -- todo 复制到剪贴板 end return SettingMainUI SqliteColumn ---@class SqliteColumn:LuaClass local SqliteColumn = defClass("SqliteColumn") function SqliteColumn:ctor(table, name, type, defaultValue, isPrimary, isMetadata) self.table = table self.name = name self.type = type -- lua type self.defaultValue = defaultValue self.isPrimary = isPrimary self.isMetadata = isMetadata self.asName = nil end function SqliteColumn:as(asName) self.asName = asName return self end function SqliteColumn:getSqlType() return SqliteUtil:luaTypeNameToSqlTypeName(self.type) end --------------------------------------------------------------------------------------------- -- 工具函数 --------------------------------------------------------------------------------------------- function SqliteColumn:toSqlStr(isFullName) if not isFullName then return self.name end return string.format("%s.%s", self.table:getName(), self.asName) end return SqliteColumn BoothUnlockCfgParse-- 摊位解锁配置解析 local BoothUnlockCfgParse = defClassStatic("BoothUnlockCfgParse") local BoothUnlockCfgData = require("data/config/boothUnlockCfg") local LOGTAG = "BoothUnlockCfgParse" -- 初始化 function BoothUnlockCfgParse:init() self.orderDic = {} for _, v in pairs(BoothUnlockCfgData) do self.orderDic[v.order] = v end end -- 根据顺序获取摊位解锁配置 function BoothUnlockCfgParse:getBoothUnlockCfgByOrder(order) return self.orderDic[order] end -- 获取所有摊位解锁配置 function BoothUnlockCfgParse:getAllBoothUnlockCfg() return BoothUnlockCfgData end BoothUnlockCfgParse:init() Chef(--[[ 大厨(先从出生点走到wander_2播放待机动作,持续6秒,然后走到wander_1播放炒菜动作,持续6秒,然后走回wander_2,播放待机动作持续6秒, --然后走到wander_3,播放切菜动作,持续4秒,然后走回wander_1,播放待机,然后循环这个过程) author:{zhangpeng} time:2025-05-30 15:14:42 ]] local Chef,super = defClass("Chef",Employe) local LOGTAG = "Chef" ---构造函数 function Chef:ctor(reslink,cfgId) super.ctor(self,reslink,cfgId) self:init() end function Chef:init() super.init(self) -- 巡逻状态 self.isPatrolling = false -- 工作流程当前步骤 self.workStep = 1 -- 标记是否已经开始工作流程(避免重复启动) self.hasStartedWorkflow = false printInfo(LOGTAG, string.format("大厨[%s]初始化完成", self.employeCfgId)) -- 初始化时播放待机动画 self:playIdleAnim() end -- 员工行为,不同员工有不同的行为,使用EmployFunsMgr来执行具体行为 function Chef:action() -- 首先检查当前状态 if self.state == EmployeConst.State.Idle then -- 开始在待机点来回走动 self:startPatrolling() self:doSpeedCooking() else -- 大厨继续工作 self:continueWorking() end end -- 开始在待机点巡逻 function Chef:startPatrolling() -- 如果已经在巡逻,则不重复开始 if self.isPatrolling then return end -- 获取待机点 if not self.wanderPoints or #self.wanderPoints == 0 then printWarn(LOGTAG, string.format("大厨[%s]没有待机点", self.employeCfgId)) -- 如果没有待机点,播放待机动画 self:playIdleAnim() return end printInfo(LOGTAG, string.format("大厨[%s]开始工作流程", self.employeCfgId)) -- 设置巡逻标志 self.isPatrolling = true -- 如果还没有开始工作流程,延迟1秒开始(避免初始化时立即播放行走动画) if not self.hasStartedWorkflow then self.hasStartedWorkflow = true self:timer(function() if self.isPatrolling then -- 确保还在巡逻状态 self:executeWorkflow() end end, 1, 1) else -- 开始工作流程 self:executeWorkflow() end end -- 执行工作流程 function Chef:executeWorkflow() -- 检查是否还在巡逻状态 if not self.isPatrolling or self.state ~= EmployeConst.State.Idle then self.isPatrolling = false printInfo(LOGTAG, string.format("大厨[%s]停止工作流程,isPatrolling=%s, state=%s", self.employeCfgId, tostring(self.isPatrolling), tostring(self.state))) return end -- 工作流程: -- 步骤1: 走到wander_2播放待机动作,持续6秒 -- 步骤2: 走到wander_1播放炒菜动作,持续6秒 -- 步骤3: 走回wander_2,播放待机动作持续6秒 -- 步骤4: 走到wander_3,播放切菜动作,持续4秒 -- 步骤5: 走回wander_1,播放待机,然后循环 if self.workStep == 1 then -- 步骤1: 走到wander_2播放待机动作,持续6秒 self:goToWanderPoint(2, function() self:playIdleAnim() printInfo(LOGTAG, string.format("大厨[%s]在wander_2播放待机动作6秒", self.employeCfgId)) self:timer(function() self.workStep = 2 self:executeWorkflow() end, EmployeConst.ActionTimes[EmployeConst.Type.Chef][self.workStep], 1) end) elseif self.workStep == 2 then -- 步骤2: 走到wander_1播放炒菜动作,持续6秒 self:goToWanderPoint(1, function() self:playFryAnim() printInfo(LOGTAG, string.format("大厨[%s]在wander_1播放炒菜动作6秒", self.employeCfgId)) self:timer(function() self.workStep = 3 self:executeWorkflow() end, EmployeConst.ActionTimes[EmployeConst.Type.Chef][self.workStep], 1) end) elseif self.workStep == 3 then -- 步骤3: 走回wander_2,播放待机动作持续6秒 self:goToWanderPoint(2, function() self:playIdleAnim() printInfo(LOGTAG, string.format("大厨[%s]在wander_2播放待机动作6秒", self.employeCfgId)) self:timer(function() self.workStep = 4 self:executeWorkflow() end, EmployeConst.ActionTimes[EmployeConst.Type.Chef][self.workStep], 1) end) elseif self.workStep == 4 then -- 步骤4: 走到wander_3,播放切菜动作,持续4秒 self:goToWanderPoint(3, function() self:playCutAnim() printInfo(LOGTAG, string.format("大厨[%s]在wander_3播放切菜动作4秒", self.employeCfgId)) self:timer(function() self.workStep = 5 self:executeWorkflow() end, EmployeConst.ActionTimes[EmployeConst.Type.Chef][self.workStep], 1) end) elseif self.workStep == 5 then -- 步骤5: 走回wander_1,播放待机,然后循环 self:goToWanderPoint(1, function() self:playIdleAnim() printInfo(LOGTAG, string.format("大厨[%s]在wander_1播放待机动作,准备开始新循环", self.employeCfgId)) self:timer(function() self.workStep = 1 -- 重置为第一步,开始新循环 self:executeWorkflow() end, EmployeConst.ActionTimes[EmployeConst.Type.Chef][self.workStep], 1) -- 短暂停留2秒后开始新循环 end) end end -- 前往指定的wander点 function Chef:goToWanderPoint(pointIndex, callback) if not self.wanderPoints or pointIndex < 1 or pointIndex > #self.wanderPoints then printError(LOGTAG, string.format("大厨[%s]wander点[%d]不存在,跳过", self.employeCfgId, pointIndex)) if callback then callback() end return end local targetPoint = self.wanderPoints[pointIndex] if not targetPoint or not targetPoint.transform then printError(LOGTAG, string.format("大厨[%s]wander点[%d]无效,跳过", self.employeCfgId, pointIndex)) if callback then callback() end return end local targetPos = targetPoint.transform.position local currentPos = self:getCurPosition() -- 计算距离,如果距离很近(小于0.5),直接执行回调,不需要移动 local distance = (targetPos - currentPos).magnitude if distance < 0.5 then printInfo(LOGTAG, string.format("大厨[%s]已在wander_%d附近,距离=%.2f,无需移动", self.employeCfgId, pointIndex, distance)) if callback then callback() end return end printInfo(LOGTAG, string.format("大厨[%s]前往wander_%d,距离=%.2f", self.employeCfgId, pointIndex, distance)) -- 播放行走动画 self:playWalkAnim() -- 移动到该点 self:rolePathFind(targetPos, function() printInfo(LOGTAG, string.format("大厨[%s]到达wander_%d", self.employeCfgId, pointIndex)) if callback then callback() end end) end -- 播放炒菜动画 function Chef:playFryAnim() local actions = EmployeConst.EmployeeAction[self.employeCfgId] if actions and actions.fry then self:playAnimation(actions.fry, true) printInfo(LOGTAG, string.format("员工[%s]播放炒菜动画: %s", self.employeCfgId, actions.fry)) else -- 如果没有炒菜动画,播放待机动画 self:playIdleAnim() printWarn(LOGTAG, string.format("员工[%s]没有炒菜动画,播放待机动画", self.employeCfgId)) end end -- 播放切菜动画 function Chef:playCutAnim() local actions = EmployeConst.EmployeeAction[self.employeCfgId] if actions and actions.cut then self:playAnimation(actions.cut, true) printInfo(LOGTAG, string.format("员工[%s]播放切菜动画: %s", self.employeCfgId, actions.cut)) else -- 如果没有切菜动画,播放待机动画 self:playIdleAnim() printWarn(LOGTAG, string.format("员工[%s]没有切菜动画,播放待机动画", self.employeCfgId)) end end -- 停止巡逻 function Chef:stopPatrolling() self.isPatrolling = false self.workStep = 1 -- 重置工作步骤 self.hasStartedWorkflow = false -- 重置工作流程标志 printInfo(LOGTAG, string.format("大厨[%s]停止工作流程", self.employeCfgId)) end -- 继续工作 (预留接口,暂时只做巡逻) function Chef:continueWorking() -- 如果状态不是空闲,暂停巡逻 if self.isPatrolling then self:stopPatrolling() end printInfo(LOGTAG, string.format("大厨[%s]当前状态为工作中,暂停工作流程", self.employeCfgId)) end -- 获取员工类型 function Chef:getEmployeType() return self.employeCfgId end -- 设置状态 function Chef:setState(state) self.state = state if self.employeeInfo then self.employeeInfo:setWorkState(state) end end -- 获取状态 function Chef:getState() return self.state end -- 加速料理 function Chef:doSpeedCooking() local workTimeLimit = self.employeeInfo:getWorkTimeLimit() local workTime = 0 local timer = nil -- 开始工作 self.employeeInfo:setWorkState(EmployeConst.State.Busy) local tempBonus = self.employeeInfo:getCurrTagValueByIdx(1, 1) BonusMgr:addTempBonus(BonusConst.BonusType.cook_efficiency, tempBonus) timer = self:timer(function() workTime = workTime + 2 self.employeeInfo:setWorkTime(workTime) if workTime >= workTimeLimit then -- 停止定时器 if timer then TimerMgr:rem(timer) timer = nil -- 停止 BonusMgr:removeTempBonus(BonusConst.BonusType.cook_efficiency, tempBonus) -- 休息 EmployFunsMgr:doResting(self, function(emp) emp:doSpeedCooking() end) end return end end, 2, 0) end return Chef guidereslinkreturn { --BASIC --ASSET guide_ui = {"Assets/AssetsPackage/Res/modules/guide/prefab/guide_ui.prefab", 0, 0}, guide_hand = {"Assets/AssetsPackage/Res/modules/guide/prefab/guide_hand.prefab", 0, 0}, } CustomerQueMgrt--[[ 顾客排队管理 author:{zhangpeng} time:2025-05-15 20:28:58 ]] local CustomerQueMgr = defClassStatic("CustomerQueMgr") local LOGTAG = "CustomerQueMgr" function CustomerQueMgr:init() printInfo(LOGTAG, "------ 顾客排队管理 初始化 ------") self.waitingCustomerQueLimit = 15 -- 等待队列最大长度 -- 门口等待顾客列表 self.waitingCustomerQue = {} -- 点餐顾客列表 self.orderingCustomerQue = {} -- 用餐顾客列表 self.diningCustomerQue = {} end -- 添加顾客到等待队列 function CustomerQueMgr:addCustomerToWaitingQueue(customer) table.insert(self.waitingCustomerQue, customer) printInfo(LOGTAG, "等待队列添加一个顾客: " .. customer:getRoleUniqueId() .. " 当前长度: " .. #self.waitingCustomerQue) -- 尝试为顾客分配餐桌 self:tryAssignToDiningDesk() end -- 尝试为等待队列中的顾客分配餐桌 function CustomerQueMgr:tryAssignToDiningDesk() -- 如果等待队列为空,则直接返回 if #self.waitingCustomerQue == 0 then return end local restaurant = RestaurantMgr:getRestaurantScene() local pointRoot = restaurant.rootNode:Seek("queue_point") local point = pointRoot:Seek("queue_point_" .. (#self.waitingCustomerQue)) -- 查找空闲餐桌 local diningDesk = DiningDeskMgr:findIdleDesk() if not diningDesk then printInfo(LOGTAG, "没有空闲餐桌") local customer = self.waitingCustomerQue[#self.waitingCustomerQue] customer:rolePathFind(point.transform.position, function() customer:playStandAnim() end) --customer:rolePathFind(restaurant:getQueuePointByIdx(#self.waitingCustomerQue).transform.position) return end -- 查找餐桌中的空闲座位 local seat = diningDesk:findIdleSeat() if not seat then printInfo(LOGTAG, "餐桌没有空闲座位") local customer = self.waitingCustomerQue[#self.waitingCustomerQue] customer:rolePathFind(point.transform.position, function() customer:playStandAnim() end) --customer:rolePathFind(restaurant:getQueuePointByIdx(#self.waitingCustomerQue).transform.position) return end -- 从等待队列中取出第一个顾客 local customer = table.remove(self.waitingCustomerQue, 1) for i, v in ipairs(self.waitingCustomerQue) do local tempPoint = pointRoot:Seek("queue_point_" .. i) v:rolePathFind(tempPoint.transform.position, function() v:playStandAnim() end) --v:rolePathFind(restaurant:getQueuePointByIdx(i).transform.position) end -- 通知顾客移动到餐桌座位 self:notifyCustomerToMoveToDiningDesk(customer, diningDesk, seat) -- 添加到点餐队列 table.insert(self.orderingCustomerQue, customer) end -- 通知顾客移动到餐桌座位 function CustomerQueMgr:notifyCustomerToMoveToDiningDesk(customer, diningDesk, seat) printInfo(LOGTAG, "通知顾客[" .. customer:getRoleUniqueId() .. "]移动到餐桌座位") -- 获取座位位置 local seatPosition = seat:getSeatPosition() -- 先将座位标记为"正在前往"状态,防止其他顾客被分配到同一座位 seat:setState(DiningConst.SeatState.approaching) -- 将座位引用与顾客关联 (双向引用) customer:setCurrentSeat(seat) seat.customer = customer -- 告诉顾客走向座位位置 customer:moveToSeat(seatPosition, function() -- 顾客到达座位后的回调 printInfo(LOGTAG, "顾客[" .. customer:getRoleUniqueId() .. "]到达座位") -- 顾客坐下 seat:sitDown(customer) -- 顾客准备点餐 customer:prepareToOrder() -- 移出点餐队列,加入用餐队列 self:moveCustomerFromOrderingToDining(customer) end) end -- 将顾客从点餐队列移至用餐队列 function CustomerQueMgr:moveCustomerFromOrderingToDining(customer) -- 在点餐队列中查找顾客 for i, c in ipairs(self.orderingCustomerQue) do if c == customer then table.remove(self.orderingCustomerQue, i) table.insert(self.diningCustomerQue, customer) break end end end -- 检查并分配所有等待的顾客 function CustomerQueMgr:checkAndAssignAllWaitingCustomers() while #self.waitingCustomerQue > 0 do local success = self:tryAssignToDiningDesk() if not success then break -- 如果没有分配成功,就跳出循环 end end end function CustomerQueMgr:getWaitingCustomerCount() return #self.waitingCustomerQue end function CustomerQueMgr:getWaitingCustomerLimit() return self.waitingCustomerQueLimit end function CustomerQueMgr:isToLimit() return #self.waitingCustomerQue >= self.waitingCustomerQueLimit end return CustomerQueMgr AppleLoginMgr5--[[ appleid登录 author:{zhangpeng} time:2024-08-05 21:47:17 ]] local AppleLoginMgr, super = defClassStatic("AppleLoginMgr") local LOG_TAG = "AppleLoginMgr" local IOC_CLASS_NAME = "AppleLoginUtil" function AppleLoginMgr:init() AppleLoginMgr:registLuaCallBack() end function AppleLoginMgr:login() if Device.isIOS() then local param = {} printInfo(LOG_TAG,"ios login...") luaoc.callStaticMethod(IOC_CLASS_NAME, "login") end end function AppleLoginMgr:registLuaCallBack() if Device.isIOS() then local param = { loginErrorCb = function() printInfo(LOG_TAG,"登录失败回调") Msg.send(Msg.USER_LOGIN_APPLE_FAILED) end, loginAuthFailedCb = function (desc) local data = json.decode(desc) printInfo(LOG_TAG,"授权失败回调") Msg.send(Msg.USER_LOGIN_APPLE_AUTH_FILED, data) end, loginSucCb = function (jsonStr) local data = json.decode(jsonStr) table.print_r(data,"Apple ID 登录成功回调数据") if User:getLoginOrigin() == LoginConst.Origin.login then Msg.send(Msg.USER_LOGIN_APPLE_SUC, data) elseif User:getLoginOrigin() == LoginConst.Origin.bind then Msg.send(Msg.USER_LOGIN_APPLE_BIND_SUC, data) end end } luaoc.callStaticMethod(IOC_CLASS_NAME, "registLuaCallback", param) end end AppleLoginMgr:init() EntityListUIw--- ---@class EntityListUI : UILayer local EntityListUI, super = defClass("EntityListUI", UILayer) function EntityListUI:ctor(title, entityType, entityIds) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/commonui/commonuireslink") self.title = title self.entityType = entityType -- 1:菜品, 2:设施 self.entityIds = entityIds end function EntityListUI:onLoad() self.ui = GameObject.Instantiate(self.R.entity_list_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end function EntityListUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.entity_title = self.ui:Seek("entity_title") self.content = self.ui:Seek("Content") self.got_it_btn = self.ui:Seek("got_it_btn") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addButtonClickEvent(self.got_it_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) end function EntityListUI:showUI() self.entity_title:GetComponent("TextMeshProUGUI").text = self.title self:showAll() end function EntityListUI:showAll() if type(self.entityIds) ~= "table" then self.entityIds = {self.entityIds} end for _, id in pairs(self.entityIds) do local cellNode = GameObject.Instantiate(self.R.entity_cell, self.content.transform) EntityCell.new(cellNode, self.entityType, id) end end return EntityListUIluafsm--[[================================================= Lua State Machine Library ----=================================================]] VERSION = "2.3.2" SUCCEEDED = 1 -- the event transitioned successfully from one state to another NOTRANSITION = 2 -- the event was successfull but no state transition was necessary CANCELLED = 3 -- the event was cancelled by the caller in a beforeEvent callback PENDING = 4 -- the event is asynchronous and the caller is in control of when the transition occurs INVALID_TRANSITION_ERROR = 'INVALID_TRANSITION_ERROR' -- caller tried to fire an event that was innapropriate in the current state PENDING_TRANSITION_ERROR = 'PENDING_TRANSITION_ERROR' -- caller tried to fire an event while an async transition was still pending INVALID_CALLBACK_ERROR = 'INVALID_CALLBACK_ERROR' -- caller provided callback function threw an exception WILDCARD = '*' ASYNC = 'async' local function do_callback(fsm, func, event, params) if type(func) == 'function' then local success, ret = pcall(func, unpack(params)) if not success then local err = ret fsm:error(event, INVALID_CALLBACK_ERROR, err) end return ret end end local function before_any_event(fsm, event, params) return do_callback(fsm, fsm.onbeforeevent, event, params) end local function after_any_event(fsm, event, params) return do_callback(fsm, fsm.onafterevent or fsm.onevent, event, params) end local function leave_any_state(fsm, event, params) return do_callback(fsm, fsm.onleavestate, event, params) end local function enter_any_state(fsm, event, params) return do_callback(fsm, fsm.onenterstate or fsm.onstate, event, params) end local function change_state(fsm, event, params) return do_callback(fsm, fsm.onchangestate, event, params) end local function before_this_event(fsm, event, params) return do_callback(fsm, fsm['onbefore' .. event.name], event, params) end local function after_this_event(fsm, event, params) return do_callback(fsm, fsm['onafter' .. event.name] or fsm['on' .. event.name], event, params) end local function leave_this_state(fsm, event, params) return do_callback(fsm, fsm['onleave' .. event.from], event, params) end local function enter_this_state(fsm, event, params) return do_callback(fsm, fsm['onenter' .. event.to] or fsm['on' .. event.to], event, params) end local function before_event(fsm, event, params) if before_this_event(fsm, event, params) == false or before_any_event(fsm, event, params) == false then return false end end local function after_event(fsm, event, params) after_this_event(fsm, event, params) after_any_event(fsm, event, params) end local function leave_state(fsm, event, params) local specific = leave_this_state(fsm, event, params) local general = leave_any_state(fsm, event, params) if specific == false or general == false then return false elseif specific == ASYNC or general == ASYNC then return ASYNC end end local function enter_state(fsm, event, params) enter_this_state(fsm, event, params) enter_any_state(fsm, event, params) end local function build_event(name, entry) return function(self, ...) local from = self.current local to = entry[from] or entry[WILDCARD] or from local event = { name = name, from = from, to = to, } local params = {self, event, ...} if self.transition then return self:error(event, PENDING_TRANSITION_ERROR, ('event %s inappropriate because previous transition did not complete'):format(name)) end if self:cannot(name) then return self:error(event, INVALID_TRANSITION_ERROR, ('event %s inappropriate in current state %s'):format(name, self.current)) end if before_event(self, event, params) == false then return CANCELLED end if from == to then after_event(self, event, params) return NOTRANSITION end -- prepare a transition method for use EITHER lower down, -- or by caller if they want an async transition (indicated by an ASYNC return value from leaveState) local fsm = self self.transition = { -- provide a way for caller to cancel async transition if desired cancel = function() fsm.transition = nil after_event(fsm, event, params) end } setmetatable(self.transition, { __call = function() fsm.transition = nil -- this method should only ever be called once fsm.current = to enter_state(fsm, event, params) change_state(fsm, event, params) after_event(fsm, event, params) return SUCCEEDED end }) local leave = leave_state(fsm, event, params) if leave == false then self.transition = nil return CANCELLED elseif leave == ASYNC then return PENDING else if self.transition then -- need to check in case user manually called transition() but forgot to return ASYNC return self.transition() end end end end function create(cfg, target) assert(type(cfg) == 'table', 'cfg must be a table') -- allow for a simple string, or an object with { state: = 'foo', event = 'setup', defer = true|false } local initial = type(cfg.initial) == 'string' and { state = cfg.initial } or cfg.initial local terminal = cfg.terminal or cfg.final local fsm = target or cfg.target or {} local events = cfg.events or {} local callbacks = cfg.callbacks or {} local map = {} local function add(e) -- allow 'wildcard' transition if 'from' is not specified local from = type(e.from) == 'table' and e.from or (e.from and {e.from} or {WILDCARD}) local entry = map[e.name] or {} map[e.name] = entry for _, v in ipairs(from) do entry[v] = e.to or v -- allow no-op transition if 'to' is not specified end end if initial then initial.event = initial.event or 'startup' add { name = initial.event, from = 'none', to = initial.state } end for _, e in ipairs(events) do add(e) end for k, v in pairs(map) do fsm[k] = build_event(k, v) end for k, v in pairs(callbacks) do fsm[k] = v end fsm.current = 'none' fsm.is = function(self, state) if type(state) == 'table' then for _, s in ipairs(state) do if s == self.current then return true end end return false else return self.current == state end end fsm.can = function(self, event) if (not self.transition) and map[event] and (map[event][self.current] or map[event][WILDCARD]) then return true else return false end end fsm.cannot = function(self, event) return not self:can(event) end -- default behavior when something unexpected happens is to throw an exception, but caller can override this behavior if desired fsm.error = cfg.error or function(self, event, error_code, err) error(error_code .. " " .. err) end fsm.is_finished = function(self) return self:is(terminal) end if initial and not initial.defer then fsm[initial.event](fsm) end return fsm end return _Mmainvrequire("Assets.LuaScripts.common.debug.luaidedebug.boot_debug_main") require("Assets.LuaScripts.boot.main_editor")main0require("modules/common/data/CommonUserData")mainKrequire("modules/buy/const/BuyConst") require("modules/buy/mgr/BuyMgr") StageDeskSeatd--[[ 舞台座位 author:{zhangpeng} time:2025-07-08 15:00:00 ]] local StageDeskSeat, super = defClass("StageDeskSeat") local LOGTAG = "StageDeskSeat" function StageDeskSeat:ctor(seatNode) self.state = StageDeskConst.SeatState.idle self.customer = nil self.stageDesk = nil -- 所属舞台 self.seatIndex = 0 -- 座位索引 self.buildingNode = seatNode -- 座位节点 self.seatPoint = nil -- 座位点,由外部设置 end -- 设置座位状态 function StageDeskSeat:setState(state) self.state = state -- 更新舞台状态 if self.stageDesk then self.stageDesk:updateState() end end -- 获取座位状态 function StageDeskSeat:getState() return self.state end -- 获取座位位置 function StageDeskSeat:getSeatPosition() -- buildingNode是座位点(yz_point),但顾客应该坐在椅子节点(yz_)的位置 -- 椅子节点是座位点的父节点 local chairNode = self.buildingNode.transform.parent.gameObject local pos = chairNode.transform.position printInfo(LOGTAG, string.format("座位[%d]使用椅子节点位置: %s", self.seatIndex, tostring(pos))) return pos end -- 是否可入座 function StageDeskSeat:isCanSitDown() -- 只有座位状态为idle(空闲)时才能入座 if self.state ~= StageDeskConst.SeatState.idle then return false end -- 确保没有顾客正在前往该座位 if self.customer ~= nil then return false end return true end -- 预订座位(顾客开始前往座位) function StageDeskSeat:reserveSeat(customer) if not self:isCanSitDown() then printWarn(LOGTAG, string.format("座位[%d]不能预订,当前状态: %d", self.seatIndex, self.state)) return false end self.customer = customer self:setState(StageDeskConst.SeatState.approaching) printInfo(LOGTAG, string.format("顾客[%s-%s]预订舞台座位[%d]", customer.customerCfgId, customer.roleUniqueId, self.seatIndex)) return true end -- 坐入顾客 function StageDeskSeat:sitDown(customer) -- 检查状态是否为"正在前往" if self.state ~= StageDeskConst.SeatState.approaching then printError(LOGTAG, string.format("座位[%d]状态错误,当前状态:%d,应为:%d", self.seatIndex, self.state, StageDeskConst.SeatState.approaching)) return false end -- 确保是同一个顾客 if self.customer ~= customer then printError(LOGTAG, string.format("座位[%d]顾客不匹配,期望:%s,实际:%s", self.seatIndex, self.customer and self.customer:getRoleUniqueId() or "nil", customer and customer:getRoleUniqueId() or "nil")) return false end -- 设置座位状态为观看中 self:setState(StageDeskConst.SeatState.watching) printInfo(LOGTAG, string.format("顾客[%s-%s]坐入舞台座位[%d]", customer.customerCfgId, customer.roleUniqueId, self.seatIndex)) return true end -- 顾客离开座位 function StageDeskSeat:leaveSeat() if self.customer then printInfo(LOGTAG, string.format("顾客[%s-%s]离开舞台座位[%d]", self.customer.customerCfgId, self.customer.roleUniqueId, self.seatIndex)) self.customer = nil end -- 恢复到空闲状态 self:setState(StageDeskConst.SeatState.idle) end -- 获取座位上的顾客 function StageDeskSeat:getCustomer() return self.customer end -- 设置所属舞台和索引 function StageDeskSeat:setStageDesk(stageDesk, index) self.stageDesk = stageDesk self.seatIndex = index end return StageDeskSeat main-- mgr require("modules/doll/DollMgr") -- const require("modules/doll/DollConst") -- data require("modules/doll/DollUserData") GameUtillocal UnityEngine = CS.UnityEngine local GameObject = UnityEngine.GameObject local Camera = UnityEngine.Camera local GameUtil = defClassStatic("GameUtil") function GameUtil:getRootNode() local gameRoot = GameObject.Find("GameRoot") if gameRoot then return gameRoot end local scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene() local rootObjects = scene:GetRootGameObjects() if rootObjects.Length > 0 then return rootObjects[0] end end function GameUtil:getCamera() local gameSceneCamera = GameObject.Find("MainCamera") if gameSceneCamera then return gameSceneCamera:GetComponent("Camera") end return Camera.allCameras.FirstOrDefault(); end return GameUtilmainrequire("modules/common/const/GameConst") require("modules/common/const/GameNodeOrder") require("modules/common/const/AudioConst") LoginScene--[[ 登录场景 author:zhangpeng time:2025-07-20 21:13:57 ]] local LoginScene, super = defClass("LoginScene", Scene) local LOGTAG = "LoginScene" function LoginScene:ctor(sceneInfo) super.ctor(self) self.sceneInfo = sceneInfo self.args = sceneInfo.args or {} self.R = ResLoader.loadResLink("modules/login/loginscenereslink") end function LoginScene:info() return { trans = nil, asset = self.R, } end function LoginScene:onLoad() printInfo(LOGTAG,"LoginScene:onLoad updateType:%s", self.updateType) self:initUI() end function LoginScene:initUI() LoginUI.new(self):show() end function LoginScene:onExit() super.onExit(self) end return LoginSceneBuildingInfoUI--[[ 建筑信息ui界面 author:{zhangpeng} time:2025-05-19 17:45:13 ]] local BuildingInfoUI, super = defClass("BuildingInfoUI", UILayer) local TMPUGUI = CS.TMPro.TextMeshProUGUI local LOGTAG = "BuildingInfoUI" function BuildingInfoUI:ctor(data) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/buildinglist/buildinginforeslink") self.data = data end function BuildingInfoUI:onLoad() self.ui = GameObject.Instantiate(self.R.building_info) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() end function BuildingInfoUI:initUI() printInfo(LOGTAG, "initUI") local info = BuildingMgr:getBuildingInfo(self.data.uid) -- 名字 local nameNode = self.ui:Seek("name") nameNode[TMPUGUI].text = info:getName() -- 描述 local descNode = self.ui:Seek("desc") descNode[TMPUGUI].text = info:getDesc() -- 图片 self:setIcon(info) -- 加成 self:initAttribute() -- 限制 self:initLimit() -- 关闭按钮 local closeBtn = self.ui:Seek("close_btn") util.ugui.addButtonClickEvent(closeBtn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) local isUnlocked = info:isUnlocked() local isPurchased = info:isPurchased() local isInUse = info:isInUse() self.btn_buy = self.ui:Seek("btn_buy") local text_buy = self.ui:Seek("text_buy") local inUseId = info:getInUseId() self.btn_buy:SetActive(isUnlocked and not isPurchased) if isUnlocked and not isPurchased then if info:getBuyPrice() <= 0 then text_buy[TMPUGUI].text = "免费" else text_buy[TMPUGUI].text = string.format("%d购买", info:getBuyPrice()) end end util.ugui.addButtonClickEvent(self.btn_buy, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:buy() end) self.video_btn = self.ui:Seek("video_btn") local price = info:getBuyPrice() local shortage = price - CurrencyMgr:getCoin() self.video_btn:SetActive(isUnlocked and (not isPurchased) and (shortage > 0) and (shortage < price * 0.2)) util.ugui.addButtonClickEvent(self.video_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:buyByVideo() end) self.btn_inuse = self.ui:Seek("btn_inuse") self.btn_inuse:SetActive(isInUse) self.btn_replace = self.ui:Seek("btn_replace") self.btn_replace:SetActive(isPurchased and not isInUse and inUseId ~= -1 and inUseId == self.data.uid) util.ugui.addButtonClickEvent(self.btn_replace, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:replace() end) local btn_use = self.ui:Seek("btn_use") btn_use:SetActive(isPurchased and not isInUse and inUseId == -1) util.ugui.addButtonClickEvent(btn_use, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:use() end) end -- 设置图片 function BuildingInfoUI:setIcon(info) -- -- 图片 local iconNode = self.ui:Seek("icon") local iconImage = iconNode[UGUI.Image] iconImage.enabled = true iconImage.sprite = info:getUiIcon() util.ugui:setImageTileSize(iconImage, 260) end --- 初始化加成标签 function BuildingInfoUI:initAttribute() -- 加成数据显示 local attributeNode = self.ui:SearchPattern("info_\\d", true) for i = 1, #attributeNode do local attDescNode = attributeNode[i]:Seek("desc") local desc_all = BonusMgr:getBonusListById(self.data.effectId) if i <= #desc_all then local desc_txt = desc_all[i].desc if desc_txt then printInfo(LOGTAG, "desc_txt:%s", desc_txt) attDescNode[TMPUGUI].text = desc_txt end else attributeNode[i]:SetActive(false) end end end -- 初始化限制标签 function BuildingInfoUI:initLimit() -- 限制数据显示 local limitNode = self.ui:SearchPattern("cond_\\d", true) for i = 1, #limitNode do local has = BuyMgr:getLimitIdByIdx(self.data.buyConditionId, i) ~= -1 limitNode[i]:SetActive(has) if has then limitNode[i][TMPUGUI].text = BuyMgr:getLimitDescByIdx(self.data.buyConditionId, i) end end end --- 购买 function BuildingInfoUI:buy() if BuildingMgr:buyBuilding(self.data.uid) then BuildingMgr:useBuilding(self.data.uid) -- 自动使用购买的建筑 UILayerUtil:closeUIByName("BuildingInfoUI") UILayerUtil:closeUIByName("BuildingsListUI") end end --- 购买 function BuildingInfoUI:buyByVideo() local info = BuildingMgr:getBuildingInfo(self.data.uid) local shortage = info:getBuyPrice() - CurrencyMgr:getCoin() if BuildingMgr:buyBuilding(self.data.uid, shortage) then BuildingMgr:useBuilding(self.data.uid) -- 自动使用购买的建筑 UILayerUtil:closeUIByName("BuildingInfoUI") UILayerUtil:closeUIByName("BuildingsListUI") end end -- 使用 function BuildingInfoUI:use() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.building) BuildingMgr:useBuilding(self.data.uid) UILayerUtil:closeUIByName("BuildingInfoUI") UILayerUtil:closeUIByName("BuildingsListUI") end -- 替换 function BuildingInfoUI:replace() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.building) BuildingMgr:replaceBuilding(self.data.uid) UILayerUtil:closeUIByName("BuildingInfoUI") UILayerUtil:closeUIByName("BuildingsListUI") end return BuildingInfoUI StenchCustomer ---臭气顾客 ---@class StenchCustomer:CustomerSpecial local StenchCustomer, super = defClass("StenchCustomer", CustomerSpecial) local LOGTAG = "StenchCustomer" --- 状态 StenchCustomer.State = { Idle = 1, -- 空闲 Entrance = 2, -- 入场 Stench = 3, -- 释放臭气 Leaving = 4, -- 离开 } --- 构造函数 function StenchCustomer:ctor(resLink) super.ctor(self, resLink, CustomerConst.CustomerSpecialType.StenchCustomer) self:init() end --- 初始化 function StenchCustomer:init() self:initDisplay("stench_customer") self:initProperties() self:startStenchLogic() end --- 初始化属性 function StenchCustomer:initProperties() self.info = SpecialCustomerMgr:getSpecialCustomerInfo(self.specialCustomerTypeId) self.state = StenchCustomer.State.Idle -- 初始状态 self.activeTime = self.info:getActiveTime() -- 活动时间 self.clickTimes = self.info:getClickTimes() -- 点击次数 self.attackInterval = self.info:getParam(1) -- 释放臭气间隔 self.currClickTimes = 0 -- 当前点击次数 self.driveLimit = 12 self.evictions = 12 end --- 开始臭气顾客逻辑 function StenchCustomer:startStenchLogic() printInfo(LOGTAG, "臭气顾客开始活动,活动时间:%d秒", self.info:getActiveTime()) -- 入场 self:enter() -- 添加点击事件 self:addClickEvent(function() self:click() end) end --- 入店 function StenchCustomer:enter() printInfo(LOGTAG, "臭气顾客入店") self.state = StenchCustomer.State.Entrance self:walkToPos(self:getSpecialCustomerCommonStandbyPoint().transform.position, super.tempSpeed, function() self:showDong() self:startTimer() end ) end --- 离开 function StenchCustomer:exit() printInfo(LOGTAG, "臭气顾客离开") self.state = StenchCustomer.State.Leaving self:stopTimer(2) self:walkToPos(self:getSpecialCustomerExitPoint().transform.position, super.tempSpeed, function() -- 离开后销毁 self:destroy() -- 触发离开事件 end ) end --- 时间更新 function StenchCustomer:timeupdate(seconds) if self.state == StenchCustomer.State.Stench then self.time = self.time + seconds if self.time >= self.activeTime then self:exit() end -- todo 顾客离场 end end --- 被驱赶 function StenchCustomer:drive() printInfo(LOGTAG, "臭气顾客被驱赶") self:exit() end --- 交互 function StenchCustomer:click() if self.state ~= StenchCustomer.State.Waiting then return end if self.clickTimes <= 1 then self:drive() else if self.currClickTimes < self.clickTimes then self.currClickTimes = self.currClickTimes + 1 self:showClickProress(self.currClickTimes / self.clickTimes) else self:drive() end end end return StenchCustomerloginscenereslinkureturn { --BASIC --ASSET SCENE = {"Assets/AssetsPackage/Res/modules/login/scene/LoginScene.unity", 0, 1}, } ReceptionDesk --[[ 接待台 author:{zhangpeng} time:2025-05-16 14:50:32 ]] local ReceptionDesk,super = defClass("ReceptionDesk",BuildingBase) require("modules/ui/reception/ReceptionUI") local LOGTAG = "ReceptionDesk" function ReceptionDesk:ctor(args) super.ctor(self,args) self.deskCfgId = args.deskId self.resId = "building_"..self.buildingInfo.buildingType self.deskView = nil self:initReceptionDesk() local info = BuildingMgr:getBuildingInfo(self.deskCfgId) info:initTips() info:refreshTips() self:tipUpdate() self.tmr = self:timer(function(dt) self:tipUpdate(dt) end, 60, 0) self:doBubbleBlink() end function ReceptionDesk:initReceptionDesk() self.buildingNode = GameObject.Instantiate(self.scene.R[self.resId]) self.sortBaseNode = self.buildingNode:Seek("base") self:setBuildingParent(self.buildingParent) self:setBuildingNodeName(self.resId) self.tip = self.buildingNode:Seek("tip") self.tipValue = self.tip:Seek("tip_value") -- 设置层级 -- local sprite_render = self.buildingNode:Seek("img"):GetComponent("Renderer") -- sprite_render.sortingOrder = 11 self:addClickEvent() end -- 点击迎宾台 function ReceptionDesk:addClickEvent() local restaurantScene = RestaurantMgr:getRestaurantScene() local touchCom = restaurantScene.touchCom touchCom:addListener(self.buildingNode:Seek("img"), TouchCom.LISTENER_TYPE.CLICK, function(p) printInfo(LOGTAG, "迎宾台被点击") self:openReceptionView() end ) touchCom:addListener(self.tip, TouchCom.LISTENER_TYPE.CLICK, function(p) printInfo(LOGTAG, "迎宾台小费气泡被点击") self:openReceptionView() end ) end -- 计时 function ReceptionDesk:tipUpdate(seconds) if not self.tip then self:clear(true, self.tmr) end local info = BuildingMgr:getBuildingInfo(self.deskCfgId) info:updateTip() local currTips = info:getCurrTips() self.tip:SetActive(currTips > 0) if currTips > 0 then self.tipValue:GetComponent("TextMeshPro").text = "小费" .. info:getCurrTips() if self.deskView then self.deskView:refreshTips(currTips) end end end -- 气泡闪烁 function ReceptionDesk:doBubbleBlink() local _initScale = 1 self.blinkAction = self.tip:RunAction(ua.RepeatForever( ua.Sequence({ ua.ScaleTo(0.1, _initScale * 1.1), ua.ScaleTo(0.2, _initScale), ua.ScaleTo(0.1, _initScale * 1.1), ua.ScaleTo(0.2, _initScale), ua.Delay(2) -- 闪烁间隔 }) )) printInfo(LOGTAG, "气泡闪烁效果已启动") end -- 停止闪烁效果 function ReceptionDesk:stopBubbleBlink() if self.blinkAction then self.tip:StopAction(self.blinkAction) self.blinkAction = nil end end function ReceptionDesk:openReceptionView() self.deskView = ReceptionUI.new(self.deskCfgId) self.deskView:show():showMask():enableCloseWhenClickMask():addCloseCallback( function() printInfo(LOGTAG, "迎宾台界面关闭") self.deskView = nil self:tipUpdate() end ) end return ReceptionDeskcommonBonusCfgi--[[ from file:通用加成参数.xlsx --]] local commonBonusCfg = { [1] = { id = 120101, bonusNums = 2, bonusId_1 = 2001, value_1 = 5, bonusId_2 = 2002, value_2 = 3, bonusId_3 = -1, value_3 = -1, }, [2] = { id = 120102, bonusNums = 2, bonusId_1 = 2001, value_1 = 10, bonusId_2 = 2002, value_2 = 5, bonusId_3 = -1, value_3 = -1, }, [3] = { id = 120103, bonusNums = 2, bonusId_1 = 2001, value_1 = 15, bonusId_2 = 2002, value_2 = 8, bonusId_3 = -1, value_3 = -1, }, [4] = { id = 120104, bonusNums = 2, bonusId_1 = 2001, value_1 = 22, bonusId_2 = 2002, value_2 = 12, bonusId_3 = -1, value_3 = -1, }, [5] = { id = 120105, bonusNums = 2, bonusId_1 = 2001, value_1 = 110, bonusId_2 = 2002, value_2 = 17, bonusId_3 = -1, value_3 = -1, }, [6] = { id = 120201, bonusNums = 2, bonusId_1 = 2001, value_1 = 7, bonusId_2 = 2003, value_2 = 7200, bonusId_3 = -1, value_3 = -1, }, [7] = { id = 120202, bonusNums = 2, bonusId_1 = 2001, value_1 = 14, bonusId_2 = 2003, value_2 = 7200, bonusId_3 = -1, value_3 = -1, }, [8] = { id = 120203, bonusNums = 2, bonusId_1 = 2001, value_1 = 21, bonusId_2 = 2003, value_2 = 7200, bonusId_3 = -1, value_3 = -1, }, [9] = { id = 120204, bonusNums = 2, bonusId_1 = 2001, value_1 = 31, bonusId_2 = 2003, value_2 = 7200, bonusId_3 = -1, value_3 = -1, }, [10] = { id = 120205, bonusNums = 2, bonusId_1 = 2001, value_1 = 155, bonusId_2 = 2003, value_2 = 7200, bonusId_3 = -1, value_3 = -1, }, [11] = { id = 120301, bonusNums = 2, bonusId_1 = 2001, value_1 = 5, bonusId_2 = 2002, value_2 = 3, bonusId_3 = -1, value_3 = -1, }, [12] = { id = 120302, bonusNums = 2, bonusId_1 = 2001, value_1 = 10, bonusId_2 = 2002, value_2 = 5, bonusId_3 = -1, value_3 = -1, }, [13] = { id = 120303, bonusNums = 2, bonusId_1 = 2001, value_1 = 15, bonusId_2 = 2002, value_2 = 8, bonusId_3 = -1, value_3 = -1, }, [14] = { id = 120304, bonusNums = 2, bonusId_1 = 2001, value_1 = 22, bonusId_2 = 2002, value_2 = 12, bonusId_3 = -1, value_3 = -1, }, [15] = { id = 120305, bonusNums = 2, bonusId_1 = 2001, value_1 = 110, bonusId_2 = 2002, value_2 = 17, bonusId_3 = -1, value_3 = -1, }, [16] = { id = 120401, bonusNums = 2, bonusId_1 = 2001, value_1 = 5, bonusId_2 = 2002, value_2 = 3, bonusId_3 = -1, value_3 = -1, }, [17] = { id = 120402, bonusNums = 2, bonusId_1 = 2001, value_1 = 10, bonusId_2 = 2002, value_2 = 5, bonusId_3 = -1, value_3 = -1, }, [18] = { id = 120403, bonusNums = 2, bonusId_1 = 2001, value_1 = 15, bonusId_2 = 2002, value_2 = 8, bonusId_3 = -1, value_3 = -1, }, [19] = { id = 120404, bonusNums = 2, bonusId_1 = 2001, value_1 = 22, bonusId_2 = 2002, value_2 = 12, bonusId_3 = -1, value_3 = -1, }, [20] = { id = 120405, bonusNums = 2, bonusId_1 = 2001, value_1 = 110, bonusId_2 = 2002, value_2 = 17, bonusId_3 = -1, value_3 = -1, }, [21] = { id = 120501, bonusNums = 2, bonusId_1 = 2001, value_1 = 5, bonusId_2 = 2002, value_2 = 3, bonusId_3 = -1, value_3 = -1, }, [22] = { id = 120502, bonusNums = 2, bonusId_1 = 2001, value_1 = 10, bonusId_2 = 2002, value_2 = 5, bonusId_3 = -1, value_3 = -1, }, [23] = { id = 120503, bonusNums = 2, bonusId_1 = 2001, value_1 = 15, bonusId_2 = 2002, value_2 = 8, bonusId_3 = -1, value_3 = -1, }, [24] = { id = 120504, bonusNums = 2, bonusId_1 = 2001, value_1 = 22, bonusId_2 = 2002, value_2 = 12, bonusId_3 = -1, value_3 = -1, }, [25] = { id = 120505, bonusNums = 2, bonusId_1 = 2001, value_1 = 110, bonusId_2 = 2002, value_2 = 17, bonusId_3 = -1, value_3 = -1, }, [26] = { id = 120901, bonusNums = 2, bonusId_1 = 2001, value_1 = 13, bonusId_2 = 2004, value_2 = 80, bonusId_3 = -1, value_3 = -1, }, [27] = { id = 120902, bonusNums = 2, bonusId_1 = 2001, value_1 = 26, bonusId_2 = 2004, value_2 = 100, bonusId_3 = -1, value_3 = -1, }, [28] = { id = 120903, bonusNums = 2, bonusId_1 = 2001, value_1 = 39, bonusId_2 = 2004, value_2 = 120, bonusId_3 = -1, value_3 = -1, }, [29] = { id = 120904, bonusNums = 2, bonusId_1 = 2001, value_1 = 45, bonusId_2 = 2004, value_2 = 200, bonusId_3 = -1, value_3 = -1, }, [30] = { id = 120905, bonusNums = 2, bonusId_1 = 2001, value_1 = 225, bonusId_2 = 2004, value_2 = 300, bonusId_3 = -1, value_3 = -1, }, [31] = { id = 121001, bonusNums = 2, bonusId_1 = 2001, value_1 = 12, bonusId_2 = 2004, value_2 = 100, bonusId_3 = -1, value_3 = -1, }, [32] = { id = 121002, bonusNums = 2, bonusId_1 = 2001, value_1 = 24, bonusId_2 = 2004, value_2 = 120, bonusId_3 = -1, value_3 = -1, }, [33] = { id = 121003, bonusNums = 2, bonusId_1 = 2001, value_1 = 36, bonusId_2 = 2004, value_2 = 140, bonusId_3 = -1, value_3 = -1, }, [34] = { id = 121004, bonusNums = 2, bonusId_1 = 2001, value_1 = 54, bonusId_2 = 2004, value_2 = 220, bonusId_3 = -1, value_3 = -1, }, [35] = { id = 121005, bonusNums = 2, bonusId_1 = 2001, value_1 = 270, bonusId_2 = 2004, value_2 = 320, bonusId_3 = -1, value_3 = -1, }, [36] = { id = 121101, bonusNums = 2, bonusId_1 = 2001, value_1 = 9, bonusId_2 = 2004, value_2 = 120, bonusId_3 = -1, value_3 = -1, }, [37] = { id = 121102, bonusNums = 2, bonusId_1 = 2001, value_1 = 18, bonusId_2 = 2004, value_2 = 150, bonusId_3 = -1, value_3 = -1, }, [38] = { id = 121103, bonusNums = 2, bonusId_1 = 2001, value_1 = 27, bonusId_2 = 2004, value_2 = 180, bonusId_3 = -1, value_3 = -1, }, [39] = { id = 121104, bonusNums = 2, bonusId_1 = 2001, value_1 = 41, bonusId_2 = 2004, value_2 = 260, bonusId_3 = -1, value_3 = -1, }, [40] = { id = 121105, bonusNums = 2, bonusId_1 = 2001, value_1 = 205, bonusId_2 = 2004, value_2 = 360, bonusId_3 = -1, value_3 = -1, }, [41] = { id = 121301, bonusNums = 2, bonusId_1 = 2001, value_1 = 15, bonusId_2 = 2002, value_2 = 8, bonusId_3 = -1, value_3 = -1, }, [42] = { id = 121302, bonusNums = 2, bonusId_1 = 2001, value_1 = 30, bonusId_2 = 2002, value_2 = 10, bonusId_3 = -1, value_3 = -1, }, [43] = { id = 121303, bonusNums = 2, bonusId_1 = 2001, value_1 = 45, bonusId_2 = 2002, value_2 = 13, bonusId_3 = -1, value_3 = -1, }, [44] = { id = 121304, bonusNums = 2, bonusId_1 = 2001, value_1 = 65, bonusId_2 = 2002, value_2 = 17, bonusId_3 = -1, value_3 = -1, }, [45] = { id = 121305, bonusNums = 2, bonusId_1 = 2001, value_1 = 325, bonusId_2 = 2002, value_2 = 22, bonusId_3 = -1, value_3 = -1, }, [46] = { id = 121401, bonusNums = 2, bonusId_1 = 2001, value_1 = 9, bonusId_2 = 2002, value_2 = 5, bonusId_3 = -1, value_3 = -1, }, [47] = { id = 121402, bonusNums = 2, bonusId_1 = 2001, value_1 = 18, bonusId_2 = 2002, value_2 = 7, bonusId_3 = -1, value_3 = -1, }, [48] = { id = 121403, bonusNums = 2, bonusId_1 = 2001, value_1 = 27, bonusId_2 = 2002, value_2 = 10, bonusId_3 = -1, value_3 = -1, }, [49] = { id = 121404, bonusNums = 2, bonusId_1 = 2001, value_1 = 40, bonusId_2 = 2002, value_2 = 14, bonusId_3 = -1, value_3 = -1, }, [50] = { id = 121405, bonusNums = 2, bonusId_1 = 2001, value_1 = 200, bonusId_2 = 2002, value_2 = 19, bonusId_3 = -1, value_3 = -1, }, [51] = { id = 121501, bonusNums = 1, bonusId_1 = 2001, value_1 = 6, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [52] = { id = 121502, bonusNums = 2, bonusId_1 = 2001, value_1 = 12, bonusId_2 = 2005, value_2 = 5, bonusId_3 = -1, value_3 = -1, }, [53] = { id = 121503, bonusNums = 2, bonusId_1 = 2001, value_1 = 18, bonusId_2 = 2005, value_2 = 10, bonusId_3 = -1, value_3 = -1, }, [54] = { id = 121504, bonusNums = 2, bonusId_1 = 2001, value_1 = 27, bonusId_2 = 2005, value_2 = 15, bonusId_3 = -1, value_3 = -1, }, [55] = { id = 121505, bonusNums = 2, bonusId_1 = 2001, value_1 = 135, bonusId_2 = 2005, value_2 = 20, bonusId_3 = -1, value_3 = -1, }, [56] = { id = 121601, bonusNums = 1, bonusId_1 = 2001, value_1 = 6, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [57] = { id = 121602, bonusNums = 2, bonusId_1 = 2001, value_1 = 12, bonusId_2 = 2005, value_2 = 5, bonusId_3 = -1, value_3 = -1, }, [58] = { id = 121603, bonusNums = 2, bonusId_1 = 2001, value_1 = 18, bonusId_2 = 2005, value_2 = 10, bonusId_3 = -1, value_3 = -1, }, [59] = { id = 121604, bonusNums = 2, bonusId_1 = 2001, value_1 = 27, bonusId_2 = 2005, value_2 = 15, bonusId_3 = -1, value_3 = -1, }, [60] = { id = 121605, bonusNums = 2, bonusId_1 = 2001, value_1 = 135, bonusId_2 = 2005, value_2 = 20, bonusId_3 = -1, value_3 = -1, }, [61] = { id = 121701, bonusNums = 2, bonusId_1 = 2001, value_1 = 8, bonusId_2 = 2006, value_2 = 25, bonusId_3 = -1, value_3 = -1, }, [62] = { id = 121702, bonusNums = 2, bonusId_1 = 2001, value_1 = 16, bonusId_2 = 2006, value_2 = 50, bonusId_3 = -1, value_3 = -1, }, [63] = { id = 121703, bonusNums = 2, bonusId_1 = 2001, value_1 = 24, bonusId_2 = 2006, value_2 = 75, bonusId_3 = -1, value_3 = -1, }, [64] = { id = 121704, bonusNums = 2, bonusId_1 = 2001, value_1 = 36, bonusId_2 = 2006, value_2 = 150, bonusId_3 = -1, value_3 = -1, }, [65] = { id = 121705, bonusNums = 2, bonusId_1 = 2001, value_1 = 180, bonusId_2 = 2006, value_2 = 300, bonusId_3 = -1, value_3 = -1, }, [66] = { id = 121801, bonusNums = 2, bonusId_1 = 2001, value_1 = 10, bonusId_2 = 2002, value_2 = 7, bonusId_3 = -1, value_3 = -1, }, [67] = { id = 121802, bonusNums = 2, bonusId_1 = 2001, value_1 = 20, bonusId_2 = 2002, value_2 = 9, bonusId_3 = -1, value_3 = -1, }, [68] = { id = 121803, bonusNums = 2, bonusId_1 = 2001, value_1 = 30, bonusId_2 = 2002, value_2 = 12, bonusId_3 = -1, value_3 = -1, }, [69] = { id = 121804, bonusNums = 2, bonusId_1 = 2001, value_1 = 45, bonusId_2 = 2002, value_2 = 16, bonusId_3 = -1, value_3 = -1, }, [70] = { id = 121805, bonusNums = 2, bonusId_1 = 2001, value_1 = 225, bonusId_2 = 2002, value_2 = 21, bonusId_3 = -1, value_3 = -1, }, [71] = { id = 121901, bonusNums = 2, bonusId_1 = 2001, value_1 = 12, bonusId_2 = 2002, value_2 = 8, bonusId_3 = -1, value_3 = -1, }, [72] = { id = 121902, bonusNums = 2, bonusId_1 = 2001, value_1 = 24, bonusId_2 = 2002, value_2 = 10, bonusId_3 = -1, value_3 = -1, }, [73] = { id = 121903, bonusNums = 2, bonusId_1 = 2001, value_1 = 25, bonusId_2 = 2002, value_2 = 13, bonusId_3 = -1, value_3 = -1, }, [74] = { id = 121904, bonusNums = 2, bonusId_1 = 2001, value_1 = 45, bonusId_2 = 2002, value_2 = 18, bonusId_3 = -1, value_3 = -1, }, [75] = { id = 121905, bonusNums = 2, bonusId_1 = 2001, value_1 = 225, bonusId_2 = 2002, value_2 = 23, bonusId_3 = -1, value_3 = -1, }, [76] = { id = 122001, bonusNums = 2, bonusId_1 = 2001, value_1 = 13, bonusId_2 = 2002, value_2 = 6, bonusId_3 = -1, value_3 = -1, }, [77] = { id = 122002, bonusNums = 2, bonusId_1 = 2001, value_1 = 26, bonusId_2 = 2002, value_2 = 8, bonusId_3 = -1, value_3 = -1, }, [78] = { id = 122003, bonusNums = 2, bonusId_1 = 2001, value_1 = 39, bonusId_2 = 2002, value_2 = 12, bonusId_3 = -1, value_3 = -1, }, [79] = { id = 122004, bonusNums = 2, bonusId_1 = 2001, value_1 = 48, bonusId_2 = 2002, value_2 = 16, bonusId_3 = -1, value_3 = -1, }, [80] = { id = 122005, bonusNums = 2, bonusId_1 = 2001, value_1 = 240, bonusId_2 = 2002, value_2 = 21, bonusId_3 = -1, value_3 = -1, }, [81] = { id = 122101, bonusNums = 1, bonusId_1 = 2001, value_1 = 6, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [82] = { id = 122102, bonusNums = 2, bonusId_1 = 2001, value_1 = 12, bonusId_2 = 2005, value_2 = 5, bonusId_3 = -1, value_3 = -1, }, [83] = { id = 122103, bonusNums = 2, bonusId_1 = 2001, value_1 = 18, bonusId_2 = 2005, value_2 = 10, bonusId_3 = -1, value_3 = -1, }, [84] = { id = 122104, bonusNums = 2, bonusId_1 = 2001, value_1 = 27, bonusId_2 = 2005, value_2 = 15, bonusId_3 = -1, value_3 = -1, }, [85] = { id = 122105, bonusNums = 2, bonusId_1 = 2001, value_1 = 135, bonusId_2 = 2005, value_2 = 20, bonusId_3 = -1, value_3 = -1, }, [86] = { id = 122201, bonusNums = 2, bonusId_1 = 2001, value_1 = 14, bonusId_2 = 2006, value_2 = 30, bonusId_3 = -1, value_3 = -1, }, [87] = { id = 122202, bonusNums = 2, bonusId_1 = 2001, value_1 = 25, bonusId_2 = 2006, value_2 = 60, bonusId_3 = -1, value_3 = -1, }, [88] = { id = 122203, bonusNums = 2, bonusId_1 = 2001, value_1 = 40, bonusId_2 = 2006, value_2 = 90, bonusId_3 = -1, value_3 = -1, }, [89] = { id = 122204, bonusNums = 2, bonusId_1 = 2001, value_1 = 80, bonusId_2 = 2006, value_2 = 180, bonusId_3 = -1, value_3 = -1, }, [90] = { id = 122205, bonusNums = 2, bonusId_1 = 2001, value_1 = 400, bonusId_2 = 2006, value_2 = 360, bonusId_3 = -1, value_3 = -1, }, [91] = { id = 122301, bonusNums = 2, bonusId_1 = 2001, value_1 = 8, bonusId_2 = 2002, value_2 = 3, bonusId_3 = -1, value_3 = -1, }, [92] = { id = 122302, bonusNums = 2, bonusId_1 = 2001, value_1 = 16, bonusId_2 = 2002, value_2 = 6, bonusId_3 = -1, value_3 = -1, }, [93] = { id = 122303, bonusNums = 2, bonusId_1 = 2001, value_1 = 24, bonusId_2 = 2002, value_2 = 9, bonusId_3 = -1, value_3 = -1, }, [94] = { id = 122304, bonusNums = 2, bonusId_1 = 2001, value_1 = 36, bonusId_2 = 2002, value_2 = 13, bonusId_3 = -1, value_3 = -1, }, [95] = { id = 122305, bonusNums = 2, bonusId_1 = 2001, value_1 = 180, bonusId_2 = 2002, value_2 = 18, bonusId_3 = -1, value_3 = -1, }, [96] = { id = 122401, bonusNums = 1, bonusId_1 = 2001, value_1 = 5, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [97] = { id = 122402, bonusNums = 2, bonusId_1 = 2001, value_1 = 10, bonusId_2 = 2005, value_2 = 5, bonusId_3 = -1, value_3 = -1, }, [98] = { id = 122403, bonusNums = 2, bonusId_1 = 2001, value_1 = 16, bonusId_2 = 2005, value_2 = 10, bonusId_3 = -1, value_3 = -1, }, [99] = { id = 122404, bonusNums = 2, bonusId_1 = 2001, value_1 = 24, bonusId_2 = 2005, value_2 = 15, bonusId_3 = -1, value_3 = -1, }, [100] = { id = 122405, bonusNums = 2, bonusId_1 = 2001, value_1 = 120, bonusId_2 = 2005, value_2 = 20, bonusId_3 = -1, value_3 = -1, }, [101] = { id = 125101, bonusNums = 2, bonusId_1 = 2001, value_1 = 30, bonusId_2 = 2007, value_2 = 2000, bonusId_3 = -1, value_3 = -1, }, [102] = { id = 125102, bonusNums = 2, bonusId_1 = 2001, value_1 = 75, bonusId_2 = 2007, value_2 = 2300, bonusId_3 = -1, value_3 = -1, }, [103] = { id = 125103, bonusNums = 2, bonusId_1 = 2001, value_1 = 105, bonusId_2 = 2007, value_2 = 2600, bonusId_3 = -1, value_3 = -1, }, [104] = { id = 125201, bonusNums = 2, bonusId_1 = 2001, value_1 = 20, bonusId_2 = 2007, value_2 = 2400, bonusId_3 = -1, value_3 = -1, }, [105] = { id = 125202, bonusNums = 2, bonusId_1 = 2001, value_1 = 60, bonusId_2 = 2007, value_2 = 2750, bonusId_3 = -1, value_3 = -1, }, [106] = { id = 125203, bonusNums = 2, bonusId_1 = 2001, value_1 = 90, bonusId_2 = 2007, value_2 = 3100, bonusId_3 = -1, value_3 = -1, }, [107] = { id = 125301, bonusNums = 2, bonusId_1 = 2001, value_1 = 35, bonusId_2 = 2007, value_2 = 2000, bonusId_3 = -1, value_3 = -1, }, [108] = { id = 125302, bonusNums = 2, bonusId_1 = 2001, value_1 = 110, bonusId_2 = 2007, value_2 = 2300, bonusId_3 = -1, value_3 = -1, }, [109] = { id = 125303, bonusNums = 2, bonusId_1 = 2001, value_1 = 150, bonusId_2 = 2007, value_2 = 2600, bonusId_3 = -1, value_3 = -1, }, [110] = { id = 125401, bonusNums = 2, bonusId_1 = 2001, value_1 = 40, bonusId_2 = 2007, value_2 = 2000, bonusId_3 = -1, value_3 = -1, }, [111] = { id = 125402, bonusNums = 2, bonusId_1 = 2001, value_1 = 125, bonusId_2 = 2007, value_2 = 2300, bonusId_3 = -1, value_3 = -1, }, [112] = { id = 125403, bonusNums = 2, bonusId_1 = 2001, value_1 = 180, bonusId_2 = 2007, value_2 = 2600, bonusId_3 = -1, value_3 = -1, }, [113] = { id = 125501, bonusNums = 2, bonusId_1 = 2001, value_1 = 38, bonusId_2 = 2007, value_2 = 2600, bonusId_3 = -1, value_3 = -1, }, [114] = { id = 125502, bonusNums = 2, bonusId_1 = 2001, value_1 = 115, bonusId_2 = 2007, value_2 = 3000, bonusId_3 = -1, value_3 = -1, }, [115] = { id = 125503, bonusNums = 2, bonusId_1 = 2001, value_1 = 160, bonusId_2 = 2007, value_2 = 3500, bonusId_3 = -1, value_3 = -1, }, [116] = { id = 125601, bonusNums = 2, bonusId_1 = 2001, value_1 = 40, bonusId_2 = 2008, value_2 = 200, bonusId_3 = -1, value_3 = -1, }, [117] = { id = 125602, bonusNums = 2, bonusId_1 = 2001, value_1 = 105, bonusId_2 = 2008, value_2 = 240, bonusId_3 = -1, value_3 = -1, }, [118] = { id = 125603, bonusNums = 2, bonusId_1 = 2001, value_1 = 150, bonusId_2 = 2008, value_2 = 280, bonusId_3 = -1, value_3 = -1, }, [119] = { id = 125701, bonusNums = 3, bonusId_1 = 2001, value_1 = 42, bonusId_2 = 2008, value_2 = 200, bonusId_3 = 2010, value_3 = 2, }, [120] = { id = 125702, bonusNums = 3, bonusId_1 = 2001, value_1 = 135, bonusId_2 = 2008, value_2 = 240, bonusId_3 = 2010, value_3 = 2, }, [121] = { id = 125703, bonusNums = 3, bonusId_1 = 2001, value_1 = 215, bonusId_2 = 2008, value_2 = 280, bonusId_3 = 2010, value_3 = 2, }, [122] = { id = 125801, bonusNums = 2, bonusId_1 = 2001, value_1 = 80, bonusId_2 = 2011, value_2 = 8, bonusId_3 = -1, value_3 = -1, }, [123] = { id = 125802, bonusNums = 2, bonusId_1 = 2001, value_1 = 150, bonusId_2 = 2011, value_2 = 8, bonusId_3 = -1, value_3 = -1, }, [124] = { id = 125803, bonusNums = 2, bonusId_1 = 2001, value_1 = 235, bonusId_2 = 2011, value_2 = 10, bonusId_3 = -1, value_3 = -1, }, [125] = { id = 250801, bonusNums = 1, bonusId_1 = 2007, value_1 = 1500, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [126] = { id = 250802, bonusNums = 1, bonusId_1 = 2007, value_1 = 1500, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [127] = { id = 250803, bonusNums = 1, bonusId_1 = 2007, value_1 = 1500, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [128] = { id = 250804, bonusNums = 1, bonusId_1 = 2007, value_1 = 1575, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [129] = { id = 250805, bonusNums = 1, bonusId_1 = 2007, value_1 = 1650, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [130] = { id = 250806, bonusNums = 1, bonusId_1 = 2007, value_1 = 1725, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [131] = { id = 250807, bonusNums = 1, bonusId_1 = 2007, value_1 = 1800, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [132] = { id = 250808, bonusNums = 1, bonusId_1 = 2007, value_1 = 1875, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [133] = { id = 250809, bonusNums = 1, bonusId_1 = 2007, value_1 = 1950, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [134] = { id = 250810, bonusNums = 1, bonusId_1 = 2007, value_1 = 2025, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [135] = { id = 250811, bonusNums = 1, bonusId_1 = 2007, value_1 = 2100, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [136] = { id = 250812, bonusNums = 1, bonusId_1 = 2007, value_1 = 2175, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [137] = { id = 250813, bonusNums = 1, bonusId_1 = 2007, value_1 = 2250, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [138] = { id = 250814, bonusNums = 1, bonusId_1 = 2007, value_1 = 2325, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [139] = { id = 250815, bonusNums = 1, bonusId_1 = 2007, value_1 = 2400, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [140] = { id = 250816, bonusNums = 1, bonusId_1 = 2007, value_1 = 2475, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [141] = { id = 250817, bonusNums = 1, bonusId_1 = 2007, value_1 = 2550, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [142] = { id = 250818, bonusNums = 1, bonusId_1 = 2007, value_1 = 2625, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [143] = { id = 250819, bonusNums = 1, bonusId_1 = 2007, value_1 = 2700, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [144] = { id = 250820, bonusNums = 1, bonusId_1 = 2007, value_1 = 2775, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [145] = { id = 250821, bonusNums = 1, bonusId_1 = 2007, value_1 = 2850, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [146] = { id = 250822, bonusNums = 1, bonusId_1 = 2007, value_1 = 2925, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [147] = { id = 250823, bonusNums = 1, bonusId_1 = 2007, value_1 = 3000, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [148] = { id = 250824, bonusNums = 1, bonusId_1 = 2007, value_1 = 3075, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [149] = { id = 250825, bonusNums = 1, bonusId_1 = 2007, value_1 = 3150, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [150] = { id = 250826, bonusNums = 1, bonusId_1 = 2007, value_1 = 3225, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [151] = { id = 250827, bonusNums = 1, bonusId_1 = 2007, value_1 = 3300, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [152] = { id = 250828, bonusNums = 1, bonusId_1 = 2007, value_1 = 3375, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [153] = { id = 250829, bonusNums = 1, bonusId_1 = 2007, value_1 = 3450, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [154] = { id = 250830, bonusNums = 1, bonusId_1 = 2007, value_1 = 3525, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [155] = { id = 250831, bonusNums = 1, bonusId_1 = 2007, value_1 = 3600, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [156] = { id = 250832, bonusNums = 1, bonusId_1 = 2007, value_1 = 3675, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [157] = { id = 250833, bonusNums = 1, bonusId_1 = 2007, value_1 = 3750, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [158] = { id = 250834, bonusNums = 1, bonusId_1 = 2007, value_1 = 3825, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, [159] = { id = 250835, bonusNums = 1, bonusId_1 = 2007, value_1 = 3900, bonusId_2 = -1, value_2 = -1, bonusId_3 = -1, value_3 = -1, }, } return commonBonusCfg ThiefCustomerM--[[ 小偷 闲逛的过程中,如果有金币,会停止闲逛,去收集金币,每隔0.5秒检测一次金币 author:{zhangpeng} time:2025-05-30 12:31:07 ]] local ThiefCustomer, super = defClass("ThiefCustomer", CustomerSpecial) local LOGTAG = "ThiefCustomer" -- 小偷状态 ThiefCustomer.State = { Idle = 1, -- 空闲 MovingToCoin = 2, -- 前往金币 Collecting = 3, -- 收集金币 Wandering = 4, -- 闲逛 Leaving = 5, -- 离开 } --- 构造函数 function ThiefCustomer:ctor(reslink) super.ctor(self, reslink, CustomerConst.CustomerSpecialType.ThiefCustomer) self:init() end function ThiefCustomer:init() self:initDisplay() self:initProperties() self:startThiefLogic() end function ThiefCustomer:initDisplay() self.display = GameObject.Instantiate(self.reslink) local scene = RestaurantMgr:getRestaurantScene() local specialCustomerRoot = scene.rootNode:Seek("special_customer") local parent = specialCustomerRoot:Seek("thief") self:setCustomerParent(parent) self.display:SetName(self.resId) -- 设置层级 self:setSortingOrder(GameNodeOrder.Order.role) -- 添加寻路组件 self:addPathFind(self.display) self:setRolePathGrid(MapsConst.GridGraphsName[MapsConst.MapType.restaurant]) end function ThiefCustomer:initProperties() -- 小偷状态 self.state = ThiefCustomer.State.Idle -- 活动时间30秒 self.activityTime = 30 -- 当前活动时间 self.currentTime = 0 -- 目标金币 self.targetCoin = nil -- 是否正在活动中 self.isActive = true -- 当前闲逛点索引 self.currentWanderIndex = 1 -- 闲逛检测间隔(秒) self.wanderCheckInterval = 1 -- 移动过程中的金币检测间隔(秒) self.moveCheckInterval = 0.5 -- 是否应该停止当前移动去收集金币 self.shouldStopForCoin = false -- 当前移动的定时器ID self.currentMoveTimerId = nil -- 被驱赶次数 self.drivenCount = 0 -- 驱赶次数上限,达到后离开餐厅 self.maxDrivenCount = 20 end -- 开始小偷逻辑 function ThiefCustomer:startThiefLogic() printInfo(LOGTAG, "小偷开始活动,活动时间:%d秒", self.activityTime) -- 开始计时 self:startTimer() -- 开始寻找金币 self:searchForCoins() -- 添加点击事件 self:addClickEvent(function() self:click() end) -- 初始化点击进度 self:initClickProgress() end -- 点击 function ThiefCustomer:click() self:onDriven() self:showClickProgress(self.drivenCount / self.maxDrivenCount) end -- 开始计时器 function ThiefCustomer:startTimer() self.timerActionId = self:timer(function(dt) if not self.isActive then return end self.currentTime = self.currentTime + dt if self.currentTime >= self.activityTime then self:stopTimer() -- 时间到,离开餐厅 self:leaveRestaurant() end end,0.01,0) end function ThiefCustomer:stopTimer() if self.timerActionId then TimerMgr:rem(self.timerActionId) self.timerActionId = nil end end -- 寻找金币 function ThiefCustomer:searchForCoins() if not self.isActive then return end -- 获取场景中所有的金币 local availableCoins = self:getAvailableCoins() if #availableCoins > 0 then -- 找到最近的金币 local nearestCoin = self:findNearestCoin(availableCoins) if nearestCoin then self:moveToCollectCoin(nearestCoin) else self:startWandering() end else -- 没有金币,开始闲逛 self:startWandering() end end -- 获取可收集的金币列表 function ThiefCustomer:getAvailableCoins() local coins = {} local coinList = CoinDeskMgr:getCoinsOnDeskList() if coinList then for _, coin in ipairs(coinList) do if coin and coin.coinGo then table.insert(coins, coin) end end end return coins end -- 找到最近的金币 function ThiefCustomer:findNearestCoin(coins) if #coins == 0 then return nil end local currentPos = self:getCurPosition() local nearestCoin = nil local minDistance = math.huge for _, coin in ipairs(coins) do if coin and coin.coinGo then local coinPos = coin.coinGo.transform.position local distance = (currentPos - coinPos).magnitude if distance < minDistance then minDistance = distance nearestCoin = coin end end end return nearestCoin end -- 移动到金币并收集 function ThiefCustomer:moveToCollectCoin(coin) if not coin or not coin.coinGo then self:searchForCoins() return end -- 停止所有闲逛相关的移动和检测 self:stopCurrentMovement() self.state = ThiefCustomer.State.MovingToCoin self.targetCoin = coin local coinPos = coin.coinGo.transform.position printInfo(LOGTAG, "小偷前往收集金币,位置:%s", coinPos) -- 移动到金币位置 self:rolePathFind(coinPos, function() self:collectCoin(coin) end) end -- 收集金币 function ThiefCustomer:collectCoin(coin) if not self.isActive or not coin or not coin.coinGo then return end self.state = ThiefCustomer.State.Collecting printInfo(LOGTAG, "小偷开始收集金币") -- 播放收集动作(这里可以添加动画效果) self.display:Delay(0.5, function() -- 销毁金币 if coin.coinGo then GameObject.Destroy(coin.coinGo) coin.coinGo = nil end -- 从金币管理器中移除 CoinDeskMgr:removeCoinFromList(coin) printInfo(LOGTAG, "小偷成功收集金币") self.targetCoin = nil -- 重置停止标志,允许继续闲逛 self.shouldStopForCoin = false -- 继续寻找下一个金币 self:searchForCoins() end) end -- 开始闲逛 function ThiefCustomer:startWandering() if not self.isActive then return end self.state = ThiefCustomer.State.Wandering printInfo(LOGTAG, "小偷开始闲逛") -- 获取闲逛点列表 local wanderPoints = self:getIdlePointList() if #wanderPoints == 0 then printWarn(LOGTAG, "小偷没有找到闲逛点,等待中...") -- 没有闲逛点,等待一段时间后继续寻找金币 self.display:Delay(2, function() self:searchForCoins() end) return end -- 开始在闲逛点之间移动 self:wanderBetweenPoints(wanderPoints) end -- 在闲逛点之间移动 function ThiefCustomer:wanderBetweenPoints(wanderPoints) if not self.isActive or #wanderPoints == 0 then return end -- 选择下一个闲逛点 local targetPoint = wanderPoints[self.currentWanderIndex] if not targetPoint then self.currentWanderIndex = 1 targetPoint = wanderPoints[self.currentWanderIndex] end if not targetPoint then printError(LOGTAG, "闲逛点无效") return end local targetPos = targetPoint.transform.position printInfo(LOGTAG, "小偷前往闲逛点:%d", self.currentWanderIndex) -- 重置停止标志 self.shouldStopForCoin = false -- 开始移动过程中的金币检测 self:startMoveWithCoinDetection(targetPos, function() if not self.isActive then return end -- 检查是否因为发现金币而被打断 if self.shouldStopForCoin then printInfo(LOGTAG, "小偷因发现金币而停止闲逛") return end printInfo(LOGTAG, "小偷到达闲逛点:%d", self.currentWanderIndex) -- 在闲逛点停留一段时间,同时检测金币 self:stayAtWanderPoint(wanderPoints) end) end -- 在闲逛点停留并检测金币 function ThiefCustomer:stayAtWanderPoint(wanderPoints) if not self.isActive then return end local stayTime = math.random(2, 4) -- 随机停留2-4秒 local checkCount = 0 local maxChecks = math.ceil(stayTime / self.wanderCheckInterval) local function checkForCoins() checkCount = checkCount + 1 if not self.isActive then return end -- 检测是否有金币 local availableCoins = self:getAvailableCoins() if #availableCoins > 0 then printInfo(LOGTAG, "小偷在闲逛时发现金币,前往收集") local nearestCoin = self:findNearestCoin(availableCoins) if nearestCoin then self:moveToCollectCoin(nearestCoin) return end end -- 继续等待或移动到下一个点 if checkCount < maxChecks then self.display:Delay(self.wanderCheckInterval, checkForCoins) else -- 移动到下一个闲逛点 self.currentWanderIndex = self.currentWanderIndex + 1 if self.currentWanderIndex > #wanderPoints then self.currentWanderIndex = 1 end self:wanderBetweenPoints(wanderPoints) end end -- 开始检测 checkForCoins() end -- 带金币检测的移动功能 function ThiefCustomer:startMoveWithCoinDetection(targetPos, onComplete) if not self.isActive then return end -- 开始寻路移动 self:rolePathFind(targetPos, function() -- 停止金币检测定时器 self:stopMoveDetection() -- 如果没有被金币打断,执行完成回调 if not self.shouldStopForCoin and onComplete then onComplete() end end) -- 开始移动过程中的金币检测 self:startMoveDetection() end -- 开始移动过程中的金币检测 function ThiefCustomer:startMoveDetection() if not self.isActive then return end local function detectCoins() if not self.isActive or self.shouldStopForCoin then return end -- 检测是否有金币 local availableCoins = self:getAvailableCoins() if #availableCoins > 0 then printInfo(LOGTAG, "小偷在移动过程中发现金币,停止当前移动") -- 设置停止标志 self.shouldStopForCoin = true -- 停止当前移动 self:stopCurrentMovement() -- 前往收集金币 local nearestCoin = self:findNearestCoin(availableCoins) if nearestCoin then self:moveToCollectCoin(nearestCoin) end return end -- 继续检测 if self.isActive and not self.shouldStopForCoin then self.currentMoveTimerId = self.display:Delay(self.moveCheckInterval, detectCoins) end end -- 开始第一次检测 self.currentMoveTimerId = self.display:Delay(self.moveCheckInterval, detectCoins) end -- 停止移动检测 function ThiefCustomer:stopMoveDetection() if self.currentMoveTimerId then self.display:StopAction(self.currentMoveTimerId) self.currentMoveTimerId = nil end end -- 停止当前移动 function ThiefCustomer:stopCurrentMovement() -- 停止所有移动动作 if self.display then self.display:StopAllActions() end -- 停止检测定时器 self:stopMoveDetection() end -- 被驱赶处理 function ThiefCustomer:onDriven() if not self.isActive then return end self.drivenCount = self.drivenCount + 1 printInfo(LOGTAG, string.format("小偷被驱赶,第%d次,上限%d次", self.drivenCount, self.maxDrivenCount)) -- 如果达到驱赶上限,立即离开餐厅 if self.drivenCount >= self.maxDrivenCount then printInfo(LOGTAG, "小偷被驱赶次数达到上限,强制离开餐厅") self:forcedLeaveRestaurant() else -- 被驱赶时的反应,例如暂停一下或改变行为 self:onDrivenReaction() end end -- 被驱赶时的反应 function ThiefCustomer:onDrivenReaction() -- 停止当前移动 self:stopCurrentMovement() -- 播放被驱赶晃动动画 self:playDrivenShakeAnimation() -- 短暂停留后继续活动 self.display:Delay(1, function() if self.isActive then -- 重新开始寻找金币 self:searchForCoins() end end) end -- 播放被驱赶晃动动画 function ThiefCustomer:playDrivenShakeAnimation() if not self.display then return end printInfo(LOGTAG, "小偷播放被驱赶晃动动画") -- 获取原始位置 local originalPos = self.display.transform.position -- 晃动参数 local shakeIntensity = 0.1 -- 晃动强度 local shakeDuration = 0.5 -- 晃动持续时间 local shakeFrequency = 0.05 -- 晃动频率 -- 创建晃动序列 local shakeActions = {} local shakeSteps = math.floor(shakeDuration / shakeFrequency) for i = 1, shakeSteps do -- 随机生成晃动偏移 local offsetX = (math.random() - 0.5) * 2 * shakeIntensity local offsetY = (math.random() - 0.5) * 2 * shakeIntensity local shakePos = Vector3(originalPos.x + offsetX, originalPos.y + offsetY, originalPos.z) -- 添加移动到晃动位置的动作 table.insert(shakeActions, ua.MoveTo(shakeFrequency, shakePos)) end -- 最后回到原始位置 table.insert(shakeActions, ua.MoveTo(shakeFrequency, originalPos)) -- 执行晃动动画 self.display:RunAction(ua.Sequence(shakeActions)) end -- 强制离开餐厅(被驱赶达到上限) function ThiefCustomer:forcedLeaveRestaurant() if not self.isActive then return end printInfo(LOGTAG, "小偷因被多次驱赶而强制离开餐厅") -- 停止所有活动 self.isActive = false self.state = ThiefCustomer.State.Leaving -- 停止所有移动和检测 self:stopCurrentMovement() -- 直接前往出口 local scene = RestaurantMgr:getRestaurantScene() if scene then local exitPoint = scene.rootNode:Seek("sp_exit_point") if exitPoint then local exitPos = exitPoint:GetPosition() -- 移动到出口 self:rolePathFind(exitPos, function() printInfo(LOGTAG, "小偷因驱赶到达出口,离开餐厅") self:destroy() end) else -- 没有出口点,直接销毁 self:destroy() end else self:destroy() end end -- 离开餐厅 function ThiefCustomer:leaveRestaurant() if not self.isActive then return end self.isActive = false self.state = ThiefCustomer.State.Leaving printInfo(LOGTAG, "小偷活动时间结束,准备离开餐厅") -- 停止所有移动和检测 self:stopCurrentMovement() -- 获取出口点 local scene = RestaurantMgr:getRestaurantScene() if scene then local exitPoint = scene.rootNode:Seek("sp_out_point") if exitPoint then local exitPos = exitPoint:GetPosition() -- 移动到出口 self:rolePathFind(exitPos, function() printInfo(LOGTAG, "小偷到达出口,离开餐厅") self:destroy() end) else -- 没有出口点,直接销毁 printWarn(LOGTAG, "没有找到小偷出口点,直接销毁") self:destroy() end else self:destroy() end end -- 销毁小偷 function ThiefCustomer:destroy() printInfo(LOGTAG, "小偷离开餐厅") -- 停止所有移动和检测 self:stopCurrentMovement() -- 从特殊顾客列表中移除 SpecialCustomerMgr:removeSpecialCustomer(self) -- 销毁游戏对象 if self.display then GameObject.Destroy(self.display) self.display = nil end -- 清理路径线 if self.pathLine then GameObject.Destroy(self.pathLine) self.pathLine = nil end end -- 获取寻路组件 (重写父类方法) function ThiefCustomer:getPathFindSeeker() return self.pathFinder end -- 设置顾客初始位置 (重写父类方法) function ThiefCustomer:setCurPosition(pos) self.curPosition = pos self.display.transform.position = self.curPosition end -- 设置父节点 function ThiefCustomer:setCustomerParent(parent) self.specialCustomerParent = parent self.display:SetParent(parent) end -- 设置地图根节点 function ThiefCustomer:setMapRoot(mapRoot) self.mapRoot = mapRoot end -- 获取当前位置 (重写父类方法) function ThiefCustomer:getCurPosition() if self.display then return self.display.transform.position end return self.curPosition end -- 走到某一个坐标 (重写父类方法) function ThiefCustomer:walkToPos(tarPos, moveSpeed, resolve) -- 计算当前位置到目标位置的距离 local currentPos = self:getCurPosition() if not currentPos then printError(LOGTAG, "无法获取小偷当前位置") if resolve then resolve() end return end local distance = (tarPos - currentPos).magnitude -- 计算移动方向并设置朝向 local moveDir = Vector3(tarPos.x - currentPos.x, tarPos.y - currentPos.y, tarPos.z - currentPos.z) if moveDir.magnitude > 0.001 then moveDir = moveDir.normalized -- 根据移动方向设置角色朝向 if moveDir.x > 0 then -- 向右移动 self:setDirection(RoleConst.direction.left) elseif moveDir.x < 0 then -- 向左移动 self:setDirection(RoleConst.direction.right) end end -- 根据距离和速度计算移动时间 local moveTime = distance / moveSpeed -- 使用Unity的动画系统移动 local moveAction = ua.MoveTo(moveTime, tarPos) self.display:RunAction(ua.Sequence{ moveAction, ua.cb(function() -- 更新当前位置 self:setCurPosition(tarPos) if resolve then resolve() end end) }) end -- 设置朝向 function ThiefCustomer:setDirection(direction) if direction == RoleConst.direction.left then -- 向左移动,翻转角色 self.display.transform.localScale = Vector3(-1, 1, 1) elseif direction == RoleConst.direction.right then -- 向右移动,设置为正向 self.display.transform.localScale = Vector3(1, 1, 1) end end return ThiefCustomerCosLuaBatchUploadTask ---@class CosLuaBatchUploadTask:LuaClass local CosLuaBatchUploadTask = defClass("CosLuaBatchUploadTask") local LOGTAG = "CosLuaBatchUploadTask" ---@param files {srcPath:string, dstPath:string}[] ---@param serviceType CosLuaServiceType ---@param progressCallback fun(progress:number, batchUploadTask:CosLuaBatchUploadTask) ---@param oneCompleteCallback fun(result:boolean, data:{errorCode:integer, errorMsg:string, srcPath:string}, batchUploadTask:CosLuaBatchUploadTask, uploadTask:CosLuaUploadTask) ---@param completeCallback fun(result:boolean, data:{sucCount:integer, failCount:integer}, batchUploadTask:CosLuaBatchUploadTask) ---@param prepareUrlCallback fun(originFilePath:string, prepareFileUrl:string) function CosLuaBatchUploadTask:ctor(files, serviceType, progressCallback, oneCompleteCallback, completeCallback, prepareUrlCallback) ---@type CosLuaUploadTask[] self.tasks = {} self.progressCallback = progressCallback self.oneCompleteCallback = oneCompleteCallback self.completeCallback = completeCallback for _, value in ipairs(files) do local task = CosLuaUploadTask.new(value.srcPath, value.dstPath, serviceType, function (progress, srcPath, uploadTask) self:onOneProgress(progress, srcPath, uploadTask) end, function (result, data, uploadTask) self:onOneComplete(result, data, uploadTask) end, prepareUrlCallback) table.insert(self.tasks, task) end end ---comment ---@param uploadTask CosLuaUploadTask ---@param progress number ---@param srcPath string function CosLuaBatchUploadTask:onOneProgress(progress, srcPath, uploadTask) local all = #self.tasks local allProgress = 0 for _, value in ipairs(self.tasks) do allProgress = allProgress + value.progress end local p = allProgress/all if self.progressCallback then self.progressCallback(p, self) end end ---@param result boolean ---@param data {errorCode:integer, errorMsg:string, srcPath:string} ---@param uploadTask CosLuaUploadTask function CosLuaBatchUploadTask:onOneComplete(result, data, uploadTask) if self.oneCompleteCallback then self.oneCompleteCallback(result, data, self, uploadTask) end local sucCount = 0 for _, value in ipairs(self.tasks) do if not value:isFinished() then return end if value:isSuc() then sucCount = sucCount + 1 end end local failCount = #self.tasks - sucCount if self.completeCallback then self.completeCallback(result, {sucCount = sucCount, failCount = failCount}, self) end end function CosLuaBatchUploadTask:cancel() printInfo(LOGTAG, "cancel") for _, value in ipairs(self.tasks) do value:cancel() end end return CosLuaBatchUploadTask SpecialCustomerMgrS--[[ 地图上可行走的特殊顾客管理 author:{zhangpeng} time:2025-05-30 11:55:48 ]] local SpecialCustomerMgr = defClassStatic("SpecialCustomerMgr") function SpecialCustomerMgr:init() self.specialCustomerList = {} self:initSpecialCustomerInfo() end -- 创建一个地图上可行走的特殊顾客 function SpecialCustomerMgr:createSpecialCustomer(type) local scene = RestaurantMgr:getRestaurantScene() local specialCustomerRoot = scene.rootNode:Seek("special_customer") local birthPoint = scene.rootNode:Seek("sp_enter_point") local birthPos = birthPoint and birthPoint:GetPosition() or Vector3(0, 0, 0) local specialCustomer = nil if type == CustomerConst.CustomerSpecialType.RichCustomer then -- 富二代 specialCustomer = RichCustomer.new(scene.customerR) elseif type == CustomerConst.CustomerSpecialType.AdCustomer then -- 广告顾客 specialCustomer = AdCustomer.new(scene.customerR) elseif type == CustomerConst.CustomerSpecialType.ThiefCustomer then -- 小偷 specialCustomer = ThiefCustomer.new(scene.customerR) elseif type == CustomerConst.CustomerSpecialType.StenchCustomer then -- 臭气顾客 specialCustomer = StenchCustomer.new(scene.customerR) elseif type == CustomerConst.CustomerSpecialType.MagicianCustomer then -- 魔术师 specialCustomer = MagicianCustomer.new(scene.customerR) elseif type == CustomerConst.CustomerSpecialType.SingerCustomer then -- 歌手 specialCustomer = SingingCustomer.new(scene.customerR) elseif type == CustomerConst.CustomerSpecialType.MysteryCustomer then -- 神秘顾客 specialCustomer = MysteryCustomer.new(scene.customerR) elseif type == CustomerConst.CustomerSpecialType.GroundhogBrothers1 then -- 土拨鼠兄弟1 --specialCustomer = GroundhogBrothers1.new(scene.customerR) elseif type == CustomerConst.CustomerSpecialType.GroundhogBrothers2 then -- 土拨鼠兄弟2 --specialCustomer = GroundhogBrothers2.new(scene.customerR) end if specialCustomer then specialCustomer:setCurPosition(Vector3(birthPos.x, birthPos.y, birthPos.z)) -- 设置地图根节点 specialCustomer:setMapRoot(scene.rootNode) self:addToQueue(specialCustomer) end return specialCustomer end function SpecialCustomerMgr:addToQueue(customer) table.insert(self.specialCustomerList, customer) end -- 初始化特殊顾客数据列表 function SpecialCustomerMgr:initSpecialCustomerInfo() self.specialCustomerInfos = {} end -- 获取特殊顾客数据列表 function SpecialCustomerMgr:getSpecialCustomerList() return self.specialCustomerList end -- 获取特殊顾客数据 function SpecialCustomerMgr:getSpecialCustomerInfo(customerId) if self.specialCustomerInfos[customerId] then return self.specialCustomerInfos[customerId] end local info = SpecialCustomerInfo.new(customerId) self.specialCustomerInfos[customerId] = info return info end function SpecialCustomerMgr:addThiefToScene() --self:createSpecialCustomer(CustomerConst.CustomerSpecialType.RichCustomer) self:createSpecialCustomer(CustomerConst.CustomerSpecialType.AdCustomer) --self:createSpecialCustomer(CustomerConst.CustomerSpecialType.ThiefCustomer) --self:createSpecialCustomer(CustomerConst.CustomerSpecialType.StenchCustomer) --self:createSpecialCustomer(CustomerConst.CustomerSpecialType.MagicianCustomer) --self:createSpecialCustomer(CustomerConst.CustomerSpecialType.SingerCustomer) --self:createSpecialCustomer(CustomerConst.CustomerSpecialType.MysteryCustomer) --self:createSpecialCustomer(CustomerConst.CustomerSpecialType.GroundhogBrothers1) --self:createSpecialCustomer(CustomerConst.CustomerSpecialType.GroundhogBrothers2) end -- 从列表中移除特殊顾客 function SpecialCustomerMgr:removeSpecialCustomer(customer) for i = #self.specialCustomerList, 1, -1 do local specialCustomer = self.specialCustomerList[i] if specialCustomer == customer then table.remove(self.specialCustomerList, i) printInfo("SpecialCustomerMgr", string.format("移除特殊顾客,剩余特殊顾客数量: %d", #self.specialCustomerList)) return true end end return false end return SpecialCustomerMgrTimerMgr---@class TimerMgr:LuaStaticClass local TimerMgr = defClassStatic("TimerMgr") local Time = UnityEngine.Time local LOGTAG = "TimerMgr" function TimerMgr:init() local name = "Timer_" .. tostring(self) if self._gameObject or GameObject.Find(name) then return printWarn(LOGTAG, "Timer init only once") end local go = GameObject(name) GameObject.DontDestroyOnLoad(go) Event.add( go, Event.Update, function(...) self:update(...) end ) self._gameObject = go self.curId = 0 self.taskDict = {} self.taskPriorityList = {} self.activeFlag = true self.pauseLevel = 0 self.isDirty = true end function TimerMgr:exit() local go = self._gameObject if go then Event.remove(go) GameObject.Destroy(go) end self._gameObject = nil end function TimerMgr:setActive(bool) self.activeFlag = bool end function TimerMgr:isActive() return self.activeFlag end -- 延迟一帧执行 function TimerMgr:runAtNextFrame(handler) CS.LuaGlobal.instance:runAtNextFrame(handler) end -- 延迟一帧执行 function TimerMgr:runAtEndOfFrame(handler) CS.LuaGlobal.instance:runAtNextFrame(handler) end ---增加一个timer, 尽量不要调用,使用 self:timer() ---@param handler fun(dt:number) ---@param interval number|nil nil 的话表示下一帧执行 ---@param loop integer|nil nil的话只执行一次, 0的话表示一直执行 ---@param priority integer 默认是0 ---@param isTimingOnBackground boolean ---@param tailHandler fun() 最后一次的回调 ---@return integer id function TimerMgr:add(handler, interval, loop, priority, isTimingOnBackground, tailHandler) self.isDirty = true self.curId = self.curId + 1 if loop == nil then loop = 1 elseif loop <= 0 then loop = nil end self.taskDict[self.curId] = { id = self.curId, handler = handler, time = interval, curTime = 0, count = loop, curCount = 0, priority = priority or 0, isTimingOnBackground = isTimingOnBackground, tailHandler = tailHandler, } return self.curId end --删除 function TimerMgr:rem(id) if id ~= nil then self.isDirty = true self.taskDict[id] = nil end end function TimerMgr:pause(isForce) if isForce then self.pauseLevel = 1 return end if self.pauseLevel < 0 then self.pauseLevel = 0 end self.pauseLevel = self.pauseLevel + 1 end function TimerMgr:resume(isForce) if isForce then self.pauseLevel = 0 return end self.pauseLevel = self.pauseLevel - 1 if self.pauseLevel < 0 then self.pauseLevel = 0 end end function TimerMgr:update() if not self:isActive() then return end if self.pauseLevel > 0 then return end self.dt = Time.deltaTime self:tryUpdateTaskPriorityList() for i, taskList in ipairs(self.taskPriorityList) do for j, task in ipairs(taskList.list) do self:tryDoTask(task) end end end function TimerMgr:tryUpdateTaskPriorityList() if not self.isDirty then return end self.isDirty = false local taskPriorityDict = {} for i, task in pairs(self.taskDict) do local taskList = taskPriorityDict[task.priority] if not taskList then taskList = {priority = task.priority, list = {}} taskPriorityDict[task.priority] = taskList end table.insert(taskList.list, task) end local taskPriorityList = {} for i, v in pairs(taskPriorityDict) do table.insert(taskPriorityList, v) end table.sort( taskPriorityList, function(a, b) return a.priority > b.priority end ) self.taskPriorityList = taskPriorityList end function TimerMgr:tryDoTask(task) if not task.time then self:tryDoTaskCount(task) return end task.curTime = task.curTime + self.dt local count = math.floor(task.curTime / task.time) if count < 1 then return end for i = 1, count do self:tryDoTaskCount(task) end task.curTime = task.curTime - (task.time * count) end function TimerMgr:tryDoTaskCount(task) local dt = task.time or self.dt if not task.count then task.handler(dt) return end task.curCount = task.curCount + 1 if task.curCount <= task.count then task.handler(dt) end if task.curCount == task.count then self:rem(task.id) if task.tailHandler then task.tailHandler() end end end function TimerMgr:onAppPause() self.enterBackgroundTime = Time.realtimeSinceStartup end function TimerMgr:onAppResume() self.enterBackgroundTime = self.enterBackgroundTime or Time.realtimeSinceStartup local dt = Time.realtimeSinceStartup - self.enterBackgroundTime if dt < 0 then dt = 0 end for _, task in pairs(self.taskDict) do if task.isTimingOnBackground then task.curTime = task.curTime + dt end end end TilemapCfg--[[ tilemap阻挡层配置信息 author:{zhangpeng} time:2025-05-14 10:30:56 ]] local TilemapCfg = defClassStatic("TilemapCfg") TilemapCfg.ObEnum = { ob_ground = 1, ob_buildings = 2, } -- 阻挡层,key为阻挡层枚举值,value为阻挡层名称 TilemapCfg.ObstacleGroups = { [TilemapCfg.ObEnum.ob_ground] = "ob_ground", [TilemapCfg.ObEnum.ob_buildings] = "ob_buildings", } TilemapCfg:init()mainfrequire("common/core/app/App") require("common/core/app/Msg") require("common/core/app/scene/main")FishingRewardCfgParse-- 捕鱼奖励配置解析 local FishingRewardCfgParse = defClassStatic("FishingRewardCfgParse") local FishingRewardCfgData = require("data/config/fishingRewardCfg") local LOGTAG = "FishingRewardCfgParse" -- -- 初始化 function FishingRewardCfgParse:init() self.idDic = {} for _, v in pairs(FishingRewardCfgData) do self.idDic[v.id] = v end end -- 通过id获取捕鱼奖励配置 function FishingRewardCfgParse:getFishingRewardCfgById(id) return self.idDic[id] end -- 获取所有捕鱼奖励配置 function FishingRewardCfgParse:getAllFishingRewardCfg() return FishingRewardCfgData end -- 按进度获取指定的捕鱼奖励配置 function FishingRewardCfgParse:getFishingRewardCfgByProgress(progress) local last = nil for _, v in ipairs(FishingRewardCfgData) do if progress < v.progress then return last end last = v end return last end -- 获取所有捕鱼奖励id function FishingRewardCfgParse:getAllIds() local ids = {} for _, v in pairs(FishingRewardCfgData) do table.insert(ids, v.id) end return ids end FishingRewardCfgParse:init()MusicBbqCustomerCfgParsex-- local MusicBbqCustomerCfgData = require("data/config/musicBbqCustomerCfg") local MusicBbqCustomerCfgParse = defClassStatic("MusicBbqCustomerCfgParse") local LOGTAG = "MusicBbqCustomerCfgParse" -- 初始化 function MusicBbqCustomerCfgParse:init() self.idDic = {} for _, v in pairs(MusicBbqCustomerCfgData) do self.idDic[v.id] = v end end -- 获取所有音乐烤吧顾客配置 function MusicBbqCustomerCfgParse:getData() return MusicBbqCustomerCfgData end -- 获取音乐烤吧顾客配置 function MusicBbqCustomerCfgParse:getMusicBbqCustomerCfgById(id) return self.idDic[id] end -- 获取音乐烤吧顾客配置列表 function MusicBbqCustomerCfgParse:getMusicBbqCustomerCfgList() local list = {} for _, v in pairs(MusicBbqCustomerCfgData) do table.insert(list, v) end return list end MusicBbqCustomerCfgParse:init() UITypeEnums$--[[ UI组件枚举 author:{zhangpeng} time:2024-04-29 21:01:39 ]] local UITypeEnums = defClassStatic("UITypeEnums") local LOG_TAG = "UITypeEnums" -- 弹窗类型 UITypeEnums.DialogType = { Common = 1, SkinPartAdUI = 2, -- 点击未解锁皮肤时的看广告弹窗 SkinPartUnlock =3, -- 金币充足,弹窗确认兑换皮肤 SuitUnlock = 4, -- 钻石充足,弹窗确认兑换套装 WorldAdBtn = 5, WorldMapUnlockUI = 6, -- 场景解锁弹窗 } UITypeEnums.DialogPrefabs = { [UITypeEnums.DialogType.Common] = "Assets/AssetsPackage/Res/common/ui/dailog/prefab/common_dailog.prefab", [UITypeEnums.DialogType.SkinPartAdUI] = "Assets/AssetsPackage/Res/common/ui/dailog/prefab/dailog_skin_ad.prefab", [UITypeEnums.DialogType.SkinPartUnlock] = "Assets/AssetsPackage/Res/common/ui/dailog/prefab/dailog_skin_unlock.prefab", [UITypeEnums.DialogType.SuitUnlock] = "Assets/AssetsPackage/Res/common/ui/dailog/prefab/dailog_suit_unlock.prefab", [UITypeEnums.DialogType.WorldAdBtn] = "Assets/AssetsPackage/Res/common/ui/dailog/prefab/dailog_worldmap_ui_ad.prefab", [UITypeEnums.DialogType.WorldMapUnlockUI] = "Assets/AssetsPackage/Res/Modules/holoworldmap/prefab/ui/unlock_scene_pop_ui.prefab", } function UITypeEnums:init() end UITypeEnums:init() Customer@--[[ 普通顾客 在地图上行走的,有就餐行为的普通顾客 author:{zhangpeng} time:2025-05-15 11:04:21 ]] ---@class Customer local Customer,super = defClass("Customer",RoleBase) local LOGTAG = "Customer" ---构造函数 function Customer:ctor(reslink,cfgId) super:ctor(RoleConst.roleType.customer) self.R = reslink -- 顾客配置表id self.customerCfgId = cfgId -- 顾客初始化 self:init() printInfo(LOGTAG, string.format("顾客[%s]初始化完成, 配置表id:%s", self.roleUniqueId, self.customerCfgId)) end function Customer:init() self:initCustomerCfg() self:initDisplay() -- 初始化点餐状态 self.isOrdering = false self.hasOrdered = false -- 用于存储头顶菜品气泡引用 self.cuisineBubble = nil -- 顾客状态 self.state = CustomerConst.State.waiting -- 当前播放的动画 self.currentAnim = nil -- 当前座位类型 (用于判断是正面还是背面坐姿) self.seatType = nil -- 移动状态标记(防止重复播放行走动画) self.isMoving = false -- 是否为测试角色(只有测试角色才能被手动打断寻路) self.isTestCustomer = false -- 寻路状态标记(仅用于测试角色) self.isPathFinding = false -- 当前移动的回调函数(仅用于测试角色) self.currentPathCallback = nil -- 所处场景类型 self.sceneType = nil -- 行为标签 self.behaviorTag = nil end --- 初始化顾客 function Customer:initCustomerCfg() -- 初始化顾客配置表id local data = CustomerCfgParse:getCustomerCfg(self.customerCfgId) if not data then printError(LOGTAG, string.format("顾客配置表id不存在:%s", self.customerCfgId)) return end self.customerCfgData = data -- 顾客名称 self.name = data.name -- 性格 self.nature = data.nature -- 顾客描述 self.desc = data.desc -- 移动速度 self.moveSpeed = data.movespeed -- 来访星数要求 self.visitNeedStar = data.visitNeedStar -- 来访物品id(key:物品类型,value:物品id) self.visitItemIds = data.visitItemId -- 喜爱食物 self.favoriteFoods = data.favoriteFoods -- 招揽标签 self.solicitTagId = data.solicitTagId -- 功能点id(key:功能点类型,value:功能点id) self.funIds = data.funId -- 美术资源 self.artRes = data.artRes -- 剧情id self.plotId = data.plotId -- 首次分享奖励金币 self.shareCoin = data.shareCoin -- 新顾客提示(0:不提示,1:提示) self.newCustomerTip = data.newCustomerTip end -- 初始化显示相关 function Customer:initDisplay() local customerName = string.format("customer_%s", self.customerCfgId) self.asset = self.R[customerName] printInfo(LOGTAG, string.format("初始化顾客[%s-%s]显示资源: %s", self.customerCfgId, self.roleUniqueId, self.asset)) self.display = GameObject.Instantiate(self.asset) -- 角色spine动画节点 self.customerAnim = self.display:Seek("customer_anim") self:setCustomerName(customerName) -- 头顶菜气泡 self.bubble = self.display:Seek("bubble") self.bubble:SetActive(false) -- 头顶吃饭进度图标 self.eatingProgress = self.display:Seek("eat_progress") self.display:Seek("order"):SetActive(false) self.sortBaseNode = self.display:Seek("base_point") -- 设置初始层级(基于Y坐标动态计算) self:updateDynamicSortingOrder() -- 添加寻路组件 self:addPathFind(self.display) -- 设置寻路网格 self:setRolePathGrid(MapsConst.GridGraphsName[MapsConst.MapType.restaurant]) -- 初始化时播放站立动画 self:playStandAnim() end -- 获取寻路组件 (重写父类方法) function Customer:getPathFindSeeker() return self.pathFinder end -- 重写寻路方法,添加测试角色的状态管理 function Customer:rolePathFind(endPos, onComplete) -- 只有测试角色才支持寻路打断功能 if self.isTestCustomer then -- 如果正在寻路,先停止之前的寻路 if self.isPathFinding then printInfo(LOGTAG, string.format("测试顾客[%s-%s]打断之前的寻路,开始新的寻路", self.customerCfgId, self.roleUniqueId)) self:stopCurrentPathFind() end -- 设置寻路状态 self.isPathFinding = true self.currentPathCallback = onComplete -- 调用父类的寻路方法 super.rolePathFind(self, endPos, function() -- 寻路完成,清除状态 self.isPathFinding = false self.currentPathCallback = nil -- 执行回调 if onComplete then onComplete() end end) else -- 正常角色直接调用父类方法,不支持打断 super.rolePathFind(self, endPos, onComplete) end end -- 停止当前的寻路(仅用于测试角色) function Customer:stopCurrentPathFind() -- 只有测试角色才支持停止寻路 if not self.isTestCustomer or not self.isPathFinding then return end printInfo(LOGTAG, string.format("测试顾客[%s-%s]停止当前寻路", self.customerCfgId, self.roleUniqueId)) -- 停止所有Unity动作 if self.display then self.display:StopAllActions() end -- 清理路径线 if self.pathLine then GameObject.Destroy(self.pathLine) self.pathLine = nil end -- 重置状态 self.isPathFinding = false self.isMoving = false self.currentPathCallback = nil -- 播放站立动画 self:playStandAnim() end -- 设置为测试角色 function Customer:setAsTestCustomer(isTest) self.isTestCustomer = isTest or true if self.isTestCustomer then printInfo(LOGTAG, string.format("顾客[%s-%s]设置为测试角色,支持手动寻路打断", self.customerCfgId, self.roleUniqueId)) end end -- 设置父节点 function Customer:setCustomerParent(parent) self.display:SetParent(parent) end -- 设置顾客GameObject名字 function Customer:setCustomerName(name) self.display:SetName(name) end -- 设置所处场景类型 function Customer:setSceneType(sceneType) self.sceneType = sceneType end -- 获取所处场景类型 function Customer:getSceneType() return self.sceneType end -- 设置所处场景类型 function Customer:setBehaviorTag(behaviorTag) self.behaviorTag = behaviorTag end -- 获取所处场景类型 function Customer:getBehaviorTag() return self.behaviorTag end -- 设置顾客sortingOrder function Customer:setSortingOrder(sortingOrder) if not self.customerAnim then printError(LOGTAG, string.format("顾客[%s-%s]customerAnim节点不存在,无法设置层级", self.customerCfgId, self.roleUniqueId)) return end -- SpriteRender local sprite_render = self.customerAnim:GetComponent("Renderer") -- SkeletonAnimation local skeletonAnimation = self.customerAnim:GetComponent("SkeletonAnimation") if sprite_render then sprite_render.sortingOrder = sortingOrder -- printInfo(LOGTAG, string.format("顾客[%s-%s]通过SpriteRender设置层级: %d", self.customerCfgId, self.roleUniqueId, sortingOrder)) elseif skeletonAnimation then local meshRender = skeletonAnimation:GetComponent(typeof(CS.UnityEngine.MeshRenderer)) if meshRender then meshRender.sortingOrder = sortingOrder end -- local spine_render = skeletonAnimation:GetComponent("Renderer") -- if spine_render then -- spine_render.sortingOrder = sortingOrder -- -- printInfo(LOGTAG, string.format("顾客[%s-%s]通过SkeletonAnimation设置层级: %d", self.customerCfgId, self.roleUniqueId, sortingOrder)) -- else -- -- printError(LOGTAG, string.format("顾客[%s-%s]SkeletonAnimation没有Renderer组件", self.customerCfgId, self.roleUniqueId)) -- end else -- printError(LOGTAG, string.format("顾客[%s-%s]customerAnim节点没有找到Renderer或SkeletonAnimation组件", self.customerCfgId, self.roleUniqueId)) end end -- 设置顾客头顶吃饭进度sortingOrder function Customer:setEatingProgressSortingOrder(sortingOrder) local eat_progress_bg_render = self.eatingProgress:Seek("bg"):GetComponent("Renderer") local eat_proress_front_render = self.eatingProgress:Seek("front"):GetComponent("Renderer") eat_progress_bg_render.sortingOrder = sortingOrder + 10 eat_proress_front_render.sortingOrder = sortingOrder + 11 end -- 设置顾客初始位置 (重写父类方法) function Customer:setCurPosition(pos) self.curPosition = pos self.display.transform.position = self.curPosition end -- 获取顾客当前位置 (重写父类方法) function Customer:getCurPosition() -- 优先使用sortBaseNode作为排序基准点 if self.sortBaseNode then return self.sortBaseNode.transform.position end -- 后备方案:使用display根节点 return self.display.transform.position end -- 获取顾客得prefab function Customer:getGameObject() return self.display end -- 走到某一个坐标 (重写父类方法) function Customer:walkToPos(tarPos, moveSpeed, resolve) -- 调用父类方法处理通用逻辑(方向计算、距离计算等) super.walkToPos(self, tarPos, moveSpeed, nil) -- 播放行走动画 self:playWalkAnim() local moveTime = (tarPos - self:getCurPosition()).magnitude / moveSpeed -- 创建一个定时器来在移动过程中更新层级 local updateTimer = self:timer(function() -- 在移动过程中持续更新动态层级 self:updateDynamicSortingOrder() end, 0.1, 0) -- 每0.1秒更新一次,0表示无限循环 self.display:RunAction( ua.Sequence{ ua.MoveTo(moveTime, tarPos), ua.cb(function() -- 停止层级更新定时器 if updateTimer then TimerMgr:rem(updateTimer) end -- 到达目的地后最后更新一次层级 self:updateDynamicSortingOrder() -- 到达目的地后播放站立动画 if not self.isMoving then self:playStandAnim() end if resolve then resolve() end end) } ) end -- 走到目标餐桌的座位 function Customer:walkToDiningDeskSeat(diningDeskSeat) local tarPos = diningDeskSeat:getSeatPosition() self:walkToPos(tarPos, self.moveSpeed, function() printInfo(LOGTAG, string.format("顾客[%s]走到目标餐桌的座位", self.customerCfgId)) end) end -- 走到目标餐桌的座位 function Customer:walkToQueue(queNode) local tarPos = queNode.transform.position self:walkToPos(tarPos, self.moveSpeed, function() printInfo(LOGTAG, string.format("顾客[%s]走到目标队列", self.customerCfgId)) end) end -- 移动到座位位置(由CustomerQueMgr调用) function Customer:moveToSeat(seatPosition, callback) -- 播放行走动画 self:playWalkAnim() self:rolePathFind(seatPosition, function() -- 记录坐下前的位置(用于离开时恢复) self.positionBeforeSitting = self:getCurPosition() -- 到达座位后,强制设置到精确的座位位置 if self.sortBaseNode then -- 计算sortBaseNode相对于display的偏移 local displayPos = self.display.transform.position local sortBasePos = self.sortBaseNode.transform.position local offset = displayPos - sortBasePos -- 将display移动到目标位置+偏移,使sortBaseNode精确到达座位位置 self.display.transform.position = seatPosition + offset else -- 如果没有sortBaseNode,直接设置display位置 self.display.transform.position = seatPosition end -- 到达座位后播放坐下待机动画 self:playSitIdleAnim() -- 到达座位后执行回调 if callback then callback() end end) end -- 显示气泡 function Customer:showBubble() self.bubble:SetActive(true) end -- 隐藏气泡 function Customer:hideBubble() self.bubble:SetActive(false) end -- 显示气泡 function Customer:showDongBubble() self.bubble:SetActive(true) self.bubble:Seek("dong"):SetActive(true) end -- 隐藏气泡 function Customer:hideDongBubble() self.bubble:SetActive(false) self.bubble:Seek("dong"):SetActive(false) end -- 获取气泡 function Customer:getBubble() return self.bubble end -- 移除头顶订单气泡 function Customer:removeOrderCuisineBubble() -- 延迟0.6秒再移除 self.display:Delay(0.1, function() printInfo(LOGTAG, string.format("移除顾客[%s-%s]头顶订单气泡", self.customerCfgId, self.roleUniqueId)) -- 销毁气泡对象 if self.cuisineBubble then -- 调用气泡对象的destroy方法销毁资源 self.cuisineBubble:destroy() self.cuisineBubble = nil end -- 隐藏气泡背景 self:hideBubble() -- 如果有currentSeat和cuisine,清理cuisine中的引用 if self.currentSeat and self.currentSeat.cuisine then local orderInfo = self.currentSeat.cuisine -- 标记气泡已经被移除 orderInfo.bubbleRemoved = true end end) end -- 显示无菜品可点的生气气泡 function Customer:showNoFoodBubble() -- 显示头顶气泡背景 self:showBubble() -- 直接显示生气图标 self.cuisineBubble:hideCuisineGo(false) local angryGo = self.bubble:Seek("angry") if angryGo then angryGo:SetActive(true) end -- 通知座位没有菜品,触发后续离开流程 if self.currentSeat then self.currentSeat:noFood() else printError(LOGTAG, string.format("顾客[%s-%s]没有关联的座位,直接离开餐厅", self.customerCfgId, self.roleUniqueId)) self:leaveRestaurant() end end -- 准备点餐(到达座位后调用) function Customer:prepareToOrder() printInfo(LOGTAG, string.format("顾客[%s-%s]准备点餐", self.customerCfgId, self.roleUniqueId)) -- 防止重复点餐 if self.isOrdering then printInfo(LOGTAG, string.format("顾客[%s-%s]已经在点餐中,忽略重复点餐请求", self.customerCfgId, self.roleUniqueId)) return end self.isOrdering = true -- 延迟一小段时间后开始点餐 self.display:Delay(0.2, function() self:orderDishes() end) end -- 点菜 function Customer:orderDishes() -- 防止重复执行 if self.hasOrdered then printInfo(LOGTAG, string.format("顾客[%s-%s]已经完成点餐,忽略重复点餐请求", self.customerCfgId, self.roleUniqueId)) return end -- 从喜爱食物中随机选择一个 local favoriteIndex = math.random(1, #self.favoriteFoods) local selectedDish = self.favoriteFoods[favoriteIndex] ---- 判断所选菜品是否解锁 --if not CuisineMgr:isCuisineLearned(selectedDish) and EmployeMgr:hasWaiterEmploye() then -- printWarn(LOGTAG, string.format("顾客[%s-%s]的喜爱食物%s未解锁,无法点餐,显示生气气泡", self.customerCfgId, self.roleUniqueId, selectedDish)) -- -- 显示生气气泡,和手动点餐一样的处理 -- self:showNoFoodBubble() -- return --end -- 创建订单参数 local orderArgs = { customer = self, customerId = self.customerCfgId, cuisineId = selectedDish, orderTime = os.time(), } -- 检查是否有服务员,设置处理模式 if EmployeMgr:hasWaiterEmploye() then orderArgs.processMode = OrderConst.ProcessMode.auto printInfo(LOGTAG, string.format("顾客[%s-%s]将使用自动点餐模式", self.customerCfgId, self.roleUniqueId)) else orderArgs.processMode = OrderConst.ProcessMode.manual printInfo(LOGTAG, string.format("顾客[%s-%s]将使用手动点餐模式", self.customerCfgId, self.roleUniqueId)) end -- 显示头顶气泡 self:showBubble() -- 创建菜品图标气泡 local bubble = CuisineBubbleMgr:createBubble(selectedDish, self.bubble, CuisineConst.BubbleShowParentType.customer) -- 保存气泡引用 self.cuisineBubble = bubble -- 通知座位生成订单 if self.currentSeat then -- 生成订单 self.currentSeat:genOrder(orderArgs) -- 设置气泡点击事件和效果 if self.cuisineBubble then self.cuisineBubble:doBubbleBlink() self.cuisineBubble:addClickEvent(self) end else printError(LOGTAG, string.format("顾客[%s-%s]没有关联的座位,无法生成订单", self.customerCfgId, self.roleUniqueId)) end end -- 设置当前座位 function Customer:setCurrentSeat(seat) self.currentSeat = seat -- 获取座位类型,用于判断动画 if seat and seat.seatIndex then self.seatType = seat.seatIndex else self.seatType = nil end end -- 开始用餐 function Customer:startEating(callback) printInfo(LOGTAG, string.format("顾客[%s-%s]开始用餐", self.customerCfgId, self.roleUniqueId)) -- 设置状态为用餐中 self.state = CustomerConst.State.eating -- 播放吃饭动画 self:playEatAnim() self:showEatingProgress(true) self:updateEatingProgress(1) local elapsedTime = 0 self.eatingTimer = self:timer(function () if self.state ~= CustomerConst.State.eating then -- 如果状态不是用餐中,停止更新 self:stopEatingTimer() return end elapsedTime = elapsedTime + 0.01 local progress = math.min(elapsedTime / CustomerConst.EatingTime, 1) local remainingProgress = 1 - progress self:updateEatingProgress(remainingProgress) if progress >= 1 then self:finishEating() if callback then callback() end end end, 0.01, 0) end function Customer:stopEatingTimer() if self.eatingTimer then TimerMgr:rem(self.eatingTimer) self.eatingTimer = nil end end -- 完成用餐 function Customer:finishEating() printInfo(LOGTAG, string.format("顾客[%s-%s]完成用餐", self.customerCfgId, self.roleUniqueId)) self:stopEatingTimer() -- 切换回坐下待机动画 self:playSitIdleAnim() -- 隐藏进度条 self:showEatingProgress(false) -- 清理订单和相关状态 self:removeOrderCuisineBubble() -- 开始闲逛 self:startWandering() end -- 强制回到坐下前的安全位置 function Customer:forceReturnToSafePosition() -- 如果有记录坐下前的位置,先恢复到那个安全位置 if self.positionBeforeSitting then printInfo(LOGTAG, string.format("顾客[%s-%s]恢复到坐下前的安全位置", self.customerCfgId, self.roleUniqueId)) -- 强制设置到坐下前的位置 if self.sortBaseNode then -- 计算sortBaseNode相对于display的偏移 local displayPos = self.display.transform.position local sortBasePos = self.sortBaseNode.transform.position local offset = displayPos - sortBasePos -- 将display移动到目标位置+偏移,使sortBaseNode到达安全位置 self.display.transform.position = self.positionBeforeSitting + offset else -- 如果没有sortBaseNode,直接设置display位置 self.display.transform.position = self.positionBeforeSitting end -- 清除记录的位置 self.positionBeforeSitting = nil return true end return false end -- 开始闲逛 function Customer:startWandering() printInfo(LOGTAG, string.format("顾客[%s-%s]开始闲逛", self.customerCfgId, self.roleUniqueId)) -- 设置状态为闲逛中 self.state = CustomerConst.State.wandering -- 清空座位类型,因为已经离开座位 self.seatType = nil -- 播放站立动画 self:playStandAnim() -- 强制回到坐下前的安全位置 self:forceReturnToSafePosition() -- 开始闲逛流程 self:wanderInRestaurant() end -- 获取随机闲逛点 function Customer:getRandomWanderPoints(count) -- 获取地图上的闲逛点 local wanderNodes = MapsMgr:getMap(MapsConst.MapType.restaurant):getCustomerWanderNodes() -- 随机选择指定数量的闲逛点 local selectedPoints = {} local availableIndices = {} -- 初始化可用索引 for i = 1, #wanderNodes do table.insert(availableIndices, i) end -- 随机选择点 count = math.min(count, #wanderNodes) for i = 1, count do if #availableIndices == 0 then break end -- 随机选择一个索引 local randomIndex = math.random(1, #availableIndices) local selectedIndex = availableIndices[randomIndex] -- 获取该索引对应的闲逛点位置 local wanderNode = wanderNodes[selectedIndex] table.insert(selectedPoints, wanderNode:GetPosition()) -- 从可用索引中移除已选择的索引 table.remove(availableIndices, randomIndex) end return selectedPoints end -- 在餐厅内闲逛 function Customer:wanderInRestaurant() -- 获取随机闲逛点 local wanderPoints = self:getRandomWanderPoints(3) -- 计算每个点的停留时间 local stayTimePerPoint = math.random(2, 4) -- 递归函数,按顺序访问所有闲逛点 local visitNextPoint local currentPointIndex = 1 visitNextPoint = function() -- 如果已经访问完所有点或者时间到了,去消费建筑 if currentPointIndex > #wanderPoints then self:goToPassivityBuilding() return end -- 获取当前闲逛点 local targetPos = wanderPoints[currentPointIndex] -- 移动到该点 self:rolePathFind(targetPos, function() printInfo(LOGTAG, string.format("顾客[%s-%s]到达闲逛点%d,停留%d秒", self.customerCfgId, self.roleUniqueId, currentPointIndex, stayTimePerPoint)) -- 播放站立动画 self:playStandAnim() -- 在当前点停留一段时间,然后前往下一个点 self.display:Delay(stayTimePerPoint, function() currentPointIndex = currentPointIndex + 1 visitNextPoint() end) end) end -- 开始访问第一个点 visitNextPoint() end -- 获取随机闲逛建筑 function Customer:getRandomPassivityBuilding() -- 从PassivityDeskMgr获取消费建筑 local passivityDesks = PassivityDeskMgr:getAllPassivityDesks() -- 随机选择一个消费建筑(不预先判断是否可接待) if not passivityDesks or table.nums(passivityDesks) == 0 then printWarn(LOGTAG, "找不到任何消费建筑") return nil end local randomIndex = math.random(1, table.nums(passivityDesks)) return passivityDesks[randomIndex] end -- 前往消费建筑 function Customer:goToPassivityBuilding() printInfo(LOGTAG, string.format("顾客[%s-%s]前往消费建筑", self.customerCfgId, self.roleUniqueId)) -- 获取随机消费建筑 local passivityBuilding = self:getRandomPassivityBuilding() if not passivityBuilding then printWarn(LOGTAG, string.format("顾客[%s-%s]找不到消费建筑,直接离开餐厅", self.customerCfgId, self.roleUniqueId)) self:leaveRestaurant(true) return end -- 保存目标建筑引用 self.targetPassivityBuilding = passivityBuilding -- 先移动到建筑的等待区域 self:moveToPassivityBuildingWaitingArea(passivityBuilding) end -- 移动到消费建筑等待区域 function Customer:moveToPassivityBuildingWaitingArea(passivityBuilding) -- 先移动到建筑位置 local buildingPos = passivityBuilding:getPosition() if not buildingPos then printWarn(LOGTAG, "无法获取消费建筑位置,顾客直接离开餐厅") self:leaveRestaurant(true) return end printInfo(LOGTAG, string.format("顾客[%s-%s]移动到建筑[%s]等待区域", self.customerCfgId, self.roleUniqueId, passivityBuilding.deskId)) -- 移动到建筑位置 self:rolePathFind(buildingPos, function() self:onArrivedAtPassivityBuildingWaitingArea(passivityBuilding) end) end -- 到达消费建筑等待区域后的处理 function Customer:onArrivedAtPassivityBuildingWaitingArea(passivityBuilding) printInfo(LOGTAG, string.format("顾客[%s-%s]到达建筑[%s]等待区域,尝试接待", self.customerCfgId, self.roleUniqueId, passivityBuilding.deskId)) -- 尝试接待顾客 if not passivityBuilding:acceptCustomer(self) then printWarn(LOGTAG, string.format("顾客[%s-%s]无法被建筑[%s]接待,直接离开餐厅", self.customerCfgId, self.roleUniqueId, passivityBuilding.deskId)) self:leaveRestaurant(true) return end -- 接待成功,保存当前使用的建筑引用 self.currentPassivityBuilding = passivityBuilding self.targetPassivityBuilding = nil -- 根据接待类型移动到对应位置 self:moveToPassivityServicePosition(passivityBuilding) end -- 移动到消费建筑服务位置 function Customer:moveToPassivityServicePosition(passivityBuilding) local targetPos = nil -- 如果顾客在等待队列中,移动到等待点 if self.waitingPointIndex then local stayPoint = passivityBuilding.stayPoints[self.waitingPointIndex] if stayPoint then targetPos = stayPoint.transform.position printInfo(LOGTAG, string.format("顾客[%s-%s]移动到建筑[%s]的等待点%d", self.customerCfgId, self.roleUniqueId, passivityBuilding.deskId, self.waitingPointIndex)) end else -- 直接接待的顾客,根据建筑类型处理 if passivityBuilding.buildingInfo.buildingType == BuildingConst.buildingType.outdoormovie then -- 露天电影院需要移动到分配的椅子 if self.assignedChair then local chairNode = passivityBuilding.buildingNode:Seek(self.assignedChair) if chairNode then targetPos = chairNode.transform.position printInfo(LOGTAG, string.format("顾客[%s-%s]移动到露天电影院椅子[%s]", self.customerCfgId, self.roleUniqueId, self.assignedChair)) end end else -- 普通建筑,停留在建筑位置即可 targetPos = passivityBuilding:getPosition() printInfo(LOGTAG, string.format("顾客[%s-%s]在普通建筑[%s]接受服务", self.customerCfgId, self.roleUniqueId, passivityBuilding.deskId)) end end if targetPos then -- 移动到目标位置 self:rolePathFind(targetPos, function() self:onArrivedAtServicePosition(passivityBuilding) end) else -- 如果没有特定位置,直接开始服务 self:onArrivedAtServicePosition(passivityBuilding) end end -- 到达服务位置后的处理 function Customer:onArrivedAtServicePosition(passivityBuilding) if self.waitingPointIndex then -- 在等待点等待 printInfo(LOGTAG, string.format("顾客[%s-%s]在建筑[%s]的等待点%d等待服务", self.customerCfgId, self.roleUniqueId, passivityBuilding.deskId, self.waitingPointIndex)) else -- 直接接待的顾客,根据建筑类型进行特殊处理 if passivityBuilding.buildingInfo.buildingType == BuildingConst.buildingType.outdoormovie then self:handleOutdoorMovieTheater(passivityBuilding) end end end -- 处理露天电影院(仅负责移动到椅子位置和动画) function Customer:handleOutdoorMovieTheater(passivityBuilding) printInfo(LOGTAG, string.format("顾客[%s-%s]在露天电影院", self.customerCfgId, self.roleUniqueId)) -- 如果有分配的椅子,移动到椅子位置 if self.assignedChair then local chairNode = passivityBuilding.buildingNode:Seek(self.assignedChair) if chairNode then local chairPos = chairNode.transform.position self:rolePathFind(chairPos, function() printInfo(LOGTAG, string.format("顾客[%s-%s]坐在露天电影院椅子[%s]上", self.customerCfgId, self.roleUniqueId, self.assignedChair)) self.display.localScale = Vector3(1, 1, 1) -- 确保坐下时朝向正常 self.bubble:GetComponent("SpriteRenderer").flipX = false -- 确保气泡朝向正常 -- 播放露天电影院背面坐下动画 self:playMovieTheaterSitAnim() end) else printWarn(LOGTAG, string.format("找不到椅子节点[%s]", self.assignedChair)) end end end -- 消费建筑服务完成回调(由PassivityDesk调用) function Customer:onPassivityServiceComplete() printInfo(LOGTAG, string.format("顾客[%s-%s]的消费建筑服务完成", self.customerCfgId, self.roleUniqueId)) -- 如果是露天电影院,播放站立动画 if self.currentPassivityBuilding and self.currentPassivityBuilding.buildingInfo.buildingType == BuildingConst.buildingType.outdoormovie then self:playStandAnim() end -- 清理当前建筑引用 self.currentPassivityBuilding = nil self.targetPassivityBuilding = nil self.assignedChair = nil self.waitingPointIndex = nil -- 离开餐厅 self:leaveRestaurant(true) end -- 处理消费建筑错误情况 function Customer:handlePassivityBuildingError(passivityBuilding) printWarn(LOGTAG, string.format("顾客[%s-%s]在消费建筑[%s]遇到错误", self.customerCfgId, self.roleUniqueId, passivityBuilding.deskId)) -- 强制离开建筑 if passivityBuilding then passivityBuilding:forceCustomerLeave(self) end -- 清理引用 self.currentPassivityBuilding = nil self.targetPassivityBuilding = nil self.assignedChair = nil self.waitingPointIndex = nil -- 离开餐厅 self:leaveRestaurant() end -- 离开餐厅 function Customer:leaveRestaurant(stayable) printInfo(LOGTAG, string.format("顾客[%s-%s]准备离开餐厅", self.customerCfgId, self.roleUniqueId)) -- 可逗留的顾客 if stayable then -- 20%的概率去往其他店铺 local chance = math.random(1, 100) local list = UserDataMgr:getSceneUnlockedList() if chance <= 20 and table.count(list) > 1 then local id = MapsConst.MapType.music_bar -- list[math.random(2, #list)] if id == MapsConst.MapType.music_bar then local bbqPos = RestaurantMgr:getRestaurantToMusicBbqEnter().transform.position self:rolePathFind(bbqPos, function() MusicbbqMgr:customerEnterTheMusicBbq(self) MusicbbqMgr:startCustomerEnterTheMusicBbqLogic(self) end) elseif id == MapsConst.MapType.fishing_island then local fishingPos = RestaurantMgr:getRestaurantToFishingEnter().transform.position self:rolePathFind(fishingPos, function() FishingMgr:customerEnterTheFishing(self) FishingMgr:startCustomerEnterTheFishingLogic(self) end) end printInfo(LOGTAG, string.format("顾客[%s-%s]离开餐厅,前往其他店铺[%s]", self.customerCfgId, self.roleUniqueId, id)) return end end -- 设置状态为离开 self.state = CustomerConst.State.leaving -- 确保当前座位引用被清理 self.currentSeat = nil -- 清理消费建筑相关引用 if self.currentPassivityBuilding then self.currentPassivityBuilding:forceCustomerLeave(self) self.currentPassivityBuilding = nil end self.targetPassivityBuilding = nil self.assignedChair = nil self.waitingPointIndex = nil -- 获取出口位置 local exitPosition = self:getRestaurantExitPosition() -- 移动到出口 self:rolePathFind(exitPosition, function() printInfo(LOGTAG, string.format("顾客[%s-%s]已到达出口,离开餐厅", self.customerCfgId, self.roleUniqueId)) -- 移除顾客对象 self:onExit() end) end -- 获取餐厅出口位置 function Customer:getRestaurantExitPosition() local exit = RestaurantMgr:getRestaurantExit() if exit then return exit:GetPosition() else printError(LOGTAG, "找不到餐厅出口位置") return Vector3(0, 0, 0) end end -- 销毁顾客对象 function Customer:onExit() printInfo(LOGTAG, string.format("销毁顾客[%s-%s]", self.customerCfgId, self.roleUniqueId)) -- 如果是测试角色,停止当前寻路 if self.isTestCustomer then self:stopCurrentPathFind() end self:removeOrderCuisineBubble() self.currentSeat = nil -- 重置点餐状态 self.isOrdering = false self.hasOrdered = false -- 销毁游戏对象 if self.display then GameObject.Destroy(self.display) self.display = nil end end -- 显示角色头顶的吃饭进度图标 function Customer:showEatingProgress(show) self.eatingProgress:SetScalef(1.5) self.eatingProgress:SetActive(show) if show then local front = self.eatingProgress:Seek("front") local sprite_fill_material = front:GetComponent(typeof(CS.UnityEngine.SpriteRenderer)).material -- 设置为360度填充 sprite_fill_material:SetInt("_FillDirect", 3) -- 使用SetInt设置为圆形填充模式 sprite_fill_material:SetInt("_FillOrigin", 2) -- 使用SetInt设置为顺时针填充 sprite_fill_material:SetFloat("_StartAngle", 90) self:setEatingProgressSortingOrder(GameNodeOrder.Order.role + 10) end end -- 设置吃饭进度 function Customer:updateEatingProgress(value) -- 获取到front上的材质球 local front = self.eatingProgress:Seek("front") local sprite_fill_material = front:GetComponent(typeof(CS.UnityEngine.SpriteRenderer)).material -- 设置fillAmount sprite_fill_material:SetFloat("_FillAmount", value) end -- ==================== 动画播放相关方法 ==================== -- 播放站立动画 (stand_1和stand_2随机播一个) function Customer:playStandAnim() local animNames = { CustomerConst.CustomerAction.stand_1, CustomerConst.CustomerAction.stand_2 } local randomIndex = math.random(1, #animNames) local animName = animNames[randomIndex] -- 清除移动状态 self.isMoving = false self:playAnimation(animName, true) -- printInfo(LOGTAG, string.format("顾客[%s-%s]播放站立动画: %s", self.customerCfgId, self.roleUniqueId, animName)) end -- 播放行走动画 function Customer:playWalkAnim() if self.currentAnim == CustomerConst.CustomerAction.walk then return end -- 设置移动状态 self.isMoving = true self:playAnimation(CustomerConst.CustomerAction.walk, true) -- printInfo(LOGTAG, string.format("顾客[%s-%s]播放行走动画: %s", self.customerCfgId, self.roleUniqueId, CustomerConst.CustomerAction.walk)) end -- 播放坐下待机动画 (根据座位类型选择正面或背面) function Customer:playSitIdleAnim() local animName -- 根据座位类型判断使用正面还是背面坐姿 (座位索引是数字类型) if self.seatType == 1 or self.seatType == 4 then -- 正面坐待机,从1、2、3中随机选择 local frontIdleAnims = { CustomerConst.CustomerAction.sit_front_idle_1, CustomerConst.CustomerAction.sit_front_idle_2, CustomerConst.CustomerAction.sit_front_idle_3 } local randomIndex = math.random(1, #frontIdleAnims) animName = frontIdleAnims[randomIndex] self.bubble:GetComponent("SpriteRenderer").flipX = false -- 确保气泡朝向正常 printInfo(LOGTAG, string.format("顾客[%s-%s]播放正面坐待机动画: %s (座位: %s)", self.customerCfgId, self.roleUniqueId, animName, self.seatType)) elseif self.seatType == 2 or self.seatType == 3 then -- 背面坐待机 animName = CustomerConst.CustomerAction.sit_back_idle self.bubble:GetComponent("SpriteRenderer").flipX = true -- 确保气泡朝向正常 printInfo(LOGTAG, string.format("顾客[%s-%s]播放背面坐待机动画: %s (座位: %s)", self.customerCfgId, self.roleUniqueId, animName, self.seatType)) else -- 默认使用正面坐待机 local frontIdleAnims = { CustomerConst.CustomerAction.sit_front_idle_1, CustomerConst.CustomerAction.sit_front_idle_2, CustomerConst.CustomerAction.sit_front_idle_3 } local randomIndex = math.random(1, #frontIdleAnims) animName = frontIdleAnims[randomIndex] printInfo(LOGTAG, string.format("顾客[%s-%s]播放默认正面坐待机动画: %s (座位: %s)", self.customerCfgId, self.roleUniqueId, animName, self.seatType or "unknown")) end self:playAnimation(animName, true) -- 朝向 if self.seatType == 1 or self.seatType == 3 then self.display.transform.localScale = Vector3(-1, 1, 1) elseif self.seatType == 2 or self.seatType == 4 then self.display.transform.localScale = Vector3(1, 1, 1) else end end -- 播放吃饭动画 function Customer:playEatAnim() local animName -- 根据座位类型判断使用正面还是背面吃饭动画 (座位索引是数字类型) if self.seatType == 1 or self.seatType == 4 then -- 正面吃饭 animName = CustomerConst.CustomerAction.sit_front_eat printInfo(LOGTAG, string.format("顾客[%s-%s]播放正面吃饭动画 (座位: %s)", self.customerCfgId, self.roleUniqueId, self.seatType)) elseif self.seatType == 2 or self.seatType == 3 then -- 背面吃饭 animName = CustomerConst.CustomerAction.sit_back_eat printInfo(LOGTAG, string.format("顾客[%s-%s]播放背面吃饭动画 (座位: %s)", self.customerCfgId, self.roleUniqueId, self.seatType)) else -- 默认使用正面吃饭 animName = CustomerConst.CustomerAction.sit_front_eat printInfo(LOGTAG, string.format("顾客[%s-%s]播放默认正面吃饭动画 (座位: %s)", self.customerCfgId, self.roleUniqueId, self.seatType or "unknown")) end self:playAnimation(animName, true) end -- 播放喝饮料动画 function Customer:playDrinkAnim() local animName -- 根据座位类型判断使用正面喝饮料还是背面吃饭动画 (座位索引是数字类型) if self.seatType == 1 or self.seatType == 4 then -- 正面喝饮料 animName = CustomerConst.CustomerAction.sit_front_drink printInfo(LOGTAG, string.format("顾客[%s-%s]播放正面喝饮料动画 (座位: %s)", self.customerCfgId, self.roleUniqueId, self.seatType)) elseif self.seatType == 2 or self.seatType == 3 then -- 背面喝饮料使用背面吃饭动画 animName = CustomerConst.CustomerAction.sit_back_eat printInfo(LOGTAG, string.format("顾客[%s-%s]播放背面喝饮料动画(使用背面吃饭动画) (座位: %s)", self.customerCfgId, self.roleUniqueId, self.seatType)) else -- 默认使用正面喝饮料 animName = CustomerConst.CustomerAction.sit_front_drink printInfo(LOGTAG, string.format("顾客[%s-%s]播放默认正面喝饮料动画 (座位: %s)", self.customerCfgId, self.roleUniqueId, self.seatType or "unknown")) end self:playAnimation(animName, true) end -- 通用动画播放方法 function Customer:playAnimation(animName, loop, callback) if self.currentAnim == animName then return end -- 记录当前播放的动画 local oldAnim = self.currentAnim self.currentAnim = animName -- printInfo(LOGTAG, string.format("顾客[%s-%s]动画切换: %s -> %s", self.customerCfgId, self.roleUniqueId, oldAnim or "nil", animName)) -- 播放动画 util.spine.play(self.customerAnim, animName, loop or false, callback) end -- 获取当前播放的动画 function Customer:getCurrentAnim() return self.currentAnim end -- 播放露天电影院背面坐下待机动画 (专门用于露天电影院) function Customer:playMovieTheaterSitAnim() local animName = CustomerConst.CustomerAction.sit_back_idle self:playAnimation(animName, true) printInfo(LOGTAG, string.format("顾客[%s-%s]播放露天电影院背面坐下待机动画: %s", self.customerCfgId, self.roleUniqueId, animName)) end -- 获取当前物体的真实SortingOrder (重写父类方法) function Customer:getCurrentSortingOrder() if not self.display then printError(LOGTAG, string.format("顾客[%s-%s]display节点不存在", self.customerCfgId, self.roleUniqueId)) return nil end -- 尝试从customerAnim节点获取SortingOrder if self.customerAnim then local renderer = self.customerAnim:GetComponent("Renderer") if renderer then local sortingOrder = renderer.sortingOrder -- printInfo(LOGTAG, string.format("顾客[%s-%s]从customerAnim获取SortingOrder: %d", self.customerCfgId, self.roleUniqueId, sortingOrder)) return sortingOrder end end -- 如果customerAnim没有Renderer,尝试从display的子节点查找 local renderer = self.display:GetComponent("Renderer") if renderer then local sortingOrder = renderer.sortingOrder printInfo(LOGTAG, string.format("顾客[%s-%s]从display获取SortingOrder: %d", self.customerCfgId, self.roleUniqueId, sortingOrder)) return sortingOrder end -- 如果都没有找到,尝试从子节点查找 local childCount = self.display.transform.childCount for i = 0, childCount - 1 do local child = self.display.transform:GetChild(i) local childRenderer = child:GetComponent("Renderer") if childRenderer then local sortingOrder = childRenderer.sortingOrder printInfo(LOGTAG, string.format("顾客[%s-%s]从子节点[%s]获取SortingOrder: %d", self.customerCfgId, self.roleUniqueId, child.name, sortingOrder)) return sortingOrder end end printWarn(LOGTAG, string.format("顾客[%s-%s]无法找到任何Renderer组件", self.customerCfgId, self.roleUniqueId)) return nil end -- 析构函数 (清理资源) function Customer:destroy() -- 清理定时器 if self.eatingTimer then TimerMgr:rem(self.eatingTimer) self.eatingTimer = nil end -- 清理Unity对象 if self.display and not CS.LuaHelper.IsNull(self.display) then GameObject.Destroy(self.display) self.display = nil end printInfo(LOGTAG, string.format("顾客[%s-%s]资源清理完成", self.customerCfgId, self.roleUniqueId)) -- 调用父类析构函数 if super.destroy then super.destroy(self) end end -- 顾客离开烤吧 function Customer:leaveMusicBbq() -- 获取烤吧出口位置 local exitNode = MusicbbqMgr:getMusicbbqExit() local exitPos = exitNode and exitNode:GetPosition() or Vector3(0, 0, 0) self:rolePathFind(exitPos, function() printInfo(LOGTAG, string.format("顾客[%s-%s]已到达烤吧出口,彻底离开", self.customerCfgId, self.roleUniqueId)) self:onExit() end) end -- 顾客在池塘的行为逻辑 function Customer:wanderInFishingMap() end -- 顾客在烤吧行为逻辑 function Customer:wanderInMusicbbqMap() -- 获取所有烤吧建筑 local bbqBuildings = BuildingMgr:getBbqWanderBuilding() if not bbqBuildings or #bbqBuildings < 1 then -- 没有足够建筑,直接离开 printInfo(LOGTAG, string.format("顾客[%s-%s]没有找到烤吧建筑,直接离开", self.customerCfgId, self.roleUniqueId)) self:leaveMusicBbq() return end -- 尝试找到一个可用的建筑 self:findAndVisitAvailableBuilding(bbqBuildings) end -- 寻找并访问可用的建筑 function Customer:findAndVisitAvailableBuilding(bbqBuildings) -- 打乱建筑列表 local shuffledBuildings = {} for i, building in ipairs(bbqBuildings) do table.insert(shuffledBuildings, building) end -- 简单的随机打乱 for i = #shuffledBuildings, 2, -1 do local j = math.random(i) shuffledBuildings[i], shuffledBuildings[j] = shuffledBuildings[j], shuffledBuildings[i] end -- 尝试访问每个建筑 self:tryVisitBuilding(shuffledBuildings, 1) end -- 尝试访问指定索引的建筑 function Customer:tryVisitBuilding(buildingList, index) if index > #buildingList then -- 所有建筑都无法访问,直接前往待机点 printInfo(LOGTAG, string.format("顾客[%s-%s]没有找到可用建筑,直接前往待机点", self.customerCfgId, self.roleUniqueId)) self:goToBbqWanderPoint() return end local selectedBuilding = buildingList[index] printInfo(LOGTAG, string.format("顾客[%s-%s]尝试访问建筑, 类型: %s", self.customerCfgId, self.roleUniqueId, selectedBuilding.buildingType)) -- 根据建筑类型决定停留时间 local stayTime = 5 -- 默认5秒(烧烤摊) if selectedBuilding.buildingType == BuildingConst.buildingType.stage then stayTime = 8 -- 舞台8秒 end -- 根据建筑类型使用不同的访问逻辑 if selectedBuilding.buildingType == BuildingConst.buildingType.stage then -- 舞台使用座位逻辑 self:handleStageBuildingVisit(selectedBuilding, function() -- 舞台访问完成,前往待机点 printInfo(LOGTAG, string.format("顾客[%s-%s]完成舞台访问,前往待机点", self.customerCfgId, self.roleUniqueId)) self:goToBbqWanderPoint() end) elseif selectedBuilding.buildingType == BuildingConst.buildingType.bbqgrill1 or selectedBuilding.buildingType == BuildingConst.buildingType.bbqgrill2 or selectedBuilding.buildingType == BuildingConst.buildingType.bbqgrill3 then -- 烤架使用停留点逻辑,需要检查是否有空闲停留点 self:handleBbqGrillVisitWithFallback(selectedBuilding, stayTime, buildingList, index) else -- 其他建筑使用简单停留逻辑 self:visitBbqBuilding(selectedBuilding, stayTime, function() -- 建筑访问完成,前往待机点 printInfo(LOGTAG, string.format("顾客[%s-%s]完成建筑访问,前往待机点", self.customerCfgId, self.roleUniqueId)) self:goToBbqWanderPoint() end) end end -- 处理烤架访问,如果失败则尝试下一个建筑 function Customer:handleBbqGrillVisitWithFallback(building, stayTime, buildingList, currentIndex) -- 获取烤架建筑实例 local bbqGrillInstance = BuildingMgr:getBuildingInstance(building.id) if not bbqGrillInstance then printWarn(LOGTAG, string.format("顾客[%s-%s]找不到烤架建筑实例[%s],尝试下一个建筑", self.customerCfgId, self.roleUniqueId, building.id)) self:tryVisitBuilding(buildingList, currentIndex + 1) return end -- 检查是否有空闲停留点 local pointIndex, stayPoint = bbqGrillInstance:getAvailableStayPoint() if not pointIndex then printInfo(LOGTAG, string.format("顾客[%s-%s]烤架[%s]的停留点都被占用,尝试下一个建筑", self.customerCfgId, self.roleUniqueId, building.id)) self:tryVisitBuilding(buildingList, currentIndex + 1) return end -- 占用停留点 if not bbqGrillInstance:occupyStayPoint(pointIndex, self) then printWarn(LOGTAG, string.format("顾客[%s-%s]占用烤架停留点失败,尝试下一个建筑", self.customerCfgId, self.roleUniqueId)) self:tryVisitBuilding(buildingList, currentIndex + 1) return end -- 前往停留点 local stayPointPos = stayPoint:GetPosition() self:rolePathFind(stayPointPos, function() printInfo(LOGTAG, string.format("顾客[%s-%s]到达烤架停留点[%d],开始停留%d秒", self.customerCfgId, self.roleUniqueId, pointIndex, stayTime)) -- 播放站立动画 self:playStandAnim() -- 停留指定时间 self.display:Delay(stayTime, function() -- 释放停留点 bbqGrillInstance:releaseStayPoint(pointIndex) printInfo(LOGTAG, string.format("顾客[%s-%s]完成烤架停留点访问,前往待机点", self.customerCfgId, self.roleUniqueId)) self:goToBbqWanderPoint() end) end) end -- 获取建筑位置的辅助方法 function Customer:getBuildingPosition(building) -- 这里需要根据建筑类型或ID获取实际的GameObject位置 -- 临时实现:尝试通过场景查找建筑节点 local scene = MusicbbqMgr:getMusicbbqScene() if not scene then return nil end -- 尝试通过建筑类型查找位置节点 local buildingPosNode = scene.rootNode:Seek("building_pos_" .. building.buildingType) if buildingPosNode then return buildingPosNode:GetPosition() end printWarn(LOGTAG, string.format("找不到建筑位置节点: building_pos_%s", building.buildingType)) return nil end -- 访问烤吧建筑(统一处理舞台和烧烤摊) function Customer:visitBbqBuilding(building, stayTime, onComplete) -- 获取建筑位置 local pos = self:getBuildingPosition(building) if not pos then printWarn(LOGTAG, string.format("顾客[%s-%s]无法获取建筑位置,跳过该建筑", self.customerCfgId, self.roleUniqueId)) if onComplete then onComplete() end return end -- 移动到建筑位置 self:rolePathFind(pos, function() printInfo(LOGTAG, string.format("顾客[%s-%s]到达建筑,开始停留%d秒", self.customerCfgId, self.roleUniqueId, stayTime)) -- 播放站立动画 self:playStandAnim() -- 停留指定时间 self.display:Delay(stayTime, function() printInfo(LOGTAG, string.format("顾客[%s-%s]完成%d秒停留", self.customerCfgId, self.roleUniqueId, stayTime)) if onComplete then onComplete() end end) end) end -- 前往烤吧待机点 function Customer:goToBbqWanderPoint() -- 获取烤吧待机点 local wanderPoint = self:getRandomBbqWanderPoint() if not wanderPoint then printWarn(LOGTAG, string.format("顾客[%s-%s]无法获取烤吧待机点,直接离开", self.customerCfgId, self.roleUniqueId)) self:leaveMusicBbq() return end -- 前往待机点 self:rolePathFind(wanderPoint, function() printInfo(LOGTAG, string.format("顾客[%s-%s]到达烤吧待机点,停留5秒", self.customerCfgId, self.roleUniqueId)) -- 播放站立动画 self:playStandAnim() -- 停留5秒 self.display:Delay(5, function() printInfo(LOGTAG, string.format("顾客[%s-%s]完成待机点停留,准备离开烤吧", self.customerCfgId, self.roleUniqueId)) self:leaveMusicBbq() end) end) end -- 获取随机烤吧待机点 function Customer:getRandomBbqWanderPoint() -- 获取音乐烤吧场景 local scene = MusicbbqMgr:getMusicbbqScene() if not scene then printWarn(LOGTAG, "找不到音乐烤吧场景") return nil end -- 获取烤吧待机点根节点 local bbqWanderRoot = scene.rootNode:Seek(MapCfg.employeeRootNames.bbqWanderRootNodeName) if not bbqWanderRoot then printWarn(LOGTAG, string.format("找不到烤吧待机点根节点: %s", MapCfg.employeeRootNames.bbqWanderRootNodeName)) return nil end -- 获取所有子节点 local childCount = bbqWanderRoot.transform.childCount if childCount == 0 then printWarn(LOGTAG, "烤吧待机点根节点没有子节点") return nil end -- 随机选择一个子节点 local randomIndex = math.random(0, childCount - 1) local selectedChild = bbqWanderRoot.transform:GetChild(randomIndex).gameObject printInfo(LOGTAG, string.format("选择烤吧待机点: %s", selectedChild.name)) return selectedChild:GetPosition() end -- 处理舞台建筑的特殊逻辑(保留原有方法以防其他地方调用) function Customer:handleStageBuildingVisit(buildingInfo, onComplete) -- 根据建筑信息获取舞台实例 local stageDesk = StageDeskMgr:getStageDeskById(buildingInfo.buildingCfgId) if not stageDesk then printWarn(LOGTAG, string.format("找不到舞台实例,建筑ID: %s", buildingInfo.buildingCfgId)) if onComplete then onComplete() end return end -- 直接寻找空闲座位,不需要先走到舞台位置 printInfo(LOGTAG, string.format("顾客[%s-%s]寻找舞台空闲座位", self.customerCfgId, self.roleUniqueId)) local idleSeat = stageDesk:findIdleSeat() if idleSeat then -- 有空闲座位,预订座位 if idleSeat:reserveSeat(self) then -- 预订成功,直接前往座位 local seatPos = idleSeat:getSeatPosition() printInfo(LOGTAG, string.format("顾客[%s-%s]预订成功,直接前往座位位置: %s", self.customerCfgId, self.roleUniqueId, tostring(seatPos))) self:rolePathFind(seatPos, function() -- 到达座位,坐下 if idleSeat:sitDown(self) then self:playMovieTheaterSitAnim() -- 观看表演8秒(修正为统一的8秒时间) local watchTime = 8 self.display:Delay(watchTime, function() -- 观看结束,离开座位 idleSeat:leaveSeat() -- 播放站立动画 self:playStandAnim() printInfo(LOGTAG, string.format("顾客[%s-%s]离开舞台座位,完成舞台访问", self.customerCfgId, self.roleUniqueId)) -- 完成回调 if onComplete then onComplete() end end) else -- 坐下失败,直接完成 printWarn(LOGTAG, string.format("顾客[%s-%s]坐下失败", self.customerCfgId, self.roleUniqueId)) if onComplete then onComplete() end end end) else -- 预订失败,直接完成 printWarn(LOGTAG, string.format("顾客[%s-%s]预订座位失败", self.customerCfgId, self.roleUniqueId)) if onComplete then onComplete() end end else -- 没有空闲座位,直接完成(不需要额外停留) printInfo(LOGTAG, string.format("顾客[%s-%s]没有找到空闲座位,跳过舞台访问", self.customerCfgId, self.roleUniqueId)) if onComplete then onComplete() end end end return Customer CommonBonusCfgParse--[[ 通用加成属性配置解析 author:{zhangpeng} time:2025-05-20 16:41:46 ]] local CommonBonusCfgData = require("data/config/commonBonusCfg") local CommonBonusCfgParse = defClassStatic("CommonBonusCfgParse") local LOGTAG = "CommonBonusCfgParse" function CommonBonusCfgParse:init() self.idDic = {} for _, v in ipairs(CommonBonusCfgData) do self.idDic[v.id] = v end end -- 根据id获取原始数据 function CommonBonusCfgParse:getCommonBonusCfgById(id) return self.idDic[id] end -- 直接获取加成配置中已解析的加成列表 function CommonBonusCfgParse:getCommonBonusListCfgById(bonusCfgId) local cfg = self:getCommonBonusCfgById(bonusCfgId) if not cfg then printError(LOGTAG, string.format("没有找到加成属性配置[%s]", bonusCfgId)) return nil end local bonusList = {} for i=1,cfg.bonusNums do local bonus = {} bonus.bonusId = cfg["bonusId_" .. i] bonus.value = cfg["value_" .. i] bonus.desc = CommonBonusTypeParse:getCommonBonusTypeCfgByType(bonus.bonusId).desc bonus.desc = string.format(bonus.desc, self:getDescValue(bonus.bonusId, bonus.value)) table.insert(bonusList, bonus) end return bonusList end -- 获取加成属性描述的展示值 function CommonBonusCfgParse:getDescValue(typeId, value) if typeId == BonusConst.BonusType.coin_income_max_time then return value / 3600 end return value end CommonBonusCfgParse:init() FishermanCharacter&-- 捕鱼操控角色 local FishermanCharacter = defClass("FishermanCharacter") local SpriteRenderer = CS.UnityEngine.SpriteRenderer local Color = CS.UnityEngine.Color local SkeletonAnimation = CS.Spine.Unity.SkeletonAnimation local trailRenderer = CS.UnityEngine.TrailRenderer -- 构造函数 function FishermanCharacter:ctor(cfgId, parent, worldPos) self.cfgId = cfgId self:createTarget(parent, worldPos) self:init() end -- 初始化 function FishermanCharacter:init() self.info = FishingGameMgr:getCharacterInfo(self.cfgId) self.spine = self.go:Seek("spine") self.dirTransform = self.go:Seek("spine_root").transform self.effect = self.go:Seek("effect")[trailRenderer] self.sprite = self.go:Seek("image")[SpriteRenderer] self.handle = self.go:Seek("handle").transform self.shield = self.go:Seek("shield") self.upgradeNode = self.go:Seek("upgrade") self.upgrade_spine = self.upgradeNode:Seek("spine") self.collider = self.go:Seek("collider").transform self.double_energy = self.go:Seek("double_energy") self:updateActiveArea() self:playAnimation("kapibala_swim", true) end function FishermanCharacter:updateActiveArea() local mapSize = FishingGameMgr.map:getWorldSize() self.activeArea = {top = 0, bottom = 0, left = 0, right = 0} self.size = {width = 1 * self.info.gradeConfig.characterZoom, height = 1 * self.info.gradeConfig.characterZoom} self.activeArea.top = 0 - self.size.height / 2 self.activeArea.bottom = -mapSize.height + self.size.height / 2 self.activeArea.left = -mapSize.width / 2 + self.size.width / 2 self.activeArea.right = mapSize.width / 2 - self.size.width / 2 self.displayAreaRadius = 0.8 end -- 重新加载角色 function FishermanCharacter:reload(cfgId, parent, worldPos) self.cfgId = cfgId self.info = FishingGameMgr:getCharacterInfo(self.cfgId) self.transform:SetParent(parent, false) self.transform.localPosition = worldPos or Vector3.zero self.go:SetActive(true) self:playAnimation("kapibala_swim", true) end -- 隐藏角色 function FishermanCharacter:hide() self.go:SetActive(false) -- 停止所有动作 self.go:StopAllActions() end -- 创建角色对象 function FishermanCharacter:createTarget(parent, worldPos) self.go = GameObject.Instantiate(FishingGameMgr.resLink.fisherman_character, parent) self.transform = self.go.transform self.transform.localPosition = worldPos or Vector3.zero end -- 无敌时闪烁动画 function FishermanCharacter:startBlink(sprite, interval) local que = {} table.insert(que, ua.Delay(interval)) table.insert(que, ua.cb(function() sprite.color = Color(0.8, 0.8, 0.8, 1) end)) table.insert(que, ua.Delay(interval)) table.insert(que, ua.cb(function() sprite.color = Color(1, 1, 1, 1) end)) self.invincibleAnim = self.go:RunAction(ua.RepeatForever(ua.Sequence(que))) end -- 吃鱼 function FishermanCharacter:eat(fish) self.info:eat(fish:getEnergy()) FishingGameMgr:updateEnergy() -- 销毁鱼对象 FishingGameMgr.map:addHideFish(fish) fish:destroy() if self.info:canUpgrade() then FishingGameMgr:upgrade() end self:playAnimation("kapibala_attack", false, function() --self.info:eat(fish:getEnergy()) --FishingGameMgr:updateEnergy() ---- 销毁鱼对象 --FishingGameMgr.map:addHideFish(fish) --fish:destroy() --if self.info:canUpgrade() then -- FishingGameMgr:upgrade() --end if self.direction == nil then self:playAnimation("kapibala_stand_swim", true) else self:playAnimation("kapibala_swim", true) end end) end -- 受伤 function FishermanCharacter:hurt() self.info:hurt() -- 更新能量 FishingGameMgr:updateEnergy() -- 停止移动 --self:clearDisplay() end -- 死亡 function FishermanCharacter:die() self:playAnimation("kapibala_die", false) end -- 升级 function FishermanCharacter:upgrade() self.info:upgrade() self:updateGrade() -- 播放升级动画 self.upgradeNode:SetActive(true) util.spine.play(self.upgrade_spine, "stand_1", false, function() self.upgradeNode:SetActive(false) end) end -- 更新等级 function FishermanCharacter:updateGrade() self.transform.localScale = Vector3.one * self.info.gradeConfig.characterZoom self.effect.widthMultiplier = self.info.gradeConfig.characterZoom self:updateActiveArea() end -- 复活 function FishermanCharacter:revive() -- 重置角色信息 self.info:revive() self:setInvincible(true, FishingConst.ReviveInvincibleTime) self:playSwimAnim() end -- 游戏胜利 function FishermanCharacter:win() self:playAnimation("kapibala_win", false) end -- 时间更新 function FishermanCharacter:timeUpdate(deltaTime) -- 无敌状态更新 if self:getInvincible() then self.info:reduceInvincibleTime(deltaTime) -- 如果无敌时间结束,取消无敌状态 if self.info:getInvincibleTime() <= 0 then self:setInvincible(false) end end -- 双倍能量状态 if self.info:getDoubleEnergy() then self.info:reduceDoubleEnergyTime(deltaTime) FishingGameMgr.mainUI:updatePropTime(self.info:getDoubleEnergyTime()) -- 如果双倍能量时间结束,取消双倍能量状态 if (self.info:getDoubleEnergyTime() <= 0) then self.info:setDoubleEnergy(false) end end -- 角色位置更新 if self.direction then local pos = self.go.transform.localPosition + self.direction * (self.info.moveSpeed / 10 * deltaTime) if pos.x > self.activeArea.right then pos.x = self.activeArea.right end if pos.x < self.activeArea.left then pos.x = self.activeArea.left end if pos.y > self.activeArea.top then pos.y = self.activeArea.top end if pos.y < self.activeArea.bottom then pos.y = self.activeArea.bottom end self.transform.localPosition = pos end end -- 设置无敌模式 function FishermanCharacter:setInvincible(isInvincible, invincibleTime) if self:getInvincible() == isInvincible then return end self.info:setInvincible(isInvincible, invincibleTime) if isInvincible then self:startBlink(self.sprite, 0.2, invincibleTime) else if self.invincibleAnim then self.go:StopAction(self.invincibleAnim) self.invincibleAnim = nil end self.sprite.color = Color(1, 1, 1, 1) end end -- 减少能量 function FishermanCharacter:reduceEnergy() -- 能量余量 self.info:reduceEnergy() FishingGameMgr:updateEnergy() if self.info:getEnergy() <= 0 then -- 如果能量为0,角色死亡 FishingGameMgr:die(true) return end end -- 获取当前能量 function FishermanCharacter:getEnergy() return self.info:getEnergy() end -- 获取当前等级 function FishermanCharacter:getGrade() return self.info:getGrade() end -- 获取无敌状态 function FishermanCharacter:getInvincible() return self.info:getInvincible() end -- 是否有护盾 function FishermanCharacter:getShield() return self.info:getShield() end -- 设置护盾 function FishermanCharacter:setShield(isOn) if self.info:getShield() == isOn then return end self.shield:SetActive(isOn) self.info:setShield(isOn) if not isOn then self:setInvincible(true, 1) end end -- 获取双倍能量状态 function FishermanCharacter:getDoubleEnergy() return self.info:getDoubleEnergy() end -- 设置双倍能量状态 function FishermanCharacter:setDoubleEnergy(isOn, timeLimit) self.double_energy:SetActive(isOn) self.info:setDoubleEnergy(isOn, timeLimit) end -- 设置显示方向 function FishermanCharacter:setDisplay(ctlDirection) ctlDirection = Vector3(ctlDirection.x, ctlDirection.y, 0) -- 图片朝向 if ctlDirection.x < 0 then self.dirTransform.localScale = Vector3(-1, 1, 1) else self.dirTransform.localScale = Vector3(1, 1, 1) end -- 摇杆的位置(提示角色行动方向和速度) local handlePos = self.dirTransform.localPosition if ctlDirection.magnitude > self.displayAreaRadius then local temp = ctlDirection.normalized * self.displayAreaRadius handlePos.x = handlePos.x + temp.x handlePos.y = handlePos.y + temp.y self.handle.localPosition = handlePos else handlePos.x = handlePos.x + ctlDirection.x handlePos.y = handlePos.y + ctlDirection.y self.handle.localPosition = handlePos end -- 角色方向 self.direction = ctlDirection.normalized self.dirTransform.up = self.direction -- 碰撞器方向 self.collider.up = self.direction end function FishermanCharacter:playSwimAnim() self:playAnimation("kapibala_swim", true) end -- 清理当前方向 function FishermanCharacter:clearDisplay() self.handle.localPosition = Vector3.zero self.direction = nil self:playAnimation("kapibala_stand_swim", true) end -- 通用动画播放方法 function FishermanCharacter:playAnimation(animName, loop, callback) if self.currentAnim == animName then return end -- 记录当前播放的动画 local oldAnim = self.currentAnim self.currentAnim = animName -- 播放动画 util.spine.play(self.spine, animName, loop or false, callback) end return FishermanCharacterBonusMgr@"--[[ 加成管理器 负责管理游戏中的各种加成效果 ]] local BonusMgr = defClassStatic("BonusMgr") local LOGTAG = "BonusMgr" -- 初始化 function BonusMgr:init() printInfo("BonusMgr", "------ 加成管理 初始化 ------") -- 加成实际数值 self.bonusActualValuesDic = {} -- 临时加成数值 self.bonusTempValuesDic = {} self:initUserData() self:initBonusActualValue() end -- 初始化用户数据 function BonusMgr:initUserData() self.userData = UserDataMgr.bonusUserData end -- 获取用户数据 function BonusMgr:getUserData() return self.userData end -- 开始计时 function BonusMgr:start() self:CalculateHourIncome() self:CalculateDailyIncome() self.tmr = TimerMgr:add(function() -- 每隔一段时间刷新加成 CurrencyMgr:changeCoin(self:getCoinIncomePer30sBonus()) self:CalculateHourIncome() self:CalculateDailyIncome() end, 30, 0) end -- 计算每一小时加成收益 function BonusMgr:CalculateHourIncome() -- 每小时收入 local lastTime = self.userData:getPerHourLastIncomeTime() if lastTime == -1 then lastTime = self.userData:updatePerHourLastIncomeTime() end local curr = os.time() local span = curr - lastTime if span >= 3600 then CurrencyMgr:changeCoin(self:getCoinIncomePerHourBonus() * (span / 3600)) self.userData:updatePerHourLastIncomeTime(span % 3600) end end -- 计算每天加成收益 function BonusMgr:CalculateDailyIncome() -- 每天收入 local lastTime = self.userData:getPerDayLastIncomeTime() if lastTime == -1 then lastTime = self.userData:updatePerDayLastIncomeTime() end local curr = os.time() local span = curr - lastTime if span >= 86400 then CurrencyMgr:changeStar(self:getCoinIncomePerDayBonus() * (span / 86400)) self.userData:updatePerDayLastIncomeTime(span % 86400) end end -- 获取加成实际数值 function BonusMgr:getBonusActualValue(bonusType) return self.bonusActualValuesDic[bonusType] or 0 end -- 设置加成实际数值 function BonusMgr:setBonusActualValue(bonusType, bonusValue) if not self.bonusActualValuesDic then self.bonusActualValuesDic = {} end if (not bonusType) or (bonusType == -1) then return end self.bonusActualValuesDic[bonusType] = bonusValue end -- 改动加成实际数值 function BonusMgr:changeBonusActualValue(bonusType, bonusValue) self:setBonusActualValue(bonusType, self:getBonusActualValue(bonusType) + bonusValue) end -- 获取指定加成id的加成数量 function BonusMgr:getBonusCountById(bonusId) local config = self:getBonusConfigById(bonusId) return config.bonusNums end -- 通过加成id获取加成配置 function BonusMgr:getBonusConfigById(bonusId) return CommonBonusCfgParse:getCommonBonusCfgById(bonusId) end -- 通过加成id获取加成列表 function BonusMgr:getBonusListById(bonusId) return CommonBonusCfgParse:getCommonBonusListCfgById(bonusId) end -- 通过顺序获取加成类型 function BonusMgr:getBonusTypeByIndex(bonusId, index) local config = self:getBonusConfigById(bonusId) if index > config.bonusNums then return -1 end return config["bonusId_" .. index] end -- 通过加成类型获取加成描述 function BonusMgr:getBonusDescByType(bonusType, ...) return string.format(CommonBonusTypeParse:getCommonBonusTypeCfgByType(bonusType).desc, ...) end -- 通过加成id获取加成列表 function BonusMgr:getBonusListById(bonusId) return CommonBonusCfgParse:getCommonBonusListCfgById(bonusId) end -- 通过顺序获取加成描述 function BonusMgr:getBonusDescByIdx(bonusId, index) local config = self:getBonusConfigById(bonusId) if not config then printError(LOGTAG, string.format("没有找到加成配置[%s]", bonusId)) return "" end local bonusType = config["bonusId_" .. index] local bonusValue = config["value_" .. index] return string.format(CommonBonusTypeParse:getCommonBonusTypeCfgByType(bonusType).desc, self:getBonusDescValue(bonusType, bonusValue)) end -- 初始化加成实际值 function BonusMgr:initBonusActualValue() local list = self.userData:getBonusObtainedIdList() for _, v in ipairs(list) do self:applyBonusId(v) end end -- 添加加成id function BonusMgr:addBonusId(bonusId) if self:applyBonusId(bonusId, true) then -- 存档 self.userData:addBonusId(bonusId) end end -- 应用加成 function BonusMgr:applyBonusId(bonusId, refresh) local config = self:getBonusConfigById(bonusId) if not config then return false end -- 计算加成并更新实际值 for i = 1, config.bonusNums do local type = config["bonusId_" .. i] local value = config["value_" .. i] self:changeBonusActualValue(type, value) if refresh then if type == BonusConst.BonusType.evaluate then CurrencyMgr:refreshStar() end end end return true end -- 获取加成描述展示值 function BonusMgr:getBonusDescValue(bonusType, bonusValue) if bonusType == BonusConst.BonusType.coin_income_max_time then return bonusValue / 3600 -- 将秒转换为小时 end return bonusValue -- 默认直接返回数值 end -- 获取临时加成 function BonusMgr:getTempBonus(bonusType) return self.bonusTempValuesDic[bonusType] or 0 end -- 添加临时加成 function BonusMgr:addTempBonus(bonusType, bonusValue) -- 临时加成,直接覆盖 self.bonusTempValuesDic[bonusType] = self:getTempBonus(bonusType) + bonusValue end -- 移除临时加成 function BonusMgr:removeTempBonus(bonusType, bonusValue) -- 临时加成,直接覆盖 self.bonusTempValuesDic[bonusType] = self:getTempBonus(bonusType) - bonusValue if self.bonusTempValuesDic[bonusType] < 0 then self.bonusTempValuesDic[bonusType] = 0 end end ----------- 建筑加成相关 ----------- -- 获取人气加成 function BonusMgr:getStarBonus() return self:getBonusActualValue(BonusConst.BonusType.evaluate) + self:getTempBonus(BonusConst.BonusType.evaluate) end -- 获取小费加成 function BonusMgr:getTipBonus() return self:getBonusActualValue(BonusConst.BonusType.coin_income) + self:getTempBonus(BonusConst.BonusType.coin_income) end -- 获取小费收入最大累计时间加成 function BonusMgr:getTipMaxTimeBonus() return self:getBonusActualValue(BonusConst.BonusType.coin_income_max_time) + self:getTempBonus(BonusConst.BonusType.coin_income_max_time) end -- 获取客人每次消费可获得收入加成 function BonusMgr:getPerConsumeBonus() return 0 end -- 获取做菜效率加成 function BonusMgr:getCookEfficiencyBonus() local fixed = self:getBonusActualValue(BonusConst.BonusType.cook_efficiency) local temp = self:getTempBonus(BonusConst.BonusType.cook_efficiency) return ((100 + fixed) / 100) * ((100 + temp) / 100) end -- 获取每隔30秒可获得额外收入加成 function BonusMgr:getCoinIncomePer30sBonus() return self:getBonusActualValue(BonusConst.BonusType.coin_income_per_30s) + self:getTempBonus(BonusConst.BonusType.coin_income_per_30s) end -- 获取每小时自助收入加成 function BonusMgr:getCoinIncomePerHourBonus() return self:getBonusActualValue(BonusConst.BonusType.coin_income_per_hour) + self:getTempBonus(BonusConst.BonusType.coin_income_per_hour) end -- 获取每日音符收入加成 function BonusMgr:getCoinIncomePerDayBonus() return self:getBonusActualValue(BonusConst.BonusType.coin_income_per_day) + self:getTempBonus(BonusConst.BonusType.coin_income_per_day) end -- 获取歌曲数量加成 function BonusMgr:getSongCountBonus() return self:getBonusActualValue(BonusConst.BonusType.music_count) + self:getTempBonus(BonusConst.BonusType.music_count) end ----------- 建筑加成相关 ----------- -- 增加建筑加成 function BonusMgr:addBonusIdByBuildingId(buildingId) local config = BuildingMgr:getConfig(buildingId) if config.effectId and (config.effectId ~= -1) then self:addBonusId(config.effectId) end end ----------- 菜品加成相关 ----------- -- 增加菜品加成 function BonusMgr:addBonusIdByCuisineId(cuisineId) local config = CuisineMgr:getConfig(cuisineId) if config.bonusId and (config.bonusId ~= -1) then self:addBonusId(config.bonusId) end end return BonusMgrEmployeeDetailUIa---- 菜品详情界面 ---@class EmployeeDetailUI : UILayer local EmployeeDetailUI, super = defClass("EmployeeDetailUI", UILayer) -- UI require("modules/ui/employeeui/EmployeeStoryCell") require("modules/ui/employeeui/EmployeeSkinCell") -- UIComponent local UIImage = CS.UnityEngine.UI.Image local UIButton = CS.UnityEngine.UI.Button local TMProUGUI = CS.TMPro.TextMeshProUGUI function EmployeeDetailUI:ctor(employeeId) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/employeeui/employeeuireslink") self.employeeId = employeeId end function EmployeeDetailUI:onLoad() self.ui = GameObject.Instantiate(self.R.employee_detail_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end --- 初始化 function EmployeeDetailUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) self:initInfo() self:initToggle() self:initAttribute() self:initStory() --self:initSkin() self.not_enough = self.ui:Seek("not_enough") self.purchase_btn = self.ui:Seek("purchase_btn") self.purchase_btn_text = self.ui:Seek("purchase_btn_text") self.upgrade_btn = self.ui:Seek("upgrade_btn") self.upgrade_btn_text = self.ui:Seek("upgrade_btn_text") self.upgrade_time = self.ui:Seek("upgrade_time") self.training = self.ui:Seek("employee_training") self.training_img = self.ui:Seek("employee_training_img") self.training_value = self.ui:Seek("employee_training_value") self.video_btn = self.ui:Seek("video_btn") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.purchase_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:purchase() end) util.ugui.addButtonClickEvent(self.upgrade_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:upgrade() end) util.ugui.addButtonClickEvent(self.video_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:videoPurchase() end) end --- 展示UI function EmployeeDetailUI:showUI() local info = EmployeMgr:getEmployeeInfo(self.employeeId) info:refreshProficiency() local isMaster = info:getPosts() == EmployeConst.Posts.Manager local isPurchased = info:isPurchased() local price = info:getHirePrice() local hireShortage = info:hireShortage() local isMaxProficiency = info:isMaxProficiency() local isCapybara = info.id == 300007 local needPurchase = info:isUnlocked() and (not isPurchased) and (not isMaster) and (not isCapybara) local needUpgrade = (isPurchased or isCapybara) and (not info:isMaxGrade()) self:showInfo(info) self:showToggle(info) self:showAttribute(info) self:showStory(info) --self:showSkin() -- 购买 self.purchase_btn:SetActive(needPurchase) self.not_enough:SetActive(needPurchase and (hireShortage > 0)) if needPurchase then if price <= 0 then self.purchase_btn_text[TMProUGUI].text = "免费" else self.purchase_btn_text[TMProUGUI].text = price .. "雇佣" end if hireShortage > 0 then self.not_enough[TMProUGUI].text = "金币不够了,还差" .. hireShortage .. "金币" end end -- 视频购买按钮 self.video_btn:SetActive(needPurchase and (hireShortage > 0) and (hireShortage < price * 0.2)) -- 升级 self.upgrade_btn:SetActive(needUpgrade) self.upgrade_time:SetActive(needUpgrade and (not isMaxProficiency)) self.training:SetActive(needUpgrade and (not isMaxProficiency)) if needUpgrade then --self.upgrade_btn[UIButton].interactable = isMaxProficiency self.upgrade_btn_text[TMProUGUI].text = info:getUpgradePrice() .. "升级" if not isMaxProficiency then local maxProficiency = info:getMaxProficiency() local proficiency = info:getProficiency() self.upgrade_time[TMProUGUI].text = info:getMaxProficiencyDesc() self.training_img[UIImage].fillAmount = proficiency / maxProficiency self.training_value[TMProUGUI].text = info:getTimeLeftDesc() end end end function EmployeeDetailUI:initInfo() self.employee_img = self.ui:Seek("employee_img") self.employee_name = self.ui:Seek("employee_name") self.employee_desc = self.ui:Seek("employee_desc") -- 角色动画父节点 self.employee_anim_root = self.ui:Seek("role_anim_ui_parent") end function EmployeeDetailUI:showInfo(info) --self.employee_img[UIImage].sprite = info:getIcon() self.employee_img[UIImage]:SetNativeSize() self.employee_name[TMProUGUI].text = info:getName() self.employee_desc[TMProUGUI].text = info:getDesc() -- 角色动画 local path = string.format("Assets/AssetsPackage/Res/modules/roles/employe/prefabs/ui/employe_%s.prefab", info.id) local prefab = ResLoader.loadAsset(path) local animNode = GameObject.Instantiate(prefab) animNode:SetParent(self.employee_anim_root) animNode:SetActive(true) animNode:SetPosition(Vector3(0, 0, 0)) if info.id == 300007 then animNode:SetScale(Vector3(0.8, 0.8, 1)) else animNode:SetScale(Vector3(1.7, 1.7, 1)) end end function EmployeeDetailUI:initToggle() self.toggle_attribute = self.ui:Seek("toggle_attribute") self.toggle_story = self.ui:Seek("toggle_story") self.toggle_attribute_mask = self.ui:Seek("toggle_attribute_mask") self.toggle_story_mask = self.ui:Seek("toggle_story_mask") self.content_attribute = self.ui:Seek("content_attribute") self.content_story = self.ui:Seek("content_story") util.ugui.addToggleValueChangeEvent(self.toggle_attribute, function(isOn) self:attributeTab(isOn) end) util.ugui.addToggleValueChangeEvent(self.toggle_story, function(isOn) self:storyTab(isOn) end) end function EmployeeDetailUI:showToggle() self:attributeTab(true) self:storyTab(false) end function EmployeeDetailUI:initAttribute() self.content_curr_attribute = self.ui:Seek("content_curr_attribute") self.content_next_attribute = self.ui:Seek("content_next_attribute") self.curr_values = {} for i = 1, 4 do self.curr_values[i] = self.ui:Seek("attribute" .. i .. "_curr_value") end self.next_values = {} for i = 1, 4 do self.next_values[i] = self.ui:Seek("attribute" .. i .. "_next_value") end end function EmployeeDetailUI:showAttribute(info) local isMaxGrade = info:isMaxGrade() self.content_next_attribute:SetActive(not isMaxGrade) for i = 1, 4 do if i == 1 then self.curr_values[i][TMProUGUI].text = info:getCurrStarDesc() else local has = not (info:getCurrTagId(i - 1) == -1) self.curr_values[i]:SetActive(has) if has then self.curr_values[i][TMProUGUI].text = info:getCurrTagDesc(i - 1) end end end if not isMaxGrade then for i = 1, 4 do if i == 1 then self.next_values[i][TMProUGUI].text = info:getNextStarDesc() else local has = not (info:getNextTagId(i - 1) == -1) self.next_values[i]:SetActive(has) if has then self.next_values[i][TMProUGUI].text = info:getNextTagDesc(i - 1) end end end end end function EmployeeDetailUI:initStory() self.no_story = self.ui:Seek("no_story") self.stories = {} for i = 1, 3 do self.stories[i] = self.ui:Seek("story" .. i) end end function EmployeeDetailUI:showStory(info) self.toggle_story:SetActive(info:getStoryNum() > 0) if info:getStoryNum() <= 0 then self.no_story:SetActive(true) for i = 1, 3 do self.stories[i]:SetActive(false) end return end self.no_story:SetActive(false) for i = 1, 3 do local sid = info:getStoryId(i) local has = not (sid == -1) self.stories[i]:SetActive(has) if has then EmployeeStoryCell.new(self.stories[i], sid, info:isPurchased()) end end end function EmployeeDetailUI:initSkin() self.skin_info = self.ui:Seek("skin_info") self.skin_name = self.ui:Seek("skin_name") self.skin_value = self.ui:Seek("skin_value") self.skin_install = self.ui:Seek("skin_install") self.skin_install_btn = self.ui:Seek("skin_install_btn") self.skin_purchase_btn = self.ui:Seek("skin_purchase_btn") self.skins = {} for i = 1, 6 do self.skins[i] = self.ui:Seek("employee_sink" .. i) end end function EmployeeDetailUI:showSkin() local configs = EmployeSkinCfgParse:getEmployeeSkinCfgByEmployeeCfgId(self.employeeId) --self:showSkin(configs[1]) self.skins = {} for i = 1, 6 do local value = self.ui:Seek("employee_sink" .. i) if i <= #configs then value:SetActive(true) EmployeeSkinCell.new(configs[i], value, function(cfg) self:showSkinInfo(cfg) end) else value:SetActive(false) end end self:showSkinInfo(configs[1]) end function EmployeeDetailUI:showSkinInfo(config) --self.content_skin = self.ui:Seek("content_skin") self.skin_name[TMProUGUI].text = config.skinName self.skin_name[TMProUGUI].text = self.info.getTagDesc() local isInstall = false local isPurchased = false end function EmployeeDetailUI:sinkSkinPurchase(skinId) end function EmployeeDetailUI:skinInstall(skinId) end function EmployeeDetailUI:attributeTab(isOn) self.content_attribute:SetActive(isOn) self.toggle_attribute_mask:SetActive(isOn) --if isOn then -- self.scroll:GetComponent("ScrollView").content = self.content_attribute --end end function EmployeeDetailUI:storyTab(isOn) self.content_story:SetActive(isOn) self.toggle_story_mask:SetActive(isOn) --if isOn then -- self.scroll:GetComponent("ScrollView").content = self.content_story --end end function EmployeeDetailUI:purchase() local info = EmployeMgr:getEmployeeInfo(self.employeeId) local hireShortage = info:hireShortage() if hireShortage > 0 then PopUpUI.new("金币不够了,还差" .. hireShortage .. "金币"):show():showMask():enableCloseWhenClickMask() return end EmployeMgr:employeeHire(self.employeeId) UILayerUtil:closeUIByName("EmployeeDetailUI") UILayerUtil:closeUIByName("EmployeeListUI") end function EmployeeDetailUI:videoPurchase() local info = EmployeMgr:getEmployeeInfo(self.employeeId) local hireShortage = info:hireShortage() EmployeMgr:employeeHireByVideo(self.employeeId, hireShortage) UILayerUtil:closeUIByName("EmployeeDetailUI") UILayerUtil:closeUIByName("EmployeeListUI") end function EmployeeDetailUI:upgrade() local info = EmployeMgr:getEmployeeInfo(self.employeeId) local isMaxProficiency = info:isMaxProficiency() if not isMaxProficiency then PopUpUI.new(info:getMaxProficiencyDesc()):show():showMask():enableCloseWhenClickMask() return end EmployeMgr:employeeUpgrade(self.employeeId) self:showUI() end return EmployeeDetailUICookingBenchStove--[[ 灶台上的一个炉子 author:{zhangpeng} time:2025-05-16 11:07:08 ]] local CookingBenchStove = defClass("CookingBenchStove") local LOGTAG = "CookingBenchStove" function CookingBenchStove:ctor(cookingBench, stoveIndex) -- 所属灶台 self.cookingBench = cookingBench -- 炉子索引 self.stoveIndex = stoveIndex -- 订单信息 self.orderInfo = nil -- 是否工作 self.working = false -- 烹饪完成回调 self.onCookFinishedCallback = nil -- 烹饪效果节点 self.cookingEffectNode = nil -- 初始化视图 self:initView() end -- 初始化视图 function CookingBenchStove:initView() -- 获取炉子节点 local benchNode = self.cookingBench.buildingNode self.stoveNode = benchNode:Seek("stove_" .. self.stoveIndex) -- 获取烹饪效果节点 if self.stoveNode then self.cookingEffectNode = self.stoveNode:Seek("cooking_effect") if self.cookingEffectNode then self.cookingEffectNode:SetActive(false) end end end -- 是否工作 function CookingBenchStove:isWorking() return self.working end -- 设置工作状态 function CookingBenchStove:setWorking(working) self.working = working -- 显示/隐藏烹饪效果 if self.cookingEffectNode then self.cookingEffectNode:SetActive(working) end end -- 烹饪订单菜品 function CookingBenchStove:cook(callback) -- 检查是否已有订单 if not self.orderInfo then printError(LOGTAG, string.format("炉子[%s]没有订单信息,无法开始烹饪", self.stoveIndex)) return end -- 保存回调 self.onCookFinishedCallback = callback -- 设置工作状态 self:setWorking(true) -- 获取烹饪时间 local cookingTime = self.orderInfo:getCookingTime() printInfo(LOGTAG, string.format("炉子[%s]开始烹饪订单[%s],预计耗时:%s秒", self.stoveIndex, self.orderInfo:getOrderId(), cookingTime)) -- 在炉子上显示菜品气泡 self:showCuisineBubble() -- 延迟一段时间后完成烹饪 -- 菜品遮罩跟随时间刷新 -- self:timer(cookingTime, 0.1, false,function (dt) -- end) -- self.stoveNode:Delay(cookingTime, function() -- self:finishCooking() -- end) self:startCookingTimer(cookingTime) end -- 开始做菜计时器 function CookingBenchStove:startCookingTimer(cookingTime) -- 记录总烹饪时间和已经过的时间 self.totalCookingTime = cookingTime self.elapsedTime = 0 self.cuisineBubble:resetCookingMaskProgress() -- 启动计时器,每0.01秒更新一次 self.cookingTimerActionId = self:timer(function(dt) -- 检查计时变量是否还存在,避免在停止计时器时出现nil值访问 if not self.elapsedTime or not self.totalCookingTime then return end self.elapsedTime = self.elapsedTime + dt -- printInfo(LOGTAG, string.format("炉子[%s]烹饪时间:%s", self.stoveIndex, self.elapsedTime)) -- 设置菜品遮罩进度 if self.cuisineBubble then self.cuisineBubble:setCookingMaskProgress(1- self.elapsedTime / self.totalCookingTime) end -- 检查是否达到烹饪时间 if self.elapsedTime >= self.totalCookingTime then self:finishCooking() end end, 0.01, 0) end -- 停止做菜计时器 function CookingBenchStove:stopCookingTimer() -- 先清空计时相关变量,让计时器回调能够及时检测到并退出 self.elapsedTime = nil self.totalCookingTime = nil -- 然后停止计时器 if self.cookingTimerActionId then TimerMgr:rem(self.cookingTimerActionId) self.cookingTimerActionId = nil end end -- 显示菜品气泡 function CookingBenchStove:showCuisineBubble() if not self.orderInfo then printError(LOGTAG, string.format("炉子[%s]没有订单信息,无法显示菜品气泡", self.stoveIndex)) return end -- 获取菜品ID local cuisineId = self.orderInfo:getCuisineId() -- 移除旧的菜品气泡(如果有) self:removeCuisineBubble() -- 创建新的菜品气泡,参数顺序:菜品ID, 父节点, 显示类型(可选) self.cuisineBubble = CuisineBubbleMgr:createBubble(cuisineId, self.stoveNode, CuisineConst.BubbleShowParentType.stove) -- 设置为灶台显示类型 if self.cuisineBubble then self.cuisineBubble:setParentType(CuisineConst.BubbleShowParentType.stove) -- 添加烹饪效果 self.cuisineBubble:doCookingEffect() end end -- 移除菜品气泡 function CookingBenchStove:removeCuisineBubble() if self.cuisineBubble then if self.cuisineBubble.destroy then self.cuisineBubble:destroy() end -- 清空引用 self.cuisineBubble = nil end end -- 设置订单信息 function CookingBenchStove:setOrderInfo(orderInfo) self.orderInfo = orderInfo end -- 获取订单信息 function CookingBenchStove:getOrderInfo() return self.orderInfo end -- 获取烹饪时间 function CookingBenchStove:getCookingTime() if not self.orderInfo then return 0 end return self.orderInfo:getCookingTime() end -- 完成烹饪 function CookingBenchStove:finishCooking() -- 检查订单信息是否存在,避免重复调用 if not self.orderInfo then printWarn(LOGTAG, string.format("炉子[%s]订单信息为空,可能已经完成烹饪", self.stoveIndex)) return end printInfo(LOGTAG, string.format("炉子[%s]完成烹饪订单[%s]", self.stoveIndex, self.orderInfo:getOrderId())) -- 停止烹饪计时器 self:stopCookingTimer() -- 保存订单信息用于后续操作 local orderInfo = self.orderInfo local callback = self.onCookFinishedCallback -- 先清除订单引用,避免重复处理 self.orderInfo = nil self.onCookFinishedCallback = nil -- 移除菜品气泡 self:removeCuisineBubble() -- 设置炉子状态为非工作状态 self:setWorking(false) -- 设置订单状态为已完成烹饪 orderInfo:setStatus(OrderConst.Status.cooked) -- 调用回调 if callback then callback() end end return CookingBenchStove HelpListUI--- ---@class HelpListUI : UILayer local HelpListUI, super = defClass("HelpListUI", UILayer) require("modules/ui/helpui/HelpCell") function HelpListUI:ctor(helpType) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/helpui/helpuireslink") self.helpType = helpType end function HelpListUI:onLoad() self.ui = GameObject.Instantiate(self.R.help_list_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end function HelpListUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.help_title = self.ui:Seek("help_title") self.content = self.ui:Seek("Content") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) end function HelpListUI:showUI() self.help_title:GetComponent("TextMeshProUGUI").text = HelpConst.TypeName[self.helpType] local list = HelpCfgParse:getDataByHelpType(self.helpType) for _, config in pairs(list) do local go = GameObject.Instantiate(self.R.help_cell, self.content.transform) HelpCell.new(go, config) end end return HelpListUIStageDeskConst--[[ 舞台常量 author:{zhangpeng} time:2025-07-07 17:42:30 ]] local StageDeskConst = defClassStatic("StageDeskConst") -- 舞台状态 StageDeskConst.State = { idle = 1, } -- 座位状态 StageDeskConst.SeatState = { -- 空闲 idle = 1, -- 有顾客正在前往该座位 approaching = 2, -- 观看中 watching = 3, } return StageDeskConststorydialogCfgh--[[ from file:对话表.xlsx --]] local storydialogCfg = { [1] = { dialogId = 630301, dialogName = "剧情.一", times = 3, roleId_1 = 300003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "店长店长!在忙吗?", roleId_2 = 300001, roleSide_2 = "r", dialogType_2 = 1, content_2 = "嗯?怎么了?", roleId_3 = 300003, roleSide_3 = "l", dialogType_3 = 1, content_3 = "我有点事情想和你说一下……", roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [2] = { dialogId = 630401, dialogName = "剧情.一", times = 4, roleId_1 = 300004, roleSide_1 = "l", dialogType_1 = 1, content_1 = "您好,听说您这儿在招大厨?", roleId_2 = 300001, roleSide_2 = "r", dialogType_2 = 1, content_2 = "是的,非常欢迎!让我来给你介绍一下餐厅的员工吧", roleId_3 = 300001, roleSide_3 = "l", dialogType_3 = 1, content_3 = "这是我们餐厅的服务员,布丁。", roleId_4 = 300003, roleSide_4 = "r", dialogType_4 = 1, content_4 = "你好!我是布丁。", roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [3] = { dialogId = 630501, dialogName = "剧情.一", times = 3, roleId_1 = 300005, roleSide_1 = "l", dialogType_1 = 1, content_1 = "您...好... 请问...这里招餐厅助理...吗?", roleId_2 = 300001, roleSide_2 = "r", dialogType_2 = 1, content_2 = "是在招人,可以先介绍一下自己吗?", roleId_3 = 300005, roleSide_3 = "l", dialogType_3 = 1, content_3 = "没...问题... ", roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [4] = { dialogId = 630601, dialogName = "剧情.一", times = 5, roleId_1 = 300006, roleSide_1 = "l", dialogType_1 = 1, content_1 = "您好!餐厅是在招保安吧?", roleId_2 = 300001, roleSide_2 = "r", dialogType_2 = 1, content_2 = "欢迎欢迎!", roleId_3 = 300006, roleSide_3 = "l", dialogType_3 = 1, content_3 = "我以前干搜救的,腿受过伤跑不了现场了。看传单说这儿氛围好,想来试试!", roleId_4 = 300001, roleSide_4 = "r", dialogType_4 = 1, content_4 = "太棒了!我们正缺警觉性高的!", roleId_5 = 300006, roleSide_5 = "l", dialogType_5 = 1, content_5 = "太好了,巡逻范围是哪儿?我保准大家安全又开心!", }, [5] = { dialogId = 630701, dialogName = "剧情.一", times = 5, roleId_1 = 300007, roleSide_1 = "l", dialogType_1 = 1, content_1 = "老板!听说你们这儿招渔夫?我这儿有最新鲜的渔获!", roleId_2 = 300001, roleSide_2 = "r", dialogType_2 = 1, content_2 = "是在招。不过,我们是不是在哪见过?", roleId_3 = 300007, roleSide_3 = "l", dialogType_3 = 1, content_3 = "没没没!绝对没见过!我就是河对岸新来的打渔的!保证渔获又快又多!", roleId_4 = 300001, roleSide_4 = "r", dialogType_4 = 1, content_4 = "河对岸新来的?...昨天电视新闻里那个动物园悬赏的那只卡皮巴拉是不是...", roleId_5 = 300007, roleSide_5 = "l", dialogType_5 = 1, content_5 = "悬赏?不是我!我是正经渔夫!...那啥,咱们这儿...管饭吗?", }, [6] = { dialogId = 630801, dialogName = "剧情.一", times = 3, roleId_1 = 300008, roleSide_1 = "l", dialogType_1 = 1, content_1 = "您好!我...我听说您这里在找驻唱?不知道我能不能来试一试?", roleId_2 = 300001, roleSide_2 = "r", dialogType_2 = 1, content_2 = "欢迎!我们正需要能打动森林的好声音!快请进。", roleId_3 = 300008, roleSide_3 = "l", dialogType_3 = 1, content_3 = "嗯...我会把整个故乡...唱给这片森林听…", roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [7] = { dialogId = 630901, dialogName = "剧情.一", times = 3, roleId_1 = 300009, roleSide_1 = "l", dialogType_1 = 1, content_1 = "您好,我是来应聘烧烤师傅的。", roleId_2 = 300001, roleSide_2 = "r", dialogType_2 = 1, content_2 = "手里这些工具… 看着有年头了。", roleId_3 = 300009, roleSide_3 = "l", dialogType_3 = 1, content_3 = "家伙趁手,火候才稳。老伙计在手,味道才正。经理,给个机会试试火? ", roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [8] = { dialogId = 640101, dialogName = "剧情", times = 2, roleId_1 = 400001, roleSide_1 = "l", dialogType_1 = 1, content_1 = "这味道真是绝了!以前啊,有位好心的大姐经常给我这个。", roleId_2 = 400001, roleSide_2 = "l", dialogType_2 = 1, content_2 = "虽说我没吃过几次,但在我心里,它就是天底下最好吃的东西!", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [9] = { dialogId = 640201, dialogName = "剧情", times = 4, roleId_1 = 400002, roleSide_1 = "l", dialogType_1 = 1, content_1 = "你好,麻烦来一杯加冰的威士忌,谢谢。", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "好的,一杯加冰威士忌,马上给您准备", roleId_3 = 400002, roleSide_3 = "l", dialogType_3 = 1, content_3 = "等等,最近血压有点高,威士忌可能不太合适,要不...换杯啤酒?不过啤酒好像也不太健康", roleId_4 = 400002, roleSide_4 = "l", dialogType_4 = 1, content_4 = "既然如此,我就喝一杯牛奶,谢谢。", roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [10] = { dialogId = 640601, dialogName = "剧情", times = 4, roleId_1 = 300001, roleSide_1 = "r", dialogType_1 = 1, content_1 = "哇......你的围巾好可爱啊,小姐!我在哪里可以买到它?", roleId_2 = 400006, roleSide_2 = "l", dialogType_2 = 1, content_2 = "感谢您的赞美。其实,这个是我自己做的。", roleId_3 = 300001, roleSide_3 = "r", dialogType_3 = 1, content_3 = "难以置信!可以教教我吗?", roleId_4 = 400006, roleSide_4 = "l", dialogType_4 = 1, content_4 = "当然了!", roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [11] = { dialogId = 640701, dialogName = "剧情", times = 5, roleId_1 = 300003, roleSide_1 = "r", dialogType_1 = 1, content_1 = "先生,今天想吃点什么?", roleId_2 = 400007, roleSide_2 = "l", dialogType_2 = 1, content_2 = "(揉眼睛)啊…来一份....蛋糕……", roleId_3 = 300003, roleSide_3 = "r", dialogType_3 = 1, content_3 = "好的!其他需要什么吗?", roleId_4 = 400007, roleSide_4 = "l", dialogType_4 = 1, content_4 = "(头栽进菜单)Zzz…呼噜…", roleId_5 = 300003, roleSide_5 = "r", dialogType_5 = 1, content_5 = "顾客?顾客?怎么还睡着了....", }, [12] = { dialogId = 640801, dialogName = "剧情", times = 5, roleId_1 = 300003, roleSide_1 = "r", dialogType_1 = 1, content_1 = "您好,这是菜单,您先看看。我稍后就过来帮您点单。", roleId_2 = 400008, roleSide_2 = "l", dialogType_2 = 1, content_2 = "好的,谢谢。", roleId_3 = 300003, roleSide_3 = "r", dialogType_3 = 1, content_3 = "(半小时后)不好意思让您久等了,刚才有点忙。您这边看得差不多了吗?", roleId_4 = 400008, roleSide_4 = "l", dialogType_4 = 1, content_4 = "没事没事!菜单内容真丰富,看得我都挑花眼了。", roleId_5 = 300001, roleSide_5 = "r", dialogType_5 = 1, content_5 = "不管您今天点什么,我都给您打九折!", }, [13] = { dialogId = 649011, dialogName = '', times = 1, roleId_1 = 400001, roleSide_1 = "l", dialogType_1 = 1, content_1 = "嗨,我们包场给朋友过生日的,人都快到了", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [14] = { dialogId = 649012, dialogName = '', times = 1, roleId_1 = 400001, roleSide_1 = "l", dialogType_1 = 1, content_1 = "今晚大家玩得很开心", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [15] = { dialogId = 649021, dialogName = '', times = 1, roleId_1 = 400002, roleSide_1 = "l", dialogType_1 = 1, content_1 = "你好,我们预定了包场", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [16] = { dialogId = 649022, dialogName = '', times = 1, roleId_1 = 400002, roleSide_1 = "l", dialogType_1 = 1, content_1 = "辛苦你们了,今晚安排得很周到", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [17] = { dialogId = 649031, dialogName = '', times = 1, roleId_1 = 400003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "现在还有位子吗?三位。", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [18] = { dialogId = 649032, dialogName = '', times = 1, roleId_1 = 400003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "味道还不错,下次再来尝尝。", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [19] = { dialogId = 649041, dialogName = '', times = 1, roleId_1 = 400014, roleSide_1 = "l", dialogType_1 = 1, content_1 = "老位置,今天带了些朋友一起来的。", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [20] = { dialogId = 649042, dialogName = '', times = 1, roleId_1 = 400014, roleSide_1 = "l", dialogType_1 = 1, content_1 = "结账!", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [21] = { dialogId = 649051, dialogName = '', times = 1, roleId_1 = 400005, roleSide_1 = "l", dialogType_1 = 1, content_1 = "你好,我们预订了今晚的包场。", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [22] = { dialogId = 649052, dialogName = '', times = 1, roleId_1 = 400005, roleSide_1 = "l", dialogType_1 = 1, content_1 = "今天很不错,下次再来!", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [23] = { dialogId = 670011, dialogName = '', times = 2, roleId_1 = 300003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "这里风景真不错啊,我们就在这里创建营地吧!", roleId_2 = 300003, roleSide_2 = "l", dialogType_2 = 1, content_2 = "你看,这儿还有块旧的木地板!不知道以前是谁在这儿搭过屋子。", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [24] = { dialogId = 670021, dialogName = '', times = 2, roleId_1 = 300003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "让我们先准备一张桌子吧!", roleId_2 = 300003, roleSide_2 = "l", dialogType_2 = 1, content_2 = "先打开点击建筑按钮。", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [25] = { dialogId = 670031, dialogName = '', times = 1, roleId_1 = 300003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "这样顾客就有地方吃饭了。", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [26] = { dialogId = 670041, dialogName = '', times = 2, roleId_1 = 300003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "让我们再买一个灶台用来给客人制作菜品。", roleId_2 = 300003, roleSide_2 = "l", dialogType_2 = 1, content_2 = "还记得如何购买设施吗?", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [27] = { dialogId = 670051, dialogName = '', times = 2, roleId_1 = 300003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "第一个客人已经来了!", roleId_2 = 300003, roleSide_2 = "l", dialogType_2 = 1, content_2 = "让我们来看看客人想吃些什么呢?", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [28] = { dialogId = 670061, dialogName = '', times = 1, roleId_1 = 300003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "灶台会自动烹饪菜品,菜品完成后会给呈上。", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [29] = { dialogId = 670071, dialogName = '', times = 1, roleId_1 = 300003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "菜品已经完成了,让我们看看客人满意吗", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [30] = { dialogId = 670081, dialogName = '', times = 1, roleId_1 = 300003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "客人很满意哎!在享用完美食后,会自动收取金币。", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [31] = { dialogId = 670091, dialogName = '', times = 1, roleId_1 = 300003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "新店客人还比较少,需要加强宣传招揽顾客,让我们宣传一下吸引客人吧!", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [32] = { dialogId = 670101, dialogName = '', times = 2, roleId_1 = 300003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "投入高级宣传,效果更优,可触及更远区域的潜在顾客", roleId_2 = 300003, roleSide_2 = "l", dialogType_2 = 1, content_2 = "我刚拿下开业酬宾团购礼包,能给你带来显著帮助!", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [33] = { dialogId = 670111, dialogName = '', times = 1, roleId_1 = 300003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "做得很好,客人这不就来了嘛!我还有点别的事要处理,后面就全靠你啦!", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [34] = { dialogId = 680101, dialogName = '', times = 2, roleId_1 = 400003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "请帮我准备一些储存粮。它必须非常易于存储!", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "没问题!保证给您树洞塞的满满的。", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [35] = { dialogId = 680102, dialogName = '', times = 2, roleId_1 = 300003, roleSide_1 = "r", dialogType_1 = 1, content_1 = "抱歉,明天一定给您准备好储存粮", roleId_2 = 400003, roleSide_2 = "l", dialogType_2 = 1, content_2 = "我明天可以再过来", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [36] = { dialogId = 680103, dialogName = '', times = 1, roleId_1 = 400003, roleSide_1 = "l", dialogType_1 = 1, content_1 = "太好了,这样我就不用担心储蓄粮不够了,非常感谢!", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [37] = { dialogId = 680201, dialogName = '', times = 2, roleId_1 = 400007, roleSide_1 = "l", dialogType_1 = 1, content_1 = "我想预订一份明天的早餐!但有些点担心明早会不会又睡过头?", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "明白了!安心睡!早餐给您留着,睡过头我们负责叫醒你!", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [38] = { dialogId = 680202, dialogName = '', times = 2, roleId_1 = 400007, roleSide_1 = "l", dialogType_1 = 1, content_1 = "我今天好像又睡过头了。", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "我们明天一定会叫醒您的。", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [39] = { dialogId = 680203, dialogName = '', times = 2, roleId_1 = 300003, roleSide_1 = "r", dialogType_1 = 1, content_1 = "昨晚睡得还好吗?", roleId_2 = 400007, roleSide_2 = "l", dialogType_2 = 1, content_2 = "非常不错,早餐也非常的不错", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [40] = { dialogId = 680301, dialogName = '', times = 2, roleId_1 = 400013, roleSide_1 = "l", dialogType_1 = 1, content_1 = "我的朋友非常喜欢吃寿司,我想预定一些,下次来的时候取走", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "没问题,到时候见,期待您的到来", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [41] = { dialogId = 680302, dialogName = '', times = 2, roleId_1 = 300003, roleSide_1 = "r", dialogType_1 = 1, content_1 = "抱歉,最近顾客太多了,可以再给我们一次机会吗?", roleId_2 = 400013, roleSide_2 = "l", dialogType_2 = 1, content_2 = "没关系,那我明天再过来", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [42] = { dialogId = 680303, dialogName = '', times = 1, roleId_1 = 400013, roleSide_1 = "l", dialogType_1 = 1, content_1 = "非常感觉,我感觉我的朋友一定会喜欢上的。", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [43] = { dialogId = 680401, dialogName = '', times = 2, roleId_1 = 400008, roleSide_1 = "l", dialogType_1 = 1, content_1 = "你好,想订几份便当带着去露营吃", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "好嘞!保证您不饿肚子,玩得尽兴!", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [44] = { dialogId = 680402, dialogName = '', times = 2, roleId_1 = 300003, roleSide_1 = "r", dialogType_1 = 1, content_1 = "抱歉,忘记了您的便当了,可以再给我们一次机会吗?", roleId_2 = 400008, roleSide_2 = "l", dialogType_2 = 1, content_2 = "没事,我们明天再去露营", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [45] = { dialogId = 680403, dialogName = '', times = 1, roleId_1 = 400008, roleSide_1 = "l", dialogType_1 = 1, content_1 = "非常感谢,这样我就可以和朋友去露营了", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [46] = { dialogId = 680501, dialogName = '', times = 2, roleId_1 = 400011, roleSide_1 = "l", dialogType_1 = 1, content_1 = "我的朋友最近一直在家里,他可能会打电话过来点便当,等便当好了我再过来取", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "我们确实接到了这样的电话", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [47] = { dialogId = 680502, dialogName = '', times = 2, roleId_1 = 300003, roleSide_1 = "r", dialogType_1 = 1, content_1 = "对不起,我们忘记了您朋友的便当了,因为最近顾客太多了,可以再给我们一次机会吗?", roleId_2 = 400011, roleSide_2 = "l", dialogType_2 = 1, content_2 = "没关系,我不介意多去几次,只要我的朋友能吃到美味的食物就行。", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [48] = { dialogId = 680503, dialogName = '', times = 2, roleId_1 = 300003, roleSide_1 = "r", dialogType_1 = 1, content_1 = "您的便当已经包裹好了,为了防止路上颠簸还多包了两层", roleId_2 = 400011, roleSide_2 = "l", dialogType_2 = 1, content_2 = "非常感谢!", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [49] = { dialogId = 680601, dialogName = '', times = 2, roleId_1 = 400012, roleSide_1 = "l", dialogType_1 = 1, content_1 = "我想预定一个便当,我会在跑步的路上来取", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "明天见,希望明天天气不错", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [50] = { dialogId = 680602, dialogName = '', times = 2, roleId_1 = 400012, roleSide_1 = "l", dialogType_1 = 1, content_1 = "最近天气不太好,我希望明天能好起来,跑完步后能立刻吃到便当,那会很棒", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "明天见,明天天气会好起来的。", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [51] = { dialogId = 680603, dialogName = '', times = 2, roleId_1 = 400012, roleSide_1 = "l", dialogType_1 = 1, content_1 = "我出发了!", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "祝您跑步愉快。", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [52] = { dialogId = 680701, dialogName = '', times = 4, roleId_1 = 400004, roleSide_1 = "l", dialogType_1 = 1, content_1 = "没想到这里居然有这么美味的餐厅", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "我也觉得很好吃", roleId_3 = 400004, roleSide_3 = "l", dialogType_3 = 1, content_3 = "我预定一些食物,带回去给我的家人也尝尝", roleId_4 = 300003, roleSide_4 = "r", dialogType_4 = 1, content_4 = "没问题", roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [53] = { dialogId = 680702, dialogName = '', times = 2, roleId_1 = 400004, roleSide_1 = "l", dialogType_1 = 1, content_1 = "我跟家人说了会预定一些美食带回去,但你好想忘记了,明天再过来可以吗?", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "对不起,请再给我们一次机会。", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [54] = { dialogId = 680703, dialogName = '', times = 1, roleId_1 = 400004, roleSide_1 = "l", dialogType_1 = 1, content_1 = "谢谢你,我相信我的家人会吃的非常高兴的。", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [55] = { dialogId = 680801, dialogName = '', times = 4, roleId_1 = 400009, roleSide_1 = "l", dialogType_1 = 1, content_1 = "这里的食物真的很好,我想为即将到来的派对预定一些食物", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "派对?什么是派对?", roleId_3 = 400009, roleSide_3 = "l", dialogType_3 = 1, content_3 = "这是朋友们一起吃饭、玩耍和聊天度过难忘一天的聚会", roleId_4 = 300003, roleSide_4 = "r", dialogType_4 = 1, content_4 = "听起来真不错!", roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [56] = { dialogId = 680802, dialogName = '', times = 2, roleId_1 = 300003, roleSide_1 = "r", dialogType_1 = 1, content_1 = "非常抱歉,忘记了您的派对订单,可以再给我们一次机会吗?", roleId_2 = 400009, roleSide_2 = "l", dialogType_2 = 1, content_2 = "没事,我们的派对可以延长时间", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [57] = { dialogId = 680803, dialogName = '', times = 2, roleId_1 = 400009, roleSide_1 = "l", dialogType_1 = 1, content_1 = "非常感谢你,你要一起来派对吗?", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "我想起来我还有必须完成的工作要做!*溜走*", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [58] = { dialogId = 680901, dialogName = '', times = 2, roleId_1 = 400005, roleSide_1 = "l", dialogType_1 = 1, content_1 = "我想预定一张桌子来和朋友一起吃个饭", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "没问题,告诉我时间", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [59] = { dialogId = 680902, dialogName = '', times = 3, roleId_1 = 400005, roleSide_1 = "l", dialogType_1 = 1, content_1 = "我预定的桌子呢?", roleId_2 = 300003, roleSide_2 = "r", dialogType_2 = 1, content_2 = "对不起,下次来给您打折怎么样?", roleId_3 = 400005, roleSide_3 = "l", dialogType_3 = 1, content_3 = "你得给我至少打5折。", roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [60] = { dialogId = 680903, dialogName = '', times = 1, roleId_1 = 400005, roleSide_1 = "l", dialogType_1 = 1, content_1 = "这次聚会非常开心,下次我们一定会再来的", roleId_2 = -1, roleSide_2 = '', dialogType_2 = -1, content_2 = '', roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [61] = { dialogId = 681001, dialogName = '', times = 2, roleId_1 = 400014, roleSide_1 = "l", dialogType_1 = 1, content_1 = "我想学习一些烹饪技巧可以吗?", roleId_2 = 300004, roleSide_2 = "r", dialogType_2 = 1, content_2 = "当然可以,但我们需要一些时间来准备一下。", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [62] = { dialogId = 681002, dialogName = '', times = 3, roleId_1 = 400014, roleSide_1 = "l", dialogType_1 = 1, content_1 = "你为什么没有来呢,我已经等了一天了。", roleId_2 = 300004, roleSide_2 = "r", dialogType_2 = 1, content_2 = "抱歉,有些食材还没准备好。", roleId_3 = 400014, roleSide_3 = "l", dialogType_3 = 1, content_3 = "也许我明天可以再过来?", roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, [63] = { dialogId = 681003, dialogName = '', times = 2, roleId_1 = 400014, roleSide_1 = "l", dialogType_1 = 1, content_1 = "料理真是一件苦差事啊,但吃到自己做的食物指的太棒了!谢谢你!", roleId_2 = 300004, roleSide_2 = "r", dialogType_2 = 1, content_2 = "与其感谢我,不如你自己吧。", roleId_3 = -1, roleSide_3 = '', dialogType_3 = -1, content_3 = '', roleId_4 = -1, roleSide_4 = '', dialogType_4 = -1, content_4 = '', roleId_5 = -1, roleSide_5 = '', dialogType_5 = -1, content_5 = '', }, } return storydialogCfg Cashier--[[ 收银员 author:{zhangpeng} time:2025-05-30 15:15:01 ]] local Cashier,super = defClass("Cashier",Employe) function Cashier:ctor(reslink,cfgId) super.ctor(self, RoleConst.roleType.employee) self.R = reslink self.employeCfgId = cfgId self:init() end function Cashier:init() super.init(self) -- self:initEmployeCfg() -- self:initDisplay() -- 初始化状态为空闲 self.state = EmployeConst.State.Idle end return Cashiermain$require("common/core/base/extend/Ext") require("common/core/base/extend/ExtendGameObject") require("common/core/base/extend/ExtendRect") require("common/core/base/extend/ExtendBounds") require("common/core/base/extend/ExtendScene") require("common/core/base/extend/ExtendPlayableDirector") CurrencyMgrq%local CurrencyMgr = defClassStatic("CurrencyMgr") function CurrencyMgr:init() printInfo("CurrencyMgr", "------ 货币管理 初始化 ------") self:initUserData() self:initEvent() end function CurrencyMgr:start() self:setEvent() end function CurrencyMgr:initUserData() self.userData = UserDataMgr.currencyUserData end function CurrencyMgr:getUserData() return self.userData end function CurrencyMgr:initEvent() self.currencyChangeEvent = SimpleEvent.new("currencyChange") -- 货币变化事件 end function CurrencyMgr:setEvent() end --- 获取货币的值 function CurrencyMgr:getCurrencyValue(currencyType) if currencyType == CurrencyConst.CurrencyType.Coin then return self:getCoin() elseif currencyType == CurrencyConst.CurrencyType.Diamond then return self:getDiamond() elseif currencyType == CurrencyConst.CurrencyType.MusicalNotes then return self:getMusicalNotes() elseif currencyType == CurrencyConst.CurrencyType.FishingBait then return self:getFishBait() elseif currencyType == CurrencyConst.CurrencyType.Star then return self:getStar() elseif currencyType == CurrencyConst.CurrencyType.AdCoupons then return self:getAdCoupons() end return 0 end --- 设置货币的值 function CurrencyMgr:setCurrencyValue(currencyType, value, refuseEvent) if currencyType == CurrencyConst.CurrencyType.Coin then return self:setCoin(value, refuseEvent) elseif currencyType == CurrencyConst.CurrencyType.Diamond then return self:setDiamond(value, refuseEvent) elseif currencyType == CurrencyConst.CurrencyType.MusicalNotes then return self:setMusicalNotes(value, refuseEvent) elseif currencyType == CurrencyConst.CurrencyType.FishingBait then return self:setFishBait(value, refuseEvent) elseif currencyType == CurrencyConst.CurrencyType.Star then return self:setStar(value, refuseEvent) elseif currencyType == CurrencyConst.CurrencyType.AdCoupons then return self:setAdCoupons(value, refuseEvent) end return 0 end --- 增加货币的值 function CurrencyMgr:changeCurrencyValue(currencyType, value, refuseEvent) if currencyType == CurrencyConst.CurrencyType.Coin then return self:changeCoin(value, refuseEvent) elseif currencyType == CurrencyConst.CurrencyType.Diamond then return self:changeDiamond(value, refuseEvent) elseif currencyType == CurrencyConst.CurrencyType.MusicalNotes then return self:changeMusicalNotes(value, refuseEvent) elseif currencyType == CurrencyConst.CurrencyType.FishingBait then return self:changeFishBait(value, refuseEvent) elseif currencyType == CurrencyConst.CurrencyType.Star then return self:changeStar(value, refuseEvent) elseif currencyType == CurrencyConst.CurrencyType.AdCoupons then return self:changeAdCoupons(value, refuseEvent) end return 0 end --- 获取金币的值 function CurrencyMgr:getCoin() return self.userData:getCoin() end --- 设置金币的值 function CurrencyMgr:setCoin(value, refuseEvent) local curr = self.userData:setCoin(value) if not refuseEvent then self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.Coin, curr) end return curr end --- 增加金币的值 function CurrencyMgr:changeCoin(value, refuseEvent) local curr = self.userData:changeCoin(value) if not refuseEvent then self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.Coin, curr) end -- 任务进度 if value > 0 then UserDataMgr:addIncomeCoinCount(math.floor(value)) end return curr end --- 更新金币的值 function CurrencyMgr:refreshCoin(animate) self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.Coin, self:getCoin(), animate) end --- 获取钻石的值 function CurrencyMgr:getDiamond() return self.userData:getDiamond() end --- 设置钻石的值 function CurrencyMgr:setDiamond(value, refuseEvent) local curr = self.userData:setDiamond(value) if not refuseEvent then self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.Diamond, curr) end return curr end --- 增加钻石的值 function CurrencyMgr:changeDiamond(value, refuseEvent) local curr = self.userData:changeDiamond(value) if not refuseEvent then self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.Diamond, curr) end return curr end --- 更新钻石的值 function CurrencyMgr:refreshDiamond() self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.Diamond, self:getDiamond()) end --- 获取音符的值 function CurrencyMgr:getMusicalNotes() return self.userData:getMusicalNotes() end --- 设置音符的值 function CurrencyMgr:setMusicalNotes(value, refuseEvent) local curr = self.userData:setMusicalNotes(value) if not refuseEvent then self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.MusicalNotes, curr) end return curr end --- 增加音符的值 function CurrencyMgr:changeMusicalNotes(value, refuseEvent) local curr = self.userData:changeMusicalNotes(value) if not refuseEvent then self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.MusicalNotes, curr) end return curr end --- 更新音符的值 function CurrencyMgr:refreshMusicalNotes() self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.MusicalNotes, self:getMusicalNotes()) end --- 获取鱼饵的值 function CurrencyMgr:getFishBait() return self.userData:getFishBait() end --- 设置鱼饵的值 function CurrencyMgr:setFishBait(value, refuseEvent) local curr = self.userData:setFishBait(value) if not refuseEvent then self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.FishingBait, curr) end return curr end --- 增加鱼饵的值 function CurrencyMgr:changeFishBait(value, refuseEvent) local curr = self.userData:changeFishBait(value) if not refuseEvent then self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.FishingBait, curr) end -- 任务进度 UserDataMgr:addNoteCount(math.floor(value)) return curr end --- 更新鱼饵的值 function CurrencyMgr:refreshFishBait() self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.FishingBait, self:getFishBait()) end --- 获取星星的值 function CurrencyMgr:getStar() return self.userData:getStar() + BonusMgr:getStarBonus() end --- 设置星星的值 function CurrencyMgr:setStar(value, refuseEvent) local curr = self.userData:setStar(value) + BonusMgr:getStarBonus() if not refuseEvent then self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.Star, curr) end -- 任务进度 UserDataMgr:setStarCount(curr) return curr end --- 增加星星的值 function CurrencyMgr:changeStar(value, refuseEvent) local curr = self.userData:changeStar(value) + BonusMgr:getStarBonus() if not refuseEvent then self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.Star, curr) end return curr end --- 更新星星的值 function CurrencyMgr:refreshStar(animate) local curr = self:getStar() -- 任务进度 UserDataMgr:setStarCount(curr) self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.Star, curr, animate) end --- 获取广告券的值 function CurrencyMgr:getAdCoupons() return self.userData:getAdCoupons() end --- 设置广告券的值 function CurrencyMgr:setAdCoupons(value, refuseEvent) local curr = self.userData:setAdCoupons(value) if not refuseEvent then self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.AdCoupons, value) end return curr end --- 增加广告券的值 function CurrencyMgr:changeAdCoupons(value, refuseEvent) local curr = self.userData:changeAdCoupons(value) if not refuseEvent then self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.AdCoupons, curr) end return curr end --- 更新广告券的值 function CurrencyMgr:refreshAdCoupons() self.currencyChangeEvent:triggerEvent(CurrencyConst.CurrencyType.AdCoupons, self:getAdCoupons()) end function CurrencyMgr:getCoinUIIcon() local str = "Assets/AssetsPackage/Res/modules/ui/common/images/coin.png" return ResLoader.loadAsset(str, typeof(CS.UnityEngine.Sprite)) end function CurrencyMgr:getDiamondUIIcon() return self:getCoinUIIcon() --local str = "Assets/AssetsPackage/Res/modules/ui/common/images/diamond.png" --return ResLoader.loadAsset(str, typeof(CS.UnityEngine.Sprite)) end function CurrencyMgr:getMusicalNotesUIIcon() return self:getCoinUIIcon() --local str = "Assets/AssetsPackage/Res/modules/ui/common/images/musical_notes.png" --return ResLoader.loadAsset(str, typeof(CS.UnityEngine.Sprite)) end function CurrencyMgr:getFishBaitUIIcon() return self:getCoinUIIcon() --local str = "Assets/AssetsPackage/Res/modules/ui/common/images/fishing_bait.png" --return ResLoader.loadAsset(str, typeof(CS.UnityEngine.Sprite)) end function CurrencyMgr:getStarUIIcon() local str = "Assets/AssetsPackage/Res/modules/ui/common/images/star.png" return ResLoader.loadAsset(str, typeof(CS.UnityEngine.Sprite)) end function CurrencyMgr:getAdCouponsUIIcon() return self:getCoinUIIcon() --local str = "Assets/AssetsPackage/Res/modules/ui/common/images/ad_coupons.png" --return ResLoader.loadAsset(str, typeof(CS.UnityEngine.Sprite)) end return CurrencyMgrFishingFishCell=-- 捕鱼展示鱼ui格子 local FishingFishCell = defClass("FishingFishCell") -- component local Image = UnityEngine.UI.Image local TMProUGUI = CS.TMPro.TextMeshProUGUI -- 构造函数 function FishingFishCell:ctor(go, id, count) self.go = go self.id = id -- 鱼的ID self.count = count -- 数量 self:showUI() end -- 展示UI function FishingFishCell:showUI() local icon = self.go:Seek("icon")[Image] local count = self.go:Seek("count") local config = FishCfgParse:getFishCfgById(self.id) local path = string.format("Assets/AssetsPackage/Res/modules/ui/fishingui/images/%s.png", config.fishIcon) local sprite = nil if ResLoader.hasAsset(path) then sprite = ResLoader.loadAsset(path, typeof(CS.UnityEngine.Sprite)) end icon.sprite = sprite util.ugui:setImageTileSize(icon, 180) count[TMProUGUI].text = "X" .. tostring(self.count) --count:SetActive(self.count > 1) -- 如果数量大于1,则显示数量 --if self.count > 1 then -- count[TMProUGUI].text = "X" .. tostring(self.count) --end end BarbecueChef--[[ 烤吧厨师(音乐烤吧) author:{zhangpeng} time:2025-07-04 17:02:01 ]] local BarbecueChef,super = defClass("BarbecueChef",Employe) local LOGTAG = "BarbecueChef" function BarbecueChef:ctor(reslink,cfgId) super.ctor(self,reslink,cfgId) self.R = reslink self.employeCfgId = cfgId self:init() end function BarbecueChef:init() super.init(self) -- 初始化状态为空闲 self.state = EmployeConst.State.Idle -- 巡逻状态 self.isPatrolling = false -- 当前闲逛点索引 self.currentWanderIndex = 1 -- 标记是否已经开始巡逻(避免重复启动) self.hasStartedPatrol = false printInfo(LOGTAG, string.format("烤吧厨师[%s]初始化完成", self.employeCfgId)) -- 初始化时播放待机动画 self:playIdleAnim() end -- 员工行为,烤吧厨师在闲逛点之间走动并播放烤串动作 function BarbecueChef:action() -- 检查当前状态 if self.state == EmployeConst.State.Idle then -- 开始在闲逛点巡逻 self:startPatrolling() else -- 烤吧厨师继续工作 self:continueWorking() end end -- 开始在闲逛点巡逻 function BarbecueChef:startPatrolling() -- 如果已经在巡逻,则不重复开始 if self.isPatrolling then return end -- 获取闲逛点 if not self.wanderPoints or #self.wanderPoints == 0 then printWarn(LOGTAG, string.format("烤吧厨师[%s]没有闲逛点", self.employeCfgId)) -- 如果没有闲逛点,播放待机动画 self:playIdleAnim() return end -- 设置巡逻标志 self.isPatrolling = true -- 如果还没有开始巡逻,延迟1秒开始(避免初始化时立即播放行走动画) if not self.hasStartedPatrol then self.hasStartedPatrol = true self:timer(function() if self.isPatrolling then -- 确保还在巡逻状态 self:moveToNextWanderPoint() end end, 1, 1) else -- 立即开始巡逻 self:moveToNextWanderPoint() end end -- 移动到下一个闲逛点 function BarbecueChef:moveToNextWanderPoint() -- 检查是否还在巡逻状态 if not self.isPatrolling or self.state ~= EmployeConst.State.Idle then self.isPatrolling = false return end -- 前往指定的闲逛点 self:goToWanderPoint(self.currentWanderIndex, function() -- 到达闲逛点后,播放烤串动作 self:playBarbecueAnim() -- 在当前点停留3-5秒 local stayTime = math.random(3, 5) printInfo(LOGTAG, string.format("烤吧厨师[%s]在点位[%d]停留%d秒", self.employeCfgId, self.currentWanderIndex, stayTime)) self:timer(function() if self.isPatrolling then -- 确保还在巡逻状态 -- 移动到下一个闲逛点 self.currentWanderIndex = self.currentWanderIndex + 1 if self.currentWanderIndex > #self.wanderPoints then self.currentWanderIndex = 1 -- 循环回到第一个点 end self:moveToNextWanderPoint() end end, stayTime, 1) end) end -- 前往指定的闲逛点 function BarbecueChef:goToWanderPoint(pointIndex, callback) if not self.wanderPoints or pointIndex < 1 or pointIndex > #self.wanderPoints then if callback then callback() end return end local targetPoint = self.wanderPoints[pointIndex] if not targetPoint or not targetPoint.transform then if callback then callback() end return end local targetPos = targetPoint.transform.position local currentPos = self:getCurPosition() -- 计算距离,如果距离很近(小于0.5),直接执行回调,不需要移动 local distance = (targetPos - currentPos).magnitude if distance < 0.5 then if callback then callback() end return end -- 播放行走动画 self:playWalkAnim() -- 移动到该点 self:rolePathFind(targetPos, function() if callback then callback() end end) end -- 播放烤串动画 function BarbecueChef:playBarbecueAnim() local actions = EmployeConst.EmployeeAction[self.employeCfgId] if actions and actions.special then self:playAnimation(actions.special, true) printInfo(LOGTAG, string.format("烤吧厨师[%s]播放烤串动画: %s", self.employeCfgId, actions.special)) end end -- 停止巡逻 function BarbecueChef:stopPatrolling() self.isPatrolling = false self.currentWanderIndex = 1 self.hasStartedPatrol = false end -- 继续工作 (预留接口,暂时只做巡逻) function BarbecueChef:continueWorking() -- 如果状态不是空闲,暂停巡逻 if self.isPatrolling then self:stopPatrolling() end end -- 获取员工类型 function BarbecueChef:getEmployeType() return self.employeCfgId end -- 设置状态 function BarbecueChef:setState(state) self.state = state if self.employeeInfo then self.employeeInfo:setWorkState(state) end end -- 获取状态 function BarbecueChef:getState() return self.state end return BarbecueChef main_webgl--[[ 用于webgl的boot文件 author:zhangpeng time:2025-07-18 17:58:16 ]] local _ENV = _G --FORCE CLEAN ENV local LOGTAG = "[boot/main_webgl]" print(LOGTAG.."start 999") local json = require("rapidjson") print(LOGTAG.."launch luaengine from here") local luaengine = require("luaengine") local UnityEngine = CS.UnityEngine local AET = CS.AET local isEditor = CS.UnityEngine.Application.isEditor local YooAssetLoader = CS.YooAssetLoader.Instance _G.BOOT_MAIN_FILE = "boot/main" _G.GAME_MAIN_FILE = "main/main" local debug_flag = true if CS.LocalDataStorage.Get("PRINT_EVERY_LUA_CALL") == "true" then debug.sethook(function(event,line) local info = debug.getinfo(2) if info.currentline > 0 then print(string.format("%s:%s:%s:%s:%s",info.short_src,tostring(info.currentline),tostring(info.linedefined),tostring(info.name),tostring(info.namewhat))) end end, "c" ) end local cached_lua_ret_map = {} local CLEAR_ALL_LUA_CACHES = function() print("[boot.main] 清空lua缓存") for k,_ in pairs(cached_lua_ret_map) do cached_lua_ret_map[k] = nil end end local _loadlua = function (bytes, file, opts, env) if bytes == nil or bytes == "" then error("lua文件不存在->"..file..":"..tostring(bytes).. "\n" .. debug.traceback()) end if opts == "b" then print(LOGTAG .. "loadlua:bytes file") bytes = AET.Dec(bytes) end local f,err = load(bytes, file, opts, env) if f then local ok,ret = xpcall(f,function(err) CS.UnityEngine.Debug.LogError(string.format("加载lua失败[%s]%s\n%s",file,tostring(err),debug.traceback())) end) if not string.lower(file):find("reslink") and not isEditor then cached_lua_ret_map[file] = {ret = ret} end return ret, env else CS.UnityEngine.Debug.LogError("加载lua失败" .. file) error(tostring(err) .. "\n" .. debug.traceback()) end end -- 热更结束后加载lua文件 -- @ filename:要加载的lua文件名 -- @ env:lua环境,用于加载 Lua 文件的执行环境 -- _require函数会根据传入的参数加载指定的 Lua 文件,然后执行它,最终返回加载结果 print(LOGTAG .. "run in hotupdate") local _require = function(env, filename) local ret = cached_lua_ret_map[filename] if ret then return ret.ret end -- print("[require]", filename) -- local filepath = string.lower(filename) local filepath = filename -- 使用YooAssetLoader同步加载资源,启动时候已经把ab加载到内存了 local src = YooAssetLoader:LoadText(filepath) return _loadlua(src, filename, "bt", env) end local _newenv = function() -- local _G = _G local _E = _G local rawset = _G.rawset local env = { _G = _G, _print = print, CLEAR_ALL_LUA_CACHES = CLEAR_ALL_LUA_CACHES, ENV_REQUIRE = _require } _G.setmetatable( env, { __index = function(t, k) local v = _E[k] rawset(t, k, v) return v end } ) return env end --Run Main Code do print(LOGTAG .. "start run main code") local _ENV = _newenv() _ENV.CLEAR_ENV = function() for k, _ in pairs(_ENV) do _ENV[k] = nil end end _ENV.raw_require = raw_require or require _ENV._require = _require _ENV.require = function(filename, _env) _env = _env or _ENV return _require(_env, filename) end require("boot/build_config") -- 执行main/main.lua print(LOGTAG.." -------- RUN GAME_MAIN_FILE -------- ") require(_G.GAME_MAIN_FILE) endSecurityr0--[[ 保安驱赶小偷,臭鼬 author:{zhangpeng} time:2025-05-30 15:14:15 ]] local Security,super = defClass("Security",Employe) local LOGTAG = "Security" -- 保安状态 Security.State = { Patrolling = 1, -- 巡逻中 Chasing = 2, -- 追逐捣乱客人 Driving = 3, -- 驱赶中 } function Security:ctor(reslink,cfgId) super.ctor(self,reslink,cfgId) self:init() end function Security:init() super.init(self) -- 保安特有属性 self.securityState = Security.State.Patrolling -- 检测捣乱客人的间隔(秒) self.detectionInterval = 1 -- 驱赶动作间隔(秒) self.driveInterval = 2 -- 当前目标捣乱客人 self.targetTroubleMaker = nil -- 是否正在驱赶 self.isDriving = false -- 检测定时器ID self.detectionTimerId = nil -- 驱赶定时器ID self.driveTimerId = nil -- 追逐距离检测定时器ID self.chaseDistanceTimerId = nil -- 当前闲逛点索引 self.currentWanderIndex = 1 printInfo(LOGTAG, "保安初始化完成") end -- 员工升级 function Security:upgrade() end -- 开始工作 function Security:action() printInfo(LOGTAG, "保安开始工作") self:setState(EmployeConst.State.Busy) -- 开始巡逻和检测 self:startPatrolAndDetection() end -- 开始巡逻和检测 function Security:startPatrolAndDetection() if self:getState() ~= EmployeConst.State.Busy then return end -- 开始检测捣乱客人 self:startTroubleMakerDetection() -- 开始巡逻(在闲逛点之间移动) self:startPatrolling() end -- 开始检测捣乱客人 function Security:startTroubleMakerDetection() local function detectTroubleMakers() if self:getState() ~= EmployeConst.State.Busy then return end -- 查找场景中的捣乱客人(小偷) local troubleMakers = self:findTroubleMakers() if #troubleMakers > 0 and not self.isDriving then -- 找到最近的捣乱客人 local nearestTroubleMaker = self:findNearestTroubleMaker(troubleMakers) if nearestTroubleMaker then self:chaseAndDriveTroubleMaker(nearestTroubleMaker) end end -- 继续检测 if self:getState() == EmployeConst.State.Busy then self.detectionTimerId = self:timer(detectTroubleMakers, self.detectionInterval, 1) end end -- 开始第一次检测 detectTroubleMakers() end -- 查找捣乱客人 function Security:findTroubleMakers() local troubleMakers = {} -- 获取特殊顾客列表 local specialCustomers = SpecialCustomerMgr:getSpecialCustomerList() for _, customer in ipairs(specialCustomers) do -- 检查是否是小偷且还在活动中 if customer.specialCustomerTypeId == CustomerConst.CustomerSpecialType.ThiefCustomer and customer.isActive and customer.state ~= ThiefCustomer.State.Leaving then table.insert(troubleMakers, customer) end end return troubleMakers end -- 找到最近的捣乱客人 function Security:findNearestTroubleMaker(troubleMakers) if #troubleMakers == 0 then return nil end local currentPos = self:getCurPosition() local nearestTroubleMaker = nil local minDistance = math.huge for _, troubleMaker in ipairs(troubleMakers) do if troubleMaker and troubleMaker.getCurPosition then local troubleMakerPos = troubleMaker:getCurPosition() local distance = (currentPos - troubleMakerPos).magnitude if distance < minDistance then minDistance = distance nearestTroubleMaker = troubleMaker end end end return nearestTroubleMaker end -- 追逐并驱赶捣乱客人 function Security:chaseAndDriveTroubleMaker(troubleMaker) if not troubleMaker or self.isDriving then return end printInfo(LOGTAG, "保安发现捣乱客人,开始追逐") self.securityState = Security.State.Chasing self.targetTroubleMaker = troubleMaker -- 停止巡逻 self:stopPatrolling() -- 驱赶有效距离(单位距离) local driveDistance = 2.0 -- 检查当前距离是否已经足够近 local currentPos = self:getCurPosition() local troubleMakerPos = troubleMaker:getCurPosition() local currentDistance = (currentPos - troubleMakerPos).magnitude if currentDistance <= driveDistance then -- 距离已经足够近,直接开始驱赶 printInfo(LOGTAG, "保安距离小偷足够近,直接开始驱赶") self:startDriving() else -- 需要移动到小偷附近 self:moveCloserToTroubleMaker(troubleMaker, driveDistance) end end -- 移动到捣乱客人附近 function Security:moveCloserToTroubleMaker(troubleMaker, driveDistance) if not troubleMaker then return end local troubleMakerPos = troubleMaker:getCurPosition() -- 计算目标位置(距离小偷driveDistance距离的位置) local currentPos = self:getCurPosition() local direction = (troubleMakerPos - currentPos).normalized local targetPos = troubleMakerPos - direction * driveDistance printInfo(LOGTAG, "保安移动到小偷附近,目标距离:%f", driveDistance) -- 开始移动过程中的距离检测 self:startChaseWithDistanceCheck(targetPos, troubleMaker, driveDistance) end -- 带距离检测的追逐移动 function Security:startChaseWithDistanceCheck(targetPos, troubleMaker, driveDistance) if not troubleMaker then return end -- 开始寻路移动 self:rolePathFind(targetPos, function() -- 移动完成后检查目标是否还在 if self.targetTroubleMaker and self.targetTroubleMaker.isActive then self:startDriving() else -- 目标已经离开,恢复巡逻 self:resumePatrolling() end end) -- 开始移动过程中的距离检测 self:startChaseDistanceDetection(troubleMaker, driveDistance) end -- 开始追逐过程中的距离检测 function Security:startChaseDistanceDetection(troubleMaker, driveDistance) if not troubleMaker or self.isDriving then return end local function checkDistance() if not self.targetTroubleMaker or self.isDriving or self.securityState ~= Security.State.Chasing then return end -- 检查当前距离 local currentPos = self:getCurPosition() local troubleMakerPos = troubleMaker:getCurPosition() local currentDistance = (currentPos - troubleMakerPos).magnitude if currentDistance <= driveDistance then -- 距离足够近,停止移动并开始驱赶 printInfo(LOGTAG, "保安在移动过程中到达驱赶距离,开始驱赶") -- 停止当前移动 if self.display then self.display:StopAllActions() end -- 开始驱赶 self:startDriving() return end -- 继续检测 self.chaseDistanceTimerId = self:timer(checkDistance, 0.1, 1) -- 每0.1秒检测一次 end -- 开始第一次检测 self.chaseDistanceTimerId = self:timer(checkDistance, 0.1, 1) end -- 开始驱赶 function Security:startDriving() if not self.targetTroubleMaker or self.isDriving then return end printInfo(LOGTAG, "保安开始驱赶捣乱客人") -- 清理追逐距离检测定时器 if self.chaseDistanceTimerId then TimerMgr:rem(self.chaseDistanceTimerId) self.chaseDistanceTimerId = nil end self.securityState = Security.State.Driving self.isDriving = true local function performDrive() if not self.targetTroubleMaker or not self.targetTroubleMaker.isActive or self:getState() ~= EmployeConst.State.Busy then -- 目标已经离开或保安停止工作,恢复巡逻 self:stopDriving() self:resumePatrolling() return end -- 执行驱赶动作 self:performDriveAction() -- 继续驱赶 self.driveTimerId = self:timer(performDrive, self.driveInterval, 1) end -- 开始第一次驱赶 performDrive() end -- 执行驱赶动作 function Security:performDriveAction() if not self.targetTroubleMaker then return end printInfo(LOGTAG, "保安执行驱赶动作") -- 通知捣乱客人被驱赶 if self.targetTroubleMaker.onDriven then self.targetTroubleMaker:onDriven(self) end -- 可以在这里添加驱赶动画效果 -- TODO: 播放驱赶动画 end -- 停止驱赶 function Security:stopDriving() if self.driveTimerId then TimerMgr:rem(self.driveTimerId) self.driveTimerId = nil end self.isDriving = false self.targetTroubleMaker = nil self.securityState = Security.State.Patrolling printInfo(LOGTAG, "保安停止驱赶") end -- 开始巡逻 function Security:startPatrolling() if not self.wanderPoints or #self.wanderPoints == 0 then printWarn(LOGTAG, "保安没有巡逻点,无法巡逻") return end if self.securityState == Security.State.Patrolling and not self.isDriving then self:moveToNextPatrolPoint() end end -- 移动到下一个巡逻点 function Security:moveToNextPatrolPoint() if self.isDriving or self:getState() ~= EmployeConst.State.Busy then return end local targetPoint = self.wanderPoints[self.currentWanderIndex] if not targetPoint then self.currentWanderIndex = 1 targetPoint = self.wanderPoints[self.currentWanderIndex] end if not targetPoint then printError(LOGTAG, "巡逻点无效") return end local targetPos = targetPoint.transform.position printInfo(LOGTAG, "保安前往巡逻点:%d", self.currentWanderIndex) self:rolePathFind(targetPos, function() if self.securityState == Security.State.Patrolling and not self.isDriving then -- 在巡逻点停留一段时间 self:timer(function() if self.securityState == Security.State.Patrolling and not self.isDriving then -- 移动到下一个巡逻点 self.currentWanderIndex = self.currentWanderIndex + 1 if self.currentWanderIndex > #self.wanderPoints then self.currentWanderIndex = 1 end self:moveToNextPatrolPoint() end end, math.random(2, 4), 1) -- 随机停留2-4秒 end end) end -- 停止巡逻 function Security:stopPatrolling() -- 停止当前移动 if self.display then self.display:StopAllActions() end end -- 恢复巡逻 function Security:resumePatrolling() printInfo(LOGTAG, "保安恢复巡逻") -- 清理追逐相关的定时器 if self.chaseDistanceTimerId then TimerMgr:rem(self.chaseDistanceTimerId) self.chaseDistanceTimerId = nil end self.securityState = Security.State.Patrolling self.targetTroubleMaker = nil self.isDriving = false -- 恢复巡逻 self:startPatrolling() end -- 停止工作 function Security:stopWork() printInfo(LOGTAG, "保安停止工作") -- 停止所有定时器 if self.detectionTimerId then TimerMgr:rem(self.detectionTimerId) self.detectionTimerId = nil end if self.driveTimerId then TimerMgr:rem(self.driveTimerId) self.driveTimerId = nil end if self.chaseDistanceTimerId then TimerMgr:rem(self.chaseDistanceTimerId) self.chaseDistanceTimerId = nil end -- 停止移动 self:stopPatrolling() -- 重置状态 self:setState(EmployeConst.State.Idle) self.securityState = Security.State.Patrolling self.isDriving = false self.targetTroubleMaker = nil end return SecurityTaskBootDetailUI3 --- 广告窗口 ---@class TaskBootDetailUI : UILayer local TaskBootDetailUI, super = defClass("TaskBootDetailUI", UILayer) function TaskBootDetailUI:ctor(taskId) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/taskui/taskuireslink") self.taskId = taskId end function TaskBootDetailUI:onLoad() self.ui = GameObject.Instantiate(self.R.task_boot_detail_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end function TaskBootDetailUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.desc = self.ui:Seek("desc") self.reward_text_1 = self.ui:Seek("reward_text_1") self.reward_icon_1 = self.ui:Seek("reward_icon_1") self.reward_text_2 = self.ui:Seek("reward_text_2") self.reward_icon_2 = self.ui:Seek("reward_icon_2") self.receive_btn = self.ui:Seek("receive_btn") self.goto_btn = self.ui:Seek("goto_btn") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addButtonClickEvent(self.receive_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:receiveClick() end) util.ugui.addButtonClickEvent(self.goto_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:gotoClick() end) end function TaskBootDetailUI:showUI() local config = TaskBootMgr:getConfig(self.taskId) local taskClear = TaskBootMgr:checkTaskClear(self.taskId) self.desc:GetComponent("TextMeshProUGUI").text = config.desc self.reward_text_1:SetActive(config.rewardType_1 ~= -1) self.reward_text_2:SetActive(config.rewardType_2 ~= -1) if config.rewardType_1 ~= -1 then self.reward_text_1:GetComponent("TextMeshProUGUI").text = "+" .. config.params_1 --self.reward_icon_1:GetComponent("Image").sprite = nil end if config.rewardType_2 ~= -1 then self.reward_text_2:GetComponent("TextMeshProUGUI").text = "+" .. config.params_2 --self.reward_icon_2:GetComponent("Image").sprite = nil end self.receive_btn:SetActive(taskClear) self.goto_btn:SetActive(not taskClear) end function TaskBootDetailUI:receiveClick() TaskBootMgr:getRewardAll(self.ui, self.taskId) TaskBootMgr:nextTask() self:close() end function TaskBootDetailUI:gotoClick() TaskBootMgr:gotoTargetPage(self.taskId) self:close() end return TaskBootDetailUI buyCommonCfg0--[[ from file:购买通用表.xlsx --]] local buyCommonCfg = { [1] = { id = 110101, currencyType = 1, price = 200, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [2] = { id = 110102, currencyType = 1, price = 12000, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [3] = { id = 110103, currencyType = 1, price = 50000, limitCount = 1, limitType_1 = 1001, value_1 = 500, }, [4] = { id = 110104, currencyType = 1, price = 150000, limitCount = 1, limitType_1 = 1001, value_1 = 1300, }, [5] = { id = 110105, currencyType = 1, price = 1000000, limitCount = 1, limitType_1 = 1001, value_1 = 3000, }, [6] = { id = 110201, currencyType = 1, price = 800, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [7] = { id = 110202, currencyType = 1, price = 20000, limitCount = 1, limitType_1 = 1001, value_1 = 190, }, [8] = { id = 110203, currencyType = 1, price = 88000, limitCount = 1, limitType_1 = 1001, value_1 = 550, }, [9] = { id = 110204, currencyType = 1, price = 210000, limitCount = 1, limitType_1 = 1001, value_1 = 1000, }, [10] = { id = 110205, currencyType = 1, price = 800000, limitCount = 1, limitType_1 = 1001, value_1 = 3500, }, [11] = { id = 110301, currencyType = 1, price = 500, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [12] = { id = 110302, currencyType = 1, price = 18000, limitCount = 1, limitType_1 = 1001, value_1 = 180, }, [13] = { id = 110303, currencyType = 1, price = 60000, limitCount = 1, limitType_1 = 1001, value_1 = 520, }, [14] = { id = 110304, currencyType = 1, price = 160000, limitCount = 1, limitType_1 = 1001, value_1 = 1500, }, [15] = { id = 110305, currencyType = 1, price = 1600000, limitCount = 1, limitType_1 = 1001, value_1 = 3250, }, [16] = { id = 110401, currencyType = 1, price = 3000, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [17] = { id = 110402, currencyType = 1, price = 24000, limitCount = 1, limitType_1 = 1001, value_1 = 240, }, [18] = { id = 110403, currencyType = 1, price = 75000, limitCount = 1, limitType_1 = 1001, value_1 = 620, }, [19] = { id = 110404, currencyType = 1, price = 180000, limitCount = 1, limitType_1 = 1001, value_1 = 1650, }, [20] = { id = 110405, currencyType = 1, price = 2500000, limitCount = 1, limitType_1 = 1001, value_1 = 4000, }, [21] = { id = 110501, currencyType = 1, price = 8000, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [22] = { id = 110502, currencyType = 1, price = 45000, limitCount = 1, limitType_1 = 1001, value_1 = 300, }, [23] = { id = 110503, currencyType = 1, price = 80000, limitCount = 1, limitType_1 = 1001, value_1 = 640, }, [24] = { id = 110504, currencyType = 1, price = 200000, limitCount = 1, limitType_1 = 1001, value_1 = 1800, }, [25] = { id = 110505, currencyType = 1, price = 3200000, limitCount = 1, limitType_1 = 1001, value_1 = 4500, }, [26] = { id = 110901, currencyType = 1, price = 5800, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [27] = { id = 110902, currencyType = 1, price = 46500, limitCount = 1, limitType_1 = 1001, value_1 = 320, }, [28] = { id = 110903, currencyType = 1, price = 160000, limitCount = 1, limitType_1 = 1001, value_1 = 700, }, [29] = { id = 110904, currencyType = 1, price = 500000, limitCount = 1, limitType_1 = 1001, value_1 = 1400, }, [30] = { id = 110905, currencyType = 1, price = 5500000, limitCount = 1, limitType_1 = 1001, value_1 = 2100, }, [31] = { id = 111001, currencyType = 1, price = 4500, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [32] = { id = 111002, currencyType = 1, price = 36000, limitCount = 1, limitType_1 = 1001, value_1 = 300, }, [33] = { id = 111003, currencyType = 1, price = 150000, limitCount = 1, limitType_1 = 1001, value_1 = 600, }, [34] = { id = 111004, currencyType = 1, price = 450000, limitCount = 1, limitType_1 = 1001, value_1 = 1350, }, [35] = { id = 111005, currencyType = 1, price = 4000000, limitCount = 1, limitType_1 = 1001, value_1 = 2800, }, [36] = { id = 111101, currencyType = 1, price = 2800, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [37] = { id = 111102, currencyType = 1, price = 22500, limitCount = 1, limitType_1 = 1001, value_1 = 270, }, [38] = { id = 111103, currencyType = 1, price = 130000, limitCount = 1, limitType_1 = 1001, value_1 = 750, }, [39] = { id = 111104, currencyType = 1, price = 400000, limitCount = 1, limitType_1 = 1001, value_1 = 1800, }, [40] = { id = 111105, currencyType = 1, price = 4500000, limitCount = 1, limitType_1 = -1, value_1 = -1, }, [41] = { id = 111301, currencyType = 1, price = 7200, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [42] = { id = 111302, currencyType = 1, price = 60000, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [43] = { id = 111303, currencyType = 1, price = 80000, limitCount = 1, limitType_1 = 1001, value_1 = 1100, }, [44] = { id = 111304, currencyType = 1, price = 300000, limitCount = 1, limitType_1 = 1001, value_1 = 1800, }, [45] = { id = 111305, currencyType = 1, price = 3500000, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [46] = { id = 111401, currencyType = 1, price = 2600, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [47] = { id = 111402, currencyType = 1, price = 19500, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [48] = { id = 111403, currencyType = 1, price = 50000, limitCount = 1, limitType_1 = 1001, value_1 = 560, }, [49] = { id = 111404, currencyType = 1, price = 180000, limitCount = 1, limitType_1 = 1001, value_1 = 1950, }, [50] = { id = 111405, currencyType = 1, price = 1600000, limitCount = 1, limitType_1 = 1001, value_1 = 5500, }, [51] = { id = 111501, currencyType = 1, price = 200, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [52] = { id = 111502, currencyType = 1, price = 16000, limitCount = 1, limitType_1 = 1001, value_1 = 180, }, [53] = { id = 111503, currencyType = 1, price = 62000, limitCount = 1, limitType_1 = 1001, value_1 = 520, }, [54] = { id = 111504, currencyType = 1, price = 200000, limitCount = 1, limitType_1 = 1001, value_1 = 1500, }, [55] = { id = 111505, currencyType = 1, price = 2000000, limitCount = 1, limitType_1 = 1001, value_1 = 5000, }, [56] = { id = 111601, currencyType = 1, price = 1000, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [57] = { id = 111602, currencyType = 1, price = 22200, limitCount = 1, limitType_1 = 1001, value_1 = 200, }, [58] = { id = 111603, currencyType = 1, price = 67000, limitCount = 1, limitType_1 = 1001, value_1 = 560, }, [59] = { id = 111604, currencyType = 1, price = 260000, limitCount = 1, limitType_1 = 1001, value_1 = 1650, }, [60] = { id = 111605, currencyType = 1, price = 3000000, limitCount = 1, limitType_1 = 1001, value_1 = 6500, }, [61] = { id = 111701, currencyType = 1, price = 1800, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [62] = { id = 111702, currencyType = 1, price = 14500, limitCount = 1, limitType_1 = 1001, value_1 = 230, }, [63] = { id = 111703, currencyType = 1, price = 120000, limitCount = 1, limitType_1 = 1001, value_1 = 600, }, [64] = { id = 111704, currencyType = 1, price = 240000, limitCount = 1, limitType_1 = 1001, value_1 = 2500, }, [65] = { id = 111705, currencyType = 1, price = 3200000, limitCount = 1, limitType_1 = 1001, value_1 = 10000, }, [66] = { id = 111801, currencyType = 1, price = 3200, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [67] = { id = 111802, currencyType = 1, price = 25000, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [68] = { id = 111803, currencyType = 1, price = 100000, limitCount = 1, limitType_1 = 1001, value_1 = 500, }, [69] = { id = 111804, currencyType = 1, price = 350000, limitCount = 1, limitType_1 = 1001, value_1 = 1550, }, [70] = { id = 111805, currencyType = 1, price = 2200000, limitCount = 1, limitType_1 = 1001, value_1 = 4600, }, [71] = { id = 111901, currencyType = 1, price = 4600, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [72] = { id = 111902, currencyType = 1, price = 36000, limitCount = 1, limitType_1 = 1001, value_1 = 350, }, [73] = { id = 111903, currencyType = 1, price = 130000, limitCount = 1, limitType_1 = 1001, value_1 = 800, }, [74] = { id = 111904, currencyType = 1, price = 360000, limitCount = 1, limitType_1 = 1001, value_1 = 2600, }, [75] = { id = 111905, currencyType = 1, price = 3800000, limitCount = 1, limitType_1 = 1001, value_1 = 3000, }, [76] = { id = 112001, currencyType = 1, price = 5800, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [77] = { id = 112002, currencyType = 1, price = 45000, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [78] = { id = 112003, currencyType = 1, price = 100000, limitCount = 1, limitType_1 = 1001, value_1 = 900, }, [79] = { id = 112004, currencyType = 1, price = 450000, limitCount = 1, limitType_1 = 1001, value_1 = 2000, }, [80] = { id = 112005, currencyType = 1, price = 2000000, limitCount = 1, limitType_1 = 1001, value_1 = 2750, }, [81] = { id = 112101, currencyType = 1, price = 2500, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [82] = { id = 112102, currencyType = 1, price = 21000, limitCount = 1, limitType_1 = 1001, value_1 = 260, }, [83] = { id = 112103, currencyType = 1, price = 85000, limitCount = 1, limitType_1 = 1001, value_1 = 700, }, [84] = { id = 112104, currencyType = 1, price = 280000, limitCount = 1, limitType_1 = 1001, value_1 = 1750, }, [85] = { id = 112105, currencyType = 1, price = 4000000, limitCount = 1, limitType_1 = 1001, value_1 = 8000, }, [86] = { id = 112201, currencyType = 1, price = 6600, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [87] = { id = 112202, currencyType = 1, price = 52000, limitCount = 1, limitType_1 = 1001, value_1 = 400, }, [88] = { id = 112203, currencyType = 1, price = 120000, limitCount = 1, limitType_1 = 1001, value_1 = 1200, }, [89] = { id = 112204, currencyType = 1, price = 800000, limitCount = 1, limitType_1 = 1001, value_1 = 2800, }, [90] = { id = 112205, currencyType = 1, price = 10000000, limitCount = 1, limitType_1 = 1001, value_1 = 6000, }, [91] = { id = 112301, currencyType = 1, price = 2000, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [92] = { id = 112302, currencyType = 1, price = 16500, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [93] = { id = 112303, currencyType = 1, price = 125000, limitCount = 1, limitType_1 = 1001, value_1 = 650, }, [94] = { id = 112304, currencyType = 1, price = 225000, limitCount = 1, limitType_1 = 1001, value_1 = 2000, }, [95] = { id = 112305, currencyType = 1, price = 4500000, limitCount = 1, limitType_1 = 1001, value_1 = 2500, }, [96] = { id = 112401, currencyType = 1, price = 1200, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [97] = { id = 112402, currencyType = 1, price = 17500, limitCount = 1, limitType_1 = 1001, value_1 = 220, }, [98] = { id = 112403, currencyType = 1, price = 90000, limitCount = 1, limitType_1 = 1001, value_1 = 580, }, [99] = { id = 112404, currencyType = 1, price = 220000, limitCount = 1, limitType_1 = 1001, value_1 = 1650, }, [100] = { id = 112405, currencyType = 1, price = 2900000, limitCount = 1, limitType_1 = 1001, value_1 = 4500, }, [101] = { id = 115101, currencyType = 1, price = 100000, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [102] = { id = 115102, currencyType = 1, price = 800000, limitCount = 1, limitType_1 = 1001, value_1 = 6000, }, [103] = { id = 115103, currencyType = 1, price = 1350000, limitCount = 1, limitType_1 = 1001, value_1 = 12000, }, [104] = { id = 115201, currencyType = -1, price = -1, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [105] = { id = 115202, currencyType = 1, price = 600000, limitCount = 1, limitType_1 = 1001, value_1 = 5000, }, [106] = { id = 115203, currencyType = 1, price = 1200000, limitCount = 1, limitType_1 = 1001, value_1 = 11000, }, [107] = { id = 115301, currencyType = 1, price = 150000, limitCount = 1, limitType_1 = 1001, value_1 = 800, }, [108] = { id = 115302, currencyType = 1, price = 1200000, limitCount = 1, limitType_1 = 1001, value_1 = 7000, }, [109] = { id = 115303, currencyType = 1, price = 1980000, limitCount = 1, limitType_1 = 1001, value_1 = 13000, }, [110] = { id = 115401, currencyType = 1, price = 200000, limitCount = 1, limitType_1 = 1001, value_1 = 900, }, [111] = { id = 115402, currencyType = 1, price = 1500000, limitCount = 1, limitType_1 = 1001, value_1 = 7500, }, [112] = { id = 115403, currencyType = 1, price = 2450000, limitCount = 1, limitType_1 = 1001, value_1 = 14000, }, [113] = { id = 115501, currencyType = 1, price = 180000, limitCount = 1, limitType_1 = 1001, value_1 = 1500, }, [114] = { id = 115502, currencyType = 1, price = 1300000, limitCount = 1, limitType_1 = 1001, value_1 = 7000, }, [115] = { id = 115503, currencyType = 1, price = 2140000, limitCount = 1, limitType_1 = 1001, value_1 = 15000, }, [116] = { id = 115601, currencyType = 1, price = 200000, limitCount = 1, limitType_1 = 1001, value_1 = 1200, }, [117] = { id = 115602, currencyType = 1, price = 1200000, limitCount = 1, limitType_1 = 1001, value_1 = 8000, }, [118] = { id = 115603, currencyType = 1, price = 1980000, limitCount = 1, limitType_1 = 1001, value_1 = 16000, }, [119] = { id = 115701, currencyType = 1, price = 260000, limitCount = 1, limitType_1 = 1001, value_1 = 1300, }, [120] = { id = 115702, currencyType = 1, price = 1700000, limitCount = 1, limitType_1 = 1001, value_1 = 9000, }, [121] = { id = 115703, currencyType = 1, price = 2800000, limitCount = 1, limitType_1 = 1001, value_1 = 17000, }, [122] = { id = 115801, currencyType = 1, price = 500000, limitCount = 1, limitType_1 = 1001, value_1 = 1000, }, [123] = { id = 115802, currencyType = 1, price = 2000000, limitCount = 1, limitType_1 = 1001, value_1 = 10000, }, [124] = { id = 115803, currencyType = 1, price = 3300000, limitCount = 1, limitType_1 = 1001, value_1 = 18000, }, [125] = { id = 210001, currencyType = 1, price = 100, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [126] = { id = 210002, currencyType = -1, price = -1, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [127] = { id = 210003, currencyType = 1, price = 500, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [128] = { id = 210004, currencyType = 1, price = 1000, limitCount = 1, limitType_1 = 1001, value_1 = 15, }, [129] = { id = 210005, currencyType = 1, price = 1300, limitCount = 1, limitType_1 = 1001, value_1 = 30, }, [130] = { id = 210006, currencyType = 1, price = 1500, limitCount = 1, limitType_1 = 1001, value_1 = 50, }, [131] = { id = 210007, currencyType = 1, price = 2500, limitCount = 1, limitType_1 = 1001, value_1 = 60, }, [132] = { id = 210008, currencyType = 1, price = 2800, limitCount = 1, limitType_1 = 1001, value_1 = 70, }, [133] = { id = 210009, currencyType = 1, price = 3200, limitCount = 1, limitType_1 = 1001, value_1 = 90, }, [134] = { id = 210010, currencyType = 1, price = 5500, limitCount = 1, limitType_1 = 1001, value_1 = 110, }, [135] = { id = 210011, currencyType = 1, price = 6000, limitCount = 1, limitType_1 = 1001, value_1 = 120, }, [136] = { id = 210012, currencyType = 1, price = 6500, limitCount = 1, limitType_1 = 1001, value_1 = 140, }, [137] = { id = 210013, currencyType = 1, price = 7000, limitCount = 1, limitType_1 = 1001, value_1 = 160, }, [138] = { id = 210014, currencyType = 1, price = 8000, limitCount = 1, limitType_1 = 1001, value_1 = 200, }, [139] = { id = 210015, currencyType = 1, price = 9000, limitCount = 1, limitType_1 = 1001, value_1 = 240, }, [140] = { id = 210016, currencyType = 1, price = 10000, limitCount = 1, limitType_1 = 1001, value_1 = 280, }, [141] = { id = 210017, currencyType = 1, price = 12000, limitCount = 1, limitType_1 = 1001, value_1 = 320, }, [142] = { id = 210018, currencyType = 1, price = 15000, limitCount = 1, limitType_1 = 1001, value_1 = 360, }, [143] = { id = 210019, currencyType = 1, price = 18000, limitCount = 1, limitType_1 = 1001, value_1 = 400, }, [144] = { id = 210020, currencyType = 1, price = 21000, limitCount = 1, limitType_1 = 1001, value_1 = 430, }, [145] = { id = 210021, currencyType = 1, price = 22050, limitCount = 1, limitType_1 = 1001, value_1 = 460, }, [146] = { id = 210022, currencyType = 1, price = 24750, limitCount = 1, limitType_1 = 1001, value_1 = 490, }, [147] = { id = 210023, currencyType = 1, price = 28175, limitCount = 1, limitType_1 = 1001, value_1 = 520, }, [148] = { id = 210024, currencyType = 1, price = 30600, limitCount = 1, limitType_1 = 1001, value_1 = 550, }, [149] = { id = 210025, currencyType = 1, price = 34375, limitCount = 1, limitType_1 = 1001, value_1 = 580, }, [150] = { id = 210026, currencyType = 1, price = 37700, limitCount = 1, limitType_1 = 1001, value_1 = 610, }, [151] = { id = 210027, currencyType = 1, price = 40500, limitCount = 1, limitType_1 = 1001, value_1 = 640, }, [152] = { id = 210028, currencyType = 1, price = 44800, limitCount = 1, limitType_1 = 1001, value_1 = 670, }, [153] = { id = 210029, currencyType = 1, price = 49300, limitCount = 1, limitType_1 = 1001, value_1 = 700, }, [154] = { id = 210030, currencyType = 1, price = 53250, limitCount = 1, limitType_1 = 1001, value_1 = 720, }, [155] = { id = 210031, currencyType = 1, price = 58125, limitCount = 1, limitType_1 = 1001, value_1 = 740, }, [156] = { id = 210032, currencyType = 1, price = 62400, limitCount = 1, limitType_1 = 1001, value_1 = 760, }, [157] = { id = 210033, currencyType = 1, price = 67650, limitCount = 1, limitType_1 = 1001, value_1 = 800, }, [158] = { id = 210034, currencyType = 1, price = 73100, limitCount = 1, limitType_1 = 1001, value_1 = 850, }, [159] = { id = 210035, currencyType = 1, price = 78750, limitCount = 1, limitType_1 = 1001, value_1 = 900, }, [160] = { id = 210036, currencyType = 1, price = 83700, limitCount = 1, limitType_1 = 1001, value_1 = 950, }, [161] = { id = 210037, currencyType = 1, price = 89725, limitCount = 1, limitType_1 = 1001, value_1 = 1000, }, [162] = { id = 210038, currencyType = 1, price = 95000, limitCount = 1, limitType_1 = 1001, value_1 = 1100, }, [163] = { id = 210039, currencyType = 1, price = 101400, limitCount = 1, limitType_1 = 1001, value_1 = 1200, }, [164] = { id = 210040, currencyType = 1, price = 108000, limitCount = 1, limitType_1 = 1001, value_1 = 1300, }, [165] = { id = 210041, currencyType = 1, price = 117600, limitCount = 1, limitType_1 = 1001, value_1 = 1400, }, [166] = { id = 210042, currencyType = 1, price = 127600, limitCount = 1, limitType_1 = 1001, value_1 = 1500, }, [167] = { id = 210043, currencyType = 1, price = 138000, limitCount = 1, limitType_1 = 1001, value_1 = 1600, }, [168] = { id = 210044, currencyType = 1, price = 148800, limitCount = 1, limitType_1 = 1001, value_1 = 1700, }, [169] = { id = 210045, currencyType = 1, price = 162500, limitCount = 1, limitType_1 = 1001, value_1 = 1800, }, [170] = { id = 210046, currencyType = 1, price = 174200, limitCount = 1, limitType_1 = 1001, value_1 = 1900, }, [171] = { id = 210047, currencyType = 1, price = 186300, limitCount = 1, limitType_1 = 1001, value_1 = 2000, }, [172] = { id = 210048, currencyType = 1, price = 198800, limitCount = 1, limitType_1 = 1001, value_1 = 2200, }, [173] = { id = 210049, currencyType = 1, price = 214600, limitCount = 1, limitType_1 = 1001, value_1 = 2400, }, [174] = { id = 210050, currencyType = 1, price = 228000, limitCount = 1, limitType_1 = 1001, value_1 = 2600, }, [175] = { id = 210051, currencyType = 1, price = 241400, limitCount = 1, limitType_1 = 1001, value_1 = 2800, }, [176] = { id = 210052, currencyType = 1, price = 254800, limitCount = 1, limitType_1 = 1001, value_1 = 3000, }, [177] = { id = 210053, currencyType = 1, price = 268200, limitCount = 1, limitType_1 = 1001, value_1 = 3200, }, [178] = { id = 210054, currencyType = 1, price = 281600, limitCount = 1, limitType_1 = 1001, value_1 = 3400, }, [179] = { id = 210055, currencyType = 1, price = 295000, limitCount = 1, limitType_1 = 1001, value_1 = 3600, }, [180] = { id = 210056, currencyType = 1, price = 308400, limitCount = 1, limitType_1 = 1001, value_1 = 3800, }, [181] = { id = 210057, currencyType = 1, price = 321800, limitCount = 1, limitType_1 = 1001, value_1 = 4000, }, [182] = { id = 210058, currencyType = 1, price = 335200, limitCount = 1, limitType_1 = 1001, value_1 = 4200, }, [183] = { id = 210059, currencyType = 1, price = 348600, limitCount = 1, limitType_1 = 1001, value_1 = 4400, }, [184] = { id = 210060, currencyType = 1, price = 362000, limitCount = 1, limitType_1 = 1001, value_1 = 4600, }, [185] = { id = 210801, currencyType = -1, price = -1, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [186] = { id = 210802, currencyType = 2, price = 50, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [187] = { id = 210803, currencyType = 2, price = 100, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [188] = { id = 210804, currencyType = 2, price = 200, limitCount = 1, limitType_1 = 1001, value_1 = 800, }, [189] = { id = 210805, currencyType = 2, price = 300, limitCount = 1, limitType_1 = 1001, value_1 = 1000, }, [190] = { id = 210806, currencyType = 2, price = 500, limitCount = 1, limitType_1 = 1001, value_1 = 1200, }, [191] = { id = 210807, currencyType = 2, price = 800, limitCount = 1, limitType_1 = 1001, value_1 = 1400, }, [192] = { id = 210808, currencyType = 2, price = 1000, limitCount = 1, limitType_1 = 1001, value_1 = 1600, }, [193] = { id = 210809, currencyType = 2, price = 1200, limitCount = 1, limitType_1 = 1001, value_1 = 1800, }, [194] = { id = 210810, currencyType = 2, price = 1400, limitCount = 1, limitType_1 = 1001, value_1 = 2000, }, [195] = { id = 210811, currencyType = 2, price = 1600, limitCount = 1, limitType_1 = 1001, value_1 = 2200, }, [196] = { id = 210812, currencyType = 2, price = 1800, limitCount = 1, limitType_1 = 1001, value_1 = 2400, }, [197] = { id = 210813, currencyType = 2, price = 2000, limitCount = 1, limitType_1 = 1001, value_1 = 2600, }, [198] = { id = 210814, currencyType = 2, price = 2200, limitCount = 1, limitType_1 = 1001, value_1 = 2800, }, [199] = { id = 210815, currencyType = 2, price = 2400, limitCount = 1, limitType_1 = 1001, value_1 = 3000, }, [200] = { id = 210816, currencyType = 2, price = 2600, limitCount = 1, limitType_1 = 1001, value_1 = 3500, }, [201] = { id = 210817, currencyType = 2, price = 2800, limitCount = 1, limitType_1 = 1001, value_1 = 4000, }, [202] = { id = 210818, currencyType = 2, price = 3000, limitCount = 1, limitType_1 = 1001, value_1 = 4500, }, [203] = { id = 210819, currencyType = 2, price = 3200, limitCount = 1, limitType_1 = 1001, value_1 = 5000, }, [204] = { id = 210820, currencyType = 2, price = 3400, limitCount = 1, limitType_1 = 1001, value_1 = 5500, }, [205] = { id = 210821, currencyType = 2, price = 3600, limitCount = 1, limitType_1 = 1001, value_1 = 6000, }, [206] = { id = 210822, currencyType = 2, price = 3800, limitCount = 1, limitType_1 = 1001, value_1 = 6500, }, [207] = { id = 210823, currencyType = 2, price = 4000, limitCount = 1, limitType_1 = 1001, value_1 = 7000, }, [208] = { id = 210824, currencyType = 2, price = 4200, limitCount = 1, limitType_1 = 1001, value_1 = 7500, }, [209] = { id = 210825, currencyType = 2, price = 4400, limitCount = 1, limitType_1 = 1001, value_1 = 8000, }, [210] = { id = 210826, currencyType = 2, price = 4600, limitCount = 1, limitType_1 = 1001, value_1 = 8500, }, [211] = { id = 210827, currencyType = 2, price = 4800, limitCount = 1, limitType_1 = 1001, value_1 = 9000, }, [212] = { id = 210828, currencyType = 2, price = 5000, limitCount = 1, limitType_1 = 1001, value_1 = 9500, }, [213] = { id = 210829, currencyType = 2, price = 5200, limitCount = 1, limitType_1 = 1001, value_1 = 10000, }, [214] = { id = 210830, currencyType = 2, price = 5400, limitCount = 1, limitType_1 = 1001, value_1 = 20000, }, [215] = { id = 210831, currencyType = 2, price = 5600, limitCount = 1, limitType_1 = 1001, value_1 = 30000, }, [216] = { id = 210832, currencyType = 2, price = 5800, limitCount = 1, limitType_1 = 1001, value_1 = 40000, }, [217] = { id = 210833, currencyType = 2, price = 6000, limitCount = 1, limitType_1 = 1001, value_1 = 50000, }, [218] = { id = 210834, currencyType = 2, price = 6200, limitCount = 1, limitType_1 = 1001, value_1 = 60000, }, [219] = { id = 210835, currencyType = 2, price = 6500, limitCount = 1, limitType_1 = 1001, value_1 = 70000, }, [220] = { id = 211001, currencyType = -1, price = -1, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [221] = { id = 211002, currencyType = 1, price = 50, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [222] = { id = 211003, currencyType = 1, price = 100, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [223] = { id = 211004, currencyType = 1, price = 200, limitCount = 0, limitType_1 = -1, value_1 = -1, }, [224] = { id = 211005, currencyType = 1, price = 300, limitCount = 1, limitType_1 = 1001, value_1 = 1000, }, [225] = { id = 211006, currencyType = 1, price = 400, limitCount = 1, limitType_1 = 1001, value_1 = 2000, }, [226] = { id = 211007, currencyType = 1, price = 500, limitCount = 2, limitType_1 = 1001, value_1 = 2500, }, [227] = { id = 211008, currencyType = 1, price = 700, limitCount = 1, limitType_1 = 1001, value_1 = 3000, }, [228] = { id = 211009, currencyType = 1, price = 900, limitCount = 1, limitType_1 = 1001, value_1 = 4000, }, [229] = { id = 211010, currencyType = 1, price = 1100, limitCount = 1, limitType_1 = 1001, value_1 = 5000, }, [230] = { id = 211011, currencyType = 1, price = 1300, limitCount = 2, limitType_1 = 1001, value_1 = 6000, }, [231] = { id = 211012, currencyType = 1, price = 1500, limitCount = 1, limitType_1 = 1001, value_1 = 7000, }, [232] = { id = 211013, currencyType = 1, price = 1700, limitCount = 1, limitType_1 = 1001, value_1 = 8000, }, [233] = { id = 211014, currencyType = 1, price = 1900, limitCount = 1, limitType_1 = 1001, value_1 = 9000, }, [234] = { id = 211015, currencyType = 1, price = 2100, limitCount = 2, limitType_1 = 1001, value_1 = 10000, }, [235] = { id = 211016, currencyType = 1, price = 2300, limitCount = 1, limitType_1 = 1001, value_1 = 11000, }, [236] = { id = 211017, currencyType = 1, price = 2500, limitCount = 1, limitType_1 = 1001, value_1 = 12000, }, [237] = { id = 211018, currencyType = 1, price = 2700, limitCount = 1, limitType_1 = 1001, value_1 = 13000, }, [238] = { id = 211019, currencyType = 1, price = 2900, limitCount = 1, limitType_1 = 1001, value_1 = 14000, }, [239] = { id = 211020, currencyType = 1, price = 3100, limitCount = 1, limitType_1 = 1001, value_1 = 15000, }, [240] = { id = 211021, currencyType = 1, price = 3300, limitCount = 2, limitType_1 = 1001, value_1 = 16000, }, [241] = { id = 211022, currencyType = 1, price = 3500, limitCount = 1, limitType_1 = 1001, value_1 = 17000, }, [242] = { id = 211023, currencyType = 1, price = 2500, limitCount = 1, limitType_1 = 1001, value_1 = 18000, }, [243] = { id = 211024, currencyType = 1, price = 2700, limitCount = 1, limitType_1 = 1001, value_1 = 19000, }, [244] = { id = 211025, currencyType = 1, price = 2900, limitCount = 1, limitType_1 = 1001, value_1 = 20000, }, [245] = { id = 211026, currencyType = 1, price = 3100, limitCount = 1, limitType_1 = 1001, value_1 = 21000, }, [246] = { id = 211027, currencyType = 1, price = 3300, limitCount = 1, limitType_1 = 1001, value_1 = 22000, }, [247] = { id = 211028, currencyType = 1, price = 3500, limitCount = 1, limitType_1 = 1001, value_1 = 23000, }, [248] = { id = 211029, currencyType = 1, price = 3700, limitCount = 1, limitType_1 = 1001, value_1 = 24000, }, [249] = { id = 211030, currencyType = 1, price = 3900, limitCount = 1, limitType_1 = 1001, value_1 = 25000, }, [250] = { id = 211031, currencyType = 1, price = 4100, limitCount = 1, limitType_1 = 1001, value_1 = 26000, }, [251] = { id = 211032, currencyType = 1, price = 4300, limitCount = 1, limitType_1 = 1001, value_1 = 27000, }, [252] = { id = 211033, currencyType = 1, price = 4500, limitCount = 1, limitType_1 = 1001, value_1 = 28000, }, [253] = { id = 211034, currencyType = 1, price = 4700, limitCount = 1, limitType_1 = 1001, value_1 = 29000, }, [254] = { id = 211035, currencyType = 1, price = 4900, limitCount = 1, limitType_1 = 1001, value_1 = 30000, }, [255] = { id = 211036, currencyType = 1, price = 5100, limitCount = 1, limitType_1 = 1001, value_1 = 31000, }, [256] = { id = 211037, currencyType = 1, price = 5300, limitCount = 1, limitType_1 = 1001, value_1 = 32000, }, [257] = { id = 211038, currencyType = 1, price = 5500, limitCount = 1, limitType_1 = 1001, value_1 = 33000, }, [258] = { id = 211039, currencyType = 1, price = 5700, limitCount = 1, limitType_1 = 1001, value_1 = 34000, }, [259] = { id = 211040, currencyType = 1, price = 5900, limitCount = 1, limitType_1 = 1001, value_1 = 35000, }, [260] = { id = 211041, currencyType = 1, price = 6100, limitCount = 1, limitType_1 = 1001, value_1 = 36000, }, [261] = { id = 211042, currencyType = 1, price = 6300, limitCount = 1, limitType_1 = 1001, value_1 = 37000, }, [262] = { id = 211043, currencyType = 1, price = 6500, limitCount = 1, limitType_1 = 1001, value_1 = 38000, }, [263] = { id = 211044, currencyType = 1, price = 6700, limitCount = 1, limitType_1 = 1001, value_1 = 39000, }, [264] = { id = 211045, currencyType = 1, price = 6900, limitCount = 1, limitType_1 = 1001, value_1 = 40000, }, [265] = { id = 211046, currencyType = 1, price = 7100, limitCount = 1, limitType_1 = 1001, value_1 = 41000, }, [266] = { id = 211047, currencyType = 1, price = 7300, limitCount = 1, limitType_1 = 1001, value_1 = 42000, }, [267] = { id = 211048, currencyType = 1, price = 7500, limitCount = 1, limitType_1 = 1001, value_1 = 43000, }, [268] = { id = 211049, currencyType = 1, price = 7700, limitCount = 1, limitType_1 = 1001, value_1 = 44000, }, [269] = { id = 211050, currencyType = 1, price = 7900, limitCount = 1, limitType_1 = 1001, value_1 = 45000, }, } return buyCommonCfg RoleBase:--[[ 所有角色的基类 author:{zhangpeng} time:2025-05-13 10:21:12 ]] ---@class RoleBase local RoleBase = defClass("RoleBase") local LOGTAG = "RoleBase" -- 唯一ID计数器,所有角色共享 RoleBase.idCounter = 0 function RoleBase:ctor(roleType) -- 角色唯一ID self.roleUniqueId = RoleMgr:genUniqueId(roleType) -- 角色类型,顾客,员工等 self.roleType = roleType -- 当前所在的地图 self.mapRoot = nil -- 寻路组件 self.pathFinder = nil -- 寻路网格 self.pathGrid = nil end -- 获取角色唯一ID,顾客,员工等所有角色通用 function RoleBase:getRoleUniqueId() return self.roleUniqueId end -- 设置角色所在地图 function RoleBase:setMapRoot(mapRoot) self.mapRoot = mapRoot end -- 获取角色所在地图 function RoleBase:getMapRoot() return self.mapRoot end -- 获取寻路组件 function RoleBase:getPathFindSeeker() return self.pathFinder end -- 设置当前位置 function RoleBase:setCurPosition(pos) self.curPosition = pos -- 子类需要重写此方法 end -- 获取当前位置 function RoleBase:getCurPosition() -- 子类需要重写此方法 return nil end function RoleBase:setDirection(direction) if direction == RoleConst.direction.left then -- 向左移动,翻转角色 self.display.transform.localScale = Vector3(-1, 1, 1) if self.bubble then self.bubble.transform.localScale = Vector3(-1, 1, 1) end elseif direction == RoleConst.direction.right then -- 向右移动,设置为正向 self.display.transform.localScale = Vector3(1, 1, 1) if self.bubble then self.bubble.transform.localScale = Vector3(1, 1, 1) end end end -- 添加寻路组件 function RoleBase:addPathFind(displayNode) local pathFind = displayNode:AddComponent(typeof(CS.Pathfinding.Seeker)) if not pathFind then printError(LOGTAG, "添加寻路组件失败") end self.pathFinder = pathFind pathFind.traversableTags = MapsConst.getRestaurantTag() end -- 设置角色寻路网格 function RoleBase:setRolePathGrid(gridName) local graphs = CS.AstarPath.active.data.graphs for _, graph in cs_ipairs(graphs) do if graph.name == gridName then self.pathGrid = graph self.graphMask = CS.Pathfinding.GraphMask.FromGraph(graph) break end end if not self.pathGrid then printError(LOGTAG, "未找到寻路网格: %s", gridName) end end -- 获取角色寻路网格 function RoleBase:getRolePathGrid() return self.pathGrid end -- 获取GraphMask function RoleBase:getGraphMask() return self.graphMask end function RoleBase:setSeekerAreaTag(areaTag) if not self.pathFinder then printError(LOGTAG, "Seeker组件不存在") return end self.pathFinder.traversableTags = areaTag end function RoleBase:getSeekerAreaTag() if not self.pathFinder then printError(LOGTAG, "Seeker组件不存在") return 0 end return self.pathFinder.traversableTags end -- 角色寻路 function RoleBase:rolePathFind(endPos, onComplete) local roleSeeker = self:getPathFindSeeker() if not roleSeeker then printError(LOGTAG, "角色上没有找到Seeker组件!") return end -- 使用角色当前位置作为起点 local currentPos = self:getCurPosition() if not currentPos then printError(LOGTAG, "无法获取角色当前位置!") return end -- printInfo(LOGTAG, "开始寻路 - 当前位置: %s, 目标位置: %s", currentPos, endPos) -- 检查AstarPath是否初始化 local astar = CS.AstarPath.active if not astar then printError(LOGTAG, "AstarPath未初始化!") return end -- 保存角色引用,在回调中使用 local role = self -- 寻路,并按照路径行走 roleSeeker:StartPath(currentPos, endPos, function(p) local path = p -- 处理路径错误 if path.error then printError(LOGTAG, "路径计算错误: %s", path.errorLog) return end -- 提取路径点 if path.vectorPath and path.vectorPath.Count > 0 then printInfo(LOGTAG, "路径点数量: %s", path.vectorPath.Count) -- 创建路径点数组,并调整坐标 local waypoints = {} for i = 0, path.vectorPath.Count - 1 do local point = path.vectorPath[i] table.insert(waypoints, point) end -- 优化路径点:如果第一个点非常接近当前位置,可以跳过 if #waypoints > 1 then local firstPoint = waypoints[1] local distToFirst = (firstPoint - currentPos).magnitude if distToFirst < 0.1 then table.remove(waypoints, 1) printInfo(LOGTAG, "跳过第一个路径点,距离太近: %s", distToFirst) end end if #waypoints > 0 then -- 绘制路径线 role:drawPathLine(waypoints) -- 沿路径移动 role:moveRoleByPathList(waypoints, function() if onComplete then onComplete() end end) else printError(LOGTAG, "路径点转换后数量为0") end else printError(LOGTAG, "路径点为空或数量为0") end end, self:getGraphMask()) end -- 绘制路径线 function RoleBase:drawPathLine(pathList, autoDestroy) -- 检查mapRoot是否存在 if not self.mapRoot then printError(LOGTAG, "无法绘制路径线:角色未设置地图根节点(mapRoot)") return end autoDestroy = autoDestroy or false -- 为每个角色创建唯一的路径线名称 local pathLineName = "PathLine_" .. tostring(self.roleUniqueId) printInfo(LOGTAG, "角色id:%s 绘制路径线名称:%s", self.roleUniqueId, pathLineName) -- 如果已有路径线,先删除 local existingLine = self.mapRoot:Seek(pathLineName) if existingLine then GameObject.Destroy(existingLine.gameObject) end -- 检查mapRoot激活 if not self.mapRoot.activeInHierarchy then printError(LOGTAG, "mapRoot未激活,无法绘制路径线") return end -- 检查路径点 if not pathList or #pathList == 0 then printError(LOGTAG, "绘制路径线失败:路径点为空") return end -- 创建路径线对象 local lineObject = GameObject(pathLineName) lineObject.transform.parent = self.mapRoot.transform lineObject.transform.localPosition = CS.UnityEngine.Vector3.zero lineObject.transform.localRotation = CS.UnityEngine.Quaternion.identity lineObject.transform.localScale = CS.UnityEngine.Vector3.one -- 确保路径线在场景最上层显示 lineObject.transform:SetAsLastSibling() -- 添加LineRenderer组件并配置 local line = lineObject:AddComponent(typeof(CS.UnityEngine.LineRenderer)) line.positionCount = #pathList line.useWorldSpace = true -- 使用世界坐标 line.startWidth = 0.04 -- 粗细 line.endWidth = 0.04 -- 设置线条材质和颜色为绿色 local lineMaterial = CS.UnityEngine.Material(CS.UnityEngine.Shader.Find("Unlit/Color")) lineMaterial.color = CS.UnityEngine.Color(0, 1, 0, 1) -- 不透明绿色 line.material = lineMaterial line.startColor = CS.UnityEngine.Color(0, 1, 0, 1) line.endColor = CS.UnityEngine.Color(0, 1, 0, 1) -- 设置LineRenderer排序层,避免被遮挡 if line.sortingLayerName ~= nil then line.sortingLayerName = "Default" end line.sortingOrder = 100 -- 设置线条位置 for i = 1, #pathList do local point = pathList[i] -- 稍微提高线条高度,避免被场景遮挡 local displayPoint = CS.UnityEngine.Vector3(point.x, point.y + 0.05, point.z) line:SetPosition(i-1, displayPoint) end -- printInfo(LOGTAG, string.format("已为角色[%s-%s]绘制路径线:%d个点", self.roleUniqueId, self.roleType, #pathList)) -- 保存路径线引用,便于后续清理 self.pathLine = lineObject -- 自动销毁设置 if autoDestroy then GameObject.Destroy(lineObject, 5.0) end end -- 根据路径列表行走到目的地 function RoleBase:moveRoleByPathList(pathList, cb) printInfo(LOGTAG, "开始移动角色, 路径长度:%s", table.nums(pathList)) -- 保存当前对象的引用,在回调中使用 local role = self -- 定义固定移动速度(单位/秒) local moveSpeed = tonumber(self.moveSpeed) * 0.009 -- 正常速度 -- local moveSpeed = tonumber(self.moveSpeed) * 0.1 -- 测试速度 util.async.foreach( pathList, function (tarPos, resolve, i) -- printInfo(LOGTAG, string.format("move step:%d, tarPos:%s", i, tarPos)) role:walkToPos(tarPos, moveSpeed, resolve) end, function () -- 到达终点后清除路径线 if role.pathLine then GameObject.Destroy(role.pathLine) role.pathLine = nil printInfo(LOGTAG, string.format("角色[%s]到达目标,已清除路径线", role.roleUniqueId)) end if role.playIdleAnim then role:playIdleAnim() end if cb then cb() end end ) end -- 走到某一个坐标 function RoleBase:walkToPos(tarPos, moveSpeed, resolve) -- 计算当前位置到目标位置的距离 local currentPos = self:getCurPosition() if not currentPos then printError(LOGTAG, "无法获取角色当前位置") if resolve then resolve() end return end local distance = (tarPos - currentPos).magnitude -- 计算移动方向 local moveDir = CS.UnityEngine.Vector3(tarPos.x - currentPos.x, tarPos.y - currentPos.y, tarPos.z - currentPos.z) if moveDir.magnitude > 0.001 then moveDir = moveDir.normalized -- printInfo(LOGTAG, string.format("移动方向: x=%.2f, y=%.2f, z=%.2f", moveDir.x, moveDir.y, moveDir.z)) -- 根据移动方向设置角色朝向 if moveDir.x > 0 then -- 向右移动 self:setDirection(RoleConst.direction.left) elseif moveDir.x < 0 then -- 向左移动 self:setDirection(RoleConst.direction.right) end end -- 根据距离和速度计算移动时间 moveSpeed = moveSpeed or 0.1 local moveTime = distance / moveSpeed -- printInfo(LOGTAG, string.format("移动距离:%.2f, 速度:%.2f, 预计用时:%.2f秒", distance, moveSpeed, moveTime)) -- 子类需要重写此方法来实际执行移动 -- 默认实现只调用回调函数 if resolve then resolve() end end -- 走到某一个建筑物 function RoleBase:autoWalkToBuilding(buildingId) -- todo: 实现走到指定建筑物 end -- ==================== 动态层级管理 ==================== -- 获取当前物体的真实SortingOrder(虚方法,由子类实现) function RoleBase:getCurrentSortingOrder() -- 子类需要重写此方法来获取prefab中的真实SortingOrder printWarn(LOGTAG, "getCurrentSortingOrder方法需要在子类中实现") return nil end -- 更新角色的动态层级 function RoleBase:updateDynamicSortingOrder() local currentPos = self:getCurPosition() if not currentPos then return end -- 获取附近的建筑物 local nearbyBuilding = self:getNearbyBuilding() if nearbyBuilding then -- printInfo(LOGTAG, string.format("建筑名字666: %s name:%s", nearbyBuilding.buildingType, nearbyBuilding.buildingName)) local buildingPos = nearbyBuilding:getBuildingPosition() local buildingOrder = nearbyBuilding:getBuildingSortingOrder() local roleOrder -- 正确的Y坐标比较逻辑:Y坐标越大(越靠上)层级越低,Y坐标越小(越靠下)层级越高 if currentPos.y < buildingPos.y then -- 角色在建筑下方(前面),层级应该比建筑高 roleOrder = buildingOrder + 1 else -- 角色在建筑上方(后面),层级应该比建筑低 roleOrder = buildingOrder - 1 end -- printInfo(LOGTAG, string.format("角色位置Y:%.2f, 建筑位置Y:%.2f, 建筑层级:%d, 角色层级:%d", currentPos.y, buildingPos.y, buildingOrder, roleOrder)) self:setSortingOrder(roleOrder) end end -- 获取附近的建筑物 function RoleBase:getNearbyBuilding() local currentPos = self:getCurPosition() if not currentPos then return nil end local nearestBuilding = nil local nearestDistance = math.huge -- 从BuildingMgr获取所有建筑 local allBuildings = BuildingMgr:getSortingBuilding() if allBuildings then for _, building in pairs(allBuildings) do if building.sortBaseNode then local buildingPos = building.sortBaseNode.transform.position local distance = Vector3.Distance(currentPos, buildingPos) -- 找到距离最近的建筑,不使用固定距离阈值 if distance < nearestDistance then nearestDistance = distance local buildingSortingOrder = building:getBuildingSortingOrder() nearestBuilding = { getBuildingPosition = function() return buildingPos end, getBuildingSortingOrder = function() return buildingSortingOrder end, buildingType = building.buildingInfo.buildingType or "unknown", buildingId = building:getBuildingId(), distance = distance, buildingName = building.buildingInfo.buildingName } end end end end -- 距离最近的建筑 -- printInfo(LOGTAG, string.format("距离最近的建筑: %s, 距离: %s", nearestBuilding.buildingName, nearestBuilding.distance)) return nearestBuilding end -- 设置角色层级(虚方法,由子类实现) function RoleBase:setSortingOrder(sortingOrder) -- 子类需要重写此方法 printWarn(LOGTAG, "setSortingOrder方法需要在子类中实现") end return RoleBase main|require("common/managers/cmd/http/main") require("common/managers/user/User") require("common/managers/user/UserDataMgr")CookingBenchMgr3--[[ 灶台管理 author:{zhangpeng} time:2025-05-16 14:38:02 ]] local CookingBenckMgr = defClassStatic("CookingBenckMgr") local LOGTAG = "CookingBenckMgr" -- init function CookingBenckMgr:init() printInfo(LOGTAG, "------ 灶台管理 初始化 ------") self.cookingBenches = {} end -- 根据建筑id创建一个灶台 function CookingBenckMgr:createCookingBench(buildingInfo) local buildingType = buildingInfo.buildingType local parentNode = RestaurantMgr:getRestaurantScene().rootNode:Seek("building_pos_" .. buildingType) local args = { deskId = buildingInfo.buildingCfgId, parent = parentNode, scene = RestaurantMgr:getRestaurantScene(), } local cookingBench = nil if not self.cookingBenches then self.cookingBenches = {} end if self:hasSameTypeBench(buildingType) then self:replaceOldBench(buildingType, buildingInfo.buildingCfgId) else cookingBench = CookingBench.new(args) table.insert(self.cookingBenches, cookingBench) end return cookingBench end -- 是否有同类不同id的灶台 function CookingBenckMgr:hasSameTypeBench(buildingType) for i = 1, table.nums(self.cookingBenches) do local cookingBench = self.cookingBenches[i] if cookingBench.buildingInfo.buildingType == buildingType then return true end end return false end -- 查找相同类型建筑的旧灶台,替换旧的灶台 -- @param buildingType 建筑类型 -- @param newBenchCfgId 新灶台配置id function CookingBenckMgr:replaceOldBench(buildingType, newBenchCfgId) for i = 1, #self.cookingBenches do local cookingBench = self.cookingBenches[i] -- 大类相同,配置id不同,则是旧灶台 if cookingBench.buildingInfo.buildingType == buildingType and cookingBench.buildingInfo.buildingCfgId ~= newBenchCfgId then -- 只修改一下prefab的名字为配置id cookingBench.buildingNode:SetName(newBenchCfgId) break end end end return CookingBenckMgr SqliteUtil> ---@class SqliteUtil:LuaStaticClass local SqliteUtil = defClassStatic("SqliteUtil") local LOGTAG = SqliteUtil.__cls_name SqliteUtil.null = { str = "NULL" } function SqliteUtil:luaTypeNameToSqlTypeName(luaTypeName) local sqlTypeName = "" if luaTypeName == "string" then sqlTypeName = "TEXT" elseif luaTypeName == "number" then sqlTypeName = "INTEGER" else printError(LOGTAG, "luaTypeNameToSqlTypeName, unsupport lua type %s", luaTypeName) end return sqlTypeName end function SqliteUtil:sqlTypeNameToLuaTypeName(sqlTypeName) local luaTypeName = "" if sqlTypeName == "TEXT" then luaTypeName = "string" elseif sqlTypeName == "INTEGER" then luaTypeName = "number" else printError(LOGTAG, "sqlTypeNameToLuaTypeName, unsupport sql type %s", sqlTypeName) end return luaTypeName end function SqliteUtil:luaValueToStr(luaValue, luaTypeName) local str = nil luaTypeName = luaTypeName or type(luaValue) if luaTypeName == "string" then str = string.format("'%s'", luaValue) elseif math.type(luaValue) == "integer" then str = string.format("%d", luaValue) elseif luaValue == SqliteUtil.null then str = SqliteUtil.null.str else str = luaValue end return str end function SqliteUtil:connectCondionWithAnd(conditionList) local ret = nil for i, condition in ipairs(conditionList) do if not ret then ret = condition else ret = ret:xand(condition) end end return ret end function SqliteUtil:connectCondionWithOr(conditionList) local ret = nil for i, condition in ipairs(conditionList) do if not ret then ret = condition else ret = ret:xor(condition) end end return ret end UnlockCommonCfgParse--[[ 解锁通用配置解析 author:{zhangpeng} time:2025-05-27 13:38:26 ]] local UnlockCommonCfgData = require("data/config/unlockCommonCfg") local UnlockCommonCfgParse = defClassStatic("UnlockCommonCfgParse") local LOGTAG = "UnlockCommonCfgParse" local unlock_common_cfg = {} function UnlockCommonCfgParse:init() for k, v in pairs(UnlockCommonCfgData) do unlock_common_cfg[v.id] = v end end function UnlockCommonCfgParse:getUnlockCondData(id) for _, v in pairs(UnlockCommonCfgData) do if v.id == id then return v end end return nil end -- 获取所有解锁条件配置 function UnlockCommonCfgParse:getUnlockCondListByType(unlockType) end UnlockCommonCfgParse:init() mainrequire("common/core/main") require("common/const/main") require("common/cos/main") require("common/ui/main") require("common/managers/main") require("common/network/main") require("common/platform/ext/main") require("common/platform/base/main") CosLuaCredentialBean7---@class CosLuaCredentialBean:LuaClass local CosLuaCredentialBean = defClass("CosLuaCredentialBean") ---comment ---@param storage_type string 存储类型(默认Standard,表示标准存储) ---@param service_type string 业务类型(1-homework 作业业务,99-extension 扩展业务) ---@param allow_prefix string 允许访问的文件前缀 ---@param max_file_size integer 最大文件大小(100M) ---@param left string 临时密钥id ---@param right string 临时密钥key ---@param session_token string 临时密钥sessionToken,用于向腾讯云发起请求时携带 ---@param start_time integer 密钥起始时间(单位:s) ---@param expired_time integer 密钥过期时间(单位:s) ---@param bucket_name string 存储桶名称 ---@param protocol string http协议 ---@param region string 存储桶地区 ---@param domain string 域名 ---@param base_url string 基础 url ---@param server_time_zone string 服务器时区 ---@param cos_appid string 对象存储的appid,用于与腾讯云通讯 function CosLuaCredentialBean:ctor(storage_type, service_type, allow_prefix, max_file_size, left, right, session_token, start_time, expired_time, bucket_name, protocol, region, domain, base_url, server_time_zone, cos_appid) self.storage_type = storage_type self.service_type = service_type self.allow_prefix = allow_prefix self.max_file_size = max_file_size self.left = left self.right = right self.session_token = session_token self.start_time = start_time self.expired_time = expired_time self.bucket_name = bucket_name self.protocol = protocol self.region = region self.domain = domain self.base_url = base_url self.server_time_zone = server_time_zone self.cos_appid = cos_appid end function CosLuaCredentialBean:initWithConfig(config) for key, value in pairs(config) do self[key] = value end self.max_file_size = tonumber(self.max_file_size) self.start_time = tonumber(self.start_time) self.expired_time = tonumber(self.expired_time) end return CosLuaCredentialBean ExtendPlayableDirector--[[ author:wanglong time:2022-08-29 15:02:41 ]] local PlayableDirector = CS.UnityEngine.Playables.PlayableDirector local PlayableDirectorCls, PlayableDirector_index = HackCSharpClass(PlayableDirector) local origin_Play = PlayableDirectorCls.Play PlayableDirectorCls.__index = function(ud, k) if k == "Play" then -- print("PlayableDirector.Play") -- rawset(_G,"__last_PlayableDirector_Play_traceback__",debug.traceback()) local result = "" for i = 2, 6 do local info = debug.getinfo(i, "Snl") if info then result = result .. string.format("%s:%s:%d;", info.source, info.name, info.currentline) end end if Msg then -- # todo next Msg.send("PlayableDirector_Play", result) end -- BuglySDK:setUserValue("PlayableDirector_Play", result) end return PlayableDirector_index(ud, k) -- c#的方法或者属性 end StrogeKeyDef\!--[[ 常量key author:{zhangpeng} time:2025-05-09 15:50:27 ]] local StrogeKeyDef = defClassStatic("StrogeKeyDef") -- 用户相关(使用LocalStorageMgr:set/:get操作) StrogeKeyDef.DEVICE_ID = "DEVICE_ID" --登录用 StrogeKeyDef.USER_ID_KEY_TEST = "USER_ID_KEY_TEST" StrogeKeyDef.USER_NAME_KEY_TEST = "USER_NAME_KEY_TEST" StrogeKeyDef.FORCE_GUIDE_STEP = "FORCE_GUIDE_STEP" -- 强制引导步骤 int StrogeKeyDef.FORCE_GUIDE_DIALOG_IDX = "FORCE_GUIDE_DIALOG_IDX" -- 强制引导剧情顺序 int StrogeKeyDef.TASK_GUIDE_STEP = "TASK_GUIDE_STEP" -- 任务引导步骤 int StrogeKeyDef.TASK_GUIDE_DIALOG_IDX = "TASK_GUIDE_DIALOG_IDX" -- 任务引导剧情顺序 int -- 用户登录信息 StrogeKeyDef.USER_LAST_USER_ID = "USER_LAST_USER_ID" -- 上次登录的userid StrogeKeyDef.USER_LAST_USER_NAME = "USER_LAST_USER_NAME" -- 上次登录的user name StrogeKeyDef.USER_LAST_TOKEN = "USER_LAST_TOKEN" -- 上次登录的token StrogeKeyDef.USER_LAST_LOGIN_TYPE = "USER_LAST_LOGIN_TYPE" -- 上次登录类型 --设备相关(使用CS.LocalDataStorage.Get/.Set操作) StrogeKeyDef.DEVICE_PRETEND_NET_NOT_OPEN = "DEVICE_PRETEND_NET_NOT_OPEN" -- 假装网络未开 StrogeKeyDef.DEVICE_PRETEND_WIFI_NET_CLOSE = "DEVICE_PRETEND_WIFI_NET_CLOSE" -- 假装wifi关闭 StrogeKeyDef.DEVICE_PRETEND_USE_CELLUAR_NET = "DEVICE_PRETEND_USE_CELLUAR_NET" -- 假装使用移动网络 StrogeKeyDef.DEVICE_ALWAYS_SHOW_ERR_DIALOG = "ALWAYS_SHOW_ERR_DIALOG" -- 允许显示lua报错弹框 -- 新手相关 StrogeKeyDef.GUIDE_WORLD_MAP = "GUIDE_WORLD_MAP" -- 是否执行大地图新手 -- 用户基础 StrogeKeyDef.IS_NEW_PLAYER = "IS_NEW_PLAYER" -- 是否为新玩家 int 0:否 1:是 StrogeKeyDef.NUMBER_OF_ADS = "NUMBER_OF_ADS" -- 观看广告次数 int -- 场景解锁 StrogeKeyDef.USER_SCENE_UNLOCK = "USER_SCENE_UNLOCK" -- 用户场景解锁数据 -- table -- 金币 StrogeKeyDef.USER_CURRENCY_COIN = "USER_CURRENCY_COIN" -- 金币 int -- 钻石 StrogeKeyDef.USER_CURRENCY_DIAMOND = "USER_CURRENCY_DIAMOND" -- 钻石 int -- 音符 StrogeKeyDef.USER_CURRENCY_MUSICAL_NOTE = "USER_CURRENCY_MUSICAL_NOTE" -- 音符 int -- 鱼饵 StrogeKeyDef.USER_CURRENCY_FISHING_BAIT = "USER_CURRENCY_FISHING_BAIT" -- 鱼饵 int -- 星星 StrogeKeyDef.USER_CURRENCY_STAR = "USER_CURRENCY_STAR" -- 星星 int -- 广告券 StrogeKeyDef.USER_CURRENCY_AD_TICKET = "USER_CURRENCY_AD_TICKET" -- 广告券 int -- 人气 StrogeKeyDef.USER_EVALUATION_STARS = "USER_EVALUATION_STARS" -- 人气星级 int -- 菜品 StrogeKeyDef.CUISINES_STATE = "CUISINES_STATE" -- 菜品状态数据 table StrogeKeyDef.CUISINES_GRADE = "CUISINES_GRADE" -- 菜品等级数据 table StrogeKeyDef.CUISINES_SALES = "CUISINES_SALES" -- 菜品销量数据 table -- 员工 StrogeKeyDef.EMPLOYEE_STATE = "EMPLOYEE_STATE" -- 员工状态数据 table StrogeKeyDef.EMPLOYEE_GRADE = "EMPLOYEE_GRADE" -- 员工等级数据 table StrogeKeyDef.EMPLOYEE_SKIN = "EMPLOYEE_SKIN" -- 员工穿着皮肤数据 table StrogeKeyDef.EMPLOYEE_SKIN_LIST = "EMPLOYEE_SKIN_LIST" -- 员工已购皮肤数据 table StrogeKeyDef.EMPLOYEE_LAST_GRADE_TIME = "EMPLOYEE_LAST_GRADE_TIME" -- 员工上次升级时间数据 table -- 顾客 StrogeKeyDef.CUSTOMER_STATE = "CUSTOMER_STATE" -- 顾客状态数据 table StrogeKeyDef.CUSTOMER_VISITS = "CUSTOMER_VISITS" -- 顾客来访次数数据 table StrogeKeyDef.CUSTOMER_FIRST_VISIT_QUEUE = "CUSTOMER_FIRST_VISIT_QUEUE" -- 首次来访顾客队列数据 table StrogeKeyDef.CUSTOMER_NORMAL_VISITS_TODAY = "CUSTOMER_NORMAL_VISITS_TODAY" -- 普通顾客当日来访次数数据 table -- 特殊顾客 StrogeKeyDef.SPECIAL_CUSTOMER_STATE = "SPECIAL_CUSTOMER_STATE" -- 特殊顾客状态数据 table StrogeKeyDef.SPECIAL_CUSTOMER_VISITS = "SPECIAL_CUSTOMER_VISITS" -- 特殊顾客来访次数数据 table StrogeKeyDef.SPECIAL_CUSTOMER_LAST_UPDATE_TIME = "SPECIAL_CUSTOMER_LAST_UPDATE_TIME" -- 特殊顾客存档最后更新时间 int (os.time()) StrogeKeyDef.SPECIAL_CUSTOMER_VISITS_TODAY = "SPECIAL_CUSTOMER_VISITS_TODAY" -- 特殊顾客当日来访次数数据 table -- 建筑 StrogeKeyDef.BUILDING_LIST_INFO = "BUILDING_LIST_INFO" -- 建筑列表数据 table StrogeKeyDef.BUILDING_USE_INFO = "BUILDING_USE_INFO" -- 使用中的建筑数据 table StrogeKeyDef.BUILDING_TIPS_TIME = "BUILDING_TIPS_TIME" -- 建筑提示时间 int StrogeKeyDef.STALL_LIST_UNLOCK_TIME = "STALL_LIST_UNLOCK_TIME" -- 摊位列表解锁时间数据 table StrogeKeyDef.STALL_LIST_DELAY_TIME = "STALL_LIST_DELAY_TIME" -- 摊位列表等待时间数据 table -- 音乐 StrogeKeyDef.background_music_isOn = "background_music_isOn" -- 背景音乐开关 bool StrogeKeyDef.sound_effect_isOn = "sound_effect_isOn" -- 音效开关 bool -- 在线时长 StrogeKeyDef.last_online_day = "last_online_day" -- 上次在线日期 int (os.date("%Y%m%d")) StrogeKeyDef.last_online_time = "last_online_time" -- 上次在线时间 int (os.time()) StrogeKeyDef.online_days = "online_days" -- 在线天数 int StrogeKeyDef.today_online_time = "today_online_time" -- 当日在线时间 int -- 成就任务 StrogeKeyDef.completed_ach_task_ids = "completed_ach_task_ids" -- 已完成的成就任务ID列表 StrogeKeyDef.claimed_ach_task_ids = "claimed_ach_task_ids" -- 已领取的成就任务ID列表 StrogeKeyDef.ach_task_states = "ach_task_states" -- 成就任务状态表 -- 每日任务 StrogeKeyDef.daily_task_ids = "daily_task_ids" -- 当前每日任务ID列表 StrogeKeyDef.daily_task_states = "daily_task_states" -- 每日任务状态表 StrogeKeyDef.daily_task_last_refresh = "daily_task_last_refresh" -- 上次刷新每日任务的时间戳 -- 订单任务 StrogeKeyDef.task_order_create_times = "task_order_create_times" -- 任务订单创建时间表 StrogeKeyDef.task_order_is_reset = "task_order_is_reset" -- 任务订单是否已重置表 StrogeKeyDef.task_order_notification_prompts = "task_order_notification_prompts" -- 任务订单通知提示表 -- 引导任务 StrogeKeyDef.task_boot_current_task_id = "task_boot_current_task_id" -- 当前引导任务ID int StrogeKeyDef.task_boot_promotion_click_count = "task_boot_promotion_click_count" -- 引导任务推广点击次数 int --任务进度累计 StrogeKeyDef.task_progress_count = "task_progress_count" -- 任务进度计数表 -- 加成 StrogeKeyDef.bonus_obtained_id_list = "bonus_obtained_id_list" -- 建筑加成ID列表 StrogeKeyDef.coin_income_per_hour_last_time = "coin_income_per_hour_last_time" -- 上次金币每小时收入计算时间 int (os.time()) StrogeKeyDef.coin_income_per_day_last_time = "coin_income_per_day_last_time" -- 上次金币每日收入计算时间 int (os.time()) -- 钓鱼 StrogeKeyDef.fishing_vendor_state = "fishing_vendor_state" -- 捕鱼玩法摊主状态 table StrogeKeyDef.fishing_game_count_today = "fishing_game_count_today" -- 捕鱼玩法当日游戏次数 int StrogeKeyDef.fishing_revive_count_today = "fishing_revive_count_today" -- 捕鱼玩法当日复活次数 int StrogeKeyDef.fishing_get_prop_count = "fishing_get_prop_count" -- 捕鱼玩法获取道具次数 int StrogeKeyDef.fishing_prop_count = "fishing_prop_count" -- 捕鱼玩法道具数量 table StrogeKeyDef.fishing_fish_count = "fishing_fish_count" -- 捕鱼玩法鱼的数量 table StrogeKeyDef.pond_vending_last_update_time = "pond_vending_last_update_time" -- 池塘贩卖机上次更新时间 int (os.time()) StrogeKeyDef.pond_vending_current_item = "pond_vending_current_item" -- 池塘贩卖机当前贩卖物品 table StrogeKeyDef.pond_vending_next_item = "pond_vending_next_item" -- 池塘贩卖机下次贩卖物品 table -- 摊主 StrogeKeyDef.vendor_visit_state = "vendor_visit_state" -- 摊主来访状态 table StrogeKeyDef.vendor_visit_count = "vendor_visit_count" -- 摊主来访次数 table -- 玩偶 StrogeKeyDef.doll_count_dic = "doll_count_dic" -- 玩偶数量 table StrogeKeyDef.doll_state_dic = "doll_state_dic" -- 玩偶状态 table -- 扭蛋机 StrogeKeyDef.gacha_free_count = "gacha_free_count" -- 免费扭蛋次数 int StrogeKeyDef.gacha_free_today_count = "gacha_free_today_count" -- 今日免费扭蛋次数 int return StrogeKeyDefMysteryCustomer---神秘顾客 ---@class MysteryCustomer:CustomerSpecial local MysteryCustomer, super = defClass("MysteryCustomer", CustomerSpecial) local LOGTAG = "MysteryCustomer" --- 状态 MysteryCustomer.State = { Idle = 1, -- 空闲 Entrance = 2, -- 入场 Waiting = 3, -- 等待 Plot = 4, -- 剧情 Leaving = 5, -- 离开 } --- 构造函数 function MysteryCustomer:ctor(resLink) super.ctor(self, resLink, CustomerConst.CustomerSpecialType.MysteriousCustomer) self:init() end --- 初始化 function MysteryCustomer:init() self:initDisplay("mystery_customer") self:initProperties() self:startMysteryLogic() end --- 初始化属性 function MysteryCustomer:initProperties() self.info = SpecialCustomerMgr:getSpecialCustomerInfo(self.specialCustomerTypeId) self.state = MysteryCustomer.State.Idle -- 初始状态 self.activeTime = self.info:getActiveTime() -- 活动时间 self.clickTimes = self.info:getClickTimes() -- 点击次数 self.currClickTimes = 0 -- 当前点击次数 end --- 开始神秘顾客逻辑 function MysteryCustomer:startMysteryLogic() printInfo(LOGTAG, "神秘顾客开始活动,活动时间:%d秒", self.info:getActiveTime()) -- 入场 self:enter() -- 添加点击事件 self:addClickEvent(function() self:click() end) -- 初始化点击进度 self:initClickProgress() end --- 入店 function MysteryCustomer:enter() printInfo(LOGTAG, "神秘顾客入店") self.state = MysteryCustomer.State.Entrance self:walkToPos(self:getSpecialCustomerCommonStandbyPoint().transform.position, super.tempSpeed, function() self.state = MysteryCustomer.State.Waiting self:showDong() self:startTimer() end ) end --- 离开 function MysteryCustomer:exit() printInfo(LOGTAG, "神秘顾客离开") self.state = MysteryCustomer.State.Leaving self:stopTimer() self:walkToPos(self:getSpecialCustomerExitPoint().transform.position, super.tempSpeed, function() -- 离开后销毁 self:destroy() -- 触发离开事件 end ) end --- 时间更新 ---@param seconds number 秒数 function MysteryCustomer:timeupdate(seconds) if self.state == MysteryCustomer.State.Plot then return end self.time = self.time + seconds if self.time >= self.activeTime then self:exit() end end --- 剧情回调 function MysteryCustomer:plotCallback() self.state = MysteryCustomer.State.Waiting if self.ui.isClear then self:videoCallback() else self:badVideoCallback() end end --- 剧情 function MysteryCustomer:plot() printInfo(LOGTAG, "神秘顾客开始剧情") -- todo 新窗口 self.state = MysteryCustomer.State.Plot self:hideDong() self:showPlot(self.info.getPlotId, function() self.ui = AdCustomerUI.new(0):show():showMask():enableCloseWhenClickMask():addCloseCallback(function() self:plotCallback() end) end) end --- 交互 function MysteryCustomer:click() if self.state ~= MysteryCustomer.State.Waiting then return end if self.clickTimes <= 1 then self:plot() else if self.currClickTimes < self.clickTimes then self.currClickTimes = self.currClickTimes + 1 self:showClickProgress(self.currClickTimes / self.clickTimes) else self:plot() end end end --- 成功回调 function MysteryCustomer:videoCallback() CurrencyMgr:changeCoin(self.coin) self:showPlot(self.info:getSuccessDialogId(), function() self:exit() end) end --- 失败回调 function MysteryCustomer:badVideoCallback() self:showPlot(self.info:getFailDialogId(), function() self:exit() end) end return MysteryCustomer MapsConst--[[ author:zhengnan time:2025-06-03 10:52:32 ]] local MapsConst = defClassStatic("MapsConst") -- 地图类型 MapsConst.MapType = { -- 餐厅 restaurant = 1, -- 音乐烤吧 music_bar = 2, -- 钓鱼岛 fishing_island = 3, -- 小屋 house = 4, } -- 不同地图的寻路网格 MapsConst.GridGraphsName = { -- 餐厅 [MapsConst.MapType.restaurant] = "restaurant_grid_graph", -- 音乐烤吧 [MapsConst.MapType.music_bar] = "music_grid_graph", -- 钓鱼岛 [MapsConst.MapType.fishing_island] = "fishing_island_grid_graph", -- 小屋 [MapsConst.MapType.house] = "house_grid_graph", } -- 节点名字 MapsConst.mapNodeNames = { -- 餐厅 [MapsConst.MapType.restaurant] = "restaurant_map", -- 音乐烤吧 [MapsConst.MapType.music_bar] = "musicbbq_map", -- 钓鱼岛 [MapsConst.MapType.fishing_island] = "fishing_island_map", -- 小屋 [MapsConst.MapType.house] = "house_map", } -- 不同寻路区域 MapsConst.areaTagNames = { -- 餐厅 [MapsConst.MapType.restaurant] = "area_restaurant", -- Tag 1 -- 钓鱼 [MapsConst.MapType.fishing_island] = "area_fishing", -- Tag 2 -- 音乐烤吧 [MapsConst.MapType.music_bar] = "area_musicbbq", -- Tag 3 } -- 寻路区域Tag值定义 MapsConst.AreaTags = { -- 餐厅区域 restaurant = 1, -- 钓鱼岛区域 fishing_island = 2, -- 音乐烤吧区域 music_bar = 3, } -- 获取餐厅区域的Tag值 function MapsConst.getRestaurantTag() return MapsConst.AreaTags.restaurant end function MapsConst.getFishingIslandTag() return MapsConst.AreaTags.fishing_island end function MapsConst.getMusicBarTag() return MapsConst.AreaTags.music_bar end return MapsConst CustomerSolicitation --- 顾客招揽 ---@class CustomerSolicitation:LuaClass ---@field useSolicitationType number 招揽顾客类型 ---@field solicitationFlowClicks number 招揽顾客点击次数 ---@field solicitationFlowClickLimit number 招揽顾客需要的点击次数 ---@field solicitationFlowQueue number[] 招揽流顾客队列 local CustomerSolicitation = defClass("CustomerSolicitation") ---构造函数 function CustomerSolicitation:ctor() self:init() end -- 初始化 function CustomerSolicitation:init() self.useSolicitationType = nil self.solicitationFlowClicks = 0 self.solicitationFlowQueue = {} self.solicitationCount = 0 -- 用于延迟招揽批量顾客 self.timeCount = 1 self.solicitationFlowClickLimit = 8 -- todo 招揽方式添加升级功能后这个会有改动 self:initEvent() self:initUserData() self:setEvent() end -- 初始化事件 function CustomerSolicitation:initEvent() self.solicitationClickEvent = SimpleEvent.new("solicitationClickEvent") end -- 初始化用户数据 function CustomerSolicitation:initUserData() end -- 设置事件 function CustomerSolicitation:setEvent() -- 这里可以设置一些事件监听 end --- 更新 function CustomerSolicitation:timeUpdate(seconds) if self.solicitationCount <= 0 then return end -- 时间计次三次为1.5秒 if self.timeCount < 4 then self.timeCount = self.timeCount + 1 return end self.timeCount = 1 local id = CustomerMgr:judgeCustomerId() self:enqueue(id) self.solicitationCount = self.solicitationCount - 1 end --- 点击招揽顾客 function CustomerSolicitation:solicitation(type) --if GuideMgr:isInGuide() then -- self.solicitationFlowClicks = 0 -- local id = CustomerMgr:judgeCustomerId() -- self:enqueue(id) -- UserDataMgr:addPromoteCount(1) -- return self.solicitationFlowClicks --end self.solicitationFlowClicks = self.solicitationFlowClicks + 1 -- 任务进度 UserDataMgr:addPromoteCount(1) self.solicitationClickEvent:triggerEvent(1) if self.solicitationFlowClicks >= self.solicitationFlowClickLimit then self.solicitationFlowClicks = 0 local id = CustomerMgr:judgeCustomerId() self:enqueue(id) end return self.solicitationFlowClicks end --- 招揽定量顾客 ---@param number number 招揽顾客数量 function CustomerSolicitation:solicitationList(number, type) --for _ = 1, number do -- local id = CustomerMgr:judgeCustomerId() -- self:enqueue(id) --end self.solicitationCount = number UserDataMgr:addPromoteCount(number) end --- 入队列 ---@param customerId number 顾客id function CustomerSolicitation:enqueue(customerId) table.insert(self.solicitationFlowQueue, customerId) end --- 出队列 ---@return number 顾客id function CustomerSolicitation:dequeue() return table.remove(self.solicitationFlowQueue, 1) end return CustomerSolicitation FishingVisits-- 钓鱼区来访 local FishingVisits = defClass("FishingVisits") -- log local LOGTAG = "FishingVisits" -- 构造函数 function FishingVisits:ctor() self:init() end -- 初始化 function FishingVisits:init() self.fishes = {} -- 已放置鱼 self.vendors = {} -- 摊主列表 self.vendorTime = {} -- 摊主开始工作时间 end -- 时间更新 function FishingVisits:timeUpdate(time) -- 摊主判断 for boothId, vendorId in pairs(self.vendors) do local customerId = self:checkVendor(boothId, vendorId, time) if customerId ~= -1 then -- 顾客来访 self:customerVisit(boothId, vendorId, customerId) end end end -- 添加鱼 function FishingVisits:addFish(boothId, fishId) self.fishes[boothId] = fishId local interval = math.random(40, 80) -- 随机间隔时间,40-80秒 TimerMgr:add(function () local fishConfig = FishCfgParse:getFishCfgById(fishId) local chance = math.random(1, 100) <= fishConfig.solicitationChance if chance then local list = VendorCfgParse:getVendorCfgListByFishId(fishId) -- 有些鱼没有在任何摊主的喜好列表里,招揽机会100%也不可能招到摊主 if #list > 0 then local vendorId = list[math.random(1, #list)].id self:addVendor(boothId, vendorId) return end end StallDeskMgr:getStallDeskById(boothId):fishInvalid() self:removeFish(boothId) end, interval, 1) end -- 移除鱼 function FishingVisits:removeFish(boothId) local fishId = self.fishes[boothId] self.fishes[boothId] = nil printInfo(LOGTAG, string.format("鱼ID: %s 已从摊位ID: %s 移除", fishId, boothId)) end -- 添加摊主 function FishingVisits:addVendor(boothId, vendorId) self.vendors[boothId] = vendorId self.vendorTime[boothId] = CustomerMgr.time local interval = math.random(10, 20) -- 随机招揽间隔时间,10-20秒 TimerMgr:add(function () self:vendorSolicit(boothId, vendorId) end, interval, 1) end -- 移除除摊主 function FishingVisits:removeVendor(boothId) local vendorId = self.vendors[boothId] self.vendors[boothId] = nil self.vendorTime[boothId] = nil printInfo(LOGTAG, string.format("摊主ID: %s 已离开摊位ID: %s", boothId, vendorId)) end -- 检查摊主 function FishingVisits:checkVendor(boothId, vendorId, time) local config = VendorCfgParse:getVendorCfg(vendorId) if time - self.vendorTime[boothId] > config.worktime then StallDeskMgr:getStallDeskById(boothId):vendorLeave() self:removeVendor(boothId, vendorId) return -1 -- 摊主离开 end if ((self.vendorTime[boothId] - time) % config.deltatime) == 0 then return CustomerMgr:judgeCustomerId() end return -1 -- 没有顾客来访 end -- 摊主招揽 function FishingVisits:vendorSolicit(boothId, vendorId) printInfo(LOGTAG, string.format("摊主ID: %s 在摊位ID: %s 招揽顾客", vendorId, boothId)) StallDeskMgr:getStallDeskById(boothId):setVendor(vendorId) -- 摊主到来后,更新摊主的来访状态 if VendorMgr:getVendorVisitState(vendorId) < VendorConst.VendorVisitState.Visited then VendorMgr:setVendorVisitState(vendorId, VendorConst.VendorVisitState.Visited) end -- 摊主到来后移除鱼 self:removeFish(boothId) end -- 顾客来访 function FishingVisits:customerVisit(boothId, vendorId, customerId) printInfo(LOGTAG, string.format("摊位ID: %s,摊主ID: %s 有顾客来访,顾客ID: %s", boothId, vendorId, customerId)) CustomerMgr:enterTheFishing(boothId, vendorId, customerId) end return FishingVisitsfishCfg--[[ from file:鱼属性.xlsx --]] local fishCfg = { [1] = { id = 600001, name = "小丑鱼", artRes = "yu_01", grade = 1, speed = 110, energy = 1, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 0, fishIcon = "yu_01", }, [2] = { id = 600002, name = "虾", artRes = "yu_02", grade = 1, speed = 80, energy = 1, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 0, fishIcon = "yu_02", }, [3] = { id = 600003, name = "蝴蝶鱼", artRes = "yu_03", grade = 2, speed = 120, energy = 2, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 0, fishIcon = "yu_03", }, [4] = { id = 600004, name = "水母", artRes = "yu_04", grade = 2, speed = 120, energy = 2, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 0, fishIcon = "yu_04", }, [5] = { id = 600005, name = "翻车鱼", artRes = "yu_05", grade = 3, speed = 100, energy = 5, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 35, fishIcon = "yu_05", }, [6] = { id = 600006, name = "红鲷鱼", artRes = "yu_06", grade = 3, speed = 100, energy = 5, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 35, fishIcon = "yu_06", }, [7] = { id = 600007, name = "海龟", artRes = "yu_07", grade = 4, speed = 100, energy = 10, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 50, fishIcon = "yu_07", }, [8] = { id = 600008, name = "狮子鱼", artRes = "yu_08", grade = 4, speed = 100, energy = 10, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 50, fishIcon = "yu_08", }, [9] = { id = 600009, name = "河豚", artRes = "yu_09", grade = 5, speed = 100, energy = 20, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 50, fishIcon = "yu_09", }, [10] = { id = 600010, name = "鱿鱼", artRes = "yu_10", grade = 5, speed = 100, energy = 20, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 50, fishIcon = "yu_10", }, [11] = { id = 600011, name = "龙鱼", artRes = "yu_11", grade = 6, speed = 100, energy = 50, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 70, fishIcon = "yu_11", }, [12] = { id = 600012, name = "金枪鱼", artRes = "yu_12", grade = 6, speed = 100, energy = 50, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 70, fishIcon = "yu_12", }, [13] = { id = 600013, name = "灯笼鱼", artRes = "yu_13", grade = 7, speed = 100, energy = 100, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 100, fishIcon = "yu_13", }, [14] = { id = 600014, name = "鲨鱼", artRes = "yu_14", grade = 7, speed = 100, energy = 100, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 100, fishIcon = "yu_14", }, [15] = { id = 600015, name = "虎鲸", artRes = "yu_15", grade = 8, speed = 100, energy = 200, type = 1, moveSpeed = 0, dodgeChance = 0, solicitationChance = 100, fishIcon = "yu_15", }, [16] = { id = 601001, name = "水雷", artRes = "zhangai", grade = 10, speed = 0, energy = 0, type = 2, moveSpeed = -1, dodgeChance = -1, solicitationChance = 0, fishIcon = "zhangai", }, [17] = { id = 601002, name = "剑鱼", artRes = "yu_16", grade = 10, speed = 2800, energy = 0, type = 2, moveSpeed = -1, dodgeChance = -1, solicitationChance = 0, fishIcon = "yu_16", }, } return fishCfg FishingFailedUIp-- 捕鱼失败界面 local FishingFailedUI = defClass("FishingFailedUI", UILayer) local TMProUGUI = CS.TMPro.TextMeshProUGUI local Vector2 = CS.UnityEngine.Vector2 -- 构造函数 function FishingFailedUI:ctor(grade, energy) UILayer.ctor(self) self.R = ResLoader.loadResLink("modules/ui/fishingui/fishinguireslink") self.grade = grade self.energy = energy end -- 当页面加载 function FishingFailedUI:onLoad() self.ui = GameObject.Instantiate(self.R.fishing_failed_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end -- 初始化界面 function FishingFailedUI:initUI() self.close_btn = self.ui:Seek("close_btn") self.restart_btn = self.ui:Seek("restart_btn") self.challenges = self.ui:Seek("challenges") self.progress_bar = self.ui:Seek("progress_bar") self.progress_value = self.progress_bar:Seek("value")[TMProUGUI] self.spine = self.ui:Seek("spine") self.progressList = {} self.progressValues = {0.5, 0.7, 0.85, 1.0} for i = 1, 4 do self.progressList[i] = self.ui:Seek("progress_" .. i) end util.ugui.addClickEvent(self.close_btn, function() self:backStartPage() end) util.ugui.addClickEvent(self.restart_btn, function() self:gameRestart() end) self.close_btn:SetActive(false) self.restart_btn:SetActive(false) end -- 展示界面 function FishingFailedUI:showUI() local count = FishingGameMgr.userData:getFishingGameCountToday() self.challenges[TMProUGUI].text = string.format("今日已挑战%d次", count) util.spine.play(self.spine, "stand_1", false, function() if self or self.challenges then self.challenges:SetActive(true) end end) local list = FishingRewardCfgParse:getAllFishingRewardCfg() for i, v in ipairs(list) do if v.reward_1 == -1 then self.progressList[i]:Seek("reward_value")[TMProUGUI].text = "X" .. v.count_fish else self.progressList[i]:Seek("reward_value")[TMProUGUI].text = "X" .. v.count_1 end end -- 计算进度 local grade = self.grade local energy = self.energy local maxGrade = FishingConst.MaxGrade local maxEnergy = FishingGradeCfgParse:getFishingGradeCfgByGrade(grade).maxEnergy local value = grade / maxGrade + (energy / maxEnergy) / maxGrade self:setProgress(value) end function FishingFailedUI:setProgress(value) local pos = Vector2(760 * value - 380, 0) self.progress_bar:RunAction(ua.Sequence({uiua.AnchoredPosTo(1, pos), ua.cb(function() for i = 1, #self.progressValues do self.progressList[i]:Seek("reward_v"):SetActive(value >= self.progressValues[i]) end self:getReward(value) self.ui:Delay(0.1, function() self.close_btn:SetActive(true) self.restart_btn:SetActive(true) end) end)})) --self.progress_bar[RectTransform].anchoredPosition = pos self.progress_value.text = math.floor(value * 100) .. "%" end -- 重新开始游戏 function FishingFailedUI:gameRestart() FishingGameMgr:gameRestart() self:close() end function FishingFailedUI:backStartPage() FishingGameMgr:backStartPage() self:close() end -- 获取奖励 function FishingFailedUI:getReward(value) local rewardCfg = FishingRewardCfgParse:getFishingRewardCfgByProgress(value * 100) if not rewardCfg then return end -- 鱼奖励 local fishes = {} for _ = 1, rewardCfg.count_fish do local id = rewardCfg.param_fish[math.random(1, #rewardCfg.param_fish)] if fishes[id] then fishes[id] = fishes[id] + 1 else fishes[id] = 1 end end self.ui:Delay(0.2, function() FishingFishListUI.new(fishes):show():showMask():enableCloseWhenClickMask() end) -- 其他奖励 for i = 1, 2 do local type = rewardCfg["reward_" .. i] if (not type) or (type == -1) then break end if type == FishingConst.PropRewardType.Star then CurrencyMgr:changeStar(rewardCfg["count_" .. i]) end end end return FishingFailedUI vendorCfg/--[[ from file:摊主表.xlsx --]] local vendorCfg = { [1] = { id = 490001, name = "棉花糖摊", nature = "温和", desc = "现做云朵糖,一口甜进童年!", storyId = -1, visiteStar = 0, likefishish = {600001}, currencyType = 1, countMin = 300, countMax = 350, starMin = 2, starMax = 3, consumeMin = 150, consumeMax = 200, worktime = 450, deltatime = 60, artBustRes = "vendor_mianhuatang", }, [2] = { id = 490002, name = "热狗摊", nature = "暴躁", desc = "香肠面包夹满料,管饱又解馋!", storyId = -1, visiteStar = 0, likefishish = {600001,600002,600003,600004,600005,600006}, currencyType = 1, countMin = 300, countMax = 350, starMin = 2, starMax = 3, consumeMin = 150, consumeMax = 200, worktime = 400, deltatime = 55, artBustRes = "vendor_regou", }, [3] = { id = 490003, name = "爆米花摊", nature = "胆小", desc = "新鲜热乎现爆,香脆快乐管够!", storyId = -1, visiteStar = 0, likefishish = {600001,600002,600003,600004,600005,600006}, currencyType = 1, countMin = 350, countMax = 400, starMin = 2, starMax = 3, consumeMin = 150, consumeMax = 200, worktime = 350, deltatime = 50, artBustRes = "vendor_baomihua", }, [4] = { id = 490004, name = "刨冰摊", nature = "温和", desc = "现刨雪花冰,浇上甜蜜,赶走夏日燥热", storyId = -1, visiteStar = 0, likefishish = {600001,600002,600003,600004,600005,600006}, currencyType = 1, countMin = 350, countMax = 400, starMin = 3, starMax = 4, consumeMin = 180, consumeMax = 220, worktime = 550, deltatime = 65, artBustRes = "vendor_baobing", }, [5] = { id = 490005, name = "手绘肖像画", nature = "暴躁", desc = "真人速写,画得像才收钱", storyId = -1, visiteStar = 0, likefishish = {600001,600002,600003,600004,600005,600006}, currencyType = 1, countMin = 350, countMax = 400, starMin = 2, starMax = 3, consumeMin = 180, consumeMax = 220, worktime = 600, deltatime = 70, artBustRes = "vendor_xiaoxianghua", }, [6] = { id = 490006, name = "章鱼烧摊", nature = "温和", desc = "热乎章鱼小丸子,外酥里嫩,要趁热吃才好吃", storyId = -1, visiteStar = 0, likefishish = {600001,600002,600003,600004,600005,600006}, currencyType = 1, countMin = 350, countMax = 400, starMin = 2, starMax = 2, consumeMin = 180, consumeMax = 220, worktime = 650, deltatime = 70, artBustRes = "vendor_zhangyushao", }, [7] = { id = 490007, name = "饮品摊", nature = "暴躁", desc = "多种口味随你挑,解腻解渴刚刚好", storyId = -1, visiteStar = 0, likefishish = {600001,600002,600003,600004,600005,600006}, currencyType = 1, countMin = 400, countMax = 450, starMin = 2, starMax = 3, consumeMin = 190, consumeMax = 230, worktime = 700, deltatime = 65, artBustRes = "vendor_yinpin", }, [8] = { id = 490008, name = "鲜花摊", nature = "暴躁", desc = "五彩缤纷,装点你的家和心情", storyId = -1, visiteStar = 0, likefishish = {600001,600002,600003,600004,600005,600006}, currencyType = 1, countMin = 400, countMax = 450, starMin = 2, starMax = 3, consumeMin = 190, consumeMax = 230, worktime = 750, deltatime = 60, artBustRes = "vendor_huatan", }, [9] = { id = 490009, name = "冰淇淋摊", nature = "暴躁", desc = "冰爽可口,口味丰富", storyId = -1, visiteStar = 500, likefishish = {600001,600002,600003,600004,600005,600006}, currencyType = 1, countMin = 400, countMax = 450, starMin = 3, starMax = 4, consumeMin = 190, consumeMax = 230, worktime = 800, deltatime = 70, artBustRes = "vendor_bingjiling", }, } return vendorCfg BuildingThemesUIK&--[[ 建筑主题ui界面 author:{zhangpeng} time:2025-05-23 12:17:25 ]] local BuildingThemesUI, super = defClass("BuildingThemesUI", UILayer) require("modules/ui/buildinglist/themeui/ThemeCell") local TMPUGUI = CS.TMPro.TextMeshProUGUI local Button = CS.UnityEngine.UI.Button local DEFAULT_CELL_HEIGHT = 302 local LOGTAG = "BuildingThemesUI" function BuildingThemesUI:ctor(mapId, themeId) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/buildinglist/themeui/buildingthemesreslink") self.mapId = mapId or 1 -- 地图ID self.themeId = themeId self.selectAllFlag = false self.allPrice = 0 self.selectList = {} -- 已选列表 self.selectListCount = 0 -- 已选数量 self.purchasedCount = 0 -- 已购买数量 self.selectableCount = 0 -- 可选数量 self.notPurchase = 0 -- 不可购买数量 self.useCount = 0 end function BuildingThemesUI:onLoad() self.ui = GameObject.Instantiate(self.R.theme_list_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:timer(function() local scrollView = self.ui:Seek("ScrollView"):GetComponent("UnityEngine.UI.ScrollRect") scrollView.verticalNormalizedPosition = 1 end, 0.2) end -- init data function BuildingThemesUI:initData() self.tableViewData = self:getThemeBuildingsData() end function BuildingThemesUI:initUI() printInfo(LOGTAG, "initUI") -- price self.priceText = self.ui:Seek("all_price") self.priceText[TMPUGUI].text = self.allPrice -- title name self.titleName = self.ui:Seek("title_name") self.titleName[TMPUGUI].text = ThemeUIConst.themeNameList[self.themeId] self:initScrollView() self.btn_select_all = self.ui:Seek("select_all") self.select_all_key = self.btn_select_all:Seek("select_all_key") self.over_budget = self.ui:Seek("over_budget") util.ugui.addButtonClickEvent(self.btn_select_all, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self.selectAllFlag = not self.selectAllFlag self.select_all_key:SetActive(self.selectAllFlag) self:select(self.selectAllFlag) end) --util.ugui.addToggleValueChangeEvent(self.btn_select_all, function(isOn) -- AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) -- self:select(isOn) --end) self.btn_buy = self.ui:Seek("btn_buy") util.ugui.addButtonClickEvent(self.btn_buy, function() self:buyAll() end) self.btn_replace = self.ui:Seek("btn_replace") util.ugui.addButtonClickEvent(self.btn_replace, function() self:replaceAll() end) self:checkUseButton() self.close_btn = self.ui:Seek("close_btn") util.ugui.addClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) end -- 全选,刷新价格 function BuildingThemesUI:selectAll() for i,v in ipairs(self.themeCells) do v:setSelect(self.selectAllFlag) end self:updateTotalPrice() end -- 计算所有选中的建筑价格 function BuildingThemesUI:getTotalPriceNum() local totalPrice = 0 for i,v in ipairs(self.themeCells) do if v:getIsSelect() then totalPrice = totalPrice + BuyCommonCfgParse:getPrice(v.data.buyConditionId) end end return totalPrice end -- 更新选中的建筑价格 function BuildingThemesUI:updateTotalPrice() self.allPrice = self:getTotalPriceNum() self.priceText[TMPUGUI].text = self.allPrice end function BuildingThemesUI:initScrollView() self:initData() -- 获取content_buildings容器 local contentContainer = self.ui:Seek("content_buildings") if not contentContainer then printError(LOGTAG, "content_buildings not found") return end -- 清除现有内容 for i = contentContainer.transform.childCount - 1, 0, -1 do local child = contentContainer.transform:GetChild(i) GameObject.Destroy(child.gameObject) end -- 每行显示3个cell local CELLS_PER_ROW = 3 local cellCount = #self.tableViewData local rowCount = math.ceil(cellCount / CELLS_PER_ROW) -- 存储所有创建的cell self.themeCells = {} -- 计算cell宽度(假设容器宽度是固定的) local containerWidth = contentContainer:GetComponent("RectTransform").rect.width local cellWidth = containerWidth / CELLS_PER_ROW local cellHeight = 180 -- 创建所有cell for i = 1, cellCount do local cell = GameObject.Instantiate(self.R.building_theme_cell, contentContainer.transform) -- 计算行和列索引 local rowIndex = math.ceil(i / CELLS_PER_ROW) - 1 -- 从0开始 local colIndex = (i - 1) % CELLS_PER_ROW -- 从0开始 -- 设置cell名称 cell:SetName("theme_cell_" .. i) -- 设置cell位置 local rectTransform = cell:GetComponent("RectTransform") local xPos = colIndex * cellWidth local yPos = -rowIndex * cellHeight -- 设置锚点为左上角 rectTransform.anchorMin = Vector2(0, 1) rectTransform.anchorMax = Vector2(0, 1) rectTransform.pivot = Vector2(0, 1) -- 设置位置 rectTransform.anchoredPosition = Vector2(xPos, yPos) -- 设置大小 rectTransform.sizeDelta = Vector2(cellWidth, cellHeight) -- 初始化cell数据 local themeCell = ThemeCell.new(cell, self.tableViewData[i]) themeCell:setSelectCallback(function(selfCell, price, isSelect, isSelectAll) self:buildingSelectCallback(selfCell, price, isSelect, isSelectAll) end) -- 保存cell引用 table.insert(self.themeCells, themeCell) if themeCell:canSelect() then self.selectableCount = self.selectableCount + 1 end if themeCell:getIsPurchase() then self.purchasedCount = self.purchasedCount + 1 end if themeCell:isInUse() then self.useCount = self.useCount + 1 end if not themeCell:isUnlocked() then self.notPurchase = self.notPurchase + 1 end end -- 设置内容区域高度 local contentRectTransform = contentContainer:GetComponent("RectTransform") local totalHeight = rowCount * cellHeight contentRectTransform.sizeDelta = Vector2(contentRectTransform.sizeDelta.x, totalHeight) -- 重新计算布局 local layoutGroup = contentContainer:GetComponent("LayoutGroup") if layoutGroup then layoutGroup:SetLayoutHorizontal() layoutGroup:SetLayoutVertical() end end function BuildingThemesUI:select(isSelect) if isSelect then if self.notPurchase > 0 then PopUpUI.new("有设施未达到购买条件,不能购买") return end self.allPrice = 0 for i, v in ipairs(self.themeCells) do v:setSelect(true, true) end else for i, v in ipairs(self.themeCells) do v:setSelect(false, true) end end self:updateCoin() self:checkBuyButton() self:checkSelectAllButton() end -- 选择回调 function BuildingThemesUI:buildingSelectCallback(cell, price, isSelect, isSelectAll) if isSelect then self.allPrice = self.allPrice + price self.selectList[cell:getId()] = cell self.selectListCount = self.selectListCount + 1 else self.selectList[cell:getId()] = nil self.allPrice = self.allPrice - price self.selectListCount = self.selectListCount - 1 end if not isSelectAll then self:updateCoin() self:checkBuyButton() self:checkSelectAllButton() end end -- 更新金币显示 function BuildingThemesUI:updateCoin() self.priceText[TMPUGUI].text = self.allPrice end -- 检查购买按钮状态 function BuildingThemesUI:checkBuyButton() local temp = CurrencyMgr:getCoin() - self.allPrice self.btn_buy[Button].interactable = (temp >= 0) self.over_budget:SetActive(temp < 0) if temp < 0 then self.over_budget[TMPUGUI].text = string.format("金币不够,还差%d", -temp) end end -- 检查是否可以使用按钮 function BuildingThemesUI:checkUseButton() self.purchasedCount = self.purchasedCount + self.selectListCount self.selectableCount = self.selectableCount - self.selectListCount self.selectList = {} self.selectListCount = 0 self.btn_replace:SetActive((self.purchasedCount == #self.themeCells) and (self.useCount ~= #self.themeCells)) self.priceText[TMPUGUI].text = self.allPrice self.btn_buy:SetActive(self.selectableCount > 0) end -- 检查全选按钮状态 function BuildingThemesUI:checkSelectAllButton() self.selectAllFlag = self.selectListCount == self.selectableCount self.select_all_key:SetActive(self.selectAllFlag) end -- 获取同一类主题的建筑 function BuildingThemesUI:getThemeBuildingsData() return BuildingCfgParse:getThemeBuildings(self.mapId, self.themeId) end function BuildingThemesUI:buyAll() for i, v in pairs(self.selectList) do BuildingMgr:buyBuilding(v:getId()) BuildingMgr:useBuilding(v:getId()) v:buy() end self:checkUseButton() UILayerUtil:closeUIByName("BuildingThemesUI") UILayerUtil:closeUIByName("BuildingsListUI") end function BuildingThemesUI:replaceAll() for k,v in pairs(self.themeCells) do BuildingMgr:replaceBuilding(v:getId()) BuildingMgr:useBuilding(v:getId()) end UILayerUtil:closeUIByName("BuildingThemesUI") UILayerUtil:closeUIByName("BuildingsListUI") end return BuildingThemesUI Util --全局变量定义 UnityEngine = CS.UnityEngine GameObject = UnityEngine.GameObject Resources = UnityEngine.Resources json = raw_require("rapidjson") IsNull = _G._IsNull IsType = _G._IsType YieldK = _G._YieldK TickGC = _G._TickGC xlua = _G.xlua cast = xlua.cast typeof = xlua.typeof Yield = function(x, co, traceback) if co == nil then local ismain = nil co, ismain = coroutine.running() if ismain or not coroutine.isyieldable() then error("[Api.Yield]is not yieldable" .. debug.traceback()) end end if type(x) == 'thread' and coroutine.status(x) ~= 'dead' then repeat Yield(nil, co) until coroutine.status(x) == 'dead' else local tb = debug.traceback() YieldK(x, function() local result,errMsg = coroutine.resume(co) if not result then local arr = {"Yield error:",errMsg,"\n",debug.traceback(co),"\n",tb,"\n",traceback,} error(table.concat(arr)) end end) coroutine.yield() end end local type = type local rawget = rawget local rawset = rawset local getmetatable = getmetatable local setmetatable = setmetatable function IsInstanceOf(inst, class) local mti = inst.class local mt for i = 1,20 do mt = getmetatable(mti) if not mt then break else mti = mt.__index end if mti == class then return true end end return false end function YieldCall(fn, a, b, c) assert(coroutine.resume(coroutine.create(function() Yield(nil) if fn then fn(a, b, c) end end))) return function() fn = nil end end function FastCopy(v, append) if type(v) == "table" then local t = append or {} for k,v in pairs(v) do t[k] = v end return t else return v end end function DeepCopy(v, hash) hash = hash or {} if type(v) == "table" then local t = {} hash[v] = t for kk,vv in pairs(v) do t[kk] = hash[vv] or DeepCopy(vv, hash) end return t else return v end end -- Util.lua 的加载在 Def 之前,可以声明全局变量 function HackFunc(cls, funcname) local f = rawget(cls, funcname .. "_origin_") if not f then f = cls[funcname] rawset(cls, funcname .. "_origin_", f) end return f end function HackCSharpClass(cls) local meta = xlua.metatable_operation(typeof(cls)) local origin_index = meta.__origin_index or meta.__index local origin_newindex = meta.__origin_newindex or meta.__newindex meta.__index = function(ud, k) local v = rawget(meta, k) if v then return v end v = origin_index(ud, k) if v then return v end end meta.__origin_index = origin_index meta.__origin_newindex = origin_newindex return meta,origin_index,origin_newindex end function HackObjectSetter(object, key, metafunction) local meta = xlua.metatable_operation(typeof(object)) local _, setters = debug.getupvalue(meta.__newindex, 1) if setters then local origin = setters[key] setters[key] = metafunction(origin) else print("setter is nil: ", typeof(object)) end end function cs_ipairs(cs_array) local enumerator = cs_array:GetEnumerator() local i = 0 return function() if enumerator:MoveNext() then i = i + 1 return i,enumerator.Current end end end function cs_pairs(cs_dict) local enumerator = cs_dict:GetEnumerator() return function() if enumerator:MoveNext() then local Current = enumerator.Current return Current.Key,Current.Value end end end AnimatorUtil --[[ author:{author} time:2022-05-17 17:58:33 ]] local AnimatorUtil = {} local Animator = CS.UnityEngine.Animator local WrapMode = CS.UnityEngine.WrapMode function AnimatorUtil._play(animator,clip,animName,loop,cb) animator:SetEndCb(clip,function() if not loop then animator.enabled = false end if cb then cb() end end) -- animator:StopPlayback() -- animator:Play(animName) animator:SetTrigger(animName) animator.enabled = true end function AnimatorUtil.play(go,animName,loop,cb) local logtag = "AnimatorUtil.play" local animator = go:GetComponent(typeof(Animator)) if animator then local clip = animator:GetClip(animName) if clip then AnimatorUtil._play(animator,clip,animName,loop,cb) else printInfo(logtag,"anim clip not found:%s",animName) end end end function AnimatorUtil.playSeq(go,...) local logtag = "AnimatorUtil.playSeq" local animator = go:GetComponent(typeof(Animator)) if animator then local args = {...} local tasks = {} local function doTask() local task = table.remove(tasks,1) if task then printInfo(logtag,task.animName) local clip = animator:GetClip(task.animName) if clip then AnimatorUtil._play(animator,clip,task.animName,task.loop,function() if task.cb then task.cb() task.cb = nil end if #tasks > 0 then doTask() end end) else printInfo(logtag,"anim clip not found:%s",task.animName) end end end local function parseNext() local arg1 = table.remove(args,1) if not arg1 then return end local t1 = type(arg1) local task = {} if t1 == "string" then task.animName = arg1 local arg2 = args[1] local t2 = type(arg2) if t2 == "boolean" then table.remove(args,1) task.loop = arg2 local arg3 = args[1] local t3 = type(arg3) if t3 == "function" then table.remove(args,1) task.cb = arg3 end elseif t2 == "function" then local f = table.remove(args,1) task.cb = f end table.insert(tasks,task) else return end parseNext() end parseNext() doTask() end end function AnimatorUtil.getCurrentClip(go) local animator = go[Animator] if animator then local clip = animator:GetCurrentAnimatorClipInfo(0); if clip.Length > 0 then return clip[0].clip end end end function AnimatorUtil.isPlaying(go,animName) local animator = go[Animator] if animator then local state = animator:GetCurrentAnimatorStateInfo(0); if state then return state:IsName(animName) end end end return AnimatorUtil CosLuaMgr<. ---@class CosLuaMgr:LuaStaticClass local CosLuaMgr = defClassStatic("CosLuaMgr") local LOGTAG = "CosLuaMgr" function CosLuaMgr:init() end ---初始化 ---@param appid string ---@param deviceid string ---@param buildEnv string ---@param oversea boolean ---@param getTimeFunc fun():integer ---@param serviceTypes CosLuaServiceType[] 指定用到的serviceTypes, 只在 tryUpateCredential 方法中用到 function CosLuaMgr:active(appid, buildEnv, oversea, deviceid, getTimeFunc, serviceTypes) self.taskList = {} self.tagTaskList = {} self.appid = appid self.deviceid = deviceid self.getTimeFunc = getTimeFunc self.serviceTypes = serviceTypes CosLuaTemporaryCredential:init(appid, buildEnv, oversea, deviceid, getTimeFunc, function (result, data) self:onCredentialFetched(result, data) end) end ---尝试更新密钥。可以不使用,在上传时会自动获取密钥。 function CosLuaMgr:tryUpateCredential() for _, value in ipairs(self.serviceTypes) do if value == CosLuaServiceType.AVATAR then goto continue end if not CosLuaTemporaryCredential:hasValidCredential(value) then CosLuaTemporaryCredential:fetchCredential(value) end ::continue:: end end ---设置用户信息,需要在app登录完成的时候设置,cos模块内不会保存用户信息,完全依赖app的设置。 function CosLuaMgr:setUserInfo(uid, utoken, showUid) printInfo(LOGTAG, "setUserInfo uid:%s, utoken:%s, showUid:%s", uid, utoken, showUid) self.uid = uid self.utoken = utoken self.showUid = showUid CosLuaTemporaryCredential:setUserInfo(uid, utoken, showUid) ---@type CosLuaUploadTask[] self.taskList = {} ---@type CosLuaTagTask[] self.tagTaskList = {} end ---清理用户信息,当用户登录的时候调用 function CosLuaMgr:clearUserInfo() printInfo(LOGTAG, "clearUserInfo") self.uid = nil self.utoken = nil self.showUid = nil self:cancelAllUploadFiles() end ---上传文件,可以多次调用, cossdk 支持批量上传 ---@param srcPath string 文件路径 ---@param dstPath string 自定义url的文件名,不能为 nil ---@param serviceType CosLuaServiceType 服务类型 ---@param progressCallback fun(progress:number, srcPath:string, uploadTask:CosLuaUploadTask) ---@param completeCallback fun(result:boolean, data:{errorCode:integer, errorMsg:string, srcPath:string}, uploadTask:CosLuaUploadTask) ---@param prepareUrlCallback fun(originFilePath:string, prepareFileUrl:string, prepareFileRelativeUrl:string) ---@return CosLuaUploadTask function CosLuaMgr:uploadFile(srcPath, dstPath, serviceType, progressCallback, completeCallback, prepareUrlCallback) local cosUploadTask = CosLuaUploadTask.new(srcPath, dstPath, serviceType, progressCallback, function (result, data, uploadTask) self:onOneTaskComplete(result, data, uploadTask) if completeCallback then completeCallback(result, data, uploadTask) end end, prepareUrlCallback) table.insert(self.taskList, cosUploadTask) self:_tryStartTask(true) return cosUploadTask end ---批量上传 ---@param files {srcPath:string, dstPath:string}[] ---@param serviceType CosLuaServiceType ---@param progressCallback fun(progress:number, batchUploadTask:CosLuaBatchUploadTask) ---@param oneCompleteCallback fun(result:boolean, data:{errorCode:integer, errorMsg:string, srcPath:string}, batchUploadTask:CosLuaBatchUploadTask, uploadTask:CosLuaUploadTask) ---@param completeCallback fun(result:boolean, data:{sucCount:integer, failCount:integer}, batchUploadTask:CosLuaBatchUploadTask) ---@param prepareUrlCallback fun(originFilePath:string, prepareFileUrl:string, prepareFileRelativeUrl:string) ---@return CosLuaBatchUploadTask function CosLuaMgr:batchUploadFile(files, serviceType, progressCallback, oneCompleteCallback, completeCallback, prepareUrlCallback) local batchUploadTask = CosLuaBatchUploadTask.new(files, serviceType, progressCallback, function (result, data, batchUploadTask, uploadTask) self:onOneTaskComplete(result, data, uploadTask) if oneCompleteCallback then oneCompleteCallback(result, data, batchUploadTask, uploadTask) end end, completeCallback, prepareUrlCallback) table.insertTo(self.taskList, batchUploadTask.tasks) self:_tryStartTask(true) return batchUploadTask end ---@private ---@param isFetchCredential boolean 是否去获取 credential function CosLuaMgr:_tryStartTask(isFetchCredential) printInfo(LOGTAG, "_tryStartTask") ---@type table local serviceTypes = {} for _, value in ipairs(self.taskList) do if value:canStart() then serviceTypes[value.serviceType] = serviceTypes[value.serviceType] or {} table.insert(serviceTypes[value.serviceType], value) end end for _, value in ipairs(self.tagTaskList) do if value:canStart() then serviceTypes[value.serviceType] = serviceTypes[value.serviceType] or {} table.insert(serviceTypes[value.serviceType], value) end end for serviceType, serviceTypeTasks in pairs(serviceTypes) do if not CosLuaTemporaryCredential:hasValidCredential(serviceType) then if isFetchCredential then CosLuaTemporaryCredential:fetchCredential(serviceType) end else for _, task in ipairs(serviceTypeTasks) do task:start() end end end end ---@private ---@param result boolean ---@param data {errorCode:integer, errorMsg:string, service_type:CosLuaServiceType} function CosLuaMgr:onCredentialFetched(result, data) if result then self:_tryStartTask(false) else for _, task in ipairs(self.taskList or {}) do task:failWithErrorCredential(data.service_type) end for _, task in ipairs(self.tagTaskList or {}) do task:failWithErrorCredential(data.service_type) end end end ---@private ---@param result boolean ---@param data {errorCode:integer, errorMsg:string, srcPath:string} ---@param uploadTask CosLuaUploadTask|CosLuaTagTask function CosLuaMgr:onOneTaskComplete(result, data, uploadTask) self:_updateTaskList() self:_tryStartTask(true) end ---@param cosUploadTask CosLuaUploadTask function CosLuaMgr:cancelUploadFile(cosUploadTask) cosUploadTask:cancel() self:_updateTaskList() end function CosLuaMgr:cancelAllUploadFiles() for _, task in ipairs(self.taskList or {}) do task:cancel() end self.taskList = {} end ---@private function CosLuaMgr:_updateTaskList() local leftList = {} for _, task in ipairs(self.taskList) do if not task:isFinished() then table.insert(leftList, task) end end self.taskList = leftList local tagLeftList = {} for _, task in ipairs(self.tagTaskList) do if not task:isFinished() then table.insert(tagLeftList, task) end end self.tagTaskList = tagLeftList end ---希望 srcPath 是已经经过压缩的图片 ---@param srcPath string 文件路径 ---@param completeCallback fun(result:boolean, data:{errorCode:integer, errorMsg:string, srcPath:string}) ---@param prepareUrlCallback fun(originFilePath:string, prepareFileUrl:string, prepareFileRelativeUrl:string) function CosLuaMgr:uploadAvatar(srcPath, completeCallback, prepareUrlCallback) if not CS.LuaHelper.IsFileExists(srcPath) then completeCallback(false, {-5, string.format("srcPath:%s not exists", srcPath)}) return end local md5 = CS.Md5Util.GetHashCodeOfFile(srcPath) local suffix = CS.System.IO.Path.GetExtension(srcPath) local dstPath = md5..suffix self:uploadFile(srcPath, dstPath, CosLuaServiceType.AVATAR, function() end, completeCallback, prepareUrlCallback) end ---@param quality integer 1-100 ---@param completeCallback fun(result:boolean, data:{errorCode:integer, errorMsg:string, srcPath:string}) ---@param prepareUrlCallback fun(originFilePath:string, prepareFileUrl:string, prepareFileRelativeUrl:string) ---@param width? integer 指定target的宽度 ---@param height? integer 指定 target 的高度 function CosLuaMgr:uploadAvatarWithTexture2D(texture, savePath, quality, completeCallback, prepareUrlCallback, width, height) local smallTexture = CS.iHuman.UnitySz.Framework.COS.CosImageUtil.ResizeTexture(texture, width or texture.width, height or texture.height) local useTexture = smallTexture and smallTexture or texture local saveResult = CS.LuaHelper.SaveImage(savePath, useTexture, quality or 50) if smallTexture then Texture2D.DestroyImmediate(smallTexture) end if not saveResult then if completeCallback then completeCallback(false, {errorCode = -4, errorMsg = string.format("savePath:%s has not right suffix", savePath)}) end return end self:uploadAvatar(savePath, completeCallback, prepareUrlCallback) end --#region tag 操作 ---设置tag ---@param cosUrl string ---@param serviceType CosLuaServiceType ---@param tags table ---@param callback fun(result:boolean, data:{errorCode:integer, errorMsg:string, tags:table}) function CosLuaMgr:setTagsForCosUrl(cosUrl, serviceType, tags, callback) local cosTagTask = CosLuaTagTask.new(cosUrl, CosLuaTagTask.OPT.SET, tags, serviceType, function (result, data, tagTask) self:onOneTaskComplete(result, data, tagTask) if callback then callback(result, data) end end) table.insert(self.tagTaskList, cosTagTask) self:_tryStartTask(true) end ---删除tag ---@param cosUrl string ---@param serviceType CosLuaServiceType ---@param callback fun(result:boolean, data:{errorCode:integer, errorMsg:string, tags:table}) function CosLuaMgr:deleteTagsForCosUrl(cosUrl, serviceType, callback) local cosTagTask = CosLuaTagTask.new(cosUrl, CosLuaTagTask.OPT.DELETE, nil, serviceType, function (result, data, tagTask) self:onOneTaskComplete(result, data, tagTask) if callback then callback(result, data) end end) table.insert(self.tagTaskList, cosTagTask) self:_tryStartTask(true) end ---查询tag ---@param cosUrl string ---@param serviceType CosLuaServiceType ---@param callback fun(result:boolean, data:{errorCode:integer, errorMsg:string, tags:table}) function CosLuaMgr:getTagsForCosUrl(cosUrl, serviceType, callback) local cosTagTask = CosLuaTagTask.new(cosUrl, CosLuaTagTask.OPT.GET, nil, serviceType, function (result, data, tagTask) self:onOneTaskComplete(result, data, tagTask) if callback then callback(result, data) end end) table.insert(self.tagTaskList, cosTagTask) self:_tryStartTask(true) end --#endregion ---查询 function CosLuaMgr:putLog(isPre, dev) local region = "na-siliconvalley" local appId = "1304062922" local secretId = "AKIDnRr1vZy70f55qKYWFivsZ0Ti1A3gCTUV" local secretKey = "0lGWXJxRgfVDhy0mIsKWTMBsuiGZdR1M" local cosXmlServer = CS.CosSDKAPI.GetCosXml(region, secretId, secretKey, true) local userId = "log"-- User:getUserId() local bucket = string.format("%s-%s", "package", appId) local filePath = isPre and CS.LogHelper.prevLuaLogFile or CS.LogHelper.luaLogFile local destPath = string.format("linkmatch/lua/dev/%s/%s.txt", dev, userId) CS.LogHelper.CloseLuaLogFile() CS.CosSDKAPI.TransferUploadFile(cosXmlServer, bucket, filePath, destPath, function() end, function(result, a, b, c) CS.LogHelper.OpenLuaLogFile() local baseUrl = "https://resource.bjfytech.com/" local toPath = string.format("%s%s", baseUrl,destPath) printInfo(LOGTAG, "日志完整cos地址: %s", toPath) end) end --#endregion return CosLuaMgr CustomerCell--- 顾客展示格子 ---@class CustomerCell : LuaClass local CustomerCell = defClass("CustomerCell") -- UIComponent local UIImage = CS.UnityEngine.UI.Image local TMProUGUI = CS.TMPro.TextMeshProUGUI --- 构造函数 function CustomerCell:ctor(go, customerId) self.ui = go self.customerId = customerId self:initUI() self:showUI() end --- 重置 function CustomerCell:reload(entityId) self.customerId = entityId self:showUI() self.ui:SetActive(true) end --- 初始化UI function CustomerCell:initUI() -- 初始化UI元素 self.customer_locked = self.ui:Seek("customer_locked") self.customer_unlocked = self.ui:Seek("customer_unlocked") self.tag_dong = self.ui:Seek("tag_dong") self.customer_icon = self.customer_unlocked:Seek("customer_icon") self.customer_name = self.customer_unlocked:Seek("customer_name") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.ui, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:click() end) end --- 展示UI function CustomerCell:showUI() local info = CustomerMgr:getCustomerInfo(self.customerId) local isUnlocked = info:isVisited() local isNewCustomer = info:isNewCustomer() self.customer_locked:SetActive(not isUnlocked) self.customer_unlocked:SetActive(isUnlocked) self.tag_dong:SetActive(isNewCustomer) if isUnlocked then self.customer_icon[UIImage].sprite = info:getBustIcon() self.customer_icon[UIImage]:SetNativeSize() self.customer_name[TMProUGUI].text = info:getName() end end --- 隐藏 function CustomerCell:hide() self.ui:SetActive(false) end --- 点击 function CustomerCell:click() local view = CustomerDetailUI.new(self.customerId, false):show():showMask():enableCloseWhenClickMask(function() end) end return CustomerCellTimeUtil%$--[[ author:{author} time:2022-05-17 17:34:08 ]] local TimeUtil = {} local TimeSpan = CS.System.TimeSpan local DateTime = CS.System.DateTime local Convert = CS.System.Convert local ts_start = DateTime(1970, 1, 1, 0, 0, 0, 0) local LOG_TAG = "TimeUtil" function TimeUtil.getTimeStamp() local ts = DateTime.UtcNow - ts_start return math.floor(ts.TotalMilliseconds) end function TimeUtil.getTimeStampInSeconds() local ts = DateTime.UtcNow - ts_start return math.floor(ts.TotalSeconds) end function TimeUtil.getHourMinSec(ostime) local formattedTime = os.date("%H:%M:%S", ostime) return formattedTime end --获取某时刻上一个自然周的date列表 (周一到周日) 参数传空为当先时间 function TimeUtil.getLastWeekDateList(timestamp) local date = os.date("*t", timestamp) printInfo("[getLastWeekDateList] %s %s %s %s",date.year, date.month, date.day, date.wday) --将周日为1 转化为周一为1 local week = (date.wday - 2) % 7 + 1 local dateTable = {} dateTable.year = date.year dateTable.month = date.month dateTable.day = date.day dateTable.hour = 0 dateTable.min = 0 dateTable.sec = 0 local timeSecond = os.time(dateTable) local list = {} for i = 1, 7 do local second = timeSecond - (week + 6 - i + 1) * 60 * 60 * 24 local table = os.date("*t", second) list[#list + 1] = table end return list end function TimeUtil.getCurWeekDateList(timestamp) local list = TimeUtil.getLastWeekDateList(timestamp) local dt = 7 * 24 * 60 * 60 for i, v in ipairs(list) do local second = os.time(v) second = second + dt list[i] = os.date("*t", second) end return list end -- 将一个时间数转换成"00:00:00"格式(天:小时:分:秒) function TimeUtil.getTimeString1(timeInt) if tonumber(timeInt) <= 0 then return "00:00:00:00" else local days = math.floor(timeInt / (24 * 60 * 60)) local hours = math.floor((timeInt % (24 * 60 * 60)) / (60 * 60)) local minutes = math.floor((timeInt % (60 * 60)) / 60) local seconds = timeInt % 60 return string.format("%02d:%02d:%02d:%02d", days, hours, minutes, seconds) end end -- 将一个时间数转换成"00:00"格式 function TimeUtil.getTimeString(timeInt) if (tonumber(timeInt) <= 0) then return "00:00" else return string.format("%02d:%02d", math.floor((timeInt/60)%60), timeInt%60) end end -- 将一个时间数转换成"00"分格式 function TimeUtil.getTimeMinuteString(timeInt) if (tonumber(timeInt) <= 0) then return "00" else return string.format("%02d", math.floor((timeInt/60)%60)) end end -- 将一个时间数转换成"00“秒格式 function TimeUtil.getTimeSecondString(timeInt) if (tonumber(timeInt) <= 0) then return "00" else return string.format("%02d", timeInt%60) end end -- 将一个时间戳转换 function TimeUtil.getTimeStampString(time,splitStr,haveSec) if not time then return "" end time = tonumber(time) if time<0 then return "" end if not splitStr then splitStr="_" end local date = os.date("*t",time) local year = date.year local month = date.month if tonumber(month)<10 then month = "0"..month end local day = date.day if tonumber(day)<10 then day = "0"..day end local hour = date.hour if tonumber(hour)<10 then hour = "0"..hour end local min = date.min if tonumber(min)<10 then min = "0"..min end if haveSec==true then local sec = date.sec if tonumber(sec)<10 then sec = "0"..sec end return date.year..splitStr..month..splitStr..day.." "..hour..":"..min.." "..sec end return date.year..splitStr..month..splitStr..day.." "..hour..":"..min end function TimeUtil.getTimeSimpleString(time,splitStr,ishaveYear, isnohaveTime) if not time then return "" end time = tonumber(time) if time<0 then return "" end if not splitStr then splitStr="_" end local date = os.date("*t",time) local year = date.year local month = date.month if tonumber(month)<10 then month = "0"..month end local day = date.day if tonumber(day)<10 then day = "0"..day end local hour = date.hour if tonumber(hour)<10 then hour = "0"..hour end local min = date.min if tonumber(min)<10 then min = "0"..min end -- if ishaveYear then if isnohaveTime then return year..splitStr..month..splitStr..day else return year..splitStr..month..splitStr..day.." "..hour..":"..min end else return month..splitStr..day.." "..hour..":"..min end end ----------------------------------服务器时间-------------------------------------- function TimeUtil.transServerTime(_timeInt) -- 将毫秒转换为秒 local totalSeconds = math.floor(_timeInt / 1000) if tonumber(totalSeconds) <= 0 then return "00:00:00:00:00:00" else local data = {} data.year = TimeUtil.getYear(totalSeconds) data.month = TimeUtil.getMonth(totalSeconds) data.day = TimeUtil.getDay(totalSeconds) data.hours = TimeUtil.getHour(totalSeconds) data.minutes = TimeUtil.getMinutes(totalSeconds) data.sec = TimeUtil.getSeconds(totalSeconds) return string.format("%02d:%02d:%02d:%02d:%02d:%02d", data.year, data.month, data.day, data.hours, data.minutes, data.sec) end end -- 服务器毫秒时间转换为秒 function TimeUtil.transServerToSec(_time) return math.floor(_time / 1000) end -- 获取服务器时间 -- useage: --[[ util.TimeUtil.getServerTime(function (time) local server_time = time printInfo(LOG_TAG, "server time:%s", server_time) end) ]] function TimeUtil.getServerTime(cb) UserCmdMgr:requestCurTime(function (ret, time) local time_1 = util.TimeUtil.transServerTime(time) printInfo(LOG_TAG, "服务器时间: %s 当前时间:%s", time, time_1) if cb then cb(util.TimeUtil.transServerToSec(time) or os.time()) end end) end -- Author: KevinYu -- Date: 2015-11-12 11:44:29 -- 扩展功能 --[[ os.date("*t", time) 返回的table time = { "day" = 12 日 "hour" = 15 时 "isdst" = false 是否夏令时 "min" = 7 分 "month" = 11 月 "sec" = 12 秒 "wday" = 5 星期几(星期天为1) "yday" = 316 一年中的第几天 "year" = 2015 年 }]] --获取本月相关数据 function TimeUtil.getMonthData(time) local data = {} data.firstDayWeek = TimeUtil.getWeekOfMonthFirstDay(time) data.year = TimeUtil.getYear(time) data.month = TimeUtil.getMonth(time) data.day = TimeUtil.getDay(time) data.monthDays = TimeUtil.getMonthDays_(data.year, data.month) return data end --获取本月一号是星期几 function TimeUtil.getWeekOfMonthFirstDay(time) time = tonumber(time) local tab = os.date("*t", time) local year, month, day, wday = tab.year, tab.month, tab.day, tab.wday local f_wday = 1 day = day % 7 --转换到1-7号对应的第几天 星期天为第1天 if day == 0 then f_wday = wday + 1 else if day > wday then f_wday = wday - day + 8 else f_wday = wday - day + 1 end end return f_wday - 1 --返回0 - 6 对应星期天-星期六 end --判断是否为闰年 function TimeUtil.isLeapYear(year) if (year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0) then return true end return false end --每个月对应的天数 local months = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} function TimeUtil.getMonthDays_(year, month) if month == 2 then if TimeUtil.isLeapYear(year) then return 29 else return 28 end else return months[month] end end function TimeUtil.getMonthDays(time) local tab = os.date("*t", time) return TimeUtil.getMonthDays_(tab.year, tab.month) end --年 function TimeUtil.getYear(time) return tonumber(os.date("%Y", time)) end --月 function TimeUtil.getMonth(time) return tonumber(os.date("%m", time)) end --日 function TimeUtil.getDay(time) return tonumber(os.date("%d", time)) end --时 function TimeUtil.getHour(time) return tonumber(os.date("%H", time)) end --分 function TimeUtil.getMinutes(time) return tonumber(os.date("%M", time)) end --秒 function TimeUtil.getSeconds(time) return tonumber(os.date("%S", time)) end --星期中的第几天,星期天为 0 与wday属性不一样 function TimeUtil.getWeekDay(time) return tonumber(os.date("%w", time)) end --足球比赛时间格式 function TimeUtil.getFootballMathTime(time, separator) separator = separator or " " local date = os.date("*t", time) local dayStr = string.format("%02d/%02d", date.month, date.day) local timeStr = string.format("%02d:%02d", date.hour, date.min) return dayStr .. separator .. timeStr end return TimeUtilGachaRewardCfgParsej--[[ 扭蛋奖励配置解析 author:{zhangpeng} time:2025-07-09 14:37:52 ]] local gachaRewardCfg = require("data/config/gachaRewardCfg") local GachaRewardCfgParse = defClassStatic("GachaRewardCfgParse") function GachaRewardCfgParse:init() self.gachaRewardCfg = {} self.allRewards = {} self.totalWeight = 0 -- 解析配置数据 for _, v in pairs(gachaRewardCfg) do self.gachaRewardCfg[v.id] = v table.insert(self.allRewards, v) self.totalWeight = self.totalWeight + v.weight end end -- 根据ID获取扭蛋奖励配置 function GachaRewardCfgParse:getGachaRewardCfg(id) return self.gachaRewardCfg[id] end -- 获取所有扭蛋奖励配置 function GachaRewardCfgParse:getAllGachaRewardCfg() return self.allRewards end -- 根据权重随机获取一个奖励 function GachaRewardCfgParse:getRandomReward() if #self.allRewards == 0 then return nil end -- 权重随机算法 local randomValue = math.random() * self.totalWeight local currentWeight = 0 for _, reward in ipairs(self.allRewards) do currentWeight = currentWeight + reward.weight if randomValue <= currentWeight then return reward end end -- 兜底返回最后一个奖励 return self.allRewards[#self.allRewards] end -- 批量获取随机奖励 function GachaRewardCfgParse:getRandomRewards(count) local rewards = {} for i = 1, count do table.insert(rewards, self:getRandomReward()) end return rewards end -- 计算奖励的实际数量(在min和max之间随机) function GachaRewardCfgParse:calculateRewardCount(rewardCfg) if rewardCfg.minCount == rewardCfg.maxCount then return rewardCfg.minCount end return math.random(rewardCfg.minCount, rewardCfg.maxCount) end -- 获取奖励类型名称 function GachaRewardCfgParse:getRewardTypeName(rewardType) local typeNames = { [1] = "星星", [2] = "音符", [3] = "玩偶" } return typeNames[rewardType] or "未知奖励" end GachaRewardCfgParse:init() return GachaRewardCfgParsecustomerTagCfg4--[[ from file:顾客标签.xlsx --]] local customerTagCfg = { [1] = { id = 4001, customerDesc = "住在森林,需要传单宣传或者更高级的宣传才能招揽", cuisineDesc = "美食1:森林的客人有一定概率点这道菜", favoriteFoods = 200003, buildingId = -1, }, [2] = { id = 4002, customerDesc = "住在城市,需要视频宣传才能招揽", cuisineDesc = "美食2:城市里的客人有一定概率点这道菜", favoriteFoods = 200005, buildingId = -1, }, } return customerTagCfg mainh--[[ 用于android/ios的boot文件 author:zhangpeng time:2025-07-18 17:58:16 ]] local _ENV = _G --FORCE CLEAN ENV local LOGTAG = "[boot/main]" print(LOGTAG.."start 000") local json = require("rapidjson") print(LOGTAG.."launch luaengine from here") local luaengine = require("luaengine") local UnityEngine = CS.UnityEngine local AET = CS.AET local isEditor = CS.UnityEngine.Application.isEditor local YooAssetLoader = CS.YooAssetLoader.Instance _G.BOOT_MAIN_FILE = "boot/main" _G.GAME_MAIN_FILE = "main/main" local debug_flag = true if CS.LocalDataStorage.Get("PRINT_EVERY_LUA_CALL") == "true" then debug.sethook(function(event,line) local info = debug.getinfo(2) if info.currentline > 0 then print(string.format("%s:%s:%s:%s:%s",info.short_src,tostring(info.currentline),tostring(info.linedefined),tostring(info.name),tostring(info.namewhat))) end end, "c" ) end local cached_lua_ret_map = {} local CLEAR_ALL_LUA_CACHES = function() print("[boot.main] 清空lua缓存") for k,_ in pairs(cached_lua_ret_map) do cached_lua_ret_map[k] = nil end end local _loadlua = function (bytes, file, opts, env) if bytes == nil or bytes == "" then error("lua文件不存在->"..file..":"..tostring(bytes).. "\n" .. debug.traceback()) end if opts == "b" then print(LOGTAG .. "loadlua:bytes file") bytes = AET.Dec(bytes) end local f,err = load(bytes, file, opts, env) if f then local ok,ret = xpcall(f,function(err) CS.UnityEngine.Debug.LogError(string.format("加载lua失败[%s]%s\n%s",file,tostring(err),debug.traceback())) end) if not string.lower(file):find("reslink") and not isEditor then cached_lua_ret_map[file] = {ret = ret} end return ret, env else CS.UnityEngine.Debug.LogError("加载lua失败" .. file) error(tostring(err) .. "\n" .. debug.traceback()) end end -- 热更结束后加载lua文件 -- @ filename:要加载的lua文件名 -- @ env:lua环境,用于加载 Lua 文件的执行环境 -- _require函数会根据传入的参数加载指定的 Lua 文件,然后执行它,最终返回加载结果 local _require = function(env, filename) local ret = cached_lua_ret_map[filename] if ret then return ret.ret end -- print("[require]", filename) -- local filepath = string.lower(filename) local filepath = filename -- 使用YooAssetLoader同步加载资源,启动时候已经把ab加载到内存了 local src = YooAssetLoader:LoadText(filepath) return _loadlua(src, filename, "bt", env) end local _newenv = function() -- local _G = _G local _E = _G local rawset = _G.rawset local env = { _G = _G, _print = print, CLEAR_ALL_LUA_CACHES = CLEAR_ALL_LUA_CACHES, ENV_REQUIRE = _require } _G.setmetatable( env, { __index = function(t, k) local v = _E[k] rawset(t, k, v) return v end } ) return env end --Run Main Code do print(LOGTAG .. "start run main code") local _ENV = _newenv() _ENV.CLEAR_ENV = function() for k, _ in pairs(_ENV) do _ENV[k] = nil end end _ENV.raw_require = raw_require or require _ENV._require = _require _ENV.require = function(filename, _env) _env = _env or _ENV return _require(_env, filename) end require("boot/build_config") -- 执行main/main.lua print(LOGTAG.." -------- RUN GAME_MAIN_FILE -------- ") require(_G.GAME_MAIN_FILE) endFishingControlArea-- 控制区域 local FishingControlArea = defClass("FishingControlArea") -- 构造函数 function FishingControlArea:ctor(go) self.go = go self.rectTransform = go.transform self.rocker = go:Seek("rocker").transform self.rocker_bg = go:Seek("rocker_bg").transform self.rocker_bar = go:Seek("rocker_bar").transform self:init() self:initEvent() end -- 初始化 function FishingControlArea:init() self.controlAreaRadius = 200 -- 控制区摇杆最大半径 self.touchable = false -- 是否可触摸 self.isDragging = false -- 是否正在拖动摇杆 self.startPos = Vector2.zero -- 摇杆起始位置 self.distance = 0 -- 0 ~ 1 self.direction = Vector2.zero -- 摇杆方向 self.displayAreaRadius = 114 -- 摇杆显示区域半径 self.canvasSize = UILayerUtil.canvas.transform.sizeDelta self.sceneSize = {x = Screen.width, y = Screen.height} -- 场景大小 end -- 初始化事件 function FishingControlArea:initEvent() util.ugui.addPointerDownEvent(self.go, function(eventData) self:onPointerDown(eventData) end) util.ugui.addDragEvent(self.go, function(eventData) self:onPointerMove(eventData) end) util.ugui.addPointerUpEvent(self.go, function(eventData) self:onPointerUp(eventData) end) end function FishingControlArea:setTouchable(touchable) self.touchable = touchable if not touchable then self.isDragging = false self.direction = Vector2.zero end end -- 设置控制角色 function FishingControlArea:setCharacter(character) self.character = character end -- 鼠标按下事件 function FishingControlArea:onPointerDown(eventData) if not self.touchable then return end self.isDragging = true self.startPos = eventData.position self.startPos.x = self.startPos.x * self.canvasSize.x / self.sceneSize.x self.startPos.y = self.startPos.y * self.canvasSize.y / self.sceneSize.y self.rocker.anchoredPosition = self.startPos self.rocker.gameObject:SetActive(true) self.character:playSwimAnim() end -- 鼠标移动事件 function FishingControlArea:onPointerMove(eventData) if self.touchable and self.isDragging then local currPos = eventData.position local dir = currPos - self.startPos self.startPos = currPos self.distance = Mathf.Min(dir.magnitude, self.controlAreaRadius) / self.controlAreaRadius self.direction = self.direction + dir.normalized * self.distance if self.character then self.character:setDisplay(self.direction) self:setDisplay(self.direction) end end end function FishingControlArea:setDisplay(ctlDirection) local handlePos = self.rocker_bg.localPosition ctlDirection = ctlDirection * self.controlAreaRadius if ctlDirection.magnitude > self.displayAreaRadius then local temp = ctlDirection.normalized * self.displayAreaRadius handlePos.x = handlePos.x + temp.x handlePos.y = handlePos.y + temp.y self.rocker_bar.localPosition = handlePos else handlePos.x = handlePos.x + ctlDirection.x handlePos.y = handlePos.y + ctlDirection.y self.rocker_bar.localPosition = handlePos end end -- 鼠标抬起事件 function FishingControlArea:onPointerUp(eventData) self.isDragging = false self.direction = Vector2.zero self.character:clearDisplay() self.rocker_bar.localPosition = Vector3.zero self.rocker.gameObject:SetActive(false) end return FishingControlArea GachaDeskInfo@--[[ 扭蛋机数据信息单独管理 author:{zhangpeng} time:2025-07-09 11:11:55 ]] local GachaDeskInfo, super = defClass("GachaDeskInfo") function GachaDeskInfo:ctor(gachaId) self.id = gachaId self.buildingType = BuildingConst.buildingType.gashapon self:init() end function GachaDeskInfo:init() self:initDynamicData() end function GachaDeskInfo:initDynamicData() self.state = 1 end --- 检查建筑是否在使用中 function GachaDeskInfo:isInUse() return self.state >= BuildingConst.buildingState.used end return GachaDeskInfo ExtendRect local RectCls = HackCSharpClass(CS.UnityEngine.Rect) local Vector3 = CS.UnityEngine.Vector3 RectCls.GetTopMid = function(self) return Vector3(self.center.x,self.max.y,0) end RectCls.GetBottomMid = function(self) return Vector3(self.center.x,self.min.y,0) end RectCls.GetLeftMid = function(self) return Vector3(self.min.x,self.center.y,0) end RectCls.GetRightMid = function(self) return Vector3(self.max.x,self.center.y,0) end RectCls.GetByPercent = function(self,rx,ry) return Vector3(self.min.x + (self.max.x - self.min.x) * rx,self.min.y + (self.max.y - self.min.y) * ry,0) end RectCls.Add = function (self, b) return Rect(self.x + b.x, self.y + b.y, self.width + b.width, self.height + b.height) end RectCls.Sub = function (self, b) return Rect(self.x - b.x, self.y - b.y, self.width - b.width, self.height - b.height) end RectCls.Multiply = function (self, b) return Rect(self.x * b, self.y * b, self.width * b, self.height * b) end VendorUserData -- 摊主用户数据 local VendorUserData = defClass("VendorUserData") -- log local LOGTAG = "VendorUserData" -- 构造函数 function VendorUserData:ctor() self:init() end -- 初始化 function VendorUserData:init() -- 摊主来访状态 self.vendor_visit_state = {} -- { [vendorId] = state } state 1:未解锁, 2:已解锁, 3:已来访 4:已分享 -- 摊主来访次数 self.vendor_visit_count = {} -- { [vendorId] = count } end -- 重置数据 function VendorUserData:resetData() self.vendor_visit_state = {} self.vendor_visit_count = {} end -- 加载数据 function VendorUserData:load() if UserDataMgr.isNewPlayer then -- 新玩家,初始化数据 self:resetData() else -- 加载已有数据 self:loadFromLocal() end self:onLoadComplete() end -- 加载完成回调 function VendorUserData:onLoadComplete() self:save() end -- 从本地加载数据 function VendorUserData:loadFromLocal() self.vendor_visit_state = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.vendor_visit_state, "{}"))) self.vendor_visit_count = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.vendor_visit_count, "{}"))) end -- 保存玩家数据 function VendorUserData:save() -- 保存玩家数据到本地 self:saveToLocal() end -- 保存玩家数据到本地 function VendorUserData:saveToLocal() PlayerPrefsMgr:setString(StrogeKeyDef.vendor_visit_state, json.encode(PlayerPrefsMgr:idToString(self.vendor_visit_state))) PlayerPrefsMgr:setString(StrogeKeyDef.vendor_visit_count, json.encode(PlayerPrefsMgr:idToString(self.vendor_visit_count))) PlayerPrefsMgr:save() end -- 获取摊主来访状态 function VendorUserData:getVendorVisitState(vendorId) return self.vendor_visit_state[vendorId] or 2 -- 默认解锁状态 end -- 设置摊主来访状态 function VendorUserData:setVendorVisitState(vendorId, state, refuseSave) self.vendor_visit_state[vendorId] = state if not refuseSave then self:saveToLocal() end return self:getVendorVisitState(vendorId) end -- 获取摊主来访次数 function VendorUserData:getVendorVisitCount(vendorId) return self.vendor_visit_count[vendorId] or 0 -- 默认未访问次数 end -- 设置摊主来访次数 function VendorUserData:setVendorVisitCount(vendorId, value, refuseSave) self.vendor_visit_count[vendorId] = value if not refuseSave then self:saveToLocal() end return self:getVendorVisitCount(vendorId) end -- 增加摊主来访次数 function VendorUserData:addVendorVisitCount(vendorId, count, refuseSave) local curr = self:getVendorVisitCount() + count self:setVendorVisitCount(vendorId, curr, refuseSave) return self:getVendorVisitCount(vendorId) end return VendorUserData ExtendLuaI[function checkNumber(value, base) return tonumber(value, base) or 0 end function checkInt(value) return math.round(checkNumber(value)) end function checkBool(value) return (value ~= nil and value ~= false) end function checkTable(value) if type(value) ~= "table" then value = {} end return value end function clone(object) local lookup_table = {} local function _copy(object) if type(object) ~= "table" then return object elseif lookup_table[object] then return lookup_table[object] end local newObject = {} lookup_table[object] = newObject for key, value in pairs(object) do newObject[_copy(key)] = _copy(value) end return setmetatable(newObject, getmetatable(object)) end return _copy(object) end local isNull = CS.LuaHelper.IsNull function handlerBind(obj, method) return function(...) if obj == nil or isNull(obj) then return end return method(...) end end function handler(obj, method) return function(...) return method(obj, ...) end end function math.newRandomSeed() local ok, socket = pcall(function() return require("socket") end) if ok then math.randomseed(socket.gettime() * 1000) else math.randomseed(os.time()) end math.random() math.random() math.random() math.random() end function math.round(value) value = checkNumber(value) if value >= 0 then return math.floor(value + 0.5) else return math.ceil(value - 0.5) end end local piDiv180 = math.pi / 180 function math.angle2radian(angle) return angle * piDiv180 end local piMul180 = math.pi * 180 function math.radian2angle(radian) return radian / piMul180 end --#region table ---获取表最后的元素 ---@param t table ---@param index integer 小于0的索引,-1表示最后一个,默认 -1 ---@return any function table.last(t, index) index = index or -1 local count = #t local index = count + 1 + index return t[index] end ---表格是否为空 ---@param t table ---@return boolean function table.isEmpty(t) return next(t) == nil end ---表格的个数 ---@param t table ---@return integer function table.nums(t) local count = 0 for k, v in pairs(t) do count = count + 1 end return count end ---返回指定表格中的所有键 ---@param hashTable table 要检查的表格 ---@return table function table.keys(hashTable) local keys = {} for k, v in pairs(hashTable) do keys[#keys + 1] = k end return keys end ---返回指定表格中的所有值 ---@param hashTable table 要检查的表格 ---@return table function table.values(hashTable) local values = {} for k, v in pairs(hashTable) do values[#values + 1] = v end return values end ---将来源表格中所有键及其值复制到目标表格对象中,如果存在同名键,则覆盖其值 ---@param des table 目标表格 ---@param src table 来源表格 function table.merge(des, src) if src and des then for k, v in pairs(src) do des[k] = v end return des end end ---在目标表格的指定位置插入来源表格,如果没有指定位置则连接两个表格 ---@param des table 目标表格 ---@param src table 来源表格 ---@param begin integer 插入位置,默认最后 function table.insertTo(des, src, begin) begin = checkInt(begin) if begin <= 0 then begin = #des + 1 end local len = #src for i = 0, len - 1 do des[i + begin] = src[i + 1] end end ---合并列表 ---@param list1 table 列表1 ---@param list2 table 列表2 ---@param removeDuplicate boolean 是否删除重复的值 ---@return table function table.mergeList(list1, list2, removeDuplicate) local unique_values = {} -- 创建一个空表用于存储唯一值 local result = {} -- 创建一个空表用于存储合并后的列表 -- 遍历第一个列表并添加到唯一值表中 for _, value in ipairs(list1) do if not unique_values[value] then unique_values[value] = true table.insert(result, value) end end -- 遍历第二个列表并添加到唯一值表中,同时检查是否已经存在于第一个列表中 for _, value in ipairs(list2) do if not removeDuplicate or not unique_values[value] then unique_values[value] = true table.insert(result, value) end end return result end ---从表格中查找指定值,返回其索引,如果没找到返回 false ---@param array table 表格 ---@param value any 要查找的值 ---@param begin integer|nil 起始索引值 ---@return integer|boolean function table.indexOf(array, value, begin) for i = begin or 1, #array do if array[i] == value then return i end end return false end ---从表格中查找指定值,返回其 key,如果没找到返回 nil ---@param hashTable table 表格 ---@param value any 要查找的值 ---@return string|nil 该值对应的 key function table.keyOf(hashTable, value) for k, v in pairs(hashTable) do if v == value then return k end end return nil end ---反转表格 ---@param array table function table.reverse(array) local i, j = 1, #array local tmp while i < j do tmp = array[i] array[i] = array[j] array[j] = tmp i = i + 1 j = j - 1 end end ---根据条件函数,从列表中删除指定值 ---@param array table 列表 ---@param cond fun(array: table, i: integer) ---@param removeAll boolean 是否删除所有相同的值 ---@return integer 返回删除的值的个数 function table.removeByCond(array, cond, removeAll) local c, i, max = 0, 1, #array while i <= max do if cond(array[i], i) then table.remove(array, i) c = c + 1 i = i - 1 max = max - 1 if not removeAll then break end end i = i + 1 end return c end ---从列表中删除指定值,返回删除的值的个数 ---@param array table 列表 ---@param value any 要删除的值 ---@param removeAll boolean 是否删除所有相同的值 ---@return integer 返回删除的值的个数 function table.removeByValue(array, value, removeAll) local c, i, max = 0, 1, #array while i <= max do if array[i] == value then table.remove(array, i) c = c + 1 i = i - 1 max = max - 1 if not removeAll then break end end i = i + 1 end return c end ---对表格中每一个值执行一次指定的函数,并用函数返回值更新表格内容 ---@param t table 表格 ---@param fn fun(v,k):any 函数, 返回值会被赋值给 t[k] function table.map(t, fn) for k, v in pairs(t) do t[k] = fn(v, k) end end ---对表格中每一个值执行一次指定的函数,但不改变表格内容 ---@param t table 表格 ---@param fn fun(v:any, k:any) 函数 function table.walk(t, fn) for k, v in pairs(t) do fn(v, k) end end ---对表格中每一个值执行一次指定的函数,如果该函数返回 false,则对应的值会从表格中删除 ---@param t table 表格 ---@param fn fun(v:any, k:any) 函数 function table.filter(t, fn) for k, v in pairs(t) do if not fn(v, k) then t[k] = nil end end end ---遍历表格,确保其中的值唯一 ---@param t table 表格 ---@param bArray boolean t是否是数组,是数组,t中重复的项被移除后,后续的项会前移 ---@return table 包含所有唯一值的新表格 function table.unique(t, bArray) local check = {} local n = {} local idx = 1 for k, v in pairs(t) do if not check[v] then if bArray then n[idx] = v idx = idx + 1 else n[k] = v end check[v] = true end end return n end ---是否包含 ---@param t table 表格 ---@param d any 要查找的值 ---@return boolean function table.contain(t, d) for k, v in pairs(t) do if v == d then return true end end return false end ---找到表格中所有符合条件的值 ---@param t table 表格 ---@param cond fun(v:any):boolean 判断函数 ---@return table function table.subByCond(t, cond) local newt = {} for _, v in pairs(t) do if cond(v) then table.insert(newt, v) end end return newt end ---找到表格中符合条件的值 ---@param t table 表格 ---@param cond fun(v:any):boolean 判断函数 ---@return any function table.getByCond(t, cond) for _, v in pairs(t) do if cond(v) then return v end end end ---获取子表 ---@param t table 原始表格 ---@param iStart integer 起始索引值 ---@param iEnd integer 结束索引值 ---@return table function table.sub(t, iStart, iEnd) local sub = {} for i = iStart, iEnd do local v = t[i] if v then table.insert(sub, v) else break end end return sub end ---洗牌 ---@param t table 原始表格 function table.shuffle(t) assert(t, "table.shuffle() expected a table, got nil") local rand = math.random local n = #t for i = n, 2, -1 do local j = rand(i) t[i], t[j] = t[j], t[i] end end ---表格拷贝 ---@param t table 原始表格 ---@return table function table.copy(t) local result = {} for k, v in pairs(t or {}) do result[k] = v end return result end local function lessThanComp(a, b) return a < b end ---表格排序 ---@param list table 排序列表 ---@param comp fun(a:any, b:any):boolean 排序方法 function table.stableSort(list, comp) comp = comp or lessThanComp local num = #list if num <= 1 then return end local sorted = false local n = num while not sorted do sorted = true for i = 1, n - 1 do if comp(list[i + 1], list[i]) then local tmp = list[i] list[i] = list[i + 1] list[i + 1] = tmp sorted = false end end n = n - 1 end end function table.toString(value) if value == nil then return "nil" end if type(value) == "string" then return value end -- 判断 myTable 是否存在 __tostring 方法 if getmetatable(value) and type(getmetatable(value).__tostring) == "function" then -- 调用 __tostring 方法并输出结果 return (tostring(value)) end local str = "{" for i, v in pairs(value) do local keyType = type(i) if keyType == "string" then str = str .. "[" .. string.format("%q", i) .. "]=" elseif keyType == "number" then str = str .. "[" .. i .. "]=" end local valueType = type(v) if valueType == "table" then str = str .. table.toString(v) .. "," elseif valueType == "string" then str = str .. string.format("%q", v) .. "," elseif valueType == "boolean" then if v then str = str .. "true," else str = str .. "false," end else str = str .. tostring(v) .. "," end end str = str .. "}" return str end function table.equal(l,r) local lL = #l local rL = #r if lL == rL then for i = 1,rL do if l[i] ~= r[i] then return false end end return true else return false end end function table.max(t,cmp_greater) local max = t[1] if max then for i = 2,#t do local v = t[i] if cmp_greater(v,max) then max = v end end return max end end function table.min(t,cmp_less) local min = t[1] if min then for i = 2,#t do local v = t[i] if cmp_less(v,min) then min = v end end return min end end function table.random(t) local l = #t if l > 0 then local i = math.random(1,l) return t[i] end end function table.reverseKV(t) local new = {} for k, v in pairs(t) do new[v] = k end return new end function table.initList(count, initValue) local t = {} for i = 1, count do t[i] = initValue end return t end --#endregion string._htmlSpecialCharsSet = {} string._htmlSpecialCharsSet["&"] = "&" string._htmlSpecialCharsSet['"'] = """ string._htmlSpecialCharsSet["'"] = "'" string._htmlSpecialCharsSet["<"] = "<" string._htmlSpecialCharsSet[">"] = ">" function string.encodeHtmlSpecialChars(input) for k, v in pairs(string._htmlSpecialCharsSet) do input = string.gsub(input, k, v) end return input end function string.decodeHtmlSpecialChars(input) for k, v in pairs(string._htmlSpecialCharsSet) do input = string.gsub(input, v, k) end return input end function string.lastIndexOf(input, pattern) local i = string.match(input, ".*" .. pattern .. "()") if i == nil then return -1 else return i - 1 end end function string.nl2br(input) return string.gsub(input, "\n", "
") end function string.text2html(input) input = string.gsub(input, "\t", " ") input = string.htmlspecialchars(input) input = string.gsub(input, " ", " ") input = string.nl2br(input) return input end ---@param input string ---@param delimiter string ---@param num integer 分割次数,默认是不限制 ---@return boolean|string[] function string.split(input, delimiter, num) input = tostring(input) delimiter = tostring(delimiter) if (delimiter == "") then return false end local pos, arr, splitCount = 0, {}, 0 -- for each divider found for st, sp in function() return string.find(input, delimiter, pos, true) end do if (num ~= nil and splitCount == num) then break end table.insert(arr, string.sub(input, pos, st - 1)) pos = sp + 1 splitCount = splitCount + 1 end table.insert(arr, string.sub(input, pos)) return arr end ---从右边开始分割字符串 ---@param input string ---@param delimiter string ---@param num integer 分割次数,默认是不限制 ---@return boolean|string[] function string.rsplit(input, delimiter, num) local arr = string.split(string.reverse(input), string.reverse(delimiter), num) if not arr then return false end table.reverse(arr) for index, value in ipairs(arr) do arr[index] = string.reverse(value) end return arr end function string.ltrim(input) return string.gsub(input, "^[ \t\n\r]+", "") end function string.rtrim(input) return string.gsub(input, "[ \t\n\r]+$", "") end function string.trim(input) input = string.gsub(input, "^[ \t\n\r]+", "") return string.gsub(input, "[ \t\n\r]+$", "") end function string.upperFirstChar(input) return string.upper(string.sub(input, 1, 1)) .. string.sub(input, 2) end local function urlEncodeChar(char) return "%" .. string.format("%02X", string.byte(char)) end function string.urlEncode(input) -- convert line endings input = string.gsub(tostring(input), "\n", "\r\n") -- escape all characters but alphanumeric, '.' and '-' input = string.gsub(input, "([^%w%.%- ])", urlEncodeChar) -- convert spaces to "+" symbols return string.gsub(input, " ", "+") end function string.urlDecode(input) input = string.gsub(input, "+", " ") input = string.gsub(input, "%%(%x%x)", function(h) return string.char(checkNumber(h, 16)) end) input = string.gsub(input, "\r\n", "\n") return input end function string.utf8Len(input) local len = string.len(input) local left = len local cnt = 0 local arr = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc } while left ~= 0 do local tmp = string.byte(input, -left) local i = #arr while arr[i] do if tmp >= arr[i] then left = left - i break end i = i - 1 end cnt = cnt + 1 end return cnt end ---用于将一个数字 num 转换为带有千位分隔符(逗号)的字符串形式 ---@param num number ---@return string function string.formatNumberThousands(num) local formatted = tostring(checkNumber(num)) local k while true do formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", "%1,%2") if k == 0 then break end end return formatted end -- 是否是中文字符 function string.isChineseChar(str) local c = string.byte(str, 1) local c1 = string.byte(str, 2) local c2 = string.byte(str, 3) -- local c = string.byte(s,k) if c >= 228 and c <= 233 then if c1 and c2 then local a1, a2, a3, a4 = 128, 191, 128, 191 if c == 228 then a1 = 184 elseif c == 233 then a2 = 190 if c1 == 190 then a4 = 165 end end if c1 >= a1 and c1 <= a2 and c2 >= a3 and c2 <= a4 then return true end end end return false end local CHINESE_SYMBOLS = { "·", "×", "—", "‘", "’", "“", "”", "…", "、", "。", "《", "》", "『", "』", "【", "】", "!", "(", ")", ",", ":", ";", "?" } ---是否中文标点 ---@param str string ---@return boolean|integer function string.isChineseSymbol(str) return table.indexOf(CHINESE_SYMBOLS, str) end -- 分割utf8字符串 function string.splitUTF8(s) local ss = {} local len = string.len(s) local index = 1 while index <= len do local curByte = string.byte(s, index) if curByte == nil then break elseif curByte > 0 and curByte <= 127 then table.insert(ss, string.char(curByte)) index = index + 1 elseif curByte >= 192 and curByte <= 223 then local c1 = string.byte(s, index + 1) table.insert(ss, string.char(curByte, c1)) index = index + 2 elseif curByte >= 224 and curByte <= 239 then local c1 = string.byte(s, index + 1) local c2 = string.byte(s, index + 2) table.insert(ss, string.char(curByte, c1, c2)) index = index + 3 elseif curByte >= 240 and curByte <= 247 then local c1 = string.byte(s, index + 1) local c2 = string.byte(s, index + 2) local c3 = string.byte(s, index + 3) table.insert(ss, string.char(curByte, c1, c2, c3)) index = index + 4 end end return ss end function string.startsWith(str, prefix) return string.sub(str, 1, string.len(prefix)) == prefix end function string.endsWith(str, suffix) local L = string.len(str) local Lsuffix = string.len(suffix) if L >= Lsuffix then return string.sub(str, L - Lsuffix + 1, L) == suffix else return false end end -- 截取中英混合的UTF8字符串,endIndex可缺省 function string.subUTF8(str, startIndex, endIndex) -- 返回当前字符实际占用的字符数 local function SubStringGetByteCount(str, index) local curByte = string.byte(str, index) local byteCount = 1 if curByte == nil then byteCount = 0 elseif 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 return byteCount end -- 获取中英混合UTF8字符串的真实字符数量 local function SubStringGetTotalIndex(str) local curIndex = 0 local i = 1 local lastCount = 1 repeat lastCount = SubStringGetByteCount(str, i) i = i + lastCount curIndex = curIndex + 1 until (lastCount == 0) return curIndex - 1 end local function SubStringGetTrueIndex(str, index) local curIndex = 0 local i = 1 local lastCount = 1 repeat lastCount = SubStringGetByteCount(str, i) i = i + lastCount curIndex = curIndex + 1 until (curIndex >= index) return i - lastCount end if startIndex < 0 then startIndex = SubStringGetTotalIndex(str) + startIndex + 1 end if endIndex ~= nil and endIndex < 0 then endIndex = SubStringGetTotalIndex(str) + endIndex + 1 end if endIndex == nil then return string.sub(str, SubStringGetTrueIndex(str, startIndex)) else return string.sub(str, SubStringGetTrueIndex(str, startIndex), SubStringGetTrueIndex(str, endIndex + 1) - 1) end end function string.isEmpty(str) return str == nil or str == "" end ---返回处理后的字符串,其中所有的Lua模式匹配中的特殊字符都被转义了 ---@param text string ---@return string function string.escapePattern(text) local magic_chars = { "%", "^", "$", "(", ")", ".", "[", "]", "*", "+", "-", "?" } for i, char in ipairs(magic_chars) do text = text:gsub("%" .. char, "%%" .. char) end return text end function string.f(format, vars) return (format:gsub("{(.-)}", function(key) return tostring(vars[key]) end)) end ---打乱字符串 ---@param inputStr string ---@return string function string.shuffle(inputStr) local charArray = {} for char in inputStr:gmatch(".") do table.insert(charArray, char) end for i = #charArray, 2, -1 do local j = math.random(1, i) charArray[i], charArray[j] = charArray[j], charArray[i] end return table.concat(charArray) end -- linkedlist双向链表 linkedlist = {} function linkedlist.create() return { len = 0, head = nil, tail = nil } end function linkedlist.push_back(list, v) local d = { data = v, next = nil } if list.head then d.prev = list.tail list.tail.next = d list.tail = d else list.head = d list.tail = d end list.len = list.len + 1 end function linkedlist.pop_back(list) if list.tail then list.tail = list.tail.prev list.len = list.len - 1 end end function linkedlist.pop_front(list) if list.head then list.head = list.head.next list.len = list.len - 1 end end function linkedlist.len(list) return list.len end function linkedlist.foreach(list, cb) local curr = list.head while curr ~= nil do cb(curr.data) curr = curr.next end end EMPTY_FUNC = function()end EntityCell --- 实体 ---@class EntityCell : LuaClass local EntityCell = defClass("EntityCell") local Image = CS.UnityEngine.UI.Image local TMProUGUI = CS.TMPro.TextMeshProUGUI -- 构造函数 function EntityCell:ctor(go, entityType, entityId) self.go = go self.entityType = entityType -- 1:菜品, 2:设施 self.entityId = entityId self:showUI() end -- 初始化ui function EntityCell:showUI() local entity_locked = self.go:Seek("entity_locked") local entity_unlocked = self.go:Seek("entity_unlocked") local entity_icon = entity_unlocked:Seek("icon") local entity_name = entity_unlocked:Seek("name") util.ugui.addButtonClickEvent(entity_unlocked, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:click() end) if self.entityType == 1 then self:showCuisine(entity_locked, entity_unlocked, entity_icon, entity_name) end if self.entityType == 2 then self:showFacility(entity_locked, entity_unlocked, entity_icon, entity_name) end end -- 展示菜品 function EntityCell:showCuisine(lockObj, infoObj, iconObj, nameObj) self.isUnlocked = CuisineMgr:isCuisineUnlocked(self.entityId) lockObj:SetActive(not self.isUnlocked) infoObj:SetActive(self.isUnlocked) if self.isUnlocked then local info = CuisineMgr:getCuisineInfo(self.entityId) local icon = iconObj[Image] icon.sprite = info:getIcon() util.ugui:setImageTileSize(icon, 220) nameObj[TMProUGUI].text = info:getName() end end -- 展示设施 function EntityCell:showFacility(lockObj, infoObj, iconObj, nameObj) local info = BuildingMgr:getBuildingInfo(self.entityId) self.isUnlocked = info:isUnlocked() lockObj:SetActive(not self.isUnlocked) infoObj:SetActive(self.isUnlocked) if self.isUnlocked then local icon = iconObj[Image] icon.sprite = info:getUiIcon() util.ugui:setImageTileSize(icon, 220) nameObj[TMProUGUI].text = info:getName() end end -- 点击事件 function EntityCell:click() if self.isUnlocked then return end --local view = EntityDetailUI.new("菜品详情", self.entityType, self.entityId):show():showMask():enableCloseWhenClickMask() if self.entityType == CustomerConst.VisitItemType.c then CuisineDetailUI.new(self.entityId):show():showMask():enableCloseWhenClickMask() end if self.entityType == CustomerConst.VisitItemType.b then local data = BuildingCfgParse:getBuildingCfg(self.entityId) BuildingInfoUI.new(data):show():showMask():enableCloseWhenClickMask() end end return EntityCellFishingFishListUI-- 捕鱼展示鱼列表界面 local FishingFishListUI, super = defClass("FishingFishListUI", UILayer) -- view require("modules/ui/fishingui/FishingFishCell") -- 构造函数 function FishingFishListUI:ctor(fishIds) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/fishingui/fishinguireslink") self.fishIds = fishIds -- { [id] : count } end -- 当页面加载 function FishingFishListUI:onLoad() self.ui = GameObject.Instantiate(self.R.fishing_fish_list_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end -- 初始化UI function FishingFishListUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.content = self.ui:Seek("Content").transform self.got_it_btn = self.ui:Seek("got_it_btn") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addButtonClickEvent(self.got_it_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) end -- 展示UI function FishingFishListUI:showUI() self:showAll() end -- 展示所有鱼 function FishingFishListUI:showAll() for id, count in pairs(self.fishIds) do local cellNode = GameObject.Instantiate(self.R.fishing_fish_cell, self.content) FishingFishCell.new(cellNode, id, count) end end return FishingFishListUI EmployFunsMgrX --[[ 员工功能管理类 author:{zhangpeng} time:2025-05-21 10:54:08 ]] local EmployFunsMgr = defClassStatic("EmployFunsMgr") local LOGTAG = "EmployFunsMgr" -- init function EmployFunsMgr:init() end -- 员工行为,不同员工有不同的行为 function EmployFunsMgr:doAction(employee) -- 具体的行为逻辑现在由各个员工类型自己实现 employee:action() end -- 通用的员工功能方法可以保留在这里 -- 例如处理员工共同的工作流程、状态变化等 -- 每隔X秒执行Y秒的动作辅助方法(保留为通用方法) function EmployFunsMgr:doActionUntilInterval(employee, actionFunc, interval, duration) if not employee or not actionFunc then return end -- 创建定时器 local timer local startTime = os.time() timer = employee:timer(function() -- 检查是否超过持续时间 if os.time() - startTime >= duration then -- 停止定时器 if timer then TimerMgr:rem(timer) timer = nil end return end -- 执行动作 actionFunc() end, interval, 0) return timer end -- 每隔X秒执行一次 --@param employee 员工 -- @param actionFunc 动作 -- @param interval 间隔时间 function EmployFunsMgr:doActionOncePerInterval(employee, actionFunc, interval) if not employee or not actionFunc then return end -- 创建定时器 local timer = employee:timer(function() actionFunc() end, interval, 0) return timer end -- 员工休息 function EmployFunsMgr:doResting(employee, callback) local restTimeLimit = employee.employeeInfo:getRestTimeLimit() local restTime = 0 local timer = nil -- 开始工作 employee.employeeInfo:setWorkState(EmployeConst.State.Resting) timer = employee:timer(function() restTime = restTime + 1 employee.employeeInfo:setRestTime(restTime) if restTime >= restTimeLimit then -- 停止定时器 if timer then TimerMgr:rem(timer) timer = nil if callback then callback(employee) end end return end end, 1, 0) end -- 初始化静态类 EmployFunsMgr:init() return EmployFunsMgr pbreslinkreturn { --BASIC --ASSET chat = {"Assets/AssetsPackage/Res/common/proto/chat.pb.bytes", 0, 9}, echo = {"Assets/AssetsPackage/Res/common/proto/echo.pb.bytes", 0, 9}, head = {"Assets/AssetsPackage/Res/common/proto/head.pb.bytes", 0, 9}, user = {"Assets/AssetsPackage/Res/common/proto/user.pb.bytes", 0, 9}, handshake = {"Assets/AssetsPackage/Res/common/proto/handshake.pb.bytes", 0, 9}, } dollCfg--[[ from file:玩偶表.xlsx --]] local dollCfg = { [1] = { id = 600001, name = "看火人亨利玩偶", desc = "能召唤隐藏顾客的玩偶", res = "cp01_hongshu", way = 1, taskId = -1, count = 45, rewardId_1 = 1, param_1 = 100, rewardId_2 = 3, param_2 = 401001, dollIcon = "kaola_toy", }, [2] = { id = 600002, name = "旅游博主阿比玩偶", desc = "能召唤隐藏顾客的玩偶", res = "cp01_hongshu", way = 1, taskId = -1, count = 30, rewardId_1 = 2, param_1 = 2000, rewardId_2 = 3, param_2 = 401002, dollIcon = "yangtuo_toy", }, [3] = { id = 600003, name = "说唱歌手莱娜玩偶", desc = "能召唤隐藏顾客的玩偶", res = "cp01_hongshu", way = 1, taskId = -1, count = 50, rewardId_1 = 1, param_1 = 100, rewardId_2 = 3, param_2 = 401003, dollIcon = "erkuohu_toy", }, [4] = { id = 600004, name = "背包客球球玩偶", desc = "能召唤隐藏顾客的玩偶", res = "cp01_hongshu", way = 1, taskId = -1, count = 15, rewardId_1 = 1, param_1 = 100, rewardId_2 = 3, param_2 = 401004, dollIcon = "keji_toy", }, [5] = { id = 600005, name = "夏令营老师欣欣玩偶", desc = "能召唤隐藏顾客的玩偶", res = "cp01_hongshu", way = 1, taskId = -1, count = 20, rewardId_1 = 2, param_1 = 2000, rewardId_2 = 3, param_2 = 401005, dollIcon = "songshu_toy", }, } return dollCfg main2require("common/data/static_config/parse/main")main[require("modules/musicbbq/map/MusicbbqMap") require("modules/musicbbq/mgr/MusicbbqMgr") PassivityIncomeConst--[[ 被动建筑常量配置 author:{zhangpeng} time:2025-06-13 11:19:49 ]] local PassivityIncomeConst = defClassStatic("PassivityIncomeConst") local LOGTAG = "PassivityIncomeConst" -- 闲逛建筑id PassivityIncomeConst.ids = { -- 备菜区 BuildingConst.buildingType.preparea, -- 被动收益,每隔30秒加金币,直接扔地上,玩家需要点击收取到货币栏 -- 水池 BuildingConst.buildingType.waterpool, -- 每分钟加金币,自动进收银台 -- 地毯 BuildingConst.buildingType.carpet, -- 被动收益,每隔N秒加金币,自动进收银台 -- 碗柜 BuildingConst.buildingType.cupboard, -- 被动收益,每隔N秒加金币,自动进收银台 -- 冰箱 BuildingConst.buildingType.refrigerator, -- 被动收益,每隔N秒加金币,自动进收银台 -- 货柜 BuildingConst.buildingType.cabinet, -- 被动收益,每隔N秒加金币,自动进收银台 -- 烤炉 BuildingConst.buildingType.oven, -- 被动收益,每隔N秒加金币,直接扔地上,玩家需要点击收取到货币栏 -- 花架 BuildingConst.buildingType.flowerstand, -- 被动收益,每隔N秒加金币,自动进收银台 -- 音乐烤吧 -- -- 1号烤架 -- BuildingConst.buildingType.bbqgrill1, -- 被动收益 -- -- 2号烤架 -- BuildingConst.buildingType.bbqgrill2, -- 被动收益 -- -- 3号烤架 -- BuildingConst.buildingType.bbqgrill3, -- 被动收益 -- 自助酒水 BuildingConst.buildingType.selfservice, -- 被动收益 -- 钢琴 BuildingConst.buildingType.instrument, -- 被动收益 -- 音箱 BuildingConst.buildingType.speaker, -- 被动收益 } return PassivityIncomeConst main-- mgr require("modules/fishing/minigame/FishingGameMgr") -- const require("modules/fishing/const/FishingConst") -- map require("modules/fishing/minigame/FishingGameMap") -- entity require("modules/fishing/minigame/FishermanCharacter") require("modules/fishing/minigame/Fish") require("modules/fishing/minigame/Cage") require("modules/fishing/minigame/Mine") require("modules/fishing/minigame/Piranha") require("modules/fishing/minigame/Swordfish") -- camera require("modules/fishing/minigame/FishingGameCameraCtl") -- anim require("modules/fishing/minigame/OpeningAnimation") require("modules/fishing/minigame/VictoryAnimation") dolluireslinkreturn { --BASIC --ASSET doll_cell = {"Assets/AssetsPackage/Res/modules/ui/doll/prefabs/doll_cell.prefab", 0, 0}, doll_main_ui = {"Assets/AssetsPackage/Res/modules/ui/doll/prefabs/doll_main_ui.prefab", 0, 0}, doll_row_cell = {"Assets/AssetsPackage/Res/modules/ui/doll/prefabs/doll_row_cell.prefab", 0, 0}, doll_detail_ui = {"Assets/AssetsPackage/Res/modules/ui/doll/prefabs/doll_detail_ui.prefab", 0, 0}, } VendorConst-- 摊主常量 local VendorConst = defClassStatic("VendorConst") -- log local LOGTAG = "VendorConst" -- 初始化 function VendorConst:init() end -- 摊主来访状态 VendorConst.VendorVisitState = { Locked = 1, -- 未解锁 Unlocked = 2, -- 已解锁 Visited = 3, -- 已来访 Viewed = 4, -- 已查看 Shared = 5, -- 已分享 } VendorConst:init() SocketMgr!--[[ author:{zhangpeng} time:2023-08-24 11:28:07 todo: 所有消息头用table,消息类型+pb 加msg名字,打印使用 ]] local SocketMgr = defClassStatic("SocketMgr") local LOG_TAG = SocketMgr.__cls_name local pb = raw_require("pb") local SocketConnection = CS.SocketConnection -- 心跳时间3秒 local HEART_DELTA = 5 local NEED_SOCKET = false -- 是否需要心跳 local NEED_HEART = false function SocketMgr:init() printInfo(LOG_TAG, "init") self.useLongConnection = true self.historyPackList = {} self.host = "" self.port = 4000 self.maxCount = 5 self.heartDelta = 0 self.handAckSuc = false -- 握手成功 self:clear() Timer:add( function(dt) self:updatePackSend(dt) end ) if NEED_SOCKET then Timer:nextTick( function() self:initServerInfo( function(serverInfo) self:setServerInfo(serverInfo) self:connect() printInfo(LOG_TAG, "init, host:%s, port:%s", self.host, self.port) end ) end ) end end function SocketMgr:initServerInfo(cb) local serverInfo = { serverIp = "192.144.239.125", serverPort = 4000, } self:setServerInfo(serverInfo) cb(serverInfo) end function SocketMgr:setServerInfo(serverInfo) self.host = serverInfo.serverIp self.port = serverInfo.serverPort self.serverInfo = serverInfo end function SocketMgr:connect() -- 建立连接 if not self.sock_con then self.sock_con = SocketConnection(self.host, self.port,self.useLongConnection) printInfo(LOG_TAG," >>>lua socket connect suc:%s:%d<<< ",self.host,self.port) -- 建立连接之后立即握手 self:sendHandShakeRequest() end end function SocketMgr:startHeartTimer() printInfo(LOG_TAG,"启动心跳...") self.startHeart = true self.heartCount = 0 if self.heartTimerId then Timer:rem(self.heartTimerId) self.heartTimerId = nil end self.heartTimerId = Timer:add( function (dt) self:doHeartUpdate() end,HEART_DELTA ) end function SocketMgr:doHeartUpdate() if not self.startHeart then return end self.heartCount = self.heartCount + 1 if self:isSocketEnable() then self:sendHeart() end -- printInfo(LOG_TAG,"heart ping pong:%s",self.heartCount) end function SocketMgr:clear() self.curCount = 0 self.isSendPackDict = {} self.toSendPackList = {} end -- 发送待发队列里的数据包 function SocketMgr:updatePackSend() if not self:isHostAndPortReady() then return end if #self.toSendPackList <= 0 then -- printInfo(LOG_TAG,"toSendPackList:%s",#self.toSendPackList) return end if self.curCount >= self.maxCount then -- printWarn(LOG_TAG,"curCount:%s maxCount:%s",self.curCount,self.maxCount) return end local pack = table.remove(self.toSendPackList, 1) self:sendByPack(pack) end -- 统一的发送接口 function SocketMgr:send(head, body, reqProtoName, rspProtoName, callback) local pack = PBSocketPack.new(head, body, reqProtoName, rspProtoName, callback) self:sendByPack(pack) end -- 发送握手请求 function SocketMgr:sendHandShakeRequest() local head = CmdDef.MsgType.REQ local body = { ["userId"] = HttpCmdMgr:getUserId(), ["token"] = HttpCmdMgr:getHeaderToken(), ["deviceId"] = "zpios", ["version"] = "1.0.0", } local pack = PBSocketPack.new(head, body,"handshake.Request", "handshake.Response", function (suc,rspData) if suc then self:setHandShakeRspSalt(rspData.rspBody.salt) self:sendHandShakeAck() else end end ) self:sendByPack(pack) end function SocketMgr:sendHandShakeAck() local head = CmdDef.MsgType.ACK PBSocketPack.headProtoName = nil local pack = PBSocketPack.new(head, nil,nil, nil, function (suc,rspData) if suc then self.handAckSuc = true printInfo(LOG_TAG,"握手ACK返回Suc") Msg.send(Msg.SOCKET_MSG_ACK_SUC, rspData) if NEED_HEART then self:startHeartTimer() end else end end ) printInfo(LOG_TAG,"发送握手ACK...") self:sendByPack(pack) end function SocketMgr:sendHeart() local head = CmdDef.MsgType.HEART PBSocketPack.headProtoName = nil local pack = PBSocketPack.new(head, nil,nil, nil, function (suc,rspData) if suc then printInfo(LOG_TAG,"rev heart pong...") end end ) printInfo(LOG_TAG,"send heart ping...") self:sendByPack(pack) end function SocketMgr:stopHeart() self.startHeart = false end function SocketMgr:sendByPack(pack) if not NetworkStateUtil:isReachable() then local errorCode = -999999 local errorMsg = "网络不可用" pack.callback(false, {errorCode = errorCode, errorMsg = errorMsg}) return end if self.curCount >= self.maxCount or (not self:isHostAndPortReady()) then table.insert(self.toSendPackList, pack) return end self.curCount = self.curCount + 1 local seq = pack:getSeq() if self.isSendPackDict[seq] then printWarn(LOG_TAG, "sendByPack, pack is send, seq:%s", seq) return end self.isSendPackDict[seq] = pack local head = nil if type(pack.reqHead) == "table" then local encode_data = self:encodePB(pack.reqHead.headData, pack.reqHeadProtoName) local headBytes = {} table.insert(headBytes, string.char(pack.reqHead.msg_type)) for i = 1, #encode_data do table.insert(headBytes, string.char(string.byte(encode_data, i))) end head = table.concat(headBytes) elseif type(pack.reqHead) == "number" then head = string.char(pack.reqHead) end local body = nil if pack.reqBody and pack.reqBodyProtoName then body = self:encodePB(pack.reqBody, pack.reqBodyProtoName) end -- pack:printReq() pack:setReqTime() -- pack:setReqLength(#head + #(body or "")) if not self:isSocketEnable() then self:connect() end self.sock_con:Send( head, body, function(bool, _head, _body) self:afterSend(bool, _head, _body, seq) end ) pack:setConnection(self.sock_con) table.insert(self.historyPackList, pack) end function SocketMgr:encodePB(data, schemeName) if data == nil or schemeName == nil then return end return assert(pb.encode(schemeName, data)) end function SocketMgr:decodePB(data, schemeName) if data == nil or schemeName == nil then return end return assert(pb.decode(schemeName, data)) end function SocketMgr:afterSend(bool, head, body, seq) if self.curCount == 0 then printWarn(LOG_TAG, "afterSend, no pask is send") return end local pack = self.isSendPackDict[seq] if not pack then printWarn(LOG_TAG, "afterSend, pack not found, seq:%s", seq) return end self.curCount = self.curCount - 1 self.isSendPackDict[seq] = nil if not bool then local errorCode = head local errorMsg = body if errorCode == -1 then -- self:addFailHostAndPort(self.host, self.port) -- self:updateHostAndPort() end pack:printError(errorCode, errorMsg) pack.callback(false, {errorCode = errorCode, errorMsg = errorMsg}) return end pack.rspMsgType = self:getMsgTypeByHead(head) pack.rspBody = self:decodePB(body, pack.rspBodyProtoName) pack:printRsp() pack:setRspTime() pack:setRspLength(#head + #(body or "")) pack.callback(true, pack) end -- 消息类型取返回包头的前4个字节 function SocketMgr:getMsgTypeByHead(head) return string.byte(head, 1) end function SocketMgr:isSocketEnable() if self.sock_con then if self.sock_con.isConnected then return true end end return false end function SocketMgr:isHostAndPortReady() if self.host == nil or self.port == nil then return false end return true end function SocketMgr:setHandShakeRspSalt(salt) CS.AESCTR.SetAckResponseSlat(salt) self.handShakeRspSalt = salt end function SocketMgr:getHandShakeRspSalt() return self.handShakeRspSalt end StallDesk--[[ 摊位桌子 author:{zhangpeng} time:2025-07-04 18:50:03 ]] local StallDesk,super = defClass("StallDesk",BuildingBase) require("modules/ui/pondui/PondRemoveStoneUI") local TMPro = CS.TMPro.TextMeshPro local SpriteRenderer = CS.UnityEngine.SpriteRenderer local LOGTAG = "StallDesk" function StallDesk:ctor(args) super.ctor(self,args) self.id = args.deskId self.resId = "building_"..args.deskId self.state = UserDataMgr.buildingUserData:getBuildingState(self.id) self:initDisplay() if self.state == StallConst.State.time then local unlockTime = UserDataMgr.buildingUserData:getStallUnlockTime(self.id) local delayTime = UserDataMgr.buildingUserData:getStallDelayTime(self.id) local now = os.time() if now - unlockTime >= delayTime then self:unlockStall() else self:delayUnlock(unlockTime, delayTime) end end -- 根据状态初始化外观 self:applyState() end function StallDesk:initDisplay() self.buildingNode = GameObject.Instantiate(self.scene.R[self.resId]) self.sortBaseNode = self.buildingNode:Seek("base") self:setBuildingParent(self.buildingParent) self:setBuildingNodeName(self.resId) self.vendor = self.buildingNode:Seek("vendor_role") self.vendor:SetActive(false) self:addClickEvent() end -- 点击摊位 function StallDesk:addClickEvent() local restaurantScene = RestaurantMgr:getRestaurantScene() local touchCom = restaurantScene.touchCom touchCom:addListener(self.buildingNode:Seek("touch_mask"), TouchCom.LISTENER_TYPE.CLICK, function(p) if self.state == StallConst.State.lock then PondRemoveStoneUI.new(self.id):show():showMask():enableCloseWhenClickMask() return end if self.state == StallConst.State.used then printInfo(LOGTAG, "摊位正在使用中,不能点击") return end -- 摊位正在解锁中,不能点击 if self.state == StallConst.State.time then return end -- 摊位已放置鱼,不能点击 if self.state == StallConst.State.fish then return end -- 放置一条鱼 if self.state == StallConst.State.idle then local list = FishCfgParse:getAllFishCfg() local fishId = list[math.random(1, #list)].id CustomerMgr.fishingVisits:addFish(self.id, fishId) self.state = StallConst.State.fish self:applyState() end end ) end -- 应用摊位状态 function StallDesk:applyState() self.buildingNode:Seek("shitou"):SetActive(self.state == StallConst.State.lock or self.state == StallConst.State.time) self.buildingNode:Seek("zhaozupai"):SetActive(self.state == StallConst.State.idle or self.state == StallConst.State.fish) self.buildingNode:Seek("zhaozu"):SetActive(self.state == StallConst.State.idle) self.buildingNode:Seek("spine_root"):SetActive(self.state == StallConst.State.time) self.buildingNode:Seek("stalls"):SetActive(self.state == StallConst.State.used) end -- 未招揽摊主,鱼失效 function StallDesk:fishInvalid() self.state = StallConst.State.idle self:applyState() end -- 设置摊位的摊主 function StallDesk:setVendor(vendorId) self.vendorId = vendorId self.buildingInfo:setVendorCfgData(VendorCfgParse:getVendorCfg(vendorId)) self:showStallType(StallConst.vendorSaleTypes[vendorId]) end -- 摊主离开 function StallDesk:vendorLeave() self.state = StallConst.State.idle self.vendorId = nil self:applyState() end function StallDesk:startUnlockStall() self.state = StallConst.State.time -- 设置摊位状态 UserDataMgr.buildingUserData:setBuildingState(self.id, self.state) local count = StallDeskMgr:getStallDeskCount() local delayTime = BoothUnlockCfgParse:getBoothUnlockCfgByOrder(count + 1).time -- 设置摊位解锁时间 local startTime = UserDataMgr.buildingUserData:setStallUnlockTime(self.id, delayTime) self:delayUnlock(startTime, delayTime) -- 切换到招聘中的状态显示 self:applyState() end -- 解锁一个摊位(暂时直接解锁,不判断解锁条件) function StallDesk:unlockStall() self.state = StallConst.State.idle -- 设置摊位状态 UserDataMgr.buildingUserData:setBuildingState(self.id, self.state) UserDataMgr.buildingUserData:clearStallUnlockTime(self.id) -- 切换到招聘中的状态显示 self:applyState() self.state = StallConst.State.idle end function StallDesk:delayUnlock(startTime, delayTime) local progress = self.buildingNode:Seek("progress") local progress_bar = self.buildingNode:Seek("progress_bar") local progress_value = self.buildingNode:Seek("progress_value")[TMPro] local progress_bar_material = progress_bar[SpriteRenderer].material progress_bar_material:SetFloat("_FillAmount", 1) progress:SetActive(true) progress_value.text = self:getRemainingTimeDesc(delayTime) local currTime = os.time() - startTime self.tmr = self:timer(function(dt) if currTime >= delayTime then self:clear(true, self.tmr) progress:SetActive(false) self:unlockStall() else currTime = currTime + dt local percent = math.max(0, currTime / delayTime) progress_bar_material:SetFloat("_FillAmount", 1 - percent) local remaining = math.max(0, delayTime - currTime) progress_value.text = self:getRemainingTimeDesc(remaining) end end, 1, 0) end function StallDesk:getRemainingTimeDesc(remaining) if remaining <= 60 then return string.format("%d秒", math.floor(remaining)) else if remaining <= 3600 then return string.format("%s分%s秒", math.floor(remaining / 60), math.floor(remaining % 60)) else return string.format("%s时%s分", math.floor(remaining / 3600), math.floor(remaining % 3600 / 60)) end end end -- 根据id显示摊位类型 function StallDesk:showStallType(stallTypeId) local stallType = StallConst.stallStyle[stallTypeId] printInfo(LOGTAG, "摊位类型: %s", stallType) -- 隐藏招租牌 self:applyState() -- 根据摊位类型设置摊位样式 self.buildingNode:Seek(stallType):SetActive(true) -- 显示摊主角色动画 self.vendor:SetActive(true) -- util.spine.play(vendor, "idle") -- 设置摊位状态 self.state = StallConst.State.used self:applyState() end -- 设置摊位状态 function StallDesk:setStallState(state) self.state = state end return StallDesk HttpCmdDef --[[ author:{zhangpeng} time:2023-08-17 12:01:47 ]] local HttpCmdDef,_ = defClassStatic("HttpCmdDef") local baseUrl = "" HttpCmdDef.CMD = { -- 登录 LOGIN_BY_DEVICE = "/account/loginByDeviceId",--设备id登录 token required: false BIND_EMAIL = "/account/bindEmail",--绑定email token required: true BIND_APPLE = "/account/bindAppleId", -- 游客绑定苹果账号 BIND_FACEBOOK = "/account/bindFacebookId", -- 游客绑定facebook账号 LOGIN_BY_EMAIL_PW = "/account/loginByEmailPassword",--邮箱账号密码登录 token required: false LOGIN_BY_FACEBOOK = "/account/loginByFacebookId",-- facebook 登录,会发送数据(id) LOGIN_BY_APPLEID = "/account/loginByAppleIdToken", -- AppleId 登录 SAVE_USER_DATA = "/user/saveData", -- 保存数据 GET_USER_DATA = "/user/getData",-- 获取数据 -- 支付 VERIFY_TRANS = "/payment/apple/verifyTransaction", -- 支付验签 GET_PRODUCTS_LIST = "/payment/product/getList", -- 支付后请求商品列表 VERIFY_TRANS_GOOGLE = "/payment/google/verifyTransaction", -- 支付验签 SHOP_ITEM_LIST = "/payment/shop/getItemList", -- 商城内商品列表 -- 使用金币/钻石购买物品 BUY_RESOURCES = "/shop/purchase", -- 购买资源 -- 角色列表相关 ROLE_CREATE = "/role/create", -- 创建角色 ROLE_UPDATE = "/role/update", --更新角色 ROLE_GET_LIST = "/role/getList", -- 拉取角色列表 ROLE_DEL = "/role/delete", -- 删除角色 -- 货币相关 -- 宠物猫互动系统 PET_UNLOCK = "/pet/unlock", -- 解锁宠物 PET_GET_PET_LIST = "/pet/getList", -- 获取宠物列表 PET_BUY_ITEM = "/pet/buyItem", -- 购买宠物道具 PET_GET_ITEM_LIST = "/pet/getItems",-- 获取已有道具列表 PET_USE_ITEM = "/pet/useItem", -- 使用道具 -- 广告 AD_GET_REWARD = "/ad/getRewards", -- 获取广告奖励 -- 获取服务器时间 GET_TIME_SERVER = "/global/getServerTime", -- 获取服务器时间 -- 更新 GET_APP_VERSION = "/global/getAppVersionInfo" -- 获取版本号 } HttpCmdDef.ErrorCode = { TOKEN_ERROR = { ID = 10002, TEXT = "token 错误", }, EMAIL_BINDED = { ID = 20001, TEXT = "邮箱已经绑定过", }, EMAIL_PW_ERROR = { ID = 20002, TEXT = "邮箱登录密码错误", } , EMAIL_BE_USED = { ID = 20003, TEXT = "邮箱已经被占用", } , APPLE_ACCOUNT_BIND_EXIST = { ID = 20005, TEXT = "该账号已经被绑定过,不能重复绑定", } , COIN_NOT_ENOUGH = { ID = 30001, TEXT = "金币不足", }, GEM_NOT_ENOUGH = { ID = 30002, TEXT = "钻石不足", }, NET_BAD_GATEWAY = { ID = 502, TEXT = "网络异常(Bad Gateway)", } } HttpCmdDef.ErrorCodeNet ={ { code = 502, text = "Bad Gateway" }, { code = 0, text = "网络异常,请重试" } } function HttpCmdDef:init() if BUILD_ENV == ENV_DEVELOPMENT then baseUrl = "http://192.144.239.125" -- 开发环境 elseif BUILD_ENV == ENV_PRODUCTION then baseUrl = "http://192.144.239.125" -- 生产环境 end baseUrl = "http://192.144.239.125" end function HttpCmdDef.getUrlByCmd(cmd) local url = string.format("%s:3000%s",baseUrl,cmd) return url end HttpCmdDef:init()CuisineDetailUI--- 菜品详情界面 ---@class CuisineDetailUI : UILayer local CuisineDetailUI, super = defClass("CuisineDetailUI", UILayer) -- UIComponent local UIImage = CS.UnityEngine.UI.Image local UIButton = CS.UnityEngine.UI.Button local TMProUGUI = CS.TMPro.TextMeshProUGUI function CuisineDetailUI:ctor(cuisineId) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/cuisineui/cuisineuireslink") self.cuisineId = cuisineId end function CuisineDetailUI:onLoad() self.ui = GameObject.Instantiate(self.R.cuisine_detail_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end --- 初始化 function CuisineDetailUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.cuisine_img = self.ui:Seek("cuisine_img") self.cuisine_name = self.ui:Seek("cuisine_name") self.cuisine_desc = self.ui:Seek("cuisine_desc") self.cuisine_time = self.ui:Seek("cuisine_time") self.cuisine_price = self.ui:Seek("cuisine_price") self.cuisine_sales = self.ui:Seek("cuisine_sales") self.cuisine_price2 = self.ui:Seek("cuisine_price2") self.purchase_btn = self.ui:Seek("purchase_btn") self.purchase_btn_text = self.ui:Seek("purchase_btn_text") self.upgrade_btn = self.ui:Seek("upgrade_btn") self.upgrade_btn_text = self.ui:Seek("upgrade_btn_text") self.not_enough = self.ui:Seek("not_enough") self.accomplish = self.ui:Seek("accomplish") self.video_btn = self.ui:Seek("video_btn") self:initLearnLimits() self:initTags() self:initUpgradeLimit() -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addButtonClickEvent(self.purchase_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:purchase() end) util.ugui.addButtonClickEvent(self.upgrade_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:upgrade() end) util.ugui.addButtonClickEvent(self.video_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:videoPurchase() end) end --- 展示UI function CuisineDetailUI:showUI() local info = CuisineMgr:getCuisineInfo(self.cuisineId) local isUnlocked = info:isUnlocked() local isPurchased = info:isPurchased() local isMaxGrade = info:isMaxGrade() local learnShortage = info:learnShortage() local price = info:getLearnPrice() local needPurchased = isUnlocked and (not isPurchased) local needUpgrade = isPurchased and (not isMaxGrade) -- 图片 self.cuisine_img[UIImage].sprite = info:getIcon() util.ugui:setImageTileSize(self.cuisine_img[UIImage], 220) -- 基础信息 self.cuisine_name[TMProUGUI].text = info:getName() self.cuisine_desc[TMProUGUI].text = info:getDesc() self.cuisine_time[TMProUGUI].text = info:getMakeTime() .. "s" self.cuisine_price[TMProUGUI].text = info:getCurrPriceDesc() self.cuisine_price2:SetActive(isPurchased and (not isMaxGrade)) self.cuisine_sales[TMProUGUI].text = info:getSales() if isPurchased and (not isMaxGrade) then self.cuisine_price2[TMProUGUI].text = info:getNextPriceDesc() end -- 购买按钮 self.purchase_btn:SetActive(needPurchased) self.not_enough:SetActive(needPurchased and (learnShortage > 0)) if needPurchased then if price <= 0 then self.purchase_btn_text[TMProUGUI].text = "免费" else self.purchase_btn_text[TMProUGUI].text = price .. "学习" end if learnShortage > 0 then self.not_enough[TMProUGUI].text = "金币不够了,还差" .. learnShortage .. "金币" end end self:showLearnLimits(info, needPurchased) -- 视频购买按钮 self.video_btn:SetActive(needPurchased and (learnShortage > 0) and (learnShortage < price * 0.2)) -- 标签 self:showTags(info) -- 升级 self.upgrade_btn:SetActive(needUpgrade) --self.upgrade_btn[UIButton].interactable = info:isPushUpgradeLimit() self.accomplish:SetActive(isPurchased and isMaxGrade) self:showUpgradeLimit(info, needUpgrade) end function CuisineDetailUI:initLearnLimits() self.learnLimits = {} for i = 1, 3 do self.learnLimits[i] = self.ui:Seek("cuisine_buy_limit_" .. i) end end function CuisineDetailUI:showLearnLimits(info, canShow) for i = 1, 3 do local has = (not (info:getLearnLimitId(i) == -1)) and canShow self.learnLimits[i]:SetActive(has) if has then self.learnLimits[i][TMProUGUI].text = info:getLearnLimitDesc(i) end end end --- 初始化标签 function CuisineDetailUI:initTags() self.tags = {} for i = 1, 3 do self.tags[i] = self.ui:Seek("cuisine_tag_" .. i) end end --- 展示标签 function CuisineDetailUI:showTags(info) for i = 1, 3 do local has = info:getTagId(i) ~= -1 self.tags[i]:SetActive(has) if has then self.tags[i][TMProUGUI].text = info:getTagDesc(i) end end end --- 初始化升级限制 function CuisineDetailUI:initUpgradeLimit() self.upgradeLimits = {} for i = 1, 3 do self.upgradeLimits[i] = self.ui:Seek("cuisine_upgrade_limit_" .. i) end end --- 展示升级限制 function CuisineDetailUI:showUpgradeLimit(info, canShow) for i = 1, 3 do local has = (not (info:getUpgradeLimitId(i) == -1)) and canShow self.upgradeLimits[i]:SetActive(has) if has then self.upgradeLimits[i][TMProUGUI].text = info:getUpgradeLimitDesc(i) end end end --- 购买菜品 function CuisineDetailUI:purchase() local info = CuisineMgr:getCuisineInfo(self.cuisineId) local learnShortage = info:learnShortage() if learnShortage > 0 then PopUpUI.new("金币不够了,还差" .. learnShortage .. "金币"):show():showMask():enableCloseWhenClickMask() return end if not info:isPushLearnLimit() then PopUpUI.new(info:getLearnDesc()):show():showMask():enableCloseWhenClickMask() return end CuisineMgr:cuisineLearn(self.cuisineId) --self:showUI() self:close() end --- 视频购买菜品 function CuisineDetailUI:videoPurchase() local info = CuisineMgr:getCuisineInfo(self.cuisineId) local learnShortage = info:learnShortage() CuisineMgr:cuisineLearnByVideo(self.cuisineId, learnShortage) --self:showUI() self:close() end --- 升级菜品 function CuisineDetailUI:upgrade() local info = CuisineMgr:getCuisineInfo(self.cuisineId) if not info:isPushUpgradeLimit() then PopUpUI.new(info:getUpgradeDesc()):show():showMask():enableCloseWhenClickMask() return end CuisineMgr:cuisineUpgrade(self.cuisineId) self:showUI() end return CuisineDetailUImainlocal LOGTAG = "[main/main]" require("common/device/main") require("common/main") require("data/main") require("modules/main") printInfo(LOGTAG,"main/main.lua 进入游戏逻辑 end 999 888 ") App.init() StageDesk --[[ 舞台 author:{zhangpeng} time:2025-07-07 17:09:21 ]] local StageDesk,super = defClass("StageDesk",BuildingBase) local LOGTAG = "StageDesk" function StageDesk:ctor(args) super.ctor(self,args) self.deskCfgId = args.deskId self.resId = "building_"..self.buildingInfo.buildingType self.state = StageDeskConst.State.idle self.seats = {} self:initDeskView() end function StageDesk:initDeskView() self.buildingNode = GameObject.Instantiate(self.scene.R[self.resId]) self.sortBaseNode = self.buildingNode:Seek("base") self:setBuildingParent(self.buildingParent) self:setBuildingNodeName(self.deskCfgId) self.sortBaseNode = self.buildingNode:Seek("base") -- 每个舞台有4把椅子(yz_1,yz_2,yz_3,yz_4),每个椅子有2个座位点(yz_point_1,yz_point_2) local seatIndex = 1 for i = 1, 4 do local chairNode = self.buildingNode:Seek("yz_"..i) if chairNode then printInfo(LOGTAG, string.format("舞台[%s]找到椅子节点: yz_%d", self.deskCfgId, i)) for j = 1, 2 do local yzPointNode = chairNode:Seek("yz_point_"..j) if yzPointNode then -- 创建座位对象 local seat = StageDeskSeat.new(yzPointNode) seat:setStageDesk(self, seatIndex) seat.seatPoint = yzPointNode -- 直接设置座位点 -- 添加到座位列表 table.insert(self.seats, seat) seatIndex = seatIndex + 1 end end end end printInfo(LOGTAG, string.format("舞台[%s]座位总数: %d", self.deskCfgId, #self.seats)) end -- 获取舞台状态 function StageDesk:getState() return self.state end -- 设置舞台状态 function StageDesk:setState(state) self.state = state end -- 获取空闲座位 function StageDesk:findIdleSeat() for i = 1, #self.seats do local seat = self.seats[i] if seat:isCanSitDown() then return seat end end return nil end -- 检查是否有空闲座位 function StageDesk:hasIdleSeat() for i = 1, #self.seats do if self.seats[i]:isCanSitDown() then return true end end return false end -- 检查是否有顾客在观看 function StageDesk:hasCustomer() for i = 1, #self.seats do if self.seats[i]:getState() == StageDeskConst.SeatState.watching then return true end end return false end -- 更新舞台状态 function StageDesk:updateState() -- 根据座位状态更新舞台状态 if self:hasIdleSeat() then self:setState(StageDeskConst.State.idle) end end -- 获取所有座位 function StageDesk:getAllSeats() return self.seats end return StageDeskAudioMgrJ local AudioMgr = defClassStatic("AudioMgr") function AudioMgr:init() self.backgroundMusic = UserDataMgr.settingUserData:isBackgroundMusicIsOn() or true self.soundEffects = UserDataMgr.settingUserData:isSoundEffectIsOn() or true self.go = GameObject("AudioManager") UnityEngine.Object.DontDestroyOnLoad(self.go) self.bmAudioSource = self.go:AddComponent(typeof(UnityEngine.AudioSource)) self.seAudioSource = self.go:AddComponent(typeof(UnityEngine.AudioSource)) self:playBackgroundMusicByName(AudioConst.audioClipName.bgm, 0.5) end --- 设置背景音乐是否开启 function AudioMgr:setBackgroundMusicEnabled(enabled) self.backgroundMusic = enabled if enabled then self.bmAudioSource:Play() else self.bmAudioSource:Pause() end UserDataMgr.settingUserData:setBackgroundMusicIsOn(enabled) end --- 设置背景音乐是否开启 function AudioMgr:setSoundEffectsEnabled(enabled) self.soundEffects = enabled if not enabled then self.bmAudioSource:Pause() end UserDataMgr.settingUserData:setSoundEffectIsOn(enabled) end --- 获取背景音乐是否开启 function AudioMgr:isBackgroundMusicEnabled() return self.backgroundMusic end --- 获取音效是否开启 function AudioMgr:isSoundEffectsEnabled() return self.soundEffects end --- 播放背景音乐 function AudioMgr:playBackgroundMusic(audioClip, volume) if not self.backgroundMusic or not audioClip then return end self.bmAudioSource.clip = audioClip self.bmAudioSource.loop = true self.bmAudioSource.volume = volume or 1.0 self.bmAudioSource:Play() end --- 播放音效 function AudioMgr:playSoundEffect(audioClip, volume) if not self.soundEffects or not audioClip then return end self.seAudioSource:Stop() -- 确保每次播放前先停止 self.seAudioSource.clip = audioClip self.seAudioSource.loop = false self.seAudioSource.volume = volume or 1.0 self.seAudioSource:Play() end --- 停止背景音乐 function AudioMgr:stopBackgroundMusic() if self.bmAudioSource then self.bmAudioSource:Stop() end end --- 停止音效 function AudioMgr:stopSoundEffect() if self.seAudioSource then self.seAudioSource:Stop() end end --- 播放背景音乐 function AudioMgr:playBackgroundMusicByName(audioClipName, volume) self:playBackgroundMusic(self:getClip(audioClipName), volume) end --- 播放音效 function AudioMgr:playSoundEffectByName(audioClipName, volume) self:playSoundEffect(self:getClip(audioClipName), volume) end --- 获取音频剪辑 function AudioMgr:getClip(audioClipName) if not self.clipList then self.clipList = {} end if self.clipList[audioClipName] then return self.clipList[audioClipName] end local path = string.format("Assets/AssetsPackage/Res/common/audios/%s.mp3", audioClipName) if ResLoader.hasAsset(path) then local clip = ResLoader.loadAsset(path, typeof(UnityEngine.AudioClip)) self.clipList[audioClipName] = clip return clip else return nil end end return AudioMgr BuildingBase --[[ 设施基类 author:{zhangpeng} time:2025-05-13 10:36:06 ]] local BuildingBase = defClass("BuildingBase") local Quaternion = CS.UnityEngine.Quaternion local LOGTAG = "BuildingBase" function BuildingBase:ctor(args) self.scene = args.scene self.buildingParent = args.parent self.buildingArgs = args -- 建筑物在场景里的名字 self.buildingNodeName = nil -- 如果是摊位,不初始化BuildingInfo self:initData(args.deskId) end function BuildingBase:initData(buildingId) self.buildingId = buildingId self.buildingInfo = BuildingMgr:getBuildingInfo(self.buildingId) self.buildingCfg = self.buildingInfo.config end -- 获取建筑ID function BuildingBase:getBuildingId() return self.buildingId end -- 获取建筑信息(再用buildingInfo调用BuildingInfo里的方法获取相关信息) function BuildingBase:getBuildingInfo() return self.buildingInfo end -- 设置建筑物在场景里的名字 function BuildingBase:setBuildingNodeName(name) self.buildingNodeName = name self.buildingNode:SetName(name) end -- 设置建筑物父节点 function BuildingBase:setBuildingParent(parent) self.buildingParent = parent self.buildingNode.transform.parent = parent.transform self.buildingNode.transform.localPosition = Vector3.zero self.buildingNode.transform.localRotation = Quaternion.identity self.buildingNode.transform.localScale = Vector3.one end -- 设置建筑物层级 function BuildingBase:setSortingOrder(sortingOrder) local gameObject = self.buildingNode -- SpriteRender local sprite_render = gameObject:GetComponent("Renderer") -- SkeletonAnimation local skeletonAnimation = gameObject:GetComponent("SkeletonAnimation") if sprite_render then sprite_render.sortingOrder = sortingOrder elseif skeletonAnimation then local spine_render = skeletonAnimation:GetComponent("Renderer") spine_render.sortingOrder = sortingOrder end end -- 获取建筑层级 function BuildingBase:getBuildingSortingOrder() local buildingImgNode = self.buildingNode:Seek("img") -- SpriteRender local sprite_render = buildingImgNode:GetComponent("Renderer") -- SkeletonAnimation local skeletonAnimation = buildingImgNode:GetComponent("SkeletonAnimation") if sprite_render then return sprite_render.sortingOrder elseif skeletonAnimation then local spine_render = skeletonAnimation:GetComponent("Renderer") return spine_render.sortingOrder end end -- 获取建筑位置 function BuildingBase:getBuildingPosition() return self.buildingNode.transform.position end return BuildingBase FishingConst-- 捕鱼玩法常量 local FishingConst = defClassStatic("FishingConst") -- 初始化 function FishingConst:init() FishingConst.MaxGrade = table.count(FishingGradeCfgParse:getAllFishingGradeCfg()) end -- 通关时间限制 FishingConst.GameTimeLimit = 300 -- 每个区块最大生成数量 FishingConst.CellMaxCount = 6 -- 最大等级 FishingConst.MaxGrade = 5 -- 水雷生成等级 FishingConst.MineSpawnGrade = 7 -- 水雷生成数量 FishingConst.MineSpawnCountMin = 5 FishingConst.MineSpawnCountMax = 10 -- 眩晕时间 FishingConst.DizzyTime = 5 -- 复活后的无敌时间 FishingConst.ReviveInvincibleTime = 2 -- 护盾后无敌时间 FishingConst.ShieldInvincibleTime = 1 -- 双倍经验时间 FishingConst.DoubleEnergyTime = 10 -- 每日免费游戏次数上限 FishingConst.FreeGameCountLimit = 3 -- 鱼被吃后刷新的时间 FishingConst.FishRefreshTime = 10 -- 道具类型 FishingConst.PropType = { -- 无 None = 0, -- 眩晕 Dizzy = 1, -- 能量 Energy = 2, -- 护盾 Shield = 3, } -- 每关道具次数上限 FishingConst.PropCountLimit = { [FishingConst.PropType.Dizzy] = 1, -- 眩晕 [FishingConst.PropType.Energy] = 1, -- 能量 [FishingConst.PropType.Shield] = 1, -- 护盾 } FishingConst.PropRewardType = { -- 无 None = 0, -- 星星 Star = 1, } FishingConst:init() GachaDeskMgrv --[[ 扭蛋机管理 author:{zhangpeng} time:2025-07-09 11:11:41 ]] local GachaDeskMgr = defClassStatic("GachaDeskMgr") local LOGTAG = "GachaDeskMgr" -- init function GachaDeskMgr:init() self.gachaDesks = {} -- todo self.userData = GachaUserData.new() self.userData:load() -- 加载扭蛋用户数据 end -- 根据建筑id创建一个扭蛋机 function GachaDeskMgr:createGachaDesk(buildingInfo) local parentNode = RestaurantMgr:getRestaurantScene().rootNode:Seek("building_pos_" .. BuildingConst.buildingType.gashapon) local args = { deskId = BuildingConst.buildingType.gashapon, parent = parentNode, scene = RestaurantMgr:getRestaurantScene(), } local gachaDesk = GachaDesk.new(args) table.insert(self.gachaDesks, gachaDesk) return gachaDesk end -- 获取扭蛋消耗 function GachaDeskMgr:getGachaCost(count) return GachaConst.DrawCost[count] end -- 检查是否可以进行扭蛋 function GachaDeskMgr:canGacha(count) local cost = self:getGachaCost(count) local currentAmount = CurrencyMgr:getCurrencyValue(GachaConst.CostType) return currentAmount >= cost end function GachaDeskMgr:doGacha(count) -- 检查消耗 if not self:canGacha(count) then PopUpUI.new("金币不足"):show():showMask():enableCloseWhenClickMask() return false, "资源不足" end local cost = self:getGachaCost(count) CurrencyMgr:changeCurrencyValue(GachaConst.CostType, -cost) printInfo(LOGTAG, string.format("扭蛋消耗:%d 金币", cost)) local rewards = GachaRewardCfgParse:getRandomRewards(count) local finalRewards = self:calculateAndGiveRewards(rewards) -- 更新扭蛋统计 UserDataMgr:addUseEggsCount(count) return true, finalRewards end -- 计算并发放奖励 function GachaDeskMgr:calculateAndGiveRewards(rewards) local finalRewards = {} for _, rewardCfg in ipairs(rewards) do local finalReward = {} finalReward.type = rewardCfg.rewardItemType finalReward.itemId = rewardCfg.rewardItemId finalReward.count = GachaRewardCfgParse:calculateRewardCount(rewardCfg) finalReward.typeName = GachaRewardCfgParse:getRewardTypeName(rewardCfg.rewardItemType) finalReward.cfgId = rewardCfg.id -- 根据奖励类型发放奖励 self:giveRewardByType(finalReward) table.insert(finalRewards, finalReward) printInfo(LOGTAG, string.format("扭蛋获得:%s x%d", finalReward.typeName, finalReward.count)) end return finalRewards end -- 根据奖励类型发放奖励 function GachaDeskMgr:giveRewardByType(reward) if reward.type == 1 then -- 金币 CurrencyMgr:changeCoin(reward.count) elseif reward.type == 2 then -- 音符 CurrencyMgr:changeMusicalNotes(reward.count) elseif reward.type == 3 then -- 玩偶 if reward.itemId > 0 then printInfo(LOGTAG, string.format("获得玩偶:ID=%d, 数量=%d", reward.itemId, reward.count)) -- TODO: 这里需要根据具体的玩偶管理系统来实现 -- 可能需要调用 DollMgr:addDoll(reward.itemId, reward.count) -- 或者 InventoryMgr:addItem(ItemType.Doll, reward.itemId, reward.count) DollMgr:addDollCount(reward.itemId, reward.count) end end end return GachaDeskMgr GuideUserData'local GuideUserData = defClass("GuideUserData") local LOGTAG = "GuideUserData" function GuideUserData:ctor() self:init() end function GuideUserData:init() self.forceGuideStep = 1 self.taskGuideStep = 1 end function GuideUserData:resetData() self.forceGuideStep = 1 self.taskGuideStep = 1 -- 保存 self:save() end function GuideUserData:load() -- 如果是新玩家,重置数据 if UserDataMgr.isNewPlayer then self:resetData() else self:loadFromLocal() end end function GuideUserData:loadFromLocal() self.forceGuideStep = PlayerPrefsMgr:getInt(StrogeKeyDef.FORCE_GUIDE_STEP, 1) self.taskGuideStep = PlayerPrefsMgr:getInt(StrogeKeyDef.TASK_GUIDE_STEP, 1) end function GuideUserData:save() self:saveToLocal() end function GuideUserData:saveToLocal() PlayerPrefsMgr:setInt(StrogeKeyDef.FORCE_GUIDE_STEP, self.forceGuideStep) PlayerPrefsMgr:setInt(StrogeKeyDef.TASK_GUIDE_STEP, self.taskGuideStep) end -- 获取当前引导步骤 function GuideUserData:getForceGuideStep() return self.forceGuideStep end -- 设置当前引导步骤 function GuideUserData:setForceGuideStep(step, refuseSave) self.forceGuideStep = step -- 保存 if not refuseSave then self:save() end printInfo(LOGTAG, "设置引导步骤为: %d", step) end -- 获取当前任务引导步骤 function GuideUserData:getTaskGuideStep() return self.taskGuideStep end -- 设置当前任务引导步骤 function GuideUserData:setTaskGuideStep(step, refuseSave) self.taskGuideStep = step -- 保存到本地存储 if not refuseSave then self:save() end printInfo(LOGTAG, "设置任务引导步骤为: %d", step) end return GuideUserData FishingMgr--[[ 池塘管理 author:{zhangpeng} time:2025-06-03 14:47:30 ]] local FishingMgr = defClassStatic("FishingMgr") local LOGTAG = "FishingMgr" function FishingMgr:getFishingScene() return self.fishingScene end function FishingMgr:setFishingScene(fishingScene) self.fishingScene = fishingScene end function FishingMgr:start() printInfo(LOGTAG, "=== 开始捕鱼流程 ===") end -- 获取餐厅地图 function FishingMgr:getFishingMapNode() local fishingScene = self:getFishingScene() return fishingScene.rootNode:Seek(MapsConst.mapNodeNames[MapsConst.MapType.restaurant]) -- todo --return fishingScene.rootNode:Seek(MapsConst.mapNodeNames[MapsConst.MapType.fishing_island]) end function FishingMgr:getFishingEnter() return self.fishingScene.rootNode:Seek("fishing_enter_point") end function FishingMgr:getFishingExit() -- todo return self.fishingScene.rootNode:Seek("out_point") end -- 获取普通顾客根节点 function FishingMgr:getCustomerRoot() local scene = self:getFishingScene() return scene.rootNode:Seek("customer_root") end -- 普通顾客入店 池塘 function FishingMgr:customerEnterTheFishing(customer) customer:setSceneType(MapsConst.MapType.fishing_island) local customerRoot = self:getCustomerRoot() local mapNode = self:getFishingMapNode() local birthPoint = self:getFishingEnter() local birthPos = birthPoint and birthPoint:GetPosition() or Vector3(0, 0, 0) -- 设置顾客父节点 customer:setCustomerParent(customerRoot) customer:setCurPosition(Vector3(birthPos.x, birthPos.y, birthPos.z)) customer:setMapRoot(mapNode) -- 设置寻路网格 customer:setRolePathGrid(MapsConst.GridGraphsName[MapsConst.MapType.fishing_island]) return customer end -- 开始普通顾客入店逻辑 function FishingMgr:startCustomerEnterTheFishingLogic(customer, boothId, vendorId) end return FishingMgr customerCfg"--[[ from file:顾客表.xlsx --]] local customerCfg = { [1] = { id = 400001, name = "豆豆", nature = "乐观", desc = "小短腿配电动臀,跑起来像移动吐司!", movespeed = "100", visitNeedStar = 0, visitItemId = {["c"]=200001}, favoriteFoods = {200001}, solicitTagId = -1, funId = {}, artRes = "gk_pt_qiutian", plotId = 640101, shareCoin = -1, newCustomerTip = 0, defaultShow = 0, artBustRes = "role_keji", }, [2] = { id = 400002, name = "阿布", nature = "懒惰", desc = "虽然是个牛仔,但却只点牛奶", movespeed = "100", visitNeedStar = 0, visitItemId = {}, favoriteFoods = {200002,200003}, solicitTagId = -1, funId = {}, artRes = "gk_pt_qiutian", plotId = 640201, shareCoin = -1, newCustomerTip = 0, defaultShow = 0, artBustRes = "role_jumao", }, [3] = { id = 400003, name = "小伞", nature = "优雅", desc = "大大的毛茸茸的尾巴,火红色的小松鼠", movespeed = "100", visitNeedStar = 10, visitItemId = {}, favoriteFoods = {200002,200003,200004}, solicitTagId = -1, funId = {40001}, artRes = "gk_pt_qiutian", plotId = -1, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_songshu", }, [4] = { id = 400004, name = "阿叶", nature = "内向", desc = "睡神附体抱树狂,吃桉叶就像嗑了安眠药。", movespeed = "100", visitNeedStar = 0, visitItemId = {["b"]=101401,["c"]=200006}, favoriteFoods = {200003,200004,200005,200006}, solicitTagId = -1, funId = {}, artRes = "gk_pt_qiutian", plotId = -1, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_kaola", }, [5] = { id = 400005, name = "泡芙", nature = "敏捷", desc = "总是赶着时间,时不时就要看一下手中的怀表", movespeed = "100", visitNeedStar = 0, visitItemId = {["c"]={200003,200004,200005}}, favoriteFoods = {200003,200004,200005}, solicitTagId = -1, funId = {40021}, artRes = "gk_pt_qiutian", plotId = -1, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_baitu", }, [6] = { id = 400006, name = "卷卷", nature = "毒舌", desc = "发型飘逸表情包,不高兴就送你“口水面膜”", movespeed = "100", visitNeedStar = 20, visitItemId = {}, favoriteFoods = {200004,200005,200006,200007}, solicitTagId = 4001, funId = {}, artRes = "gk_pt_qiutian", plotId = 640601, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_yangtuo", }, [7] = { id = 400007, name = "阿呆", nature = "温和", desc = "可爱憨厚爱睡觉", movespeed = "100", visitNeedStar = 30, visitItemId = {["c"]=200004}, favoriteFoods = {200002,200003,200004}, solicitTagId = 4001, funId = {}, artRes = "gk_pt_qiutian", plotId = 640701, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_mianyang", }, [8] = { id = 400008, name = "毛球", nature = "温和", desc = "脾气非常好,就算被冷眼相待,也会不计前嫌和你说话", movespeed = "100", visitNeedStar = 30, visitItemId = {["c"]=200004}, favoriteFoods = {200002,200003,200004}, solicitTagId = 4001, funId = {}, artRes = "gk_pt_qiutian", plotId = 640801, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_jinmao", }, [9] = { id = 400009, name = "蜜豆", nature = "温和", desc = "一副长不大的模样,对冷饮的热爱倒是与日俱增", movespeed = "100", visitNeedStar = 30, visitItemId = {["c"]=200004}, favoriteFoods = {200002,200003,200004}, solicitTagId = 4001, funId = {}, artRes = "gk_pt_qiutian", plotId = -1, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_erkuohu", }, [10] = { id = 400010, name = "奶盖", nature = "温和", desc = "去过很多地方,见过很多事件", movespeed = "100", visitNeedStar = 30, visitItemId = {["c"]=200004}, favoriteFoods = {200002,200003,200004}, solicitTagId = 4001, funId = {}, artRes = "gk_pt_qiutian", plotId = -1, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_xuediao", }, [11] = { id = 400011, name = "糯糯", nature = "温和", desc = "嘴里一直需要嚼着吃什么东西", movespeed = "100", visitNeedStar = 30, visitItemId = {["c"]=200004}, favoriteFoods = {200002,200003,200004}, solicitTagId = 4001, funId = {}, artRes = "gk_pt_qiutian", plotId = -1, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_huitu", }, [12] = { id = 400012, name = "垂垂", nature = "温和", desc = "拳馆老板,浑身都是肌肉", movespeed = "100", visitNeedStar = 30, visitItemId = {["c"]=200004}, favoriteFoods = {200002,200003,200004}, solicitTagId = 4001, funId = {}, artRes = "gk_pt_qiutian", plotId = -1, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_mihuan", }, [13] = { id = 400013, name = "嘟嘟", nature = "温和", desc = "总是喜欢往荒野里钻,尾巴一翘就能闻着古秘宝藏的味儿", movespeed = "100", visitNeedStar = 30, visitItemId = {["c"]=200004}, favoriteFoods = {200002,200003,200004}, solicitTagId = 4001, funId = {}, artRes = "gk_pt_qiutian", plotId = -1, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_sanhuakeji", }, [14] = { id = 400014, name = "雪绒", nature = "温和", desc = "头上长着大大的角,走路小心翼翼的怕撞到别人", movespeed = "100", visitNeedStar = 30, visitItemId = {["c"]=200004}, favoriteFoods = {200002,200003,200004}, solicitTagId = 4001, funId = {}, artRes = "gk_pt_qiutian", plotId = -1, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_xunlu", }, [15] = { id = 401001, name = "看火人亨利", nature = "温和", desc = "慢悠悠爬上树杈往远处瞭望,一有风吹草动就随时准备敲响防火警报", movespeed = "100", visitNeedStar = 30, visitItemId = {["c"]=200004}, favoriteFoods = {200002,200003,200004}, solicitTagId = 4001, funId = {}, artRes = "gk_pt_qiutian", plotId = -1, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_kanhuoren", }, [16] = { id = 401002, name = "旅游博主阿比", nature = "温和", desc = "穿梭在大街小巷,喜欢用镜头记录下每一处风景和美食", movespeed = "100", visitNeedStar = 30, visitItemId = {["c"]=200004}, favoriteFoods = {200002,200003,200004}, solicitTagId = 4001, funId = {}, artRes = "gk_pt_qiutian", plotId = -1, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_lvyoubozhu", }, [17] = { id = 401003, name = "说唱歌手莱娜", nature = "温和", desc = "火力全开的说唱歌手,用节奏和韵律当武器", movespeed = "100", visitNeedStar = 30, visitItemId = {["c"]=200004}, favoriteFoods = {200002,200003,200004}, solicitTagId = 4001, funId = {}, artRes = "gk_pt_qiutian", plotId = -1, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_shuochanggeshou", }, [18] = { id = 401004, name = "背包客球球", nature = "温和", desc = "背着大大的背包,怀揣着对世界的好奇,用脚步丈量大地", movespeed = "100", visitNeedStar = 30, visitItemId = {["c"]=200004}, favoriteFoods = {200002,200003,200004}, solicitTagId = 4001, funId = {}, artRes = "gk_pt_qiutian", plotId = -1, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_beibaoke", }, [19] = { id = 401005, name = "夏令营老师欣欣", nature = "温和", desc = "背着装满小惊喜的背包,在带着人们探索自然", movespeed = "100", visitNeedStar = 30, visitItemId = {["c"]=200004}, favoriteFoods = {200002,200003,200004}, solicitTagId = 4001, funId = {}, artRes = "gk_pt_qiutian", plotId = -1, shareCoin = -1, newCustomerTip = 1, defaultShow = 0, artBustRes = "role_xialingying", }, } return customerCfg CookBookCfgParse --[[ 菜谱配置解析 author:{zhangpeng} time:2025-05-17 19:34:37 ]] local CookBookCfgData = require("data/config/cookBookCfg") ---@class CookBookCfgParse local CookBookCfgParse = defClassStatic("CookBookCfgParse") local LOGTAG = "CookBookCfgParse" local cookBook_list = {} local function initAllIds() for k, v in pairs(CookBookCfgData) do table.insert(cookBook_list, v.id) end end function CookBookCfgParse:init() initAllIds() end function CookBookCfgParse:getCookBookCfg(id) for k,v in pairs(CookBookCfgData) do if v.id == id then return v end end printError(LOGTAG, string.format("没有找到菜品配置[%s]", id)) return nil end -- 获取制作时间 function CookBookCfgParse:getMakeTime(id) local cfg = self:getCookBookCfg(id) return cfg.maketime end -- 获取菜品列表 function CookBookCfgParse:getData() return CookBookCfgData end -- 获取菜品列表 ---@param typeId number 菜品类型id ---@return table 菜品列表数据 function CookBookCfgParse:getDataByType(typeId) local tableViewData = {} for i, cuisineData in ipairs(CookBookCfgData) do if cuisineData.ctype == typeId then table.insert(tableViewData, cuisineData) end end return tableViewData end -- 获取菜品列表 ---@param typeId number 菜品类型id ---@return table 菜品列表数据 function CookBookCfgParse:getDataByTypeList(typeList) local tableViewData = {} for _, cuisineData in ipairs(CookBookCfgData) do for i, v in ipairs(typeList) do if cuisineData.ctype == v then table.insert(tableViewData, cuisineData) end end end return tableViewData end -- 获取菜品列表里tableview需要的数据结构 ---@param typeId number 菜品类型id ---@return table 菜品列表数据 function CookBookCfgParse:getDataForTableView(typeId) local tableViewData = {} local rowIndex = 1 for _, cuisineData in pairs(CookBookCfgData) do if cuisineData.ctype == typeId then local oneRow = { value = cuisineData, rowIndex = rowIndex } table.insert(tableViewData, oneRow) rowIndex = rowIndex + 1 end end return tableViewData end --- 根据菜品id列表获取菜品配置数据 function CookBookCfgParse:getDataByIds(ids) if type(ids) ~= "table" then return { self:getCookBookCfg(ids) } end local list = {} for _, id in ipairs(ids) do local cfg = self:getCookBookCfg(id) if cfg then table.insert(list, cfg) else printError(LOGTAG, string.format("没有找到菜品配置[%s]", id)) end end return list end CookBookCfgParse:init() SingingCustomer} ---歌手 ---@class SingingCustomer:CustomerSpecial local SingingCustomer, super = defClass("SingingCustomer", CustomerSpecial) local LOGTAG = "SingingCustomer" --- 状态 SingingCustomer.State = { Idle = 1, -- 空闲 Entrance = 2, -- 入场 Waiting = 3, -- 等待 Sing = 4, -- 唱歌 Leaving = 5, -- 离开 } --- 构造函数 function SingingCustomer:ctor(resLink) super.ctor(self, resLink, CustomerConst.CustomerSpecialType.SingerCustomer) self:init() end --- 初始化 function SingingCustomer:init() self:initDisplay("singing_customer") self:initProperties() self:startSingingLogic() end --- 初始化属性 function SingingCustomer:initProperties() self.info = SpecialCustomerMgr:getSpecialCustomerInfo(self.specialCustomerTypeId) self.state = SingingCustomer.State.Idle -- 初始状态 self.activeTime = self.info:getActiveTime() -- 活动时间 self.solicitors = self.info:getParam(1) -- 唱歌揽客数量 self.currSolicitors = 0 -- 重置揽客数量 self.clickTimes = self.info:getClickTimes() -- 点击次数 self.currClickTimes = 0 -- 当前点击次数 end --- 开始歌手逻辑 function SingingCustomer:startSingingLogic() printInfo(LOGTAG, "歌手开始活动,活动时间:%d秒", self.info:getActiveTime()) -- 入场 self:enter() -- 添加点击事件 self:addClickEvent(function() self:click() end) end --- 入店 function SingingCustomer:enter() printInfo(LOGTAG, "歌手入店") self.state = SingingCustomer.State.Entrance self:walkToPos(self:getSpecialCustomerCommonStandbyPoint().transform.position, super.tempSpeed, function() self:showDong() self:startTimer() end ) end --- 离开 function SingingCustomer:exit() printInfo(LOGTAG, "歌手离开") self.state = SingingCustomer.State.Leaving self:stopTimer(0.5) self:walkToPos(self:getSpecialCustomerExitPoint().transform.position, super.tempSpeed, function() -- 离开后销毁 self:destroy() -- 触发离开事件 end ) end --- 时间 ---@param seconds number 秒数 function SingingCustomer:timeupdate(seconds) self.time = self.time + seconds if self.state == SingingCustomer.State.Sing then if self.currSolicitors > self.solicitors then self:exit() return end self.currSolicitors = self.currSolicitors + 1 CustomerMgr.customerSolicitation:solicitation() return end if self.state == SingingCustomer.State.Waiting then if self.time >= self.activeTime then self:exit() end end end --- 开始唱歌 function SingingCustomer:sing() printInfo(LOGTAG, "歌手开始唱歌") self.state = SingingCustomer.State.Sing self.time = 0 -- 重置时间 -- todo 播放唱歌动画 end --- 交互 function SingingCustomer:click() if self.state ~= SingingCustomer.State.Waiting then return end if self.clickTimes <= 1 then self:sing() else if self.currClickTimes < self.clickTimes then self.currClickTimes = self.currClickTimes + 1 self:showClickProress(self.currClickTimes / self.clickTimes) else self:sing() end end end return SingingCustomerEmployeeFunCfgParse--[[ 员工功能配置解析 author:{zhangpeng} time:2025-05-21 10:05:46 ]] local EmployeeFunCfg = require("data/config/employeFunCfg") local EmployeeFunCfgParse = defClassStatic("EmployeeFunCfgParse") local LOG_TAG = "EmployeeFunCfgParse" local employee_fun_list = {} local function initAllIds() for k, v in pairs(EmployeeFunCfg) do table.insert(employee_fun_list, v.id) end end -- init function EmployeeFunCfgParse:init() initAllIds() end -- 根据员工功能配置id取一个员工功能配置的数据 function EmployeeFunCfgParse:getEmployeeFunCfg(employeeFunId) for k, v in pairs(EmployeeFunCfg) do if v.id == employeeFunId then return v end end end EmployeeFunCfgParse:init() FishingPauseUI-- 捕鱼暂停界面 local FishingPauseUI, super = defClass("FishingPauseUI", UILayer) -- 构造函数 function FishingPauseUI:ctor() super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/fishingui/fishinguireslink") end -- 当页面加载 function FishingPauseUI:onLoad() self.ui = GameObject.Instantiate(self.R.fishing_pause_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end -- 初始化界面 function FishingPauseUI:initUI() self.close_btn = self.ui:Seek("close_btn") self.continue_btn = self.ui:Seek("continue_btn") self.exit_btn = self.ui:Seek("exit_btn") util.ugui.addClickEvent(self.close_btn, function() self:gameContinue() end) util.ugui.addClickEvent(self.continue_btn, function() self:gameContinue() end) util.ugui.addClickEvent(self.exit_btn, function() self:backStartPage() end) end -- 展示界面 function FishingPauseUI:showUI() end -- 继续游戏 function FishingPauseUI:gameContinue() FishingGameMgr:gameContinue() self:close() end -- 退出游戏 function FishingPauseUI:backStartPage() FishingGameMgr:backStartPage() self:close() end return FishingPauseUI SpineUtil*--[[ author:{zhangpeng} time:2022-05-17 17:44:42 ]] local SpineUtil = {} local SkeletonAnimation = typeof(CS.Spine.Unity.SkeletonAnimation) local SkeletonGraphic = typeof(CS.Spine.Unity.SkeletonGraphic) local LOG_TAG = "SpineUtil" local function getSpine(gameObject) if gameObject:GetType() ~= typeof(CS.UnityEngine.GameObject) then CS.UnityEngine.Debug.LogError("SpineUtil:第一个参数不是GameObject"); return nil end return gameObject:GetComponent(SkeletonAnimation) or gameObject:GetComponent(SkeletonGraphic) end function SpineUtil.play(gameObject,animName,loop,cb,timescale) if loop == nil then loop = false end if type(loop) ~= "boolean" then CS.UnityEngine.Debug.LogError("SpineUtil.play loop is not boolean") return end local spine = getSpine(gameObject) if spine then timescale = timescale or 1 local trackEntry = spine.AnimationState:SetAnimation(0, animName, loop); spine.AnimationState.TimeScale = timescale if cb then local cb_func cb_func = function() gameObject.__spineFinishCB__ = nil trackEntry:Complete("-",cb_func) cb_func = nil cb() end if gameObject.__spineFinishCB__ then trackEntry:Complete("-", gameObject.__spineFinishCB__) gameObject.__spineFinishCB__ = nil end trackEntry:Complete("+",cb_func) gameObject.__spineFinishCB__ = cb_func Event.add(gameObject,Event.OnDestroy,function() if cb_func then trackEntry:Complete("-",cb_func) cb_func = nil end end) end else error("SpineUtil.play Spine Component not Exist on " .. gameObject.name) end end --[[ 功能: 播放完本次播放的动画之后,开始播放animName cb1 播放完本次动画之后回调 cb2 播放完animName之后回调 timescale1 默认是之前的1倍速,快速播放完本次播放的动画 timescale2 默认是1倍速播放animName ]] function SpineUtil.add(gameObject,animName,cb1,cb2,timescale1, timescale2) local spine = getSpine(gameObject) if spine then timescale1 = timescale1 or 1 local curTrackEntry = spine.AnimationState:GetCurrent(0) curTrackEntry.TimeScale = timescale1 if cb1 then local cb_func cb_func = function() gameObject.__spineFinishCB__ = nil curTrackEntry:Complete("-",cb_func) cb_func = nil cb1() end if gameObject.__spineFinishCB__ then curTrackEntry:Complete("-", gameObject.__spineFinishCB__) gameObject.__spineFinishCB__ = nil end curTrackEntry:Complete("+",cb_func) gameObject.__spineFinishCB__ = cb_func Event.add(gameObject,Event.OnDestroy,function() if cb_func then curTrackEntry:Complete("-",cb_func) cb_func = nil end end) end timescale2 = timescale2 or 1 -- spine.AnimationState:ClearTracks(); local trackEntry = spine.AnimationState:AddAnimation(0, animName, false, 0); trackEntry.TimeScale = timescale2 spine.AnimationState.TimeScale = timescale2 if cb2 then local cb_func cb_func = function() gameObject.__spineFinishCB__ = nil trackEntry:Complete("-",cb_func) cb_func = nil cb2() end if gameObject.__spineFinishCB__ then trackEntry:Complete("-", gameObject.__spineFinishCB__) gameObject.__spineFinishCB__ = nil end trackEntry:Complete("+",cb_func) gameObject.__spineFinishCB__ = cb_func Event.add(gameObject,Event.OnDestroy,function() if cb_func then trackEntry:Complete("-",cb_func) cb_func = nil end end) end else error("SpineUtil.play Spine Component not Exist on " .. gameObject.name) end end function SpineUtil.pause(gameObject) local sp = getSpine(gameObject) gameObject.__spineTimeScaleWhenPause = sp.AnimationState.TimeScale sp.AnimationState.TimeScale = 0 end function SpineUtil.resume(gameObject) local sp = getSpine(gameObject) if gameObject.__spineTimeScaleWhenPause then sp.AnimationState.TimeScale = gameObject.__spineTimeScaleWhenPause end end local tryPlay,parseFunction tryPlay = function(ctx,spine,args,name,loop,delay) local trackEntry if not ctx.playedFirst then ctx.current_info.playedFirst = not (#ctx.tasks == 0) ctx.current_info.loop = loop ctx.playedFirst = true else ctx.current_info.playedFirst = not (#ctx.tasks == 0) local nextArg = args[1] local tNext = type(nextArg) if tNext == "number" then local delay = table.remove(args,1) table.insert(ctx.parsed_args,(delay)) ctx.current_info.loop = loop ctx.current_info.delay = delay elseif tNext == "function" then parseFunction(ctx,spine,args,name,loop,0) else ctx.current_info.loop = loop ctx.current_info.delay = delay end end return trackEntry end parseFunction = function(ctx,spine,args,name,loop,delay) local cb = table.remove(args,1) table.insert(ctx.parsed_args,(cb)) local trackEntry = tryPlay(ctx,spine,args,name,loop,delay) ctx.current_info.callback = cb ctx.hasCallback = true local argNext = args[1] local tNext = type(argNext) if tNext == "number" then local delay = table.remove(args,1) table.insert(ctx.parsed_args,(delay)) tryPlay(ctx,spine,args,name,false,delay) end return trackEntry end local function parseNextSeqArg(spine,args,ctx) local first = args[1] if not first then return end local t1 = type(first) if t1 == "string" then local trackEntry local name = table.remove(args,1) table.insert(ctx.parsed_args,(name)) ctx.current_info = { name = name, loop = false, delay = 0, callback = nil, } local second = args[1] local t2 = type(second) if t2 == "boolean" then local loop = table.remove(args,1) table.insert(ctx.parsed_args,(loop)) parseFunction(ctx,spine,args,name,loop,0) elseif t2 == "function" then parseFunction(ctx,spine,args,name,false,0) else trackEntry = tryPlay(ctx,spine,args,name,false,0) end table.insert(ctx.tasks,ctx.current_info) else local iWrong = #ctx.parsed_args + 1 local argWrong = ctx.orig_args[iWrong] local argPrev = ctx.orig_args[iWrong - 1] local argNext = ctx.orig_args[iWrong + 1] if type(argWrong) == "number" and type(argNext) == "function" then error("SpineUtil.playSeq 不可以先延时后回调,不支持该语义。只能先回调后延时") end for k,v in ipairs(ctx.orig_args) do ctx.orig_args[k] = tostring(v) end for k,v in ipairs(ctx.parsed_args) do ctx.parsed_args[k] = tostring(v) end local orig = table.concat(ctx.orig_args,",") local parsed = table.concat(ctx.parsed_args,",") error(string.format("SpineUtil.playSeq 参数错误\n原始的参数:%s\n成功的参数:%s",orig,parsed)) return end parseNextSeqArg(spine,args,ctx) end function SpineUtil.playSeq(gameObject,...) local args = {...} local spine = getSpine(gameObject) if spine then local ctx = { orig_args = {}, parsed_args = {}, current_info = {}, tasks = {}, playedFirst = false, hasCallback = false, } for _,v in ipairs(args) do table.insert(ctx.orig_args,v) end parseNextSeqArg(spine,args,ctx) local prevTask for _,task in ipairs(ctx.tasks) do if task.playedFirst then task.trackEntry = spine.AnimationState:AddAnimation(0, task.name, task.loop, prevTask.delay); printInfo(LOG_TAG, string.format("[AddAnimation]name:%s,loop:%s,delay:%s",task.name,task.loop,prevTask.delay)) else -- spine.AnimationState:ClearTracks(); task.trackEntry = spine.AnimationState:SetAnimation(0, task.name, task.loop); -- printInfo(LOG_TAG, string.format("[SetAnimation]name:%s,loop:%s",task.name,task.loop)) end if task.callback then local cb_func cb_func = function() gameObject.__spineFinishCB__ = nil task.trackEntry:Complete("-",cb_func) task.callback() cb_func = nil end if gameObject.__spineFinishCB__ then task.trackEntry:Complete("-", gameObject.__spineFinishCB__) gameObject.__spineFinishCB__ = nil end task.trackEntry:Complete("+",cb_func) gameObject.__spineFinishCB__ = cb_func Event.add(gameObject,Event.OnDestroy,function() if cb_func then task.trackEntry:Complete("-",cb_func) cb_func = nil end end) end prevTask = task end return spine else error("SpineUtil.play Spine Component not Exist on " .. gameObject.name) end end function SpineUtil.hideSlot(go,name) local skeleton = getSpine(go) local slot = skeleton.Skeleton:FindSlot(name) slot.A = 0 end function SpineUtil.showSlot(go,name) local skeleton = getSpine(go) local slot = skeleton.Skeleton:FindSlot(name) slot.A = 1 end -- 替换slot图片 function SpineUtil.changeSlotTexture(go, name, sprite) if not go or not name or not sprite then return end local skeleton = getSpine(go) if skeleton then local skeletonDataAsset = skeleton.SkeletonDataAsset local sourceMaterial = skeletonDataAsset.atlasAssets[0].PrimaryMaterial local slot = skeleton.Skeleton:FindSlot(name) local oldAtt = slot.Attachment local newAtt = CS.Spine.Unity.AttachmentTools.AttachmentCloneExtensions.GetRemappedClone(oldAtt, sprite, sourceMaterial, true, true, false, false) slot.Attachment = newAtt end end -- 获取骨骼的世界坐标 function SpineUtil.getBoneWorldPos( go, boneName ) -- 获取骨骼位置 local skeleton = getSpine(go) local bone = skeleton.skeleton:FindBone(boneName) local tmpEndPos = CS.UnityEngine.Vector3(bone.WorldX, bone.WorldY, 0) return tmpEndPos end -- 创建骨骼跟随 默认全部跟随 function SpineUtil.createBoneFollow(go, followObj, boneName, followArgs) followArgs = followArgs or {} if followArgs.followXYPosition == nil then followArgs.followXYPosition = true end if followArgs.followZPosition == nil then followArgs.followZPosition = true end if followArgs.followBoneRotation == nil then followArgs.followBoneRotation = true end local BoneFollower = typeof(CS.Spine.Unity.BoneFollower) local boneFollower = followObj:AddComponent(BoneFollower) local skeleton = getSpine(go) boneFollower.skeletonRenderer = skeleton boneFollower.followXYPosition = followArgs.followXYPosition boneFollower.followZPosition = followArgs.followZPosition boneFollower.followBoneRotation = followArgs.followBoneRotation boneFollower:SetBone(boneName) return boneFollower end function SpineUtil.reset(go) local skeleton = getSpine(go) skeleton.Skeleton:SetToSetupPose() skeleton.AnimationState:ClearTracks() end function SpineUtil.setSkin( go, skinName ) local skeleton = getSpine(go) skeleton.Skeleton:SetSkin(skinName) end function SpineUtil.getCurrentAnim(gameObj) return getSpine(gameObj).AnimationState:GetCurrent(0).Animation.Name end function SpineUtil.getAnimTime(gameObj, name) return getSpine(gameObj).SkeletonDataAsset:GetAnimationStateData().SkeletonData:FindAnimation(name).Duration end return SpineUtilNetworkStateUtil--[[ author:{zhangpeng} time:2023-08-17 14:24:45 ]] local NetworkStateUtil = defClassStatic("NetworkStateUtil") local Application = CS.UnityEngine.Application local NetworkReachability = CS.UnityEngine.NetworkReachability function NetworkStateUtil:init() self.mobileEnableFlag = false end function NetworkStateUtil:isMobileEnable() return self.mobileEnableFlag end function NetworkStateUtil:setMobildEnable(bool) self.mobileEnableFlag = bool end function NetworkStateUtil:isReachable() if CS.LocalDataStorage.Get("DEVICE_PRETEND_NET_NOT_OPEN") == "true" then return false end return Application.internetReachability ~= NetworkReachability.NotReachable end function NetworkStateUtil:isWifiReachable() if CS.LocalDataStorage.Get("DEVICE_PRETEND_WIFI_NET_CLOSE") == "true" then return false end return Application.internetReachability == NetworkReachability.ReachableViaLocalAreaNetwork end function NetworkStateUtil:isMobileReachable() if CS.LocalDataStorage.Get("DEVICE_PRETEND_USE_CELLUAR_NET") == "true" then return true end return Application.internetReachability == NetworkReachability.ReachableViaCarrierDataNetwork end NetworkStateUtil:init() VendorCell-- 摊主列表ui展示格子 local VendorCell = defClass("VendorCell") -- component local Image = CS.UnityEngine.UI.Image local TMProUGUI = CS.TMPro.TextMeshProUGUI --- 构造函数 function VendorCell:ctor(go, vendorId) self.ui = go self.vendorId = vendorId self:initUI() self:showUI() end --- 重置 function VendorCell:reload(entityId) self.vendorId = entityId self:showUI() self.ui:SetActive(true) end --- 初始化UI function VendorCell:initUI() -- 初始化UI元素 self.unvisited = self.ui:Seek("unvisited") self.visited = self.ui:Seek("visited") self.tag_dong = self.ui:Seek("tag_dong") self.icon = self.visited:Seek("icon") self.name = self.visited:Seek("name") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.ui, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:click() end) end --- 展示UI function VendorCell:showUI() local info = VendorMgr:getVendorInfo(self.vendorId) local isVisited = info:isVisited() local isViewed = info:isViewed() self.unvisited:SetActive(not isVisited) self.visited:SetActive(isVisited) self.tag_dong:SetActive(isVisited and (not isViewed)) if isVisited then self.icon[Image].sprite = info:getBustIcon() self.icon[Image]:SetNativeSize() self.name[TMProUGUI].text = info:getName() end end --- 隐藏 function VendorCell:hide() self.ui:SetActive(false) end --- 点击 function VendorCell:click() local info = VendorMgr:getVendorInfo(self.vendorId) if info:isVisited() and (not info:isViewed()) then info:setVisitState(VendorConst.VendorVisitState.Viewed) end VendorDetailUI.new(self.vendorId, false):show():showMask():enableCloseWhenClickMask(function() self:showUI() end) end return VendorCellemployerreslinkreturn { --BASIC --ASSET } CustomerFirstVisit--- 顾客首次来访 ---@class CustomerFirstVisit:LuaClass ---@field isWalkingPlot boolean 是否在走剧情 ---@field firstVisitPlotLastTime boolean 上一个顾客首次来访剧情时间点 ---@field firstVisitPlotInterval boolean 队列中每个顾客首次来访剧情之间的间隔 ---@field customerFirstVisitQueue number[] 顾客首次来访队列 ---@field customerNextVisit table 顾客下次暂缓来访的id列表 ---@field currentNewCustomerId number 当前正在新顾客流程中的顾客id local CustomerFirstVisit = defClass("CustomerFirstVisit") -- 构造函数 function CustomerFirstVisit:ctor() self.isWalkingPlot = false self.firstVisitPlotInterval = math.random(10, 20) -- 随机间隔 self.firstVisitPlotLastTime = CustomerMgr.time self.customerFirstVisitQueue = CustomerMgr.userData:getFirstVisitQueue() self.customerNextVisit = {} self.currentNewCustomerId = -1 end -- 更新 --@param time number 在线时长 function CustomerFirstVisit:timeUpdate(time) -- 如果正在走剧情,则不处理 if self.isWalkingPlot then return end -- 如果没有顾客在队列中,则不处理 if #self.customerFirstVisitQueue <= 0 then return end -- 如果当前时间与上次剧情时间间隔小于设定的间隔,则不处理 if time - self.firstVisitPlotLastTime < self.firstVisitPlotInterval then return end -- 开始剧情 self.isWalkingPlot = true -- 顾客来访 local customerId = self:peek() CustomerMgr:enterTheRestaurant(customerId) printInfo("CustomerMgr", "首次来访顾客入店" .. customerId) end --- 添加id首次来访顾客到队列 function CustomerFirstVisit:addFirstVisitQueue(customerId) self:enqueue(customerId) end --- 上个首次来访后回调 新顾客完成剧情后调用 function CustomerFirstVisit:firstVisitComplete(customerId) self.firstVisitPlotLastTime = CustomerMgr.time self.firstVisitPlotInterval = math.random(10, 20) self.isWalkingPlot = false self:dequeue(customerId) CustomerMgr:unlock(customerId) -- 任务进度 UserDataMgr:addUnlockCustomerCount(1) -- todo 暂时处理 游戏不弹窗 if FishingGameMgr.map then return end -- 显示顾客详情 CustomerDetailUI.new(customerId, true):show():showMask():enableCloseWhenClickMask():addCloseCallback(function() local config = CustomerCfgParse:getCustomerCfg(customerId) if config.plotId ~= -1 then StoryDialogMgr:showRoleStoryDialogUI(customerId, function() TaskOrderMgr:triggerTargetOrder(customerId) end) end end) end --- 新顾客招待失败 function CustomerFirstVisit:firstVisitFail(customerId) self.firstVisitPlotLastTime = CustomerMgr.time self.firstVisitPlotInterval = math.random(10, 20) self.isWalkingPlot = false table.insert(self.customerNextVisit, customerId) self:dequeue(-1) printInfo("CustomerFirstVisit", "顾客" .. customerId .. "首次来访失败,下次登录后再来访") end -- 入队列 解锁 -- @param customerId number 顾客id function CustomerFirstVisit:enqueue(customerId) self.customerFirstVisitQueue = CustomerMgr.userData:enqueueFirstVisit(customerId) return customerId end -- 出队列 -- @param customerId number 顾客id function CustomerFirstVisit:dequeue(customerId) self.customerFirstVisitQueue = CustomerMgr.userData:dequeueFirstVisit(customerId) for i = #self.customerFirstVisitQueue, 1, -1 do for _, n in ipairs(self.customerNextVisit) do if self.customerFirstVisitQueue[i] == n then table.remove(self.customerFirstVisitQueue, i) end end end self.currentNewCustomerId = -1 return customerId end -- 查看队首 -- @return number 顾客id function CustomerFirstVisit:peek() if self.currentNewCustomerId == -1 then for _, v in ipairs(self.customerFirstVisitQueue) do if v then self.currentNewCustomerId = v break end end end return self.currentNewCustomerId end -- 获取正在新顾客流程中顾客id ---@return number 顾客id function CustomerFirstVisit:getCurrentNewCustomerId() return self.currentNewCustomerId end return CustomerFirstVisitmainw require("common/ui/uibase/UIOrderDef") require("common/ui/uibase/UILayerUtil") require("common/ui/uibase/UILayer") require("common/ui/uibase/UIRoot") Msg.def("UI_LAYER_ON_LOAD") Msg.def("UI_LAYER_ON_EXIT") Msg.def("UI_LAYER_SET_TAG") Msg.add(Msg.PAUSE, function() UILayerUtil:onAppPause() end) Msg.add(Msg.RESUME, function() UILayerUtil:onAppResume() end) CustomerSpecialCfgParselocal CustomerSpecialCfgData = require("data/config/specialCustomerCfg") local CustomerSpecialCfgParse = defClassStatic("CustomerSpecialCfgParse", UILayer) function CustomerSpecialCfgParse:init() end function CustomerSpecialCfgParse:getData() return CustomerSpecialCfgData end function CustomerSpecialCfgParse:getCustomerSpecialCfg(customerSpecialCfgId) for _, v in pairs(CustomerSpecialCfgData) do if v.id == customerSpecialCfgId then return v end end printError("CustomerSpecialCfgParse", string.format("没有找到特殊顾客配置[%s]", customerSpecialCfgId)) return nil end CustomerSpecialCfgParse:init()CustomerNormalO--[[ 普通顾客 author:{zhangpeng} time:2025-05-15 11:03:25 ]] --- 普通顾客 --- @class CustomerNormal local CustomerNormal, super = defClass("CustomerNormal", Customer) ---构造函数 function CustomerNormal:ctor(reslink, cfgId) super:ctor(reslink, cfgId) end return CustomerNormal BuildingTypesCfgParse--[[ 建筑类型配置解析 author:{zhangpeng} time:2025-05-19 14:46:00 ]] local BuildingTypesCfg = require("data/config/buildingTypesCfg") local BuildingTypesCfgParse = defClassStatic("BuildingTypesCfgParse") local LOGTAG = "BuildingTypesCfgParse" local building_types_cfg = {} function BuildingTypesCfgParse:init() for k,v in pairs(BuildingTypesCfg) do building_types_cfg[v.id] = v end end function BuildingTypesCfgParse:getBuildingTypesCfg(id) return building_types_cfg[id] end -- 获取所有建筑分类 function BuildingTypesCfgParse:getAllBuildingTypes() return building_types_cfg end -- 获取指定场景的建筑分类 -- @param scene 餐厅/烤吧 function BuildingTypesCfgParse:getBuildingTypesByScene(scene) if not self.building_types then self.building_types = {} end if self.building_types[scene] then return self.building_types[scene] end -- 缓存一下 self.building_types[scene] = {} for k,v in pairs(building_types_cfg) do if v.scene == scene then table.insert(self.building_types[scene], v) end end return self.building_types[scene] end -- 根据建筑类型id取所属场景 function BuildingTypesCfgParse:getCategoryIdByBuildingTypeId(building_type_id) for k,v in pairs(building_types_cfg) do if v.typeId == building_type_id then return v.scene end end end -- 根据大类id获取类型名字 function BuildingTypesCfgParse:getCategoryNameById(id) for k,v in pairs(building_types_cfg) do if v.typeId == id then return v.name end end end BuildingTypesCfgParse:init() PerfUtil--[[ performance benchmark 性能分析工具 author:{zhangpeng} time:2022-05-21 18:04:32 ]] local PerfUtil = {} local LOG_TAG = "PerfUtil" local Time = CS.UnityEngine.Time local Stopwatch = CS.System.Diagnostics.Stopwatch local ms_inv = (1000*1000) / Stopwatch.Frequency local BenchMarkMeta = { init = function(self) self.stopWatch = Stopwatch() self.stopWatch:Start() self.lastTime = self.stopWatch.ElapsedMilliseconds -- 所用时间 毫秒 end, dump = function(self,msg) local time = self.stopWatch.ElapsedMilliseconds if msg then self.stopWatch:Stop() local dt = (time - self.lastTime) printInfo(LOG_TAG,"[性能]<%.3fms>-%s<当前帧:%d>",dt,msg,Time.frameCount) self.stopWatch:Start() else local info = debug.getinfo(2) local src = info.short_src local line = info.currentline self.stopWatch:Stop() local dt = (time - self.lastTime) printInfo(LOG_TAG,"[性能]<%.3fms,frm:%d>-%s:%d",dt,Time.frameCount,src,line) self.stopWatch:Start() end self.lastTime = time end, stop = function(self) self.stopWatch:Stop() end } function PerfUtil.BenchMark() local inst = {} setmetatable(inst,{ __index = BenchMarkMeta, }) inst:init() return inst end return PerfUtil CurrencyConst'--[[ author:{author} time:2025-05-21 18:39:07 ]] local CurrencyConst = defClassStatic("CurrencyConst") --- 货币类型 CurrencyConst.CurrencyType = { Coin = 1, -- 金币 Diamond = 2, -- 钻石 MusicalNotes = 3, -- 音符 FishingBait = 4, -- 鱼饵 Star = 5, -- 人气 AdCoupons = 6, -- 广告券 } --- 货币类型对应的名称 CurrencyConst.CurrencyTypeName = { [CurrencyConst.CurrencyType.Coin] = "金币", [CurrencyConst.CurrencyType.Diamond] = "钻石", [CurrencyConst.CurrencyType.MusicalNotes] = "音符", [CurrencyConst.CurrencyType.FishingBait] = "鱼饵", [CurrencyConst.CurrencyType.Star] = "人气", [CurrencyConst.CurrencyType.AdCoupons] = "广告券", } return CurrencyConst CustomerConst--[[ 顾客常量 author:{zhangpeng} time:2025-05-15 11:09:36 ]] --- 顾客常量 ---@class CustomerConst local CustomerConst = defClassStatic("CustomerConst") -- 就餐时间,暂时固定位2秒,所有顾客一致 CustomerConst.EatingTime = 4 -- 顾客闲逛相关常量 CustomerConst.Wandering = { -- 闲逛最小时间(秒) MinTime = 8, -- 闲逛最大时间(秒) MaxTime = 10, -- 在消费建筑前停留的时间(秒) StayAtBuildingTime = 6, } -- 顾客状态 CustomerConst.State = { -- 门口等待 waiting = 1, -- 走向餐桌 walking = 2, -- 点餐 dining = 3, -- 用餐 eating = 4, -- 闲逛 wandering = 5, -- 离开 leaving = 6, } CustomerConst.CustomerType = { -- 白兔 whiteRabbit = 1, -- 鹿 deer = 2, } -- 顾客来访状态 CustomerConst.CustomerVisitState = { -- 未来访 NotVisit = 1, -- 已来访 Visited = 2, -- 已分享 Shared = 3, } -- 特殊顾客类型 ---@class CustomerSpecialType CustomerConst.CustomerSpecialType = { -- 富二代 RichCustomer = 410001, -- 广告顾客 AdCustomer = 410002, -- 小偷 ThiefCustomer = 410003, -- 臭气顾客 StenchCustomer = 410004, -- 魔术师 MagicianCustomer = 410005, -- 歌手 SingerCustomer = 410006, -- 神秘顾客 MysteriousCustomer = 410007, -- 土拨鼠兄弟1 GroundhogBrothers1 = 410008, -- 土拨鼠兄弟2 GroundhogBrothers2 = 410009, } -- 来访物品类型(key:物品类型,value:物品id, 满足该物品时候,顾客会来访) CustomerConst.VisitItemType = { -- 菜 c = "c", -- 建筑 b = "b", -- 任务 t = "task", -- 星星数 s = "star", } --- 来访物品窗口标题 CustomerConst.VisitItemTypeTitle = { -- 菜 [CustomerConst.VisitItemType.c] = "来访所需菜品", -- 建筑 [CustomerConst.VisitItemType.b] = "来访所需设施", -- 任务 [CustomerConst.VisitItemType.t] = "来访所需任务", -- 星星数 [CustomerConst.VisitItemType.s] = "来访所需人气", } -- 招揽方式 ---@class SolicitationType CustomerConst.SolicitationType = { -- 传单 Flyer = 4001, -- 视频宣传 Video = 4002, } --- 招揽方式名称 CustomerConst.SolicitationTypeName = { -- 传单 [CustomerConst.SolicitationType.Flyer] = "传单宣传", -- 视频宣传 [CustomerConst.SolicitationType.Video] = "视频宣传", } --- 招揽方式菜品描述 CustomerConst.SolicitationTypeCuisineDesc = { -- 传单 [CustomerConst.SolicitationType.Flyer] = "美食1:森林的客人有一定概率点这道菜", -- 视频宣传 [CustomerConst.SolicitationType.Video] = "美食2:城市里的客人有一定概率点这道菜", } --- 招揽方式顾客描述 CustomerConst.SolicitationCustomerTypeDesc = { -- 传单 [CustomerConst.SolicitationType.Flyer] = "住在森林,需要传单宣传或者更高级的宣传才能招揽", -- 视频宣传 [CustomerConst.SolicitationType.Video] = "住在城市,需要视频宣传才能招揽", } ----- 招揽标签 --CustomerConst.SolicitationTag = { -- -- 传单 -- Flyer = 4001, -- -- 视频宣传 -- Video = 4002, --} --- 功能id CustomerConst.FunTag = { -- 金主 Rich = 401, -- 好评如潮 RaveReviews = 402, -- 大胃王 BigBelly = 403, } --- 功能id描述 CustomerConst.FunTagDesc = { -- 金主 [CustomerConst.FunTag.Rich] = "金主:每次付费,都会有%s%%的概率给予%s倍金币奖励", -- 好评如潮 [CustomerConst.FunTag.RaveReviews] = "好评如潮:有%s%%的概率给予%s好评", -- 大胃王 [CustomerConst.FunTag.BigBelly] = "大胃王:会连续吃%s道菜,受大胃王鼓励,厨房做菜的速度加快%s%%。", } -- 顾客动作列表 -- 说明:stand_1和stand_2随机播一个; 正面坐待机1,2,3随机播一个,坐在yz_1和yz_4的时候使用正面坐待机动画,坐在yz_2和yz_3的时候使用背面坐待机动画 -- 吃菜的时候播吃饭动画 -- 正面喝饮料播喝饮料的动画,背面喝饮料播背面吃饭的动画 CustomerConst.CustomerAction = { stand_1 = "guke_front_stand_stand_1", -- 站立1 stand_2 = "guke_front_stand_stand_2", -- 站立2 walk = "guke_front_stand_move", -- 行走 sit_front_idle_1 = "guke_front_sit_front_1", -- 正面坐待机1 sit_front_idle_2 = "guke_front_sit_front_2", -- 正面坐待机2 sit_front_idle_3 = "guke_front_sit_front_3", -- 正面坐待机3 sit_back_idle = "guke_back_sit_stand", -- 背面坐待机 sit_front_eat = "guke_front_sit_eat", -- 正面坐吃饭 sit_back_eat = "guke_back_sit_eat", -- 背面坐吃饭 sit_front_drink = "guke_front_sit_drink", -- 正面坐喝饮料 } -- 顾客行为标签 CustomerConst.BehaviorTag = { musicBbqTeam = 1, -- 音乐烤吧顾客团 } function CustomerConst:init() end CustomerConst:init() main require("common/cos/CosLuaEnum") require("common/cos/CosLuaCredentialBean") require("common/cos/CosLuaTemporaryCredential") require("common/cos/CosLuaUploadTask") require("common/cos/CosLuaTagTask") require("common/cos/CosLuaBatchUploadTask") require("common/cos/CosLuaMgr")XSdk---@diagnostic disable: duplicate-set-field --[[ author:{zhangpeng} time:2023-09-24 17:18:10 ]] local XSdk, super = defClassStatic("XSdk", XSdkBase) function XSdk:init() end XSdk:init()BuildingUserData--[[ 建筑用户数据 author:{zhangpeng} time:2025-06-04 15:40:15 ]] local BuildingUserData = defClass("BuildingUserData") local LOGTAG = "BuildingUserData" function BuildingUserData:ctor() self:init() end function BuildingUserData:init() self.building_list_info = {} -- 玩家已有的建筑列表 self.building_use_info = {} -- 玩家使用中的建筑列表 self.stall_list_unlock_time = {} -- 玩家摊位解锁时间 self.stall_list_delay_time = {} -- 玩家摊位解锁等待时间 self.building_tips_time = -1 -- 玩家小费领取时间 end -- 重置 function BuildingUserData:resetData() -- 摊位默认为未解锁状态 local ids = {BuildingConst.buildingType.booth1, BuildingConst.buildingType.booth2, BuildingConst.buildingType.booth3, BuildingConst.buildingType.booth4, BuildingConst.buildingType.booth5, BuildingConst.buildingType.booth6,} for _, id in ipairs(ids) do self:setBuildingState(id, 1, true) -- 默认锁定 end end --- 从服务器或者本地存档加载 function BuildingUserData:load() if UserDataMgr.isNewPlayer then self:resetData() else self:loadFromLocal() -- -- 从服务器加载 -- self:loadFromServer() -- -- 如果服务器没有数据,使用默认数据 -- if table.count(self.building_list_info) == 0 then -- self:loadDefaultData() -- end -- printInfo(LOGTAG, "加载了 %d 个用户建筑数据", table.count(self.building_list_info)) end self:onLoadComplete() end --- 从服务器加载数据 function BuildingUserData:loadFromServer() -- TODO: 实现从服务器加载逻辑 -- 这里先留空,后续接入服务器API时实现 printInfo(LOGTAG, "从服务器加载建筑数据 - 待实现") end --- 从本地存档加载数据 function BuildingUserData:loadFromLocal() self.building_list_info = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.BUILDING_LIST_INFO, "{}"))) self.building_use_info = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.BUILDING_USE_INFO, "{}"))) self.stall_list_unlock_time = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.STALL_LIST_UNLOCK_TIME, "{}"))) self.stall_list_delay_time = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.STALL_LIST_DELAY_TIME, "{}"))) self.building_tips_time = PlayerPrefsMgr:getInt(StrogeKeyDef.BUILDING_TIPS_TIME, -1) end -- 加载完成回调 function BuildingUserData:onLoadComplete() -- 没有解锁条件的对象默认解锁 local data = BuildingCfgParse:getData() for _, v in pairs(data) do if v.unlockConditionId == -1 and (self:getBuildingState(v.uid) < 1) then self:setBuildingState(v.uid, 1, true) -- 默认解锁 end end self:save() end --- 加载默认数据(新用户或测试用) function BuildingUserData:loadDefaultData() -- 默认解锁并购买一些基础建筑 local defaultBuildings = { -- 餐厅基础建筑 [100101] = self:getDefaultInfo(100101), -- 原木1号餐桌 [100201] = self:getDefaultInfo(100201), -- 迎宾台 [100301] = self:getDefaultInfo(100301), -- 主灶台(如果存在) } for buildingCfgId, buildingData in pairs(defaultBuildings) do -- 检查配置是否存在 local cfg = BuildingCfgParse:getBuildingCfg(buildingCfgId) if cfg then self.building_list_info[buildingCfgId] = buildingData end end printInfo(LOGTAG, "加载了默认建筑数据:%d 个建筑", table.count(self.building_list_info)) end --- 保存到服务器 function BuildingUserData:saveToServer() -- TODO: 实现保存到服务器的逻辑 -- printInfo(LOGTAG, "保存建筑数据到服务器 - 待实现") end --- 保存到本地 function BuildingUserData:saveToLocal() PlayerPrefsMgr:setString(StrogeKeyDef.BUILDING_LIST_INFO, json.encode(PlayerPrefsMgr:idToString(self.building_list_info))) PlayerPrefsMgr:setString(StrogeKeyDef.BUILDING_USE_INFO, json.encode(PlayerPrefsMgr:idToString(self.building_use_info))) PlayerPrefsMgr:setString(StrogeKeyDef.STALL_LIST_UNLOCK_TIME, json.encode(PlayerPrefsMgr:idToString(self.stall_list_unlock_time))) PlayerPrefsMgr:setString(StrogeKeyDef.STALL_LIST_DELAY_TIME, json.encode(PlayerPrefsMgr:idToString(self.stall_list_delay_time))) PlayerPrefsMgr:setInt(StrogeKeyDef.BUILDING_TIPS_TIME, self.building_tips_time) PlayerPrefsMgr:save() end --- 保存所有数据 function BuildingUserData:save() self:saveToServer() self:saveToLocal() end --- 获取默认建筑信息 function BuildingUserData:getDefaultInfo(buildingId) return { buildingCfgId = buildingId, state = BuildingConst.buildingState.locked, -- 默认状态为锁定 } end --- 向已有的建筑列表中插入一个建筑 function BuildingUserData:insertBuilding(buildingInfo) self.building_list_info[buildingInfo.buildingCfgId] = buildingInfo printInfo(LOGTAG, "更新建筑数据: %d", buildingInfo.buildingCfgId) end --- 获取玩家已有的建筑列表 function BuildingUserData:getBuildingList() return self.building_list_info end --- 获取特定建筑的用户数据 function BuildingUserData:getBuildingData(buildingCfgId) return self.building_list_info[buildingCfgId] or self:getDefaultInfo(buildingCfgId) end --- 获取特定建筑的用户状态 function BuildingUserData:getBuildingState(buildingCfgId) local buildingData = self:getBuildingData(buildingCfgId) or self:getDefaultInfo(buildingCfgId) return buildingData.state end --- 设置特定建筑的用户状态 function BuildingUserData:setBuildingState(buildingId, state, refuseSave) local buildingData = self.building_list_info[buildingId] if not buildingData then buildingData = self:getDefaultInfo(buildingId) self.building_list_info[buildingId] = buildingData end buildingData.state = state if not refuseSave then self:save() end return state end --- 获取特定类型建筑的使用状态 function BuildingUserData:getBuildingUseState(buildingType) return self.building_use_info[buildingType] or -1 end --- 设置特定类型建筑的使用状态 function BuildingUserData:setBuildingUseState(buildingType, buildingId, refuseSave) self.building_use_info[buildingType] = buildingId if not refuseSave then self:save() end return buildingId end --- 获取摊位开始解锁时间 function BuildingUserData:getStallUnlockTime(stallId) return self.stall_list_unlock_time[stallId] or -1 end --- 获取摊位解锁等待时间 function BuildingUserData:getStallDelayTime(stallId) return self.stall_list_delay_time[stallId] or -1 end --- 设置摊位解锁时间 function BuildingUserData:setStallUnlockTime(stallId, delayTime, refuseSave) self.stall_list_unlock_time[stallId] = os.time() self.stall_list_delay_time[stallId] = delayTime if not refuseSave then self:save() end return self.stall_list_unlock_time[stallId], delayTime end --- 清理摊位解锁时间 function BuildingUserData:clearStallUnlockTime(stallId, refuseSave) self.stall_list_unlock_time[stallId] = nil self.stall_list_delay_time[stallId] = nil if not refuseSave then self:save() end return nil, nil end --- 获取当前小费领取时间 function BuildingUserData:getBuildingTipsTime() return self.building_tips_time or -1 end --- 设置当前小费领取时间 function BuildingUserData:updateBuildingTipsTime(refuseSave) self.building_tips_time = os.time() if not refuseSave then self:save() end return self.building_tips_time end --- 清理当前小费领取时间 function BuildingUserData:clearBuildingTipsTime(refuseSave) self.building_tips_time = -1 if not refuseSave then self:save() end return self.building_tips_time end return BuildingUserData TaskBootUserData local TaskBootUserData = defClass("TaskBootUserData") -- 构造函数 function TaskBootUserData:ctor() self:init() end -- 初始化 function TaskBootUserData:init() -- 初始化数据 self.task_boot_current_task_id = 530001 -- 当前引导任务ID self.task_boot_promotion_click_count = 0 -- 引导任务推广点击次数 end -- 重置数据 function TaskBootUserData:resetData() self.task_boot_current_task_id = 530001 -- 重置为初始任务ID self.task_boot_promotion_click_count = 0 -- 重置推广点击次数 self:save() end -- 加载数据 function TaskBootUserData:load() self:loadFromLocal() -- 如果是新玩家,重置数据 if UserDataMgr.isNewPlayer then self:resetData() end end -- 从本地加载数据 function TaskBootUserData:loadFromLocal() self.task_boot_current_task_id = PlayerPrefsMgr:getInt(StrogeKeyDef.task_boot_current_task_id, 530001) self.task_boot_promotion_click_count = PlayerPrefsMgr:getInt(StrogeKeyDef.task_boot_promotion_click_count, 0) end -- 保存数据 function TaskBootUserData:save() self:saveToLocal() end -- 保存到本地 function TaskBootUserData:saveToLocal() PlayerPrefsMgr:setInt(StrogeKeyDef.task_boot_current_task_id, self.task_boot_current_task_id) PlayerPrefsMgr:setInt(StrogeKeyDef.task_boot_promotion_click_count, self.task_boot_promotion_click_count) end -- 获取当前任务ID function TaskBootUserData:getCurrentBootTaskId() return self.task_boot_current_task_id end -- 设置当前任务ID function TaskBootUserData:setCurrentBootTaskId(taskId, refuseSave) self.task_boot_current_task_id = taskId if not refuseSave then self:save() end end -- 获取推广点击次数 function TaskBootUserData:getPromotionClickCount() return self.task_boot_promotion_click_count or 0 end -- 设置推广点击次数 function TaskBootUserData:setPromotionClickCount(value, refuseSave) self.task_boot_promotion_click_count = value if not refuseSave then self:save() end return self.task_boot_promotion_click_count end -- 增加推广点击次数 function TaskBootUserData:addPromotionClickCount(value, refuseSave) local curr = self:getPromotionClickCount() + value return self:setPromotionClickCount(curr, refuseSave) end -- 重置推广点击次数 function TaskBootUserData:resetPromotionClickCount(refuseSave) return self:setPromotionClickCount(0, refuseSave) end return TaskBootUserDataguideCfgn--[[ from file:引导表.xlsx --]] local guideCfg = { [1] = { guideId = 1, desc = "入场剧情", guideType = 1, opt = 1, guideView = '', guideNode = '', params = {}, time = 0.2, backId = -1, showhand = 0, sdialog = 0, scontent = '', dislogNum = 670011, }, [2] = { guideId = 2, desc = "购买餐桌剧情", guideType = 1, opt = 1, guideView = '', guideNode = '', params = {}, time = 0.2, backId = -1, showhand = 0, sdialog = 0, scontent = '', dislogNum = 670021, }, [3] = { guideId = 3, desc = "引导点击建设按钮", guideType = 1, opt = 2, guideView = "main", guideNode = "btn_building", params = {}, time = 0.2, backId = -1, showhand = 0, sdialog = 0, scontent = '', dislogNum = 0, }, [4] = { guideId = 4, desc = "引导点击餐桌图标", guideType = 1, opt = 2, guideView = "building", guideNode = "btn_building_cell", params = {1,100101}, time = 0.26, backId = 2, showhand = 0, sdialog = 1, scontent = "点击这里就可以看到设施的信息了,让我们买一张桌子吧!", dislogNum = 0, }, [5] = { guideId = 5, desc = "引导点击购买", guideType = 1, opt = 2, guideView = "building_info", guideNode = "btn_buy", params = {}, time = 0.2, backId = 2, showhand = 0, sdialog = 0, scontent = '', dislogNum = 0, }, [6] = { guideId = 6, desc = "购买餐桌后的剧情", guideType = 1, opt = 1, guideView = '', guideNode = '', params = {}, time = 0.2, backId = -1, showhand = 0, sdialog = 0, scontent = '', dislogNum = 670031, }, [7] = { guideId = 7, desc = "购买灶台引导", guideType = 1, opt = 1, guideView = '', guideNode = '', params = {}, time = 0.2, backId = -1, showhand = 0, sdialog = 0, scontent = '', dislogNum = 670041, }, [8] = { guideId = 8, desc = "引导点击建设按钮", guideType = 1, opt = 2, guideView = "main", guideNode = "btn_building", params = {}, time = 0.2, backId = -1, showhand = 0, sdialog = 0, scontent = '', dislogNum = 0, }, [9] = { guideId = 9, desc = "引导点击灶台图标", guideType = 1, opt = 2, guideView = "building", guideNode = "btn_building_cell", params = {2,101501}, time = 0.26, backId = 7, showhand = 0, sdialog = 0, scontent = '', dislogNum = 0, }, [10] = { guideId = 10, desc = "引导点击购买", guideType = 1, opt = 2, guideView = "building_info", guideNode = "btn_buy", params = {}, time = 0.2, backId = 7, showhand = 0, sdialog = 0, scontent = '', dislogNum = 0, }, [11] = { guideId = 11, desc = "生成顾客", guideType = 1, opt = 3, guideView = '', guideNode = '', params = {400001}, time = 0.2, backId = -1, showhand = 0, sdialog = 0, scontent = '', dislogNum = 0, }, [12] = { guideId = 12, desc = "顾客落座后的剧情", guideType = 1, opt = 1, guideView = '', guideNode = '', params = {}, time = 0.2, backId = 11, showhand = 0, sdialog = 0, scontent = '', dislogNum = 670051, }, [13] = { guideId = 13, desc = "引导点击菜品气泡", guideType = 1, opt = 4, guideView = "customer", guideNode = "bubble", params = {400001}, time = 27.0, backId = 11, showhand = 0, sdialog = 0, scontent = '', dislogNum = 0, }, [14] = { guideId = 14, desc = "菜品制作剧情", guideType = 1, opt = 1, guideView = '', guideNode = '', params = {}, time = 6.0, backId = 11, showhand = 0, sdialog = 0, scontent = '', dislogNum = 670061, }, [15] = { guideId = 15, desc = "菜品完成剧情", guideType = 1, opt = 1, guideView = '', guideNode = '', params = {}, time = 0.2, backId = -1, showhand = 0, sdialog = 0, scontent = '', dislogNum = 670071, }, [16] = { guideId = 16, desc = "金币收取说明具体", guideType = 1, opt = 1, guideView = '', guideNode = '', params = {}, time = 0.2, backId = -1, showhand = 0, sdialog = 0, scontent = '', dislogNum = 670081, }, [17] = { guideId = 17, desc = "招揽顾客剧情", guideType = 1, opt = 1, guideView = '', guideNode = '', params = {}, time = 0.2, backId = -1, showhand = 0, sdialog = 0, scontent = '', dislogNum = 670091, }, [18] = { guideId = 18, desc = "引导点击传单", guideType = 1, opt = 2, guideView = "main", guideNode = "btn_flyer", params = {}, time = 0.2, backId = -1, showhand = 0, sdialog = 0, scontent = "加油!继续点击!!!!!", dislogNum = 0, }, [19] = { guideId = 19, desc = "视频宣传剧情", guideType = 1, opt = 1, guideView = '', guideNode = '', params = {}, time = 0.2, backId = -1, showhand = 0, sdialog = 0, scontent = '', dislogNum = 670101, }, [20] = { guideId = 20, desc = "引导点击“视频”按钮", guideType = 1, opt = 2, guideView = "main", guideNode = "btn_video", params = {}, time = 0.2, backId = -1, showhand = 0, sdialog = 0, scontent = '', dislogNum = 0, }, [21] = { guideId = 21, desc = "引导点击观看按钮", guideType = 1, opt = 2, guideView = "video_promotion", guideNode = "btn_video_promotion", params = {}, time = 0.2, backId = 19, showhand = 1, sdialog = 0, scontent = '', dislogNum = 0, }, [22] = { guideId = 22, desc = "引导结束剧情", guideType = 1, opt = 1, guideView = '', guideNode = '', params = {}, time = 0.2, backId = -1, showhand = 0, sdialog = 0, scontent = '', dislogNum = 670111, }, } return guideCfg mainHrequire("modules/role/employee/EmployeConst") require("modules/role/employee/EmployeInfo") require("modules/role/employee/EmployeeUserData") require("modules/role/employee/EmployFunsMgr") require("modules/role/employee/Employe") require("modules/role/employee/category/main") require("modules/role/employee/EmployeMgr") main"require("common/device/Device") TestScene--[[ 测试场景 author:{zhangpeng} time:2025-05-28 18:43:18 ]] local TestScene, super = defClass("TestScene", Scene) local LOGTAG = "TestScene" function TestScene:ctor(sceneInfo) super.ctor(self) self.sceneInfo = sceneInfo self.args = sceneInfo.args or {} self.R = ResLoader.loadResLink("modules/test/testscenereslink") printInfo(LOGTAG,"TestScene:ctor R:%s", self.R) printInfo(LOGTAG, "测试场景构造函数 666") end function TestScene:info() return { trans = nil, asset = self.R, } end function TestScene:onLoad() super.onLoad(self) printInfo(LOGTAG, "TestScene:onLoad") -- 检查相机状态 if self.cams and self.cams.scene then local camera = self.cams.scene printInfo(LOGTAG, "相机状态 - enabled: %s, cullingMask: %s, depth: %s", tostring(camera.enabled), tostring(camera.cullingMask), tostring(camera.depth)) printInfo(LOGTAG, "相机位置: %s", tostring(camera.transform.position)) printInfo(LOGTAG, "相机正交大小: %s, 正交模式: %s", tostring(camera.orthographicSize), tostring(camera.orthographic)) else printError(LOGTAG, "相机未正确设置!") end local coin = self.rootNode:Seek("test_coin") if coin then printInfo(LOGTAG, "金币 found") coin:SetActive(true) -- 检查金币的状态 local sr = coin:GetComponent("SpriteRenderer") if sr then printInfo(LOGTAG, "金币SpriteRenderer - enabled: %s, sprite: %s, sortingOrder: %s", tostring(sr.enabled), tostring(sr.sprite), tostring(sr.sortingOrder)) printInfo(LOGTAG, "金币颜色: %s", tostring(sr.color)) printInfo(LOGTAG, "金币位置: %s", tostring(coin.transform.position)) printInfo(LOGTAG, "金币缩放: %s", tostring(coin.transform.localScale)) end else printInfo(LOGTAG, "金币 not found") end -- 创建一个默认Sprite local sprite = self.rootNode:Seek("test_sprite") if sprite then sprite:SetActive(true) end end function TestScene:onExit() super.onExit(self) end return TestScene UnlockMgr --- ---@class UnlockMgr : LuaStaticClass local UnlockMgr = defClassStatic("UnlockMgr") function UnlockMgr:init() self:initEvent() end function UnlockMgr:start() self:setEvent() -- test self:initMapUnlock() end function UnlockMgr:initEvent() self.taskButtonUnlockEvent = SimpleEvent.new("taskButtonUnlock", true) -- 任务锁定事件 end function UnlockMgr:setEvent() end --- 是否已解锁 function UnlockMgr:isUnlock(unlockId) if (not unlockId) or (unlockId == -1) then return true end local config = UnlockCommonCfgParse:getUnlockCondData(unlockId) -- if config.limitType == UnlockConst.LimitType.Star then return CurrencyMgr:getStar() >= config.value elseif config.limitType == UnlockConst.LimitType.Scene then local fishingNeedStar = SceneCfgParse:getSceneCfgById(config.value).unlockStarNum -- todo return CurrencyMgr:getStar() >= fishingNeedStar end end --- 获取解锁条件描述 function UnlockMgr:getUnlockDesc(unlockId) local config = UnlockCommonCfgParse:getUnlockCondData(unlockId) local base = UnlockConst.LimitTypeDesc[config.limitType] if config.limitType == UnlockConst.LimitType.Star then return string.format(base, config.value) elseif config.limitType == UnlockConst.LimitType.Scene then return string.format(base, BuildingConst.buildingCategoryName[config.value]) end return nil end -- 获取任务按钮解锁状态 function UnlockMgr:getTaskButtonUnlockState() return CurrencyMgr:getStar() >= 30 end -- 设置事件 function UnlockMgr:setTaskButtonUnlockEvent(func) self.taskButtonUnlockEvent:addEvent(func) CurrencyMgr.currencyChangeEvent:addEvent(function (id, currencyType, value) self:checkTaskButtonUnlock(id, currencyType, value) end) end -- 检查任务按钮是否解锁 function UnlockMgr:checkTaskButtonUnlock(id, currencyType, star) if currencyType ~= CurrencyConst.CurrencyType.Star then return end if star >= 30 then self.taskButtonUnlockEvent:triggerEvent() CurrencyMgr.currencyChangeEvent:removeEventById(id) end end -- 解锁触发 -- 星星变动解锁 function UnlockMgr:unlockStarTrigger(count) local list = UnlockCommonCfgParse:getUnlockCondListByType(UnlockConst.LimitType.Star) end -- 地图解锁 -- 地图解锁初始化 function UnlockMgr:initMapUnlock() local list = SceneCfgParse:getData() for _, v in ipairs(list) do if not UserDataMgr:getSceneUnlocked(v.id) then if self:isMapUnlocked(v.id) then -- 如果当前地图未解锁,尝试解锁 self:unlockMap(v.id) end end end end -- 解锁地图 function UnlockMgr:unlockMap(mapId) if not mapId or mapId == -1 then return end UserDataMgr:unlockScene(mapId) end -- 地图是否解锁 function UnlockMgr:isMapUnlocked(mapId) if not mapId or mapId == -1 then return false end local config = SceneCfgParse:getSceneCfgById(mapId) if config.unlockStarNum <= CurrencyMgr:getStar() then return true end return false end return UnlockMgrTextCfg--[[ from file:TextCfg.xlsx --]] local TextCfg = { [1] = { textId = "buy_suc", cn = "购买成功", en = "Purchase successful!", jp = '', desc = '', }, } return TextCfg StoryDialogMgr --[[ 剧情对话管理器 author:{zhangpeng} time:2025-05-29 10:07:51 ]] local StoryDialogMgr = defClassStatic("StoryDialogMgr") local LOGTAG = "StoryDialogMgr" function StoryDialogMgr:init() end -- 显示新手引导剧情对话 function StoryDialogMgr:showGuideDialogUI(dialogId, cb) local storyDialogUI = StoryDialogUI.new(nil, dialogId) storyDialogUI:show():showMask():addCloseCallback(function() printInfo(LOGTAG, "剧情对话结束,关闭对话框") if cb then cb() end end) end -- 显示角色剧情对话管理 function StoryDialogMgr:showRoleStoryDialogUI(roleId ,cb) local roleType = self:getRoleTypeById(roleId) local plotId = nil if roleType == StorydialogConst.RoleType.Customer then plotId = CustomerCfgParse:getStoryDialogId(roleId) elseif roleType == StorydialogConst.RoleType.Employee then plotId = EmployeCfgParse:getFirstStoryDialogId(roleId) end -- 有剧情对话 if self:isValidStoryDialogId(plotId) then local storyDialogUI = StoryDialogUI.new(roleType, plotId) storyDialogUI:show():showMask():addCloseCallback(function() printInfo(LOGTAG, "剧情对话结束,关闭对话框") if cb then cb() end end) else printInfo(LOGTAG, "角色:%s 无剧情对话", roleId) end end -- 剧情id是否有效 function StoryDialogMgr:isValidStoryDialogId(id) return id ~= -1 and id ~= nil end -- 显示订单剧情对话 function StoryDialogMgr:showOrderStoryDialogUI(plotId, cb) if not self:isValidStoryDialogId(plotId) then printError(LOGTAG, "此id无剧情对话配置 ID:%s", plotId) if cb then cb() end return end local storyDialogUI = StoryDialogUI.new(StorydialogConst.RoleType.Customer, plotId) storyDialogUI:show():showMask():addCloseCallback(function() printInfo(LOGTAG, "剧情对话结束,关闭对话框") if cb then cb() end end) end -- 显示剧情对话 function StoryDialogMgr:showPageStoryDialogUI(plotId, roleType, cb) if not self:isValidStoryDialogId(plotId) then printError(LOGTAG, "此id无剧情对话配置 ID:%s", plotId) if cb then cb() end return end local storyDialogUI = StoryDialogUI.new(roleType, plotId) storyDialogUI:show():showMask():addCloseCallback(function() printInfo(LOGTAG, "剧情对话结束,关闭对话框") if cb then cb() end end) end -- 根据id判断是顾客还是员工 function StoryDialogMgr:getRoleTypeById(id) local firstNum = tostring(id):sub(1, 1) if firstNum == StorydialogConst.RoleType.Customer then return StorydialogConst.RoleType.Customer elseif firstNum == StorydialogConst.RoleType.Employee then return StorydialogConst.RoleType.Employee end end return StoryDialogMgrcartoonuireslinkreturn { --BASIC --ASSET page_1 = {"Assets/AssetsPackage/Res/modules/ui/cartoon/prefabs/startstory/page_1.prefab", 0, 0}, } XSdkBase--[[ author:{zhangpeng} time:2023-09-24 17:17:27 ]] local XSdkBase = defClassStatic("XSdkBase") local LOG_TAG = "XSdkBase" function XSdkBase:showWebView(url, title, sceneName, orientation, callback) CS.UnityEngine.Application.OpenURL(url) end --@region 支付 function XSdkBase:purchaseProduct(productOrder) printError(LOG_TAG, "purchaseProduct not implement") end function XSdkBase:purchaseProductWithChannel(productOrder, paymentChannel) printError(LOG_TAG, "purchaseProductWithChannel not implement") end function XSdkBase:restorePurchase() printError(LOG_TAG, "restorePurchase not implement") end function XSdkBase:clearIAPCache() printError(LOG_TAG, "clearIAPCache not implement") end function XSdkBase:cancelPromotedPayment(purchaseId, count) printError(LOG_TAG, "cancelPromotedPayment not implement") end function XSdkBase:getPromoteProduct() printError(LOG_TAG, "getPromoteProduct not implement") end function XSdkBase:clearPromoteProduct() printError(LOG_TAG, "clearPromoteProduct not implement") end function XSdkBase:getPurchaseState(purchaseId) printError(LOG_TAG, "getPurchaseState not implement") end function XSdkBase:getLocalProductInfoListAsyn(productInfoList, callback) printError(LOG_TAG, "getLocalProductInfoListAsyn not implement") end function XSdkBase:suggestedRegion() printError(LOG_TAG, "suggestedRegion not implement") end --@endregionSceneComponentw---@class SceneComponent:LuaClass local SceneComponent = defClass("SceneComponent") ---@param scene Scene function SceneComponent:ctor(scene) self.scene = scene self.coms = scene.coms end function SceneComponent:onLoad() end function SceneComponent:afterOnLoad() end function SceneComponent:onSceneTransitionOver() end function SceneComponent:DrawIMGUI() end function SceneComponent:getSceneArgs() return self.scene:getSceneArgs() end function SceneComponent:getRootNode() return self.scene.rootNode end function SceneComponent:getSceneCamera() return self.scene.cams.scene end return SceneComponent CustomerTagCellB--- ---@class CustomerTagCell : LuaClass local CustomerTagCell = defClass("CustomerTagCell") function CustomerTagCell:ctor(go, sprite, desc, clickFunc) local img = go:Seek("img") util.ugui.addButtonClickEvent(img, function () if clickFunc then AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) clickFunc() end end) local image = img:GetComponent("Image") image.sprite = sprite image:SetNativeSize() go:GetComponent("TextMeshProUGUI").text = desc end return CustomerTagCell UserDataMgr8H--[[ 临时用户数据管理 author:{zhangpeng} time:2025-05-26 15:43:30 ]] local UserDataMgr = defClassStatic("UserDataMgr") local PlayerPrefs = UnityEngine.PlayerPrefs -- Unity的PlayerPrefs类,用于存储用户数据 local GetString = PlayerPrefs.GetString -- 获取指定key的值 fun(key, defValue):string local SetString = PlayerPrefs.SetString -- 设置指定key的值 fun(key, value):void local GetInt = PlayerPrefs.GetInt -- 获取指定key的值 fun(key, defValue):int local SetInt = PlayerPrefs.SetInt -- 设置指定key的值 fun(key, value):void local GetFloat = PlayerPrefs.GetFloat -- 获取指定key的值 fun(key, defValue):float local SetFloat = PlayerPrefs.SetFloat -- 设置指定key的值 fun(key, value):void local DeleteKey = PlayerPrefs.DeleteKey -- 删除指定key fun(key):void local DeleteAll = PlayerPrefs.DeleteAll -- 删除所有key fun():void local Save = PlayerPrefs.Save -- 保存所有修改 fun():void -- init function UserDataMgr:init() self:initBase() self:initOnlineTime() self:initScene() self:initTaskData() self.currencyUserData = CurrencyUserData.new() -- 初始化货币数据 self.guideUserData = GuideUserData.new() -- 初始化引导数据 self.cuisineUserData = CuisineUserData.new() -- 初始化菜品数据 self.employeeUserData = EmployeeUserData.new() -- 初始化员工数据 self.customerUserData = CustomerUserData.new() -- 初始化顾客数据 self.specialCustomerUserData = SpecialCustomerUserData.new() -- 初始化特殊顾客数据 self.buildingUserData = BuildingUserData.new() -- 初始化建筑数据 self.bonusUserData = BonusUserData.new() -- 初始化加成数据 self.settingUserData = SettingUserData.new() -- 初始化设置数据 self.taskUserData = TaskUserData.new() -- 初始化任务数据 self.fishingUserData = FishingUserData.new() -- 初始化捕鱼数据 self.vendorUserData = VendorUserData.new() -- 初始化摊主数据 self.dollUserData = DollUserData.new() -- 初始化玩偶数据 self:load() -- 加载数据 end --- 全部保存 function UserDataMgr:save() self.currencyUserData:save() -- 保存货币数据 self.guideUserData:save() -- 保存引导数据 self.cuisineUserData:save() -- 保存菜品数据 self.employeeUserData:save() -- 保存员工数据 self.customerUserData:save() -- 保存顾客数据 self.specialCustomerUserData:save() -- 保存特殊顾客数据 self.buildingUserData:save() -- 保存建筑数据 self.bonusUserData:save() -- 保存奖励数据 self.settingUserData:save() -- 保存设置数据 self.taskUserData:save() -- 保存任务数据 self.fishingUserData:save() -- 保存捕鱼数据 self.vendorUserData:save() -- 保存摊主数据 self.dollUserData:save() -- 保存玩偶数据 end --- 删除全部数据 function UserDataMgr:deleteAll() DeleteAll(); end -- 全部加载 function UserDataMgr:load() self.currencyUserData:load() -- 加载货币数据 self.guideUserData:load() -- 加载引导数据 self.cuisineUserData:load() -- 加载菜品数据 self.employeeUserData:load() -- 加载员工数据 self.customerUserData:load() -- 加载顾客数据 self.specialCustomerUserData:load() -- 加载特殊顾客数据 self.buildingUserData:load() -- 加载建筑数据 self.bonusUserData:load() -- 加载奖励数据 self.settingUserData:load() -- 加载设置数据 self.taskUserData:load() -- 加载任务数据 self.fishingUserData:load() -- 加载捕鱼数据 self.vendorUserData:load() -- 加载摊主数据 self.dollUserData:load() -- 加载玩偶数据 --self:resetGuideProgress() end function UserDataMgr:initBase() self.isNewPlayer = GetInt(StrogeKeyDef.IS_NEW_PLAYER, 1) == 1 SetInt(StrogeKeyDef.IS_NEW_PLAYER, 0) -- 设置为非新玩家 end -- 获取当前引导步骤 function UserDataMgr:getForceGuideStep() return self.guideUserData:getForceGuideStep() end -- 设置当前引导步骤 function UserDataMgr:setForceGuideStep(step) self.guideUserData:setForceGuideStep(step) end -- 获取当前引导对话序号 function UserDataMgr:getForceGuideDialogIdx() return self.guideUserData:getForceGuideDialogIdx() end -- 设置当前引导对话序号 function UserDataMgr:setForceGuideDialogIdx(idx) self.guideUserData:setForceGuideDialogIdx(idx) end -- 检查引导是否已完成 function UserDataMgr:isGuideCompleted() return self:getForceGuideStep() >= GuideMgr.guideComplete end -- 重置引导进度(用于测试) function UserDataMgr:resetGuideProgress() self:setForceGuideStep(11) printInfo("UserDataMgr", "引导进度已重置") end -- 获取引导步骤描述 function UserDataMgr:getGuideStepDescription(step) step = step or self:getForceGuideStep() return GuideConst.stepDescription[step] or "未知步骤" end -- 初始化场景解锁数据 function UserDataMgr:initScene() if self.isNewPlayer then self.scene_unlocked = { 1, 2, 3 } -- 默认第一个场景解锁 SetString(StrogeKeyDef.USER_SCENE_UNLOCK, json.encode(self.scene_unlocked)) -- 保存到存档 else local sceneUnlockStr = GetString(StrogeKeyDef.USER_SCENE_UNLOCK, "{}") if sceneUnlockStr == "" then self.scene_unlocked = { 1, 2, 3 } -- 默认第一个场景解锁 SetString(StrogeKeyDef.USER_SCENE_UNLOCK, json.encode(self.scene_unlocked)) -- 保存到存档 return end self.scene_unlocked = json.decode(sceneUnlockStr) end end -- 获取指定场景解锁状态 function UserDataMgr:getSceneUnlocked(sceneId) for _, v in ipairs(self.scene_unlocked) do if v == sceneId then return true end end return false end -- 指定场景解锁 function UserDataMgr:unlockScene(sceneId) if not self.scene_unlocked[sceneId] then self.scene_unlocked[sceneId] = true -- 保存到存档 SetString(StrogeKeyDef.SCENE_UNLOCKED, json.encode(self.scene_unlocked)) end end function UserDataMgr:getSceneUnlockedList() return self.scene_unlocked end -- 初始化任务相关数据 ---@class UserDataMgr function UserDataMgr:initTaskData() -- 初始化所有任务相关数据为0 self.star_count = 0 self.entertain_customer_count = 0 self.coin_count = 0 self.income_coin_count = 0 self.unlock_cuisine_count = 0 self.unlock_building_count = 0 self.unlock_customer_count = 0 self.promote_count = 0 self.promote_video_count = 1 self.watch_ad_count = 0 self.unlock_cuisine_total_count = 0 self.use_eggs_count = 0 self.interact_special_customer_count = 0 self.fish_count = 0 self.note_count = 0 self.unlock_employee_count = 0 self.visit_friend_count = 0 self.recruit_count = 0 self.recruit_customer_count = 0 self.unlock_scene_count = 0 self.task_progress_count = 0 self.recruit_specific_customer_count = {} -- 招揽特定顾客的数量 {id, count} self.entertain_specific_customer_count = {} -- 招揽特定顾客的数量 {id, count} -- 初始化每日任务相关数据 self:initDailyTaskData() -- 初始化成就任务相关数据 self:initAchTaskData() end -- 1. 得星星数量相关 function UserDataMgr:getStarCount() return self.star_count end function UserDataMgr:setStarCount(count) self.star_count = count end function UserDataMgr:addStarCount(count) self.star_count = self.star_count + count end -- 2. 招待顾客相关 function UserDataMgr:getEntertainCustomerCount() return self.entertain_customer_count end function UserDataMgr:setEntertainCustomerCount(count) self.entertain_customer_count = count end function UserDataMgr:addEntertainCustomerCount(count) self.entertain_customer_count = self.entertain_customer_count + count end -- 3. 收入金币累计 function UserDataMgr:getIncomeCoinCount() return self.income_coin_count end function UserDataMgr:setIncomeCoinCount(count) self.income_coin_count = count end function UserDataMgr:addIncomeCoinCount(count) self.income_coin_count = self.income_coin_count + count end -- 4. 解锁菜品 function UserDataMgr:getUnlockCuisineCount() return self.unlock_cuisine_count end function UserDataMgr:setUnlockCuisineCount(count) self.unlock_cuisine_count = count end function UserDataMgr:addUnlockCuisineCount(count) self.unlock_cuisine_count = self.unlock_cuisine_count + count end -- 5. 解锁设施 function UserDataMgr:getUnlockBuildingCount() return self.unlock_building_count end function UserDataMgr:setUnlockBuildingCount(count) self.unlock_building_count = count end function UserDataMgr:addUnlockBuildingCount(count) self.unlock_building_count = self.unlock_building_count + count end -- 6. 解锁顾客数量 function UserDataMgr:getUnlockCustomerCount() return self.unlock_customer_count end function UserDataMgr:setUnlockCustomerCount(count) self.unlock_customer_count = count end function UserDataMgr:addUnlockCustomerCount(count) self.unlock_customer_count = self.unlock_customer_count + count end -- 7. 累计宣传次数 function UserDataMgr:getPromoteCount() return self.promote_count end function UserDataMgr:setPromoteCount(count) self.promote_count = count end function UserDataMgr:addPromoteCount(count) self.promote_count = self.promote_count + count end -- 8. 累计完成视频宣传次数 function UserDataMgr:getPromoteVideoCount() return self.promote_video_count end function UserDataMgr:setPromoteVideoCount(count) self.promote_video_count = count end function UserDataMgr:addPromoteVideoCount(count) self.promote_video_count = self.promote_video_count + count end -- 9. 累计观看广告次数 function UserDataMgr:getWatchAdCount() return self.watch_ad_count end function UserDataMgr:setWatchAdCount(count) self.watch_ad_count = count end function UserDataMgr:addWatchAdCount(count) self.watch_ad_count = self.watch_ad_count + count end -- 10. 解锁烧烤菜累计次数 function UserDataMgr:getUnlockCuisineTotalCount() return self.unlock_cuisine_total_count end function UserDataMgr:setUnlockCuisineTotalCount(count) self.unlock_cuisine_total_count = count end function UserDataMgr:addUnlockCuisineTotalCount(count) self.unlock_cuisine_total_count = self.unlock_cuisine_total_count + count end -- 11. 累计完成扭蛋次数 function UserDataMgr:getUseEggsCount() return self.use_eggs_count end function UserDataMgr:setUseEggsCount(count) self.use_eggs_count = count end function UserDataMgr:addUseEggsCount(count) self.use_eggs_count = self.use_eggs_count + count end -- 12. 与特殊顾客互动次数 function UserDataMgr:getInteractSpecialCustomerCount() return self.interact_special_customer_count end function UserDataMgr:setInteractSpecialCustomerCount(count) self.interact_special_customer_count = count end function UserDataMgr:addInteractSpecialCustomerCount(count) self.interact_special_customer_count = self.interact_special_customer_count + count end -- 13. 累计钓鱼次数 function UserDataMgr:getFishCount() return self.fish_count end function UserDataMgr:setFishCount(count) self.fish_count = count end function UserDataMgr:addFishCount(count) self.fish_count = self.fish_count + count end -- 14. 获得音符数量累计 function UserDataMgr:getNoteCount() return self.note_count end function UserDataMgr:setNoteCount(count) self.note_count = count end function UserDataMgr:addNoteCount(count) self.note_count = self.note_count + count end -- 15. 解锁员工累计 function UserDataMgr:getUnlockEmployeeCount() return self.unlock_employee_count end function UserDataMgr:setUnlockEmployeeCount(count) self.unlock_employee_count = count end function UserDataMgr:addUnlockEmployeeCount(count) self.unlock_employee_count = self.unlock_employee_count + count end -- 16. 访问好友餐厅次数累计 function UserDataMgr:getVisitFriendCount() return self.visit_friend_count end function UserDataMgr:setVisitFriendCount(count) self.visit_friend_count = count end function UserDataMgr:addVisitFriendCount(count) self.visit_friend_count = self.visit_friend_count + count end -- 17. 招揽摊主累计 function UserDataMgr:getRecruitCount() return self.recruit_count end function UserDataMgr:setRecruitCount(count) self.recruit_count = count end function UserDataMgr:addRecruitCount(count) self.recruit_count = self.recruit_count + count end -- 18. 招揽顾客累计 function UserDataMgr:getRecruitCustomerCount() return self.recruit_customer_count end function UserDataMgr:setRecruitCustomerTotalCount(count) self.recruit_customer_count = count end function UserDataMgr:addRecruitCustomerCount(count) self.recruit_customer_count = self.recruit_customer_count + count end -- 19. 解锁场景累计 function UserDataMgr:getUnlockSceneCount() return self.unlock_scene_count end function UserDataMgr:setUnlockSceneCount(count) self.unlock_scene_count = count end function UserDataMgr:addUnlockSceneCount(count) self.unlock_scene_count = self.unlock_scene_count + count end -- 20. 任务进度累计 function UserDataMgr:getTaskProgressCount() return self.task_progress_count end function UserDataMgr:setTaskProgressCount(count) self.task_progress_count = count end function UserDataMgr:addTaskProgressCount(count) self.task_progress_count = self.task_progress_count + count end -- 21. 招揽特定顾客的数量 function UserDataMgr:getRecruitSpecificCustomerCount(customerId) return self.recruit_specific_customer_count[customerId] or 0 end function UserDataMgr:setRecruitSpecificCustomerCount(customerId, count) self.recruit_specific_customer_count[customerId] = count end function UserDataMgr:addRecruitSpecificCustomerCount(customerId, count) self.recruit_specific_customer_count[customerId] = self:getRecruitSpecificCustomerCount(customerId) + count end -- 22. 招待特定顾客的数量 function UserDataMgr:getEntertainSpecificCustomerCount(customerId) return self.entertain_specific_customer_count[customerId] or 0 end function UserDataMgr:setEntertainSpecificCustomerCount(customerId, count) self.entertain_specific_customer_count[customerId] = count end function UserDataMgr:addEntertainSpecificCustomerCount(customerId, count) self.entertain_specific_customer_count[customerId] = self:getEntertainSpecificCustomerCount(customerId) + count end -- 每日任务相关数据 -- 这些字段由 TaskDailyMgr 管理,只在 UserDataMgr 中存储 function UserDataMgr:initDailyTaskData() -- 当前每日任务ID列表 self.daily_task_ids = json.decode(GetString(StrogeKeyDef.daily_task_ids, "{}")) -- 每日任务状态表 {task_id: state} state: 1进行中 2可完成 3已领取 self.daily_task_states = json.decode(GetString(StrogeKeyDef.daily_task_states, "{}")) -- 上次刷新每日任务的时间戳 self.daily_task_last_refresh = GetInt(StrogeKeyDef.daily_task_last_refresh, 0) end -- 成就任务相关数据 -- 这些字段由 TaskAchMgr 管理,只在 UserDataMgr 中存储 function UserDataMgr:initAchTaskData() -- 已完成的成就任务ID列表 self.completed_ach_task_ids = json.decode(GetString(StrogeKeyDef.completed_ach_task_ids, "{}")) -- 已领取的成就任务ID列表 self.claimed_ach_task_ids = json.decode(GetString(StrogeKeyDef.claimed_ach_task_ids, "{}")) -- 成就任务状态表 {task_id: state} state: 1进行中 2可完成 3已领取 self.ach_task_states = json.decode(GetString(StrogeKeyDef.ach_task_states, "{}")) end -- 在线时长 function UserDataMgr:initOnlineTime() -- 上次在线日期 self.last_online_day = GetString(StrogeKeyDef.last_online_day, "20220601") -- 上次在线时间 self.last_online_time = GetInt(StrogeKeyDef.last_online_time, 0) -- 在线天数 self.online_days = GetInt(StrogeKeyDef.online_days, 0) -- 当日在线时间 self.today_online_time = GetInt(StrogeKeyDef.today_online_time, 0) local today = os.date("%Y%m%d") -- 获取今天的日期 self.isFirstLoginToday = self.last_online_day ~= today -- 是否是今天第一次登录 if self.isFirstLoginToday then self:setTodayOnlineTime(0) self:updateLastOnlineDay(today) self:setOnlineDays(self.online_days + 1) end end function UserDataMgr:getLastOnlineDay() return self.last_online_day end function UserDataMgr:updateLastOnlineDay(date) self.last_online_day = date or os.date("%Y%m%d") SetString(StrogeKeyDef.last_online_day, self.last_online_day) -- 保存到存档 end function UserDataMgr:getOnlineDays() return self.online_days end function UserDataMgr:updateLastOnlineTime(time) self.last_online_time = time or os.time() SetInt(StrogeKeyDef.last_online_time, self.last_online_time) -- 保存到存档 end function UserDataMgr:getLastOnlineTime() return self.last_online_time end function UserDataMgr:setOnlineDays(days) self.online_days = days SetInt(StrogeKeyDef.online_days, days) -- 保存到存档 end function UserDataMgr:getTodayOnlineTime() return self.today_online_time end -- 更新在线时间(每分钟调用一次) function UserDataMgr:updateOnlineTime() self:setLastOnlineTime() self:setTodayOnlineTime(self.today_online_time + 60) end function UserDataMgr:setTodayOnlineTime(time) self.today_online_time = time SetInt(StrogeKeyDef.today_online_time, time) -- 保存到存档 end return UserDataMgr XSdk--[[ author:{zhangpeng} time:2023-09-24 17:18:28 ]] local XSdk, super = defClassStatic("XSdk", XSdkBase) function XSdk:init() end XSdk:init()mainirequire("modules/ui/storydialogui/StorydialogConst") require("modules/ui/storydialogui/StoryDialogUI")mainerequire("modules/currency/coin/view/CoinOnDesk") require("modules/currency/coin/mgr/CoinDeskMgr") boothUnlockCfg9--[[ from file:摊位解锁.xlsx --]] local boothUnlockCfg = { [1] = { id = 1, order = 1, price = 0, time = 60, }, [2] = { id = 2, order = 2, price = 500000, time = 3600, }, [3] = { id = 3, order = 3, price = 2000000, time = 7200, }, [4] = { id = 4, order = 4, price = 5000000, time = 14400, }, [5] = { id = 5, order = 5, price = 10000000, time = 28800, }, [6] = { id = 6, order = 6, price = 25000000, time = 57600, }, } return boothUnlockCfg serpent* local n, v = "serpent", "0.302" -- (C) 2012-18 Paul Kulchenko; MIT License local c, d = "Paul Kulchenko", "Lua serializer and pretty printer" local snum = {[tostring(1 / 0)] = "1/0 --[[math.huge]]", [tostring(-1 / 0)] = "-1/0 --[[-math.huge]]", [tostring(0 / 0)] = "0/0"} local badtype = {thread = true, userdata = true, cdata = true} local getmetatable = debug and debug.getmetatable or getmetatable local pairs = function(t) return next, t end -- avoid using __pairs in Lua 5.2+ local keyword, globals, G = {}, {}, (_G or _ENV) for _, k in ipairs( { "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while" } ) do keyword[k] = true end for k, v in pairs(G) do globals[v] = k end -- build func to name mapping for _, g in ipairs({"coroutine", "debug", "io", "math", "string", "table", "os"}) do for k, v in pairs(type(G[g]) == "table" and G[g] or {}) do globals[v] = g .. "." .. k end end local function s(t, opts) local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge local space, maxl = (opts.compact and "" or " "), (opts.maxlevel or math.huge) local maxlen, metatostring = tonumber(opts.maxlength), opts.metatostring local iname, comm = "_" .. (name or ""), opts.comment and (tonumber(opts.comment) or math.huge) local numformat = opts.numformat or "%.17g" local seen, sref, syms, symn = {}, {"local " .. iname .. "={}"}, {}, 0 local function gensym(val) return "_" .. (tostring(tostring(val)):gsub("[^%w]", ""):gsub( "(%d%w+)", -- tostring(val) is needed because __tostring may return a non-string value function(s) if not syms[s] then symn = symn + 1 syms[s] = symn end return tostring(syms[s]) end )) end local function safestr(s) return type(s) == "number" and tostring(huge and snum[tostring(s)] or numformat:format(s)) or type(s) ~= "string" and tostring(s) or -- escape NEWLINE/010 and EOF/026 ("%q"):format(s):gsub("\010", "n"):gsub("\026", "\\026") end local function comment(s, l) return comm and (l or 0) < comm and " --[[" .. select(2, pcall(tostring, s)) .. "]]" or "" end local function globerr(s, l) return globals[s] and globals[s] .. comment(s, l) or not fatal and safestr(select(2, pcall(tostring, s))) or error("Can't serialize " .. tostring(s)) end local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r'] local n = name == nil and "" or name local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n] local safe = plain and n or "[" .. safestr(n) .. "]" return (path or "") .. (plain and path and "." or "") .. safe, safe end local alphanumsort = type(opts.sortkeys) == "function" and opts.sortkeys or function(k, o, n) -- k=keys, o=originaltable, n=padding local maxn, to = tonumber(n) or 12, {number = "a", string = "b"} local function padnum(d) return ("%0" .. tostring(maxn) .. "d"):format(tonumber(d)) end table.sort( k, function(a, b) -- sort numeric keys first: k[key] is not nil for numerical keys return (k[a] ~= nil and 0 or to[type(a)] or "z") .. (tostring(a):gsub("%d+", padnum)) < (k[b] ~= nil and 0 or to[type(b)] or "z") .. (tostring(b):gsub("%d+", padnum)) end ) end local function val2str(t, name, indent, insref, path, plainindex, level) local ttype, level, mt = type(t), (level or 0), getmetatable(t) local spath, sname = safename(path, name) local tag = plainindex and ((type(name) == "number") and "" or name .. space .. "=" .. space) or (name ~= nil and sname .. space .. "=" .. space or "") if seen[t] then -- already seen this element sref[#sref + 1] = spath .. space .. "=" .. space .. seen[t] return tag .. "nil" .. comment("ref", level) end -- protect from those cases where __tostring may fail if type(mt) == "table" and metatostring ~= false then local to, tr = pcall( function() return mt.__tostring(t) end ) local so, sr = pcall( function() return mt.__serialize(t) end ) if (to or so) then -- knows how to serialize itself seen[t] = insref or spath t = so and sr or tr ttype = type(t) end -- new value falls through to be serialized end if ttype == "table" then if level >= maxl then return tag .. "{}" .. comment("maxlvl", level) end seen[t] = insref or spath if next(t) == nil then return tag .. "{}" .. comment(t, level) end -- table empty if maxlen and maxlen < 0 then return tag .. "{}" .. comment("maxlen", level) end local maxn, o, out = math.min(#t, maxnum or #t), {}, {} for key = 1, maxn do o[key] = key end if not maxnum or #o < maxnum then local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables for key,_ in pairs(t) do if o[key] ~= key then n = n + 1 o[n] = key end end end if maxnum and #o > maxnum then o[maxnum + 1] = nil end if opts.sortkeys and #o > maxn then alphanumsort(o, t, opts.sortkeys) end local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output) for n, key in ipairs(o) do local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse if opts.valignore and opts.valignore[value] or -- skip ignored values; do nothing opts.keyallow and not opts.keyallow[key] or opts.keyignore and opts.keyignore[key] or opts.valtypeignore and opts.valtypeignore[type(value)] or -- skipping ignored value types sparse and value == nil then -- skipping nils; do nothing elseif ktype == "table" or ktype == "function" or badtype[ktype] then if not seen[key] and not globals[key] then sref[#sref + 1] = "placeholder" local sname = safename(iname, gensym(key)) -- iname is table for local variables sref[#sref] = val2str(key, sname, indent, sname, iname, true) end sref[#sref + 1] = "placeholder" local path = seen[t] .. "[" .. tostring(seen[key] or globals[key] or gensym(key)) .. "]" sref[#sref] = path .. space .. "=" .. space .. tostring(seen[value] or val2str(value, nil, indent, path)) else out[#out + 1] = val2str(value, key, indent, nil, seen[t], plainindex, level + 1) if maxlen then maxlen = maxlen - #out[#out] if maxlen < 0 then break end end end end local prefix = string.rep(indent or "", level) local head = indent and "{\n" .. prefix .. indent or "{" local body = table.concat(out, "," .. (indent and "\n" .. prefix .. indent or space)) local tail = indent and "\n" .. prefix .. "}" or "}" return (custom and custom(tag, head, body, tail, level) or tag .. head .. body .. tail) .. comment(t, level) elseif badtype[ttype] then seen[t] = insref or spath return tag .. globerr(t, level) elseif ttype == "function" then seen[t] = insref or spath if opts.nocode then return tag .. "function() --[[..skipped..]] end" .. comment(t, level) end local ok, res = pcall(string.dump, t) local func = ok and "((loadstring or load)(" .. safestr(res) .. ",'@serialized'))" .. comment(t, level) return tag .. (func or globerr(t, level)) else return tag .. safestr(t) end -- handle all other types end local sepr = indent and "\n" or ";" .. space local body = val2str(t, name, indent) -- this call also populates sref local tail = #sref > 1 and table.concat(sref, sepr) .. sepr or "" local warn = opts.comment and #sref > 1 and space .. "--[[incomplete output with shared/self-references skipped]]" or "" return not name and body .. warn or "do local " .. body .. sepr .. tail .. "return " .. name .. sepr .. "end" end local function deserialize(data, opts) local env = (opts and opts.safe == false) and G or setmetatable( {}, { __index = function(t, k) return t end, __call = function(t, ...) error("cannot call functions") end } ) local f, res = (loadstring or load)("return " .. data, nil, nil, env) if not f then f, res = (loadstring or load)(data, nil, nil, env) end if not f then return f, res end if setfenv then setfenv(f, env) end return pcall(f) end local function merge(a, b) if b then for k, v in pairs(b) do a[k] = v end end return a end return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s, load = deserialize, dump = function(a, opts) return s(a, merge({name = "_", compact = true, sparse = true}, opts)) end, line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end, block = function(a, opts) return s(a, merge({indent = " ", sortkeys = true, comment = true}, opts)) end } Def$------------------------------------ --@func defClass(name, super=nil) --@desc 定义类 --@ret class:定义的类 --@ret super:定义类的父类,同输入super --@arg name:string,类名称 --@arg super:table,已经定义的其他类 --@func defClassStatic(name, super=nil) --@desc 定义静态类,生成的类不含new方法,直接通过类名调用 --@ret class:定义的类 --@ret super:定义类的父类,同输入super --@arg name:string,类名称 --@arg super:table,已经定义的其他类 ------------------------------------ local type = type local rawget = rawget local rawset = rawset local getmetatable = getmetatable local setmetatable = setmetatable local UnityEngine = CS.UnityEngine local LOGTAG = "Def" ---@class LuaClassBase local _cls_base = { ---需要退出之时,会遍历自己保持的所有子object和table中的object, 调用他们的 exit. ---如果保持了生命周期长于自己的object, 需要在 onExit 内改为nil, 例如 self.scene=nil ---最后要调用 super.onExit(self) ---@param self LuaClassBase onExit = function(self) if not self.__cls_inst then printError(LOGTAG, "[cls.onExit]invalid self", self.__cls_name) end self.__cls_call_onExit = true local selfStr = string.format("%s", self) for k, v in pairs(self) do if type(v) == "table" then if v.__cls_inst then if not v.__exited then v:exit(string.format("%s %s", selfStr, k)) end else for k, t in pairs(v) do if type(t) == "table" and t.__cls_inst then if not t.__exited then t:exit(string.format("%s %s", selfStr, k)) end end end end end end end, onmsg = function(self, ids, func, obj, priority) if obj ~= self then self._onmsgRefList = self._onmsgRefList or {} table.insert(self._onmsgRefList, { ids, func, obj }) end Msg.add(ids, func, obj, priority) end, timer = function(self, handler, interval, loop, priority, isTimingOnBackground, tailHandler) self._timerRefList = self._timerRefList or {} local ref = TimerMgr:add(handler, interval, loop, priority, isTimingOnBackground, tailHandler) table.insert(self._timerRefList, ref) return ref end, clear = function(self, onmsg, timer) self.__cls_call_clear = true if self._onmsgRefList and onmsg ~= false then for i, v in ipairs(self._onmsgRefList) do Msg.del(v[1], v[2], v[3]) end self._onmsgRefList = nil end if onmsg ~= false then Msg.del(nil, nil, self) end if self._timerRefList and timer ~= false then for i, v in ipairs(self._timerRefList) do TimerMgr:rem(v) end self._timerRefList = nil end end, valid = function(self) return (not self.__exited) end, ---退出,需要手动调用。尽量不要重构,除非需要hook exit, 比如第一次exit不退出,第二次exit才退出 ---@param self LuaClassBase exit = function(self, srcStr) if self.__exited then return end self.__exited = true printVerbose("Def", "exit of %s srcStr:%s", self, srcStr) self:clear() if not self.__cls_call_clear then printError("Def", "missing super.clear %s", self.__cls_name) end self:onExit() if not self.__cls_call_onExit then printError("Def", "[cls.exit]missing super.onExit. %s", self.__cls_name) end for k, _v in pairs(self) do self[k] = nil end self.__exited = true end } local _cls_mark = function(name, cls, env) local p = env or _ENV for ns in string.gmatch(name, "(.-)%.") do local t = rawget(p, ns) if not t then t = {} rawset(p, ns, t) end p = t end local mod = string.match(name, "([^%.]+)$") rawset(p, mod, cls) end function isClassOf(obj, name) local curr = obj local i = 0 while true do i = i + 1 if i > 1000 then error("死循环了") break end if not curr then break end if curr == _cls_base then return false end if curr.__cls_name == name then return true end local mt = getmetatable(curr) if mt then curr = mt.__index else return false end end end function defClass(name, super, env) env = env or _ENV if rawget(env, name) then local str = "defClass:redefined" .. name UnityEngine.Debug.LogError(str) error(str) end ---@class LuaClass:LuaClassBase local cls = {} cls.__cls_type = cls cls.__cls_name = name cls.__cls_source = debug.getinfo(2) cls.__ori_str = tostring(cls) local __tostring = function(t) return "LuaObject of " .. name .. " " .. t.__ori_str end local cls_meta = { __index = cls, __tostring = __tostring } cls.new = function(...) local obj = {} obj.__cls_inst = true obj.__ori_str = tostring(obj) obj.class = cls setmetatable(obj, cls_meta) -- 不添加,如果是从原生启动 unity 退出的时候需要清理的话,obj 自己添加消息 -- Msg.add(Msg.EXIT, function() -- if obj.exit then obj:exit("Msg.EXIT") end -- end, obj) if obj.ctor then obj:ctor(...) end return obj end super = super or _cls_base setmetatable(cls, { __index = super, __tostring = function(t) return "LuaClass of " .. name .. " " .. t.__ori_str end }) cls.super = super _cls_mark(name, cls, env) return cls, super end function defClassStatic(name, super, env) env = env or _ENV if rawget(env, name) then local str = "defClassStatic:redefined" .. name UnityEngine.Debug.LogError(str) error(str) end ---@class LuaStaticClass local cls = {} cls.__cls_type = cls cls.__cls_name = name cls.__cls_static = true cls.__ori_str = tostring(cls) cls.__cls_source = debug.getinfo(2) local __tostring = function(t) return "LuaStaticObject of " .. name .. " " .. t.__ori_str end if super then setmetatable(cls, { __index = super, __tostring = __tostring }) cls.super = super else setmetatable(cls, { __tostring = __tostring }) end _cls_mark(name, cls, env) return cls, super end function defGlobal(name, env) env = env or _ENV if rawget(env, name) then local str = "defGlobal:redefined" .. name UnityEngine.Debug.LogError(str) error(str) end local t = {} _cls_mark(name, t, env) return t end local _Lua_G = _G or _ENV local rawget = _Lua_G.rawget local rawset = _Lua_G.rawset local UnityEngineUI = UnityEngine.UI local _global_find = function(k) local v = rawget(_Lua_G, k) if v ~= nil then return v end local v = rawget(UnityEngineUI, k) if v then return v end xlua.import_type("UnityEngine.UI." .. k) local v = rawget(UnityEngineUI, k) if v then return v end local v = rawget(UnityEngine, k) if v then return v end xlua.import_type("UnityEngine." .. k) local v = rawget(UnityEngine, k) if v then return v end return nil end local _meta_newindex = function(t, k, v) local str = string.format("不允许修改全局变量:k:%s,v:%s\n%s", tostring(k), tostring(v), debug.traceback()) UnityEngine.Debug.Log(str) end local _meta_newindex_error = function(t, k, v) local str = string.format("不允许修改全局变量:k:%s,v:%s\n%s", tostring(k), tostring(v), debug.traceback()) UnityEngine.Debug.LogError(str) error(str) end local _meta_index = function(t, k) local v = _global_find(k) if v ~= nil then rawset(t, k, v) return v else UnityEngine.Debug.Log(string.format("全局变量不存在:k:%s,v:%s\n%s", tostring(k), tostring(v), debug.traceback())) return nil end end local _meta_index_error = function(t, k) local v = _global_find(k) if v ~= nil then rawset(t, k, v) return v else UnityEngine.Debug.LogError(string.format("全局变量不存在:k:%s,v:%s\n%s", tostring(k), tostring(v), debug.traceback())) return nil end end function isGlobalExists(k) local v = _global_find(k) return v ~= nil end function EnableGlobalCheck(env) setmetatable(env or _ENV, { __index = _meta_index_error, __newindex = _meta_newindex_error, }) end function DisableGlobalCheck(env) setmetatable(env or _ENV, { __index = _meta_index, __newindex = _meta_newindex, }) end DisableGlobalCheck() PauseUtilW--[[ 暂停工具 author:{zhangpeng} time:2023-04-17 17:23:20 ]] local PauseUtil = {} function PauseUtil.pauseAudio(goOrScene) local result = {} local coms = goOrScene:SeekType(typeof(CS.AudiosComponent)) for i =0,coms.Count - 1 do local com = coms[i] for _,clip in cs_ipairs(com.clips)do if clip.state == CS.AudiosComponent.State.Playing then table.insert(result,clip) clip:Pause() -- printInfo("PauseUtil", "pauseAudio, %s", clip.useSource.source.clip.name) end end end return result end function PauseUtil.resumeAudio(result) for _,clip in ipairs(result or {})do if not CS.LuaHelper.IsNull(clip) then clip:Resume() end end end function PauseUtil.pauseTimeline(goOrScene) local timelines = goOrScene:SeekType(typeof(CS.UnityEngine.Playables.PlayableDirector)) local playstate = CS.UnityEngine.Playables.PlayState.Playing local result = {} for i =0,timelines.Count - 1 do local t = timelines[i] if t.isActiveAndEnabled and t.state == playstate then t.enabled = false table.insert(result, t) end end return result end function PauseUtil.resumeTimeline(result) for i,t in ipairs(result or {}) do if not CS.LuaHelper.IsNull(t) then t.enabled = true end end end function PauseUtil.pauseVideoPlayer(goOrScene) local coms = goOrScene:SeekType(typeof(CS.UnityEngine.Video.VideoPlayer)) local result = {} for i =0,coms.Count - 1 do local com = coms[i] if com.isActiveAndEnabled and not com.isPaused then -- com.gameObject.__videoPlayerOnAppBackgroud = com.time com:Pause() printDebug("PauseUtil.pauseVideoPlayer:" .. tostring(com.gameObject.name)) table.insert(result , com) end end return result end function PauseUtil.resumeVideoPlayer(result) for _,videoPlayer in ipairs(result or {}) do if not CS.LuaHelper.IsNull(videoPlayer) then videoPlayer:Play() for _,f in ipairs(videoPlayer.gameObject.__videoPlayerNotCalledPreparedCallbacks or {})do f() end videoPlayer.gameObject.__videoPlayerNotCalledPreparedCallbacks = {} end end end function PauseUtil.stopVideoPlayer(goOrScene) local coms = goOrScene:SeekType(typeof(CS.UnityEngine.Video.VideoPlayer)) for i =0,coms.Count - 1 do local com = coms[i] com:Stop() printDebug("PauseUtil.stopVideoPlayer:" .. tostring(com.gameObject.name)) end end function PauseUtil.stopAction(goOrScene) local coms = goOrScene:SeekType(typeof(CS.wtween.ActionUpdater)) local result = {} local Destroy = CS.UnityEngine.Object.Destroy for i =0,coms.Count - 1 do local com = coms[i] com.actionList:Clear() com.isDestoried = true Destroy(com) end return result end function PauseUtil.pauseAction(goOrScene) local coms = goOrScene:SeekType(typeof(CS.wtween.ActionUpdater)) local result = {} for i =0,coms.Count - 1 do local com = coms[i] if not com.isPaused then com:Pause() printDebug("PauseUtil.pauseAction:" .. tostring(com.gameObject.name)) table.insert(result , com) end end return result end function PauseUtil.resumeAction(result) for _,action in ipairs(result or {}) do if not CS.LuaHelper.IsNull(action) then action:Resume() end end end function PauseUtil.pauseSpine(goOrScene) local coms = goOrScene:SeekType(typeof(CS.Spine.Unity.SkeletonAnimation)) local result = {} for i =0,coms.Count - 1 do local com = coms[i] com:Initialize(false) if com.AnimationState and com.AnimationState.TimeScale ~= 0 then util.spine.pause(com.gameObject) printDebug("PauseUtil.pauseSpine:" .. tostring(com.gameObject.name)) table.insert(result , com.gameObject) end end return result end function PauseUtil.resumeSpine(result) for _,spineObj in ipairs(result or {}) do if not CS.LuaHelper.IsNull(spineObj) then util.spine.resume(spineObj) end end end function PauseUtil.pauseAll(goOrScene) local result = { audio = PauseUtil.pauseAudio(goOrScene), timeline = PauseUtil.pauseTimeline(goOrScene), video = PauseUtil.pauseVideoPlayer(goOrScene), action = PauseUtil.pauseAction(goOrScene), spine = PauseUtil.pauseSpine(goOrScene), } return result end function PauseUtil.resumeAll(result) PauseUtil.resumeAudio(result.audio) PauseUtil.resumeTimeline(result.timeline) PauseUtil.resumeVideoPlayer(result.video) PauseUtil.resumeAction(result.action) PauseUtil.resumeSpine(result.spine) end function PauseUtil.stopAll(goOrScene) PauseUtil.pauseAudio(goOrScene) PauseUtil.pauseTimeline(goOrScene) PauseUtil.stopVideoPlayer(goOrScene) PauseUtil.stopAction(goOrScene) PauseUtil.pauseSpine(goOrScene) end return PauseUtil TaskDailyMgr--[[ 每日任务管理 author:{zhangpeng} time:2025-05-27 11:09:27 ]] local TaskDailyMgr = defClassStatic("TaskDailyMgr") -- 辅助函数:检查表中是否包含指定值 local function tableContains(tbl, value) for _, v in ipairs(tbl) do if v == value then return true end end return false end function TaskDailyMgr:init() printInfo("TaskDailyMgr", "------ 每日任务管理 初始化 ------") -- 数据保存在 UserDataMgr 中,这里不需要初始化 -- 确保 UserDataMgr 中有相关字段 if not UserDataMgr.daily_task_ids then UserDataMgr.daily_task_ids = {} end if not UserDataMgr.daily_task_states then UserDataMgr.daily_task_states = {} end if not UserDataMgr.daily_task_last_refresh then UserDataMgr.daily_task_last_refresh = 0 end end -- 设置当前每日任务ID列表 function TaskDailyMgr:setDailyTaskIds(taskIds) UserDataMgr.daily_task_ids = taskIds or {} -- 初始化所有任务状态为进行中 UserDataMgr.daily_task_states = {} for _, taskId in ipairs(UserDataMgr.daily_task_ids) do UserDataMgr.daily_task_states[taskId] = TaskConst.TaskState.in_progress end -- 记录刷新时间 UserDataMgr.daily_task_last_refresh = os.time() end -- 获取当前每日任务ID列表 function TaskDailyMgr:getDailyTaskIds() return UserDataMgr.daily_task_ids end -- 获取任务状态 function TaskDailyMgr:getTaskState(taskId) -- 如果是每日任务,从每日任务状态表中获取 if tableContains(UserDataMgr.daily_task_ids, taskId) then return UserDataMgr.daily_task_states[taskId] or TaskConst.TaskState.in_progress end -- 默认返回进行中 return TaskConst.TaskState.in_progress end -- 设置任务状态 function TaskDailyMgr:setTaskState(taskId, state) -- 如果是每日任务,更新每日任务状态表 if tableContains(UserDataMgr.daily_task_ids, taskId) then UserDataMgr.daily_task_states[taskId] = state end end -- 检查任务是否已领取 function TaskDailyMgr:isTaskClaimed(taskId) return self:getTaskState(taskId) == TaskConst.TaskState.claimed end -- 标记任务为已领取 function TaskDailyMgr:setTaskClaimed(taskId) self:setTaskState(taskId, TaskConst.TaskState.claimed) end -- 检查是否需要刷新每日任务 function TaskDailyMgr:shouldRefreshDailyTasks() -- 获取当前时间 local currentTime = os.time() -- 获取当前日期 local currentDate = os.date("%Y-%m-%d", currentTime) -- 获取上次刷新的日期 local lastRefreshDate = os.date("%Y-%m-%d", UserDataMgr.daily_task_last_refresh or 0) -- 如果日期不同,需要刷新 return currentDate ~= lastRefreshDate end -- 生成可用的每日任务列表 function TaskDailyMgr:genAvailableDailyTasks() local taskList = TaskCfgParse:getTaskListByType(TaskConst.TaskType.daily) local availableTasks = {} for _, taskData in ipairs(taskList) do -- 判断解锁条件 local unlockCond = taskData.unlockCond if unlockCond == -1 then -- -1为无任何限制条件 table.insert(availableTasks, taskData) else -- 从通用解锁条件表中取出解锁限制数据 local unlockCondData = UnlockCommonCfgParse:getUnlockCondData(unlockCond) if unlockCondData then -- 判断是否满足解锁条件 if unlockCondData.limitType == TaskConst.UnlockCondType.star then -- 星星达到 if UserDataMgr:getStarCount() >= unlockCondData.value then table.insert(availableTasks, taskData) end elseif unlockCondData.limitType == TaskConst.UnlockCondType.scene then -- 场景解锁 local scene_unlock = UserDataMgr:getSceneUnlocked(unlockCondData.value) if scene_unlock then table.insert(availableTasks, taskData) end end end end end return availableTasks end return TaskDailyMgr DollUserData -- 用户数据 玩偶 local DollUserData = defClass("DollUserData") -- 构造函数 function DollUserData:ctor() -- 玩偶数量 self.doll_count_dic = {} -- { dollId: count} self.doll_state_dic = {} -- { dollId: state} 玩偶状态 1:未获得 2:已获得 3:已集齐 end -- 加载用户数据 function DollUserData:load() if UserDataMgr.isNewPlayer then self:resetData() else self:loadFromLocal() end self:onLoadComplete() end -- 从本地加载数据 function DollUserData:loadFromLocal() self.doll_count_dic = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.doll_count_dic, "{}"))) self.doll_state_dic = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.doll_state_dic, "{}"))) end -- 保存用户数据 function DollUserData:save() self:saveToLocal() end -- 保存到本地 function DollUserData:saveToLocal() PlayerPrefsMgr:setString(StrogeKeyDef.doll_count_dic, json.encode(PlayerPrefsMgr:idToString(self.doll_count_dic))) PlayerPrefsMgr:setString(StrogeKeyDef.doll_state_dic, json.encode(PlayerPrefsMgr:idToString(self.doll_state_dic))) PlayerPrefsMgr:save() end -- 重置数据 function DollUserData:resetData() end -- 加载数据完成回调 function DollUserData:onLoadComplete() self:save() end -- 获取玩偶数量 function DollUserData:getDollCount(dollId) return self.doll_count_dic[dollId] or 0 end -- 设置玩偶数量 function DollUserData:setDollCount(dollId, count, refuseSave) self.doll_count_dic[dollId] = count if not refuseSave then self:save() end return count end -- 增加玩偶数量 function DollUserData:addDollCount(dollId, count, refuseSave) local curr = self:getDollCount(dollId) + count return self:setDollCount(dollId, curr, refuseSave) end -- 获取玩偶状态 function DollUserData:getDollState(dollId) return self.doll_state_dic[dollId] or DollConst.State.None -- 默认状态为未获得 end -- 设置玩偶状态 function DollUserData:setDollState(dollId, state, refuseSave) self.doll_state_dic[dollId] = state if not refuseSave then self:save() end return state end return DollUserDataCustomerSpecialVisits--- 特殊顾客来访 ---@class CustomerSpecialVisit:LuaClass ---@field isNewPlayer boolean 是否新手 ---@field isFirstLoginToday boolean 是否当日首次登录 ---@field useSolicitationType boolean 使用招揽类型 ---@field customerSpecialInterval number 特殊顾客间隔 ---@field customerSpecialLastUpdate number 上次特殊顾客更新时间 ---@field customerSpecialQueue CustomerSpecialType[] 特殊顾客队列 ---@field customerSpecialCanVisits CustomerSpecialType[] 可来访特殊顾客 ---@field customerSpecialVisitsToday number[] 特殊顾客来访次数 local CustomerSpecialVisit = defClass("CustomerSpecialVisit") --- 构造函数 function CustomerSpecialVisit:ctor() self.useSolicitationType = nil self.customerSpecialQueue = {} -- 每次登录重置 self.customerSpecialInterval = math.random(8 * 60, 12 * 60) self.isNewPlayer = UserDataMgr.isNewPlayer end --- 加载存档 function CustomerSpecialVisit:loadSave() self.isFirstLoginToday = CustomerMgr.isFirstLoginToday self.customerSpecialQueue = {} self.customerSpecialCanVisits = {} self.customerSpecialVisitsToday = {} -- 与customerSpecialCanVisits长度同等 self.customerSpecialLastUpdate = CustomerMgr.time end --- 更新 ---@param time number 在线时长 function CustomerSpecialVisit:timeUpdate(time) if self.isNewPlayer then self:addCustomerSpecialNewPlayer(time) else self:addCustomerSpecial(time) end end --- 增加特殊顾客来访队列 新手 ---@param time number 在线时长 function CustomerSpecialVisit:addCustomerSpecialNewPlayer(time) if time == 180 then self:enqueue(CustomerConst.CustomerSpecialType.RichCustomer) end if time == 600 then self:enqueue(CustomerConst.CustomerSpecialType.AdCustomer) end if time == 900 then self:enqueue(CustomerConst.CustomerSpecialType.ThiefCustomer) end if time == 1200 then -- 挂机不算时间 --for _, type in #self.customerSpecialQueue do -- if type == CustomerConst.CustomerSpecialType.RichCustomer then -- return -- end --end self:enqueue(CustomerConst.CustomerSpecialType.RichCustomer) end if time == 1800 then self:enqueue(CustomerConst.CustomerSpecialType.StenchCustomer) self.isNewPlayer = false end end --- 增加特殊顾客来访队列 ---@param time number 在线时长 function CustomerSpecialVisit:addCustomerSpecial(time) if self.isFirstLoginToday then if time == 180 then self:enqueue(self:judgeCustomerSpecialType()) end self.isFirstLoginToday = false return end if time - self.customerSpecialLastUpdate < self.customerSpecialInterval then return end self.customerSpecialLastUpdate = time self.customerSpecialInterval = math.random(8 * 60, 12 * 60) self:enqueue(self:judgeCustomerSpecialType()) end --- 宣传手段更新时三分钟内点击宣传来更新 todo::函数放入解锁逻辑 function CustomerSpecialVisit:updateSolicitationList(type) local list = CustomerMgr.customerUnlockSpecialList local result = {} for _, item in ipairs(list) do if item.solicitationType <= type then self:enqueue(item.solicitationType) end end self.customerSpecialCanVisits = result end --- 获取每日来访次数上限 ---@param type CustomerSpecialType 招揽类型 ---@return number 每日来访次数上限 function CustomerSpecialVisit:getCustomerSpecialLimit(type) return 10 end --- 获取可来访特殊顾客类型 ---@return CustomerSpecialType 招揽类型 function CustomerSpecialVisit:judgeCustomerSpecialType() local list = {} for _, item in ipairs(self.customerSpecialCanVisits) do if item < self:getCustomerSpecialLimit(item.solicitationType) then self:enqueue(item.solicitationType) end end if #list == 0 then return CustomerConst.CustomerSpecialType.AdCustomer end return list[math.random(1, #list)] end --- 入队列 ---@param customerType number 顾客类型 function CustomerSpecialVisit:enqueue(customerType) table.insert(self.customerSpecialQueue, customerType) end --- 出队列 ---@return number 顾客id function CustomerSpecialVisit:dequeue() return table.remove(self.customerSpecialQueue, 1) end return CustomerSpecialVisitCurrencyUserData--- 货币数值管理 local CurrencyUserData = defClass("CurrencyUserData") local LOGTAG = "CurrencyUserData" function CurrencyUserData:ctor() self:init() end function CurrencyUserData:init() -- 初始化数据 -- 金币 self.coin = 0 -- 钻石 self.diamond = 0 -- 音符 self.musicalNotes = 0 -- 鱼饵 self.fishingBait = 0 -- 星星 self.star = 0 -- 广告券 self.adCoupons = 0 -- 如果是新玩家,重置数据 if UserDataMgr.isNewPlayer then self:resetData() end end --- 重置数据 function CurrencyUserData:resetData() self:setCoin(99999999, true) self:setDiamond(0, true) self:setMusicalNotes(100000, true) self:setFishingBait(0, true) self:setStar(99999, true) self:setAdCoupons(0, true) self:save() end --- 从本地或服务器加载数据 function CurrencyUserData:load() self:loadFromLocal() self:loadFromServer() end --- 保存数据到本地和服务器 function CurrencyUserData:save() self:saveToLocal() self:saveToServer() end --- 从本地加载数据 function CurrencyUserData:loadFromLocal() self.coin = PlayerPrefsMgr:getInt(StrogeKeyDef.USER_CURRENCY_COIN, self.coin) self.diamond = PlayerPrefsMgr:getInt(StrogeKeyDef.USER_CURRENCY_DIAMOND, self.diamond) self.musicalNotes = PlayerPrefsMgr:getInt(StrogeKeyDef.USER_CURRENCY_MUSICAL_NOTE, self.musicalNotes) self.fishingBait = PlayerPrefsMgr:getInt(StrogeKeyDef.USER_CURRENCY_FISHING_BAIT, self.fishingBait) self.star = PlayerPrefsMgr:getInt(StrogeKeyDef.USER_CURRENCY_STAR, self.star) self.adCoupons = PlayerPrefsMgr:getInt(StrogeKeyDef.USER_CURRENCY_AD_TICKET, self.adCoupons) end --- 保存数据到本地 function CurrencyUserData:saveToLocal() PlayerPrefsMgr:setInt(StrogeKeyDef.USER_CURRENCY_COIN, self.coin) PlayerPrefsMgr:setInt(StrogeKeyDef.USER_CURRENCY_DIAMOND, self.diamond) PlayerPrefsMgr:setInt(StrogeKeyDef.USER_CURRENCY_MUSICAL_NOTE, self.musicalNotes) PlayerPrefsMgr:setInt(StrogeKeyDef.USER_CURRENCY_FISHING_BAIT, self.fishingBait) PlayerPrefsMgr:setInt(StrogeKeyDef.USER_CURRENCY_STAR, self.star) PlayerPrefsMgr:setInt(StrogeKeyDef.USER_CURRENCY_AD_TICKET, self.adCoupons) PlayerPrefsMgr:save() end --- 从服务器加载数据 function CurrencyUserData:loadFromServer() end --- 保存数据到服务器 function CurrencyUserData:saveToServer() end --- 获取金币数值 function CurrencyUserData:getCoin() return self.coin end --- 设置金币数值 function CurrencyUserData:setCoin(value, refuseSave) self.coin = self:checkSetValue(self.coin, value, refuseSave, StrogeKeyDef.USER_CURRENCY_COIN) if not refuseSave then self:save() end return self:getCoin() end --- 改变金币数值 function CurrencyUserData:changeCoin(value, refuseSave) local curr = self:checkChangeValue(self.coin, value, refuseSave, StrogeKeyDef.USER_CURRENCY_COIN) self:setCoin(curr, refuseSave) return self:getCoin() end --- 获取钻石数值 function CurrencyUserData:getDiamond() return self.diamond end --- 设置钻石数值 function CurrencyUserData:setDiamond(value, refuseSave) self.diamond = self:checkSetValue(self.diamond, value, refuseSave, StrogeKeyDef.USER_CURRENCY_DIAMOND) if not refuseSave then self:save() end return self:getDiamond() end --- 改变钻石数值 function CurrencyUserData:changeDiamond(value, refuseSave) local curr = self:checkChangeValue(self.diamond, value, refuseSave, StrogeKeyDef.USER_CURRENCY_DIAMOND) self:setDiamond(curr, refuseSave) return self:getDiamond() end --- 获取音符数值 function CurrencyUserData:getMusicalNotes() return self.musicalNotes end --- 设置音符数值 function CurrencyUserData:setMusicalNotes(value, refuseSave) self.musicalNotes = self:checkSetValue(self.musicalNotes, value, refuseSave, StrogeKeyDef.USER_CURRENCY_MUSICAL_NOTE) if not refuseSave then self:save() end return self:getMusicalNotes() end --- 改变音符数值 function CurrencyUserData:changeMusicalNotes(value, refuseSave) local curr = self:checkChangeValue(self.musicalNotes, value, refuseSave, StrogeKeyDef.USER_CURRENCY_MUSICAL_NOTE) self:setMusicalNotes(curr, refuseSave) return self:getMusicalNotes() end --- 获取鱼饵数值 function CurrencyUserData:getFishingBait() return self.fishingBait end --- 设置鱼饵数值 function CurrencyUserData:setFishingBait(value, refuseSave) self.fishingBait = self:checkSetValue(self.fishingBait, value, refuseSave, StrogeKeyDef.USER_CURRENCY_FISHING_BAIT) if not refuseSave then self:save() end return self:getFishingBait() end --- 改变鱼饵数值 function CurrencyUserData:changeFishingBait(value, refuseSave) local curr = self:checkChangeValue(self.fishingBait, value, refuseSave, StrogeKeyDef.USER_CURRENCY_FISHING_BAIT) self:setFishingBait(curr, refuseSave) return self:getFishingBait() end --- 获取星星数值 function CurrencyUserData:getStar() return self.star end --- 设置星星数值 function CurrencyUserData:setStar(value, refuseSave) self.star = self:checkSetValue(self.star, value, refuseSave, StrogeKeyDef.USER_CURRENCY_STAR) if not refuseSave then self:save() end return self:getStar() end --- 改变星星数值 function CurrencyUserData:changeStar(value, refuseSave) local curr = self:checkChangeValue(self.star, value, refuseSave, StrogeKeyDef.USER_CURRENCY_STAR) self:setStar(curr, refuseSave) return self:getStar() end --- 获取广告券数值 function CurrencyUserData:getAdCoupons() return self.adCoupons end --- 设置广告券数值 function CurrencyUserData:setAdCoupons(value, refuseSave) self.adCoupons = self:checkSetValue(self.adCoupons, value, refuseSave, StrogeKeyDef.USER_CURRENCY_AD_TICKET) if not refuseSave then self:save() end return self:getAdCoupons() end --- 改变广告券数值 function CurrencyUserData:changeAdCoupons(value, refuseSave) local curr = self:checkChangeValue(self.adCoupons, value, refuseSave, StrogeKeyDef.USER_CURRENCY_AD_TICKET) self:setAdCoupons(curr, refuseSave) return self:getAdCoupons() end --- 检查货币数值是否有效 function CurrencyUserData:checkSetValue(curr, value, refuseSave, saveKey) value = math.floor(value) if not value or type(value) ~= "number" then printError(LOGTAG, "数值错误%s", tostring(value)) return curr end if value < 0 then printError(LOGTAG, "数值不能小于0", tostring(value)) return 0 end --if not refuseSave then -- PlayerPrefsMgr:setInt(saveKey, value) -- self:save() --end return value end --- 检查货币数值是否有效 function CurrencyUserData:checkChangeValue(curr, value, refuseSave, saveKey) value = math.floor(value) if not value or type(value) ~= "number" then printError(LOGTAG, "", tostring(value)) return -1 end curr = curr + value if curr < 0 then printError(LOGTAG, "数值不能小于0") return 0 end --if not refuseSave then -- PlayerPrefsMgr:setInt(saveKey, curr) -- self:save() --end return curr end return CurrencyUserDataasync--[[ luaide 模板位置位于 Template/FunTemplate/NewFileTemplate.lua 其中 Template 为配置路径 与luaide.luaTemplatesDir luaide.luaTemplatesDir 配置 https://www.showdoc.cc/web/#/luaide?page_id=713062580213505 author:{author} time:2022-05-17 17:31:23 ]] local async = {} function async.wait_all(names,finishCb) local cloned = {} for k,v in ipairs(names)do cloned[k] = v end local proxy = { notify = function(name) for i = #cloned,1,-1 do if cloned[i] == name then table.remove(cloned,i) end end if #cloned == 0 then finishCb() end end, } return proxy end function async.foreach(list,iterator,finishCb) local i = 0 local resolve local isAborted = false resolve = function() i = i + 1 local item = list[i] if item and not isAborted then iterator(item,resolve,i) else if finishCb then finishCb() end end end resolve() -- 返回一个函数,用于在外部函数内终止迭代 return function() isAborted = true end end function async.fori(total,iterator,finishCb) local i = 0 local resolve resolve = function() i = i + 1 if i <= total then iterator(i,resolve) else if finishCb then finishCb() end end end resolve() end function async.forij(from,total,iterator,finishCb) local i = from - 1 local resolve resolve = function() i = i + 1 if i <= total then iterator(i,resolve) else if finishCb then finishCb() end end end resolve() end function async.seq(steps) local i = 0 local resolve resolve = function() i = i + 1 local step = steps[i] local resolved = false if step then step(function() if not resolved then resolved = true resolve() end end) end end resolve() end return async UserTable local UserTable, super = defClass("UserTable", SqliteTable) function UserTable:init() -- 表模型 self.modelCls = UserModel -- 表名 self.name = "UserTable" -- 字段 向表中添加一个字段 self:c("userId", "", true) -- 用户id self:c("curLevel", 1) -- 当前等级 end function UserTable:syncToServer(data) -- todo:: end return UserTableLocalStorageMgrQ ---@class LocalStorageMgr:LuaStaticClass local LocalStorageMgr = defClassStatic("LocalStorageMgr") function LocalStorageMgr:init() -- KVMgr KVMgr:addDBTableCls(KVTable, false, false, "LocalStorageTable") KVMgr:addDBTableCls(KVTable, false, false, "LocalDebugTable") KVMgr:addDBTableCls(KVTable, true, false, "LocalStorageTableForUser") end function LocalStorageMgr:get(key, defaultValue) return KVMgr.LocalStorageTable:get(key, defaultValue) end function LocalStorageMgr:set(key, value) return KVMgr.LocalStorageTable:set(key, value) end function LocalStorageMgr:getKeys() return KVMgr.LocalStorageTable:getKeys() end function LocalStorageMgr:getDebug(key, defaultValue) return KVMgr.LocalDebugTable:get(key, defaultValue) end function LocalStorageMgr:setDebug(key, value) return KVMgr.LocalDebugTable:set(key, value) end function LocalStorageMgr:getDebugKeys() return KVMgr.LocalDebugTable:getKeys() end function LocalStorageMgr:getUserKeys() if not KVMgr.LocalStorageTableForUser then return {} end return KVMgr.LocalStorageTableForUser:getKeys() end function LocalStorageMgr:getForUser(key, defaultValue) return KVMgr.LocalStorageTableForUser:get(key, defaultValue) end function LocalStorageMgr:setForUser(key, value) return KVMgr.LocalStorageTableForUser:set(key, value) end cuisinereslink{return { --BASIC --ASSET cuisinen_200001 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200001.prefab", 0, 0}, cuisinen_200002 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200002.prefab", 0, 0}, cuisinen_200003 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200003.prefab", 0, 0}, cuisinen_200004 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200004.prefab", 0, 0}, cuisinen_200005 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200005.prefab", 0, 0}, cuisinen_200006 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200006.prefab", 0, 0}, cuisinen_200007 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200007.prefab", 0, 0}, cuisinen_200008 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200008.prefab", 0, 0}, cuisinen_200009 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200009.prefab", 0, 0}, cuisinen_200010 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200010.prefab", 0, 0}, cuisinen_200011 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200011.prefab", 0, 0}, cuisinen_200012 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200012.prefab", 0, 0}, cuisinen_200013 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200013.prefab", 0, 0}, cuisinen_200014 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200014.prefab", 0, 0}, cuisinen_200015 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200015.prefab", 0, 0}, cuisinen_200016 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200016.prefab", 0, 0}, cuisinen_200017 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200017.prefab", 0, 0}, cuisinen_200018 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200018.prefab", 0, 0}, cuisinen_200019 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200019.prefab", 0, 0}, cuisinen_200020 = {"Assets/AssetsPackage/Res/modules/cuisinen/prefabs/cuisinen_200020.prefab", 0, 0}, } taskCfg~--[[ from file:任务表.xlsx --]] local taskCfg = { [1] = { id = 500001, desc = "招待柯基20次", taskType = 1, unlockCond = -1, completeCond = 22, count = 20, refId = 400001, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 2500, rewardType_2 = 5, params_2 = 1, }, [2] = { id = 500002, desc = "招待橘猫15次", taskType = 1, unlockCond = -1, completeCond = 22, count = 15, refId = 400002, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 2000, rewardType_2 = 5, params_2 = 1, }, [3] = { id = 500003, desc = "累计招待顾客100位", taskType = 1, unlockCond = -1, completeCond = 2, count = 100, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 2000, rewardType_2 = 5, params_2 = 1, }, [4] = { id = 500004, desc = "累计宣传200次", taskType = 1, unlockCond = -1, completeCond = 7, count = 200, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 1500, rewardType_2 = 5, params_2 = 1, }, [5] = { id = 500005, desc = "累计收入10000金币", taskType = 1, unlockCond = -1, completeCond = 3, count = 10000, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 3000, rewardType_2 = 5, params_2 = 1, }, [6] = { id = 500006, desc = "累计招揽50个顾客", taskType = 1, unlockCond = 580002, completeCond = 18, count = 50, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 2000, rewardType_2 = 5, params_2 = 1, }, [7] = { id = 500007, desc = "累计招揽60个顾客", taskType = 1, unlockCond = 580002, completeCond = 18, count = 60, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 3000, rewardType_2 = 5, params_2 = 1, }, [8] = { id = 500008, desc = "累计收入20000个金币", taskType = 1, unlockCond = 580002, completeCond = 3, count = 20000, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 2000, rewardType_2 = 5, params_2 = 1, }, [9] = { id = 500009, desc = "累计收入30000个金币", taskType = 1, unlockCond = 580002, completeCond = 3, count = 30000, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 3000, rewardType_2 = 5, params_2 = 1, }, [10] = { id = 500010, desc = "累计宣传300次", taskType = 1, unlockCond = 580002, completeCond = 7, count = 300, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 4000, rewardType_2 = 5, params_2 = 1, }, [11] = { id = 500011, desc = "累计宣传400次", taskType = 1, unlockCond = 580002, completeCond = 7, count = 400, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 4500, rewardType_2 = 5, params_2 = 1, }, [12] = { id = 500012, desc = "完成2次视频宣传", taskType = 1, unlockCond = 580002, completeCond = 8, count = 2, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 2000, rewardType_2 = 5, params_2 = 1, }, [13] = { id = 500013, desc = "完成3次视频宣传", taskType = 1, unlockCond = 580002, completeCond = 8, count = 3, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 3000, rewardType_2 = 5, params_2 = 1, }, [14] = { id = 500014, desc = "完成5次视频宣传", taskType = 1, unlockCond = 580002, completeCond = 8, count = 5, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 4000, rewardType_2 = 5, params_2 = 1, }, [15] = { id = 500015, desc = "累计招待100个顾客", taskType = 1, unlockCond = 580002, completeCond = 2, count = 100, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 2000, rewardType_2 = 5, params_2 = 1, }, [16] = { id = 500016, desc = "累计招待150个顾客", taskType = 1, unlockCond = 580002, completeCond = 2, count = 150, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 3000, rewardType_2 = 5, params_2 = 1, }, [17] = { id = 500017, desc = "累计招待200个顾客", taskType = 1, unlockCond = 580002, completeCond = 2, count = 200, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 2, rewardType_1 = 1, params_1 = 4000, rewardType_2 = 5, params_2 = 1, }, [18] = { id = 510001, desc = "累计获得100颗星星", taskType = 2, unlockCond = -1, completeCond = 1, count = 100, refId = -1, preTaskId = -1, nextTaskId = 510002, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 3000, rewardType_2 = -1, params_2 = -1, }, [19] = { id = 510002, desc = "累计获得200颗星星", taskType = 2, unlockCond = -1, completeCond = 1, count = 200, refId = -1, preTaskId = 510001, nextTaskId = 510003, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 7000, rewardType_2 = -1, params_2 = -1, }, [20] = { id = 510003, desc = "累计获得500颗星星", taskType = 2, unlockCond = -1, completeCond = 1, count = 500, refId = -1, preTaskId = 510002, nextTaskId = 510004, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 10000, rewardType_2 = -1, params_2 = -1, }, [21] = { id = 510004, desc = "累计获得1000颗星星", taskType = 2, unlockCond = -1, completeCond = 1, count = 1000, refId = -1, preTaskId = 510003, nextTaskId = 510005, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 20000, rewardType_2 = -1, params_2 = -1, }, [22] = { id = 510005, desc = "累计获得1200颗星星", taskType = 2, unlockCond = -1, completeCond = 1, count = 1200, refId = -1, preTaskId = 510004, nextTaskId = 510006, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 25000, rewardType_2 = -1, params_2 = -1, }, [23] = { id = 510006, desc = "累计获得1500颗星星", taskType = 2, unlockCond = -1, completeCond = 1, count = 1500, refId = -1, preTaskId = 510005, nextTaskId = 510007, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 30000, rewardType_2 = -1, params_2 = -1, }, [24] = { id = 510007, desc = "累计获得3000颗星星", taskType = 2, unlockCond = -1, completeCond = 1, count = 3000, refId = -1, preTaskId = 510006, nextTaskId = 510008, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 60000, rewardType_2 = -1, params_2 = -1, }, [25] = { id = 510008, desc = "累计获得5000颗星星", taskType = 2, unlockCond = -1, completeCond = 1, count = 5000, refId = -1, preTaskId = 510007, nextTaskId = 510009, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 100000, rewardType_2 = -1, params_2 = -1, }, [26] = { id = 510009, desc = "累计获得10000颗星星", taskType = 2, unlockCond = -1, completeCond = 1, count = 10000, refId = -1, preTaskId = 510008, nextTaskId = 510010, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 200000, rewardType_2 = -1, params_2 = -1, }, [27] = { id = 510010, desc = "累计获得30000颗星星", taskType = 2, unlockCond = -1, completeCond = 1, count = 30000, refId = -1, preTaskId = 510009, nextTaskId = 510011, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 600000, rewardType_2 = -1, params_2 = -1, }, [28] = { id = 510011, desc = "累计获得50000颗星星", taskType = 2, unlockCond = -1, completeCond = 1, count = 50000, refId = -1, preTaskId = 510010, nextTaskId = -1, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 1000000, rewardType_2 = -1, params_2 = -1, }, [29] = { id = 510012, desc = "餐厅累计招待500个顾客", taskType = 2, unlockCond = -1, completeCond = 2, count = 500, refId = -1, preTaskId = -1, nextTaskId = 510013, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 5000, rewardType_2 = -1, params_2 = -1, }, [30] = { id = 510013, desc = "餐厅累计招待1000个顾客", taskType = 2, unlockCond = -1, completeCond = 2, count = 1000, refId = -1, preTaskId = 510012, nextTaskId = 510014, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 10000, rewardType_2 = -1, params_2 = -1, }, [31] = { id = 510014, desc = "餐厅累计招待1500个顾客", taskType = 2, unlockCond = -1, completeCond = 2, count = 1500, refId = -1, preTaskId = 510013, nextTaskId = 510015, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 15000, rewardType_2 = -1, params_2 = -1, }, [32] = { id = 510015, desc = "餐厅累计招待2000个顾客", taskType = 2, unlockCond = -1, completeCond = 2, count = 2000, refId = -1, preTaskId = 510014, nextTaskId = 510016, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 20000, rewardType_2 = -1, params_2 = -1, }, [33] = { id = 510016, desc = "餐厅累计招待5000个顾客", taskType = 2, unlockCond = -1, completeCond = 2, count = 5000, refId = -1, preTaskId = 510015, nextTaskId = 510017, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 50000, rewardType_2 = -1, params_2 = -1, }, [34] = { id = 510017, desc = "餐厅累计招待8000个顾客", taskType = 2, unlockCond = -1, completeCond = 2, count = 8000, refId = -1, preTaskId = 510016, nextTaskId = 510018, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 80000, rewardType_2 = -1, params_2 = -1, }, [35] = { id = 510018, desc = "餐厅累计招待10000个顾客", taskType = 2, unlockCond = -1, completeCond = 2, count = 10000, refId = -1, preTaskId = 510017, nextTaskId = 510019, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 100000, rewardType_2 = -1, params_2 = -1, }, [36] = { id = 510019, desc = "餐厅累计招待20000个顾客", taskType = 2, unlockCond = -1, completeCond = 2, count = 20000, refId = -1, preTaskId = 510018, nextTaskId = 510020, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 200000, rewardType_2 = -1, params_2 = -1, }, [37] = { id = 510020, desc = "餐厅累计招待30000个顾客", taskType = 2, unlockCond = -1, completeCond = 2, count = 30000, refId = -1, preTaskId = 510019, nextTaskId = 510021, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 300000, rewardType_2 = -1, params_2 = -1, }, [38] = { id = 510021, desc = "餐厅累计招待50000个顾客", taskType = 2, unlockCond = -1, completeCond = 2, count = 50000, refId = -1, preTaskId = 510020, nextTaskId = -1, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 50000, rewardType_2 = -1, params_2 = -1, }, [39] = { id = 510022, desc = "累计收入金币20000", taskType = 2, unlockCond = -1, completeCond = 3, count = 20000, refId = -1, preTaskId = -1, nextTaskId = 510023, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 2000, rewardType_2 = -1, params_2 = -1, }, [40] = { id = 510023, desc = "累计收入金币50000", taskType = 2, unlockCond = -1, completeCond = 3, count = 50000, refId = -1, preTaskId = 510022, nextTaskId = 510024, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 5000, rewardType_2 = -1, params_2 = -1, }, [41] = { id = 510024, desc = "累计收入金币100000", taskType = 2, unlockCond = -1, completeCond = 3, count = 100000, refId = -1, preTaskId = 510023, nextTaskId = 510025, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 10000, rewardType_2 = -1, params_2 = -1, }, [42] = { id = 510025, desc = "累计收入金币200000", taskType = 2, unlockCond = -1, completeCond = 3, count = 500000, refId = -1, preTaskId = 510024, nextTaskId = 510026, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 20000, rewardType_2 = -1, params_2 = -1, }, [43] = { id = 510026, desc = "累计收入金币500000", taskType = 2, unlockCond = -1, completeCond = 3, count = 200000, refId = -1, preTaskId = 510025, nextTaskId = 510027, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 50000, rewardType_2 = -1, params_2 = -1, }, [44] = { id = 510027, desc = "累计收入金币1000000", taskType = 2, unlockCond = -1, completeCond = 3, count = 1000000, refId = -1, preTaskId = 510026, nextTaskId = 510028, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 100000, rewardType_2 = -1, params_2 = -1, }, [45] = { id = 510028, desc = "累计收入金币5000000", taskType = 2, unlockCond = -1, completeCond = 3, count = 5000000, refId = -1, preTaskId = 510027, nextTaskId = 510029, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 500000, rewardType_2 = -1, params_2 = -1, }, [46] = { id = 510029, desc = "累计收入金币10000000", taskType = 2, unlockCond = -1, completeCond = 3, count = 10000000, refId = -1, preTaskId = 510028, nextTaskId = -1, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 1000000, rewardType_2 = -1, params_2 = -1, }, [47] = { id = 510030, desc = "累计解锁10个菜品", taskType = 2, unlockCond = -1, completeCond = 4, count = 10, refId = -1, preTaskId = -1, nextTaskId = 510031, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 2000, rewardType_2 = -1, params_2 = -1, }, [48] = { id = 510031, desc = "累计解锁20个菜品", taskType = 2, unlockCond = -1, completeCond = 4, count = 20, refId = -1, preTaskId = 510030, nextTaskId = 510032, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 5000, rewardType_2 = -1, params_2 = -1, }, [49] = { id = 510032, desc = "累计解锁30个菜品", taskType = 2, unlockCond = -1, completeCond = 4, count = 30, refId = -1, preTaskId = 510031, nextTaskId = 510033, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 8000, rewardType_2 = -1, params_2 = -1, }, [50] = { id = 510033, desc = "累计解锁50个菜品", taskType = 2, unlockCond = -1, completeCond = 4, count = 50, refId = -1, preTaskId = 510032, nextTaskId = -1, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 10000, rewardType_2 = -1, params_2 = -1, }, [51] = { id = 510034, desc = "累计解锁10个设施", taskType = 2, unlockCond = -1, completeCond = 5, count = 10, refId = -1, preTaskId = -1, nextTaskId = 510035, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 1000, rewardType_2 = -1, params_2 = -1, }, [52] = { id = 510035, desc = "累计解锁30个设施", taskType = 2, unlockCond = -1, completeCond = 5, count = 30, refId = -1, preTaskId = 510034, nextTaskId = 510036, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 5000, rewardType_2 = -1, params_2 = -1, }, [53] = { id = 510036, desc = "累计解锁50个设施", taskType = 2, unlockCond = -1, completeCond = 5, count = 50, refId = -1, preTaskId = 510035, nextTaskId = 510037, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 10000, rewardType_2 = -1, params_2 = -1, }, [54] = { id = 510037, desc = "累计解锁80个设施", taskType = 2, unlockCond = -1, completeCond = 5, count = 80, refId = -1, preTaskId = 510036, nextTaskId = -1, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 50000, rewardType_2 = -1, params_2 = -1, }, [55] = { id = 510038, desc = "累计解锁10个顾客", taskType = 2, unlockCond = -1, completeCond = 6, count = 10, refId = -1, preTaskId = -1, nextTaskId = 510039, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 3000, rewardType_2 = -1, params_2 = -1, }, [56] = { id = 510039, desc = "累计解锁20个顾客", taskType = 2, unlockCond = -1, completeCond = 6, count = 20, refId = -1, preTaskId = 510038, nextTaskId = 510040, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 8000, rewardType_2 = -1, params_2 = -1, }, [57] = { id = 510040, desc = "累计解锁30个顾客", taskType = 2, unlockCond = -1, completeCond = 6, count = 30, refId = -1, preTaskId = 510039, nextTaskId = 510041, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 15000, rewardType_2 = -1, params_2 = -1, }, [58] = { id = 510041, desc = "累计解锁50个顾客", taskType = 2, unlockCond = -1, completeCond = 6, count = 50, refId = -1, preTaskId = 510040, nextTaskId = -1, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 50000, rewardType_2 = -1, params_2 = -1, }, [59] = { id = 510042, desc = "累计宣传500次", taskType = 2, unlockCond = -1, completeCond = 7, count = 500, refId = -1, preTaskId = -1, nextTaskId = 510043, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 1000, rewardType_2 = -1, params_2 = -1, }, [60] = { id = 510043, desc = "累计宣传1000次", taskType = 2, unlockCond = -1, completeCond = 7, count = 1000, refId = -1, preTaskId = 510042, nextTaskId = 510044, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 5000, rewardType_2 = -1, params_2 = -1, }, [61] = { id = 510044, desc = "累计宣传3000次", taskType = 2, unlockCond = -1, completeCond = 7, count = 3000, refId = -1, preTaskId = 510043, nextTaskId = 510045, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 8000, rewardType_2 = -1, params_2 = -1, }, [62] = { id = 510045, desc = "累计宣传5000次", taskType = 2, unlockCond = -1, completeCond = 7, count = 5000, refId = -1, preTaskId = 510044, nextTaskId = -1, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 30000, rewardType_2 = -1, params_2 = -1, }, [63] = { id = 510046, desc = "累计视频宣传5次", taskType = 2, unlockCond = -1, completeCond = 8, count = 5, refId = -1, preTaskId = -1, nextTaskId = 510047, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 500, rewardType_2 = -1, params_2 = -1, }, [64] = { id = 510047, desc = "累计视频宣传15次", taskType = 2, unlockCond = -1, completeCond = 8, count = 15, refId = -1, preTaskId = 510046, nextTaskId = 510048, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 2000, rewardType_2 = -1, params_2 = -1, }, [65] = { id = 510048, desc = "累计视频宣传30次", taskType = 2, unlockCond = -1, completeCond = 8, count = 30, refId = -1, preTaskId = 510047, nextTaskId = 510049, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 5000, rewardType_2 = -1, params_2 = -1, }, [66] = { id = 510049, desc = "累计视频宣传50次", taskType = 2, unlockCond = -1, completeCond = 8, count = 50, refId = -1, preTaskId = 510048, nextTaskId = -1, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 10000, rewardType_2 = -1, params_2 = -1, }, [67] = { id = 510050, desc = "累计观看广告80次", taskType = 2, unlockCond = -1, completeCond = 9, count = 80, refId = -1, preTaskId = -1, nextTaskId = 510051, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 20000, rewardType_2 = -1, params_2 = -1, }, [68] = { id = 510051, desc = "累计观看广告120次", taskType = 2, unlockCond = -1, completeCond = 9, count = 120, refId = -1, preTaskId = 510050, nextTaskId = 510052, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 30000, rewardType_2 = -1, params_2 = -1, }, [69] = { id = 510052, desc = "累计观看广告200次", taskType = 2, unlockCond = -1, completeCond = 9, count = 200, refId = -1, preTaskId = 510051, nextTaskId = -1, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 1, params_1 = 50000, rewardType_2 = -1, params_2 = -1, }, [70] = { id = 520001, desc = "完成所有任务,获得星星大奖", taskType = 4, unlockCond = -1, completeCond = 100, count = 5, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = '', rewardTypeCount = 1, rewardType_1 = 6, params_1 = 25, rewardType_2 = -1, params_2 = -1, }, [71] = { id = 530001, desc = "购买2号餐桌", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 100301, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 100, rewardType_2 = -1, params_2 = -1, }, [72] = { id = 530002, desc = "学习菜品蛋包饭", taskType = 3, unlockCond = -1, completeCond = 4, count = 1, refId = 200002, preTaskId = -1, nextTaskId = -1, jumpRef = "2", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 100, rewardType_2 = -1, params_2 = -1, }, [73] = { id = 530003, desc = "点击宣传16次", taskType = 3, unlockCond = -1, completeCond = 7, count = 16, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = "3", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 200, rewardType_2 = -1, params_2 = -1, }, [74] = { id = 530004, desc = "购买迎宾台", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 100201, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 200, rewardType_2 = -1, params_2 = -1, }, [75] = { id = 530005, desc = "购买2号炉灶", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 101601, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 250, rewardType_2 = -1, params_2 = -1, }, [76] = { id = 530006, desc = "学习菜品华夫饼", taskType = 3, unlockCond = -1, completeCond = 4, count = 1, refId = 200003, preTaskId = -1, nextTaskId = -1, jumpRef = "2", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 150, rewardType_2 = -1, params_2 = -1, }, [77] = { id = 530007, desc = "购买水吧台", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 102401, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 300, rewardType_2 = -1, params_2 = -1, }, [78] = { id = 530008, desc = "学习菜品可乐", taskType = 3, unlockCond = -1, completeCond = 4, count = 1, refId = 200004, preTaskId = -1, nextTaskId = -1, jumpRef = "2", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 280, rewardType_2 = -1, params_2 = -1, }, [79] = { id = 530009, desc = "购买备菜区", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 101701, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 600, rewardType_2 = -1, params_2 = -1, }, [80] = { id = 530010, desc = "购买3号餐桌", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 100401, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 800, rewardType_2 = -1, params_2 = -1, }, [81] = { id = 530011, desc = "购买水池", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 102301, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 650, rewardType_2 = -1, params_2 = -1, }, [82] = { id = 530012, desc = "购买3号炉灶", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 102101, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 700, rewardType_2 = -1, params_2 = -1, }, [83] = { id = 530013, desc = "学习菜品蔬菜沙拉", taskType = 3, unlockCond = -1, completeCond = 4, count = 1, refId = 200005, preTaskId = -1, nextTaskId = -1, jumpRef = "2", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 400, rewardType_2 = -1, params_2 = -1, }, [84] = { id = 530014, desc = "购买水果台", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 101101, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 800, rewardType_2 = -1, params_2 = -1, }, [85] = { id = 530015, desc = "购买碗柜", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 101801, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 850, rewardType_2 = -1, params_2 = -1, }, [86] = { id = 530016, desc = "购买露天电影", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 101001, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 1000, rewardType_2 = -1, params_2 = -1, }, [87] = { id = 530017, desc = "购买冰箱", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 101901, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 1100, rewardType_2 = -1, params_2 = -1, }, [88] = { id = 530018, desc = "购买货柜", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 102001, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 1200, rewardType_2 = -1, params_2 = -1, }, [89] = { id = 530019, desc = "学习菜品可颂面包", taskType = 3, unlockCond = -1, completeCond = 4, count = 1, refId = 200006, preTaskId = -1, nextTaskId = -1, jumpRef = "2", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 400, rewardType_2 = -1, params_2 = -1, }, [90] = { id = 530020, desc = "购买烤炉", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 102201, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 1300, rewardType_2 = -1, params_2 = -1, }, [91] = { id = 530021, desc = "点击宣传32次", taskType = 3, unlockCond = -1, completeCond = 7, count = 32, refId = -1, preTaskId = -1, nextTaskId = -1, jumpRef = "3", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 800, rewardType_2 = -1, params_2 = -1, }, [92] = { id = 530022, desc = "购买花架", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 101301, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 1600, rewardType_2 = -1, params_2 = -1, }, [93] = { id = 530023, desc = "购买4号餐桌", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 100501, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 1800, rewardType_2 = -1, params_2 = -1, }, [94] = { id = 530024, desc = "雇佣服务员", taskType = 3, unlockCond = -1, completeCond = 15, count = 1, refId = 300003, preTaskId = -1, nextTaskId = -1, jumpRef = "4", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 5000, rewardType_2 = -1, params_2 = -1, }, [95] = { id = 530025, desc = "购买酒桶", taskType = 3, unlockCond = -1, completeCond = 5, count = 1, refId = 100901, preTaskId = -1, nextTaskId = -1, jumpRef = "1", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 1500, rewardType_2 = -1, params_2 = -1, }, [96] = { id = 530026, desc = "学习菜品甜甜圈", taskType = 3, unlockCond = -1, completeCond = 4, count = 1, refId = 200007, preTaskId = -1, nextTaskId = -1, jumpRef = "2", rewardTypeCount = 1, rewardType_1 = 1, params_1 = 600, rewardType_2 = -1, params_2 = -1, }, } return taskCfg HelpConst--[[ 帮助常量 ]] local HelpConst = defClassStatic("HelpConst") function HelpConst:init() end -- 帮助分类 HelpConst.Type = { Restaurant = 701, GrillBar = 702, Fishing = 703, Cabin = 704, Customer = 705, Gacha = 706, } --- 帮助分类名称 HelpConst.TypeName = { [HelpConst.Type.Restaurant] = "餐厅", [HelpConst.Type.GrillBar] = "音乐烤吧", [HelpConst.Type.Fishing] = "钓鱼", [HelpConst.Type.Cabin] = "小木屋", [HelpConst.Type.Customer] = "顾客", [HelpConst.Type.Gacha] = "扭蛋机", } HelpConst:init()GuideCom%--[[ 引导组件 author:{zhangpeng} time:2025-06-13 16:29:37 ]] local GuideCom, super = defClass("GuideCom", SceneComponent) local GuideUI = require("modules/guide/GuideUI") local GameObject = CS.UnityEngine.GameObject local Vector3 = CS.UnityEngine.Vector3 local Vector2 = CS.UnityEngine.Vector2 local LOG_TAG = "GuideCom" function GuideCom:ctor(scene) super.ctor(self, scene) self.guideFlag = false self.forceFlag = false end function GuideCom:onLoad() super.onLoad(self) end function GuideCom:onExit() self:finishGuide() super.onExit(self) end --@region 引导 -- go可以是场景中的go,也可以是ui中的go -- guideCallback会在原本go的回调函数执之前执行,通常可以用来进行引导状态的设置 -- cancelCallback 如果不为空则会在强制引导时,点击引导遮罩调用并自动结束引导 --[[ cfg = { audioPath = , text = , delay, interval, duration = , dx,dy = , r, w, h, d =, needClickCount = , -- 需要点击的次数,默认为1 } ]] function GuideCom:showTouchClickGuide(go, isForceGuide, cfg, guideCallback, cancelCallback) -- 添加点击次数支持 local needClickCount = cfg.needClickCount or 1 local currentClickCount = 0 local callback1 = function(...) currentClickCount = currentClickCount + 1 if currentClickCount >= needClickCount then self:finishGuide() if guideCallback then guideCallback(...) end end end local callback2 = function() end if cancelCallback then callback2 = function(...) self:finishGuide() cancelCallback(...) end end if self.forceFlag then printInfo(LOG_TAG, "showTouchClickGuide, 当前正在进行强引导,取消此次引导") return end if self.guideFlag then printInfo(LOG_TAG, "showTouchClickGuide, 当前正在进行弱引导,取消当前引导") self:finishGuide() end self:showTouchClick(go, cfg) if isForceGuide then self:setGuideUITouchHook(go, callback1, callback2) else self:setTouchHook(go, callback1) end --先显示引导画面,再开始引导。否则可能因为引导关闭了ui弹出导致无法显示引导画面 self:beginGuide(isForceGuide) end function GuideCom:beginGuide(isForceGuide) if self.guideFlag then return end self.guideFlag = true self.forceFlag = isForceGuide if self.forceFlag then end end function GuideCom:finishGuide() if not self.guideFlag then return end if self.forceFlag then end self:hideTips() self.guideFlag = false self.forceFlag = false end function GuideCom:isGuide() return self.guideFlag end function GuideCom:isForceGuide() return self.forceFlag end function GuideCom:isShowTips() return self.ui ~= nil end function GuideCom:hideTips() if not self:isShowTips() then return end self.ui:close() self.ui = nil end --[[ cfg = { isShowMask =, audioPath = , text = , delay, interval, duration = , times, callback = , dx,dy = , r, w, h, d =, } ]] function GuideCom:showTouchClick(goOrPos, cfg) if self:isShowTips() then return end self.ui = self:createTouchTipsUI(goOrPos, cfg) -- ui组件可能因为自适应而调整位置,为了防止调整后位置变动,导致小手错位,延迟一帧获取ui组件位置 self.ui:setMaskCenter(0, 0, {r = 0, d = 0}) self.ui.ui:Delay( 1 / 60, function() if self.__exited then printError(LOG_TAG, "GuideCom:showTouchClick delay wrong") return end local pos = self:getGoPositionInCanvasSpace(goOrPos) self.ui:showTouchClick(pos, cfg) end ) end --@endregion --@region 内部工具函数 function GuideCom:setGuideUITouchHook(go, guideCallback, cancelCallback) local isUIGo = util.ugui.isUIGameObject(go) if isUIGo then self:setGuideUITouchHookForUI(go, guideCallback, cancelCallback) else self:setGuideUITouchHookForObj(go, guideCallback, cancelCallback) end end function GuideCom:setGuideUITouchHookForUI(go, guideCallback, cancelCallback) local clickCallbackList = nil if go._clickCallbackList and #go._clickCallbackList > 0 then clickCallbackList = go._clickCallbackList end if not clickCallbackList then printInfo(LOG_TAG, "setGuideUITouchHookForUI, 当前引导对象没有回调函数") end self.ui:setMaskActive(true) self.ui:setTouchHandler( function(...) -- 调用引导回调,内部会处理点击次数逻辑 guideCallback(...) -- 只有当引导完成时才调用原始回调,这里需要检查引导状态 if not self.guideFlag then for i, clickCallback in ipairs(clickCallbackList or {}) do clickCallback(...) end end end, cancelCallback ) end function GuideCom:setGuideUITouchHookForObj(go, guideCallback, cancelCallback) local touchComMgr = self.scene.touchComMgr local touchComList = nil if touchComMgr and touchComMgr.comDict[go] and #touchComMgr.comDict[go] > 0 then touchComList = touchComMgr.comDict[go] end if not touchComList then printInfo(LOG_TAG, "setGuideUITouchHookForObj, 当前引导对象没有回调函数") end self.ui:setMaskActive(true) self.ui:setTouchHandler( function(...) -- 调用引导回调,内部会处理点击次数逻辑 guideCallback(...) -- 只有当引导完成时才调用原始回调,这里需要检查引导状态 if not self.guideFlag then for i, touchCom in ipairs(touchComList or {}) do touchCom._endCb() end end end, cancelCallback ) end function GuideCom:setTouchHook(go, callback) local isUIGo = util.ugui.isUIGameObject(go) if isUIGo then self:setTouchHookForUI(go, callback) else self:setTouchHookForObj(go, callback) end end function GuideCom:setTouchHookForUI(go, callback) local clickCallbackList = nil if go._clickCallbackList and #go._clickCallbackList > 0 then clickCallbackList = go._clickCallbackList end if not clickCallbackList then callback() return end local isCall = false local originalCallbacks = {} -- 保存原始回调 for i, clickCallback in ipairs(clickCallbackList) do table.insert(originalCallbacks, clickCallback) end go._hookCallback = function(...) if isCall then return end isCall = true callback(...) -- 只有当引导完成时才调用原始回调 if not self.guideFlag then for i, clickCallback in ipairs(originalCallbacks) do clickCallback(...) end end go._hookCallback = nil end end function GuideCom:setTouchHookForObj(go, callback) local touchComMgr = self.scene.touchComMgr local touchComList = nil if touchComMgr and touchComMgr.comDict[go] and #touchComMgr.comDict[go] > 0 then touchComList = touchComMgr.comDict[go] end if touchComList == nil then callback() return end local oldCallback = touchComList[1]._endCb touchComList[1]._endCb = function(...) callback(...) -- 只有当引导完成时才调用原始回调 if not self.guideFlag then oldCallback() end end end function GuideCom:getGoPositionInCanvasSpace(goOrPos) if not _IsType(goOrPos, typeof(GameObject)) then return goOrPos end local pos = Vector2.zero local go = goOrPos if CS.LuaHelper.IsNull(go) then printWarn(LOG_TAG, "getGoPositionInCanvasSpace, go is null") return pos end local isUIGo = util.ugui.isUIGameObject(go) if isUIGo then pos = go.transform:TransformPoint(Vector3.zero) else local uiCamSize = UILayerUtil.cameraCom.orthographicSize local mainCamSize = self.scene.cams.scene.orthographicSize local k = uiCamSize / mainCamSize pos = go.transform:TransformPoint(Vector3.zero) * k end pos = util.ugui.worldSpaceToCanvasSpace(pos,UILayerUtil.canvas) return pos end function GuideCom:getGoTouchRectInCanvasSpace(go) local isUIGo = util.ugui.isUIGameObject(go) local rect = nil if isUIGo then else end return rect end function GuideCom:createTouchTipsUI(goOrPos, cfg) local isGlobal = false local priority = UILayer.UI_ORDER.GUIDE if _IsType(goOrPos, typeof(GameObject)) then local go = goOrPos isGlobal = util.ugui.isGlobalUIGameObject(go) local function findUILayer(go) if go.uiLayerInst then return go.uiLayerInst end local parent = go:GetParent() if not parent then return nil end findUILayer(parent) end local uiLayer = findUILayer(go) if uiLayer then priority = uiLayer.priority end end local ui = GuideUI.new() ui:show(isGlobal) ui:setPriority(priority) ui:setMaskActive(cfg.isShowMask) ui:addCloseCallback( function() self.ui = nil end ) return ui end return GuideCom MagicianCustomer--- 魔术师 ---@class MagicianCustomer : CustomerSpecial local MagicianCustomer,super = defClass("MagicianCustomer", CustomerSpecial) local LOGTAG = "MagicianCustomer" --- 状态 MagicianCustomer.State = { Idle = 1, -- 空闲 Entrance = 2, -- 入场 Waiting = 3, -- 等待 Plot = 4, -- 剧情 Leaving = 5, -- 离开 } --- 构造函数 function MagicianCustomer:ctor(resLink) super.ctor(self, resLink, CustomerConst.CustomerSpecialType.MagicianCustomer) self:init() end --- 初始化 function MagicianCustomer:init() self:initDisplay("magician_customer") self:initProperties() self:startMagicianLogic() end --- 初始化属性 function MagicianCustomer:initProperties() self.info = SpecialCustomerMgr:getSpecialCustomerInfo(self.specialCustomerTypeId) self.state = MagicianCustomer.State.Idle -- 初始状态 self.activeTime = self.info:getActiveTime() -- 活动时间 self.clickTimes = self.info:getClickTimes() -- 点击次数 self.currClickTimes = 0 -- 当前点击次数 end --- 开始魔术师逻辑 function MagicianCustomer:startMagicianLogic() printInfo(LOGTAG, "魔术师开始活动,活动时间:%d秒", self.info:getActiveTime()) -- 入场 self:enter() -- 添加点击事件 self:addClickEvent(function() self:click() end) -- 初始化点击进度 self:initClickProgress() end --- 入店 function MagicianCustomer:enter() printInfo(LOGTAG, "魔术师入店") self.state = MagicianCustomer.State.Entrance self:walkToPos(self:getSpecialCustomerCommonStandbyPoint().transform.position, super.tempSpeed, function() self.state = MagicianCustomer.State.Waiting self:showDong() self:startTimer() end ) end --- 离开 function MagicianCustomer:exit() printInfo(LOGTAG, "魔术师离开") self.state = MagicianCustomer.State.Leaving self:stopTimer() self:walkToPos(self:getSpecialCustomerExitPoint().transform.position, super.tempSpeed, function() -- 离开后销毁 self:destroy() -- 触发离开事件 end ) end --- 时间更新 ---@param seconds number 秒数 function MagicianCustomer:timeupdate(seconds) if self.state == MagicianCustomer.State.Plot then return end self.time = self.time + seconds if self.time >= self.activeTime then self:exit() end end --- 剧情回调 function MagicianCustomer:plotCallback() self.state = MagicianCustomer.State.Waiting if self.ui.isClear then self:videoCallback() else self:badVideoCallback() end end --- 剧情 function MagicianCustomer:plot() printInfo(LOGTAG, "魔术师开始剧情") -- todo 新窗口 self.state = MagicianCustomer.State.Plot self:hideDong() self:showPlot(self.info.getPlotId, function() self.ui = AdCustomerUI.new(0):show():showMask():enableCloseWhenClickMask():addCloseCallback(function() self:plotCallback() end) end) end --- 交互 function MagicianCustomer:click() if self.state ~= MagicianCustomer.State.Waiting then return end if self.clickTimes <= 1 then self:plot() else if self.currClickTimes < self.clickTimes then self.currClickTimes = self.currClickTimes + 1 self:showClickProgress(self.currClickTimes / self.clickTimes) else self:plot() end end end --- 成功回调 function MagicianCustomer:videoCallback() CurrencyMgr:changeCoin(self.coin) self:showPlot(self.info:getSuccessDialogId(), function() self:exit() end) end --- 失败回调 function MagicianCustomer:badVideoCallback() self:showPlot(self.info:getFailDialogId(), function() self:exit() end) end return MagicianCustomerSceneCfg---@class SceneCfg:LuaStaticClass local SceneCfg = defClassStatic("SceneCfg") function SceneCfg.init(SCENES) SceneCfg.SCENES = SCENES end function SceneCfg.addScene(k,v) SceneCfg.SCENES[k] = v end function SceneCfg.addSubScene(k, kk, v) if SceneCfg.SCENES[k] == nil then SceneCfg.SCENES[k] = {} end SceneCfg.SCENES[k][kk] = v end function SceneCfg.isSubSceneExist(k, kk) if SceneCfg.SCENES[k] and SceneCfg.SCENES[k][kk] then return true else return false end end function SceneCfg.initModuleId(cfg, args) local path = cfg[1] local name = cfg[2] or "" local moduleId = cfg[3] if moduleId == nil then return nil end local t = type(moduleId) if t == "function" then local func = moduleId moduleId = func(args) elseif t == "string" then if moduleId == "_dir" then local tmp = string.split(path,"/") moduleId = tmp[#tmp-1] end end return moduleId end ReslinkLoad---@alias AssetItem {[1]:string, [2]:number} ---@class ReslinkLoad:LuaClass local ReslinkLoad,super = defClass("ReslinkLoad") local LOGTAG = "ReslinkLoad" ReslinkLoad.ST_WAIT = 1 ReslinkLoad.ST_LOAD = 2 ReslinkLoad.ST_STOP = 3 function ReslinkLoad.inTrans() return (ReslinkLoad._cur ~= nil) end function ReslinkLoad:ctor() self.state = ReslinkLoad.ST_WAIT self.progress = 0 self.isDone = false self._cur = 0 self._max = 0 self._time = nil self._func = nil self._loadSceneFlag = false ---@type AssetItem[] self._loadSceneList = {} self._loadAssetList = {} self._loadedSceneList = {} end ---comment ---@param set ResLink ---@param p1 number ---@param p2 number function ReslinkLoad:addResLink(set, p1, p2) printInfo(LOGTAG, "addResLink") if self.state == ReslinkLoad.ST_WAIT and set then local scene = set:getAssetInfo("SCENE") if scene then self:addSceneFile(scene[1], p1) end local list = set:getAssetList() for i,v in ipairs(list) do -- preload 预加载 if v[2] == 1 then if v[3] == "Prefab" then self:addAssetFile(v[1], p2) elseif v[3] == "Scene" then self:addSceneFile(v[1], p1) elseif v[3] == "ResLink" then self:addResLink(ResLoader.loadResLink(v[1]), p1, p2) elseif v[3] == typeof(AudioClip) then self:addAssetFile(v[1], p2) end end end end end ---comment ---@param scene string 路径 ---@param p any function ReslinkLoad:addSceneFile(scene, p) if self.state == ReslinkLoad.ST_WAIT and scene then for i,v in ipairs(self._loadSceneList) do if v[1] == scene then return end end table.insert(self._loadSceneList, {scene, p or 10}) end end function ReslinkLoad:addAssetFile(asset, p) if self.state == ReslinkLoad.ST_WAIT and asset then if type(asset) == "table" then p = p and (p / (#asset)) or 1 for i,v in ipairs(asset) do table.insert(self._loadAssetList, {v, p}) end else table.insert(self._loadAssetList, {asset, p or 1}) end end end ---开始加载 ---@param func fun(sceneList:CS.UnityEngine.SceneManagement.Scene[]) 完成回调 ---@param progressCb fun(progress:number) 加载进度回调 ---@param isAsync boolean 是否异步加载 function ReslinkLoad:load(func, progressCb, isAsync) printInfo(LOGTAG, "load") if self.state ~= ReslinkLoad.ST_WAIT then return end self.state = ReslinkLoad.ST_LOAD ReslinkLoad._cur = self self._time = Time.time self._func = func self._progressCb = progressCb self._isAsync = isAsync self._cur = 0 self._max = 0 for i,v in ipairs(self._loadSceneList) do self._max = self._max + v[2] end for i,v in ipairs(self._loadAssetList) do self._max = self._max + v[2] end self:_loadStart() end function ReslinkLoad:stop() self.state = ReslinkLoad.ST_STOP self._func = nil self._progressCb = nil end function ReslinkLoad:onExit() ReslinkLoad._cur = nil self:stop() super.onExit(self) end -- 加载开始 function ReslinkLoad:_loadStart() printInfo(LOGTAG, "_loadStart") self:_unloadLastScene() self:_loadScene() end function ReslinkLoad:_unloadLastScene() printInfo(LOGTAG, "_unloadLastScene") ResLoader.unloadAssets() end function ReslinkLoad:_loadScene() printInfo(LOGTAG, "_loadScene") if self.state ~= ReslinkLoad.ST_LOAD then return end ---@type AssetItem local scene = table.remove(self._loadSceneList or {}, 1) if scene then local flag = self._loadSceneFlag self._loadSceneFlag = true self:_loadSceneIMPL(scene[1], scene[2], flag) else if self._loadSceneList then self._loadSceneList = nil -- self:onLoadSceneFinish() end if self._loadAssetList then self:_loadAsset() else self:_loadComplete() end end end ---comment ---@param res any ---@param p any ---@param addtive boolean unity加载场景是否 Additive 模式(不卸载之前的场景) 目前不支持 function ReslinkLoad:_loadSceneIMPL(res, p, addtive) printInfo(LOGTAG, "_loadSceneIMPL res:%s", res) local function onFinish(scene) if self.state == ReslinkLoad.ST_LOAD then table.insert(self._loadedSceneList, scene) self:onProgress(res, p, true) self:_loadScene() else self:stop() end end if self._isAsync then ResLoader.loadSceneAsync(res, function (progress) self:onProgress(res, p, progress) end, onFinish) else ResLoader.loadSceneSync(res, onFinish) end end function ReslinkLoad:_loadAsset() if self.state ~= ReslinkLoad.ST_LOAD then return end ---@type AssetItem local asset = table.remove(self._loadAssetList or {}, 1) if asset then self:_loadAssetIMPL(asset[1], asset[2]) else self._loadAssetList = nil if self._loadSceneList then self:_loadScene() else self:_loadComplete() end end end function ReslinkLoad:_loadAssetIMPL(res, p) if self._isAsync then ResLoader.loadAssetAsync(res, nil, function (progress) self:onProgress(res, p, progress) end, function () self:onProgress(res, p, true) self:_loadAsset() end) else ResLoader.loadAsset(res, nil) self:onProgress(res, p, true) self:_loadAsset() end end function ReslinkLoad:_loadComplete() if self.state ~= ReslinkLoad.ST_LOAD then return end if self.state == ReslinkLoad.ST_LOAD then self:onComplete() self:stop() end end function ReslinkLoad:onProgress(res, p, progress) if self.state == ReslinkLoad.ST_LOAD then local cur = 0 if progress == true then self._cur = self._cur + p cur = self._cur else cur = self._cur + p * progress end if self._max > 0 then self._progress = cur/self._max end if self._progressCb then self._progressCb(self._progress) end end end function ReslinkLoad:onComplete() self.isDone = true if self._func then self._func(self._loadedSceneList) self._func = nil end end -- function ReslinkLoad:onLoadAsset(res, obj) -- end -- function ReslinkLoad:onLoadScene(res, addtive) -- end -- function ReslinkLoad:onLoadScenePaused() -- end -- function ReslinkLoad:onLoadSceneFinish() -- end return ReslinkLoad OrderConst--[[ author:{zhangpeng} time:2025-05-17 19:50:12 ]] local OrderConst = defClassStatic("OrderConst") -- 订单状态 OrderConst.Status = { created = 1, -- 创建 waiting = 2, -- 等待制作 cooking = 3, -- 正在制作 cooked = 4, -- 制作完成 serving = 5, -- 正在上菜 completed = 6, -- 完成 cancelled = 7, -- 取消 pending = 8, -- 待确认处理(需要点击头顶气泡确认) } -- 状态描述 OrderConst.StatusDesc = { [OrderConst.Status.created] = "创建", [OrderConst.Status.waiting] = "等待制作", [OrderConst.Status.cooking] = "正在制作", [OrderConst.Status.cooked] = "制作完成", [OrderConst.Status.serving] = "正在上菜", [OrderConst.Status.completed] = "完成", [OrderConst.Status.cancelled] = "取消", [OrderConst.Status.pending] = "待确认处理", } -- 订单处理模式 OrderConst.ProcessMode = { auto = 1, -- 自动接单处理(默认模式) manual = 2, -- 手动接单处理(需要点击头顶气泡) } -- 处理模式描述 OrderConst.ProcessModeDesc = { [OrderConst.ProcessMode.auto] = "自动接单", [OrderConst.ProcessMode.manual] = "手动接单", } function OrderConst:init() end OrderConst:init() PaymentErrorCodeM --[[ ios/google play支付返回的错误代码 author:{zhangpeng} time:2024-08-20 15:08:55 ]] local PaymentErrorCode,_ = defClassStatic("PaymentErrorCode") -- ios payment PaymentErrorCode.iOS = { SKErrorClientInvalid = 0, -- 当前设备或用户不被允许进行App内购买操作 SKErrorPaymentCancelled = 2, -- 用户取消了支付请求 SKErrorPaymentInvalid = 3, -- 请求支付的产品标识符无效 SKErrorPaymentNotAllowed = 4, -- 设备不允许进行支付 SKErrorStoreProductNotAvailable = 5, -- 请求的产品在当前店面不可用 SKErrorCloudServicePermissionDenied=6, -- 用户没有授权使用云服务 SKErrorCloudServiceNetworkConnectionFailed = 7, -- 无法连接到网络 SKErrorCloudServiceRevoked = 8, -- 用户的云服务权限已被撤销 SKErrorPrivacyAcknowledgementRequired = 9, -- 用户需要先同意Apple的隐私政策 SKErrorUnauthorizedRequestData = 10, -- 应用试图使用未经授权的数据 SKErrorInvalidOfferIdentifier = 11, -- 提供的优惠标识符无效 SKErrorInvalidSignature = 12, -- 提供的签名无效 SKErrorMissingOfferParams = 13, -- 提供的优惠参数缺失 SKErrorInvalidOfferPrice = 14, -- 提供的优惠价格无效 } -- google play payment PaymentErrorCode.GP = { SERVICE_TIMEOUT = -3, -- 服务超时 FEATURE_NOT_SUPPORTED = -2, -- 不支持功能 SERVICE_DISCONNECTED = -1, -- 服务单元已断开 OK = 0, -- 成功 USER_CANCELED = 1, -- 用户按上一步或取消对话框 SERVICE_UNAVAILABLE = 2, -- 网络连接断开 BILLING_UNAVAILABLE = 3, -- 所请求的类型不支持 Google Play 结算服务 AIDL 版本 ITEM_UNAVAILABLE = 4, -- 请求的商品已不再出售。 DEVELOPER_ERROR = 5, -- 提供给 API 的参数无效。此错误也可能说明应用未针对结算服务正确签名或设置,或者在其清单中缺少必要的权限。 ERROR = 6, -- 操作期间出现严重错误 ITEM_ALREADY_OWNED = 7, -- 未能购买,因为已经拥有此商品 ITEM_NOT_OWNED = 8, -- 未能消费,因为尚未拥有此商品 NETWORK_ERROR = 12, -- 网络故障 } function PaymentErrorCode:init() end PaymentErrorCode:init()buildingthemesreslinkreturn { --BASIC --ASSET } main_G.json = raw_require("rapidjson") if Device.isIOS() then _G.luaoc = require("common/platform/ext/luaoc") elseif Device.isAndroid() then _G.luaj = require("common/platform/ext/luaj") end buildinglistuireslink7return { --BASIC --ASSET building_cell = {"Assets/AssetsPackage/Res/modules/ui/buildbuildinglist/prefabs/building_cell.prefab", 0, 0}, building_list_ui = {"Assets/AssetsPackage/Res/modules/ui/buildbuildinglist/prefabs/buildings_list_ui/building_list_ui.prefab", 0, 0}, one_building_cell = {"Assets/AssetsPackage/Res/modules/ui/buildbuildinglist/prefabs/buildings_list_ui/one_building_cell.prefab", 0, 0}, one_row_buildings = {"Assets/AssetsPackage/Res/modules/ui/buildbuildinglist/prefabs/buildings_list_ui/one_row_buildings.prefab", 0, 0}, } TaskOrderCell'--- ---@class TaskOrderCell : LuaClass local TaskOrderCell = defClass("TaskOrderCell") -- require("modules/ui/taskui/order/TaskOrderDetailUI") --function TaskOrderCell:ctor(taskId, resLink, parent) -- self.ui = GameObject.Instantiate(resLink, parent) -- self.taskId = taskId -- self:initUI() -- self:showUI() --end function TaskOrderCell:ctor(obj, id) self.ui = obj self.taskId = id self:initUI() self:showUI() --self:initTime() --self:startTime() end function TaskOrderCell:reload(taskId) self.ui:SetActive(true) self.taskId = taskId self:showUI() end function TaskOrderCell:hide() self.ui:SetActive(false) end function TaskOrderCell:initUI() -- 初始化UI元素 self.order_title = self.ui:Seek("order_title") self.order_desc = self.ui:Seek("order_desc") self.progress_bar = self.ui:Seek("progress_bar") self.progress_value = self.ui:Seek("progress_value") self.receive_btn = self.ui:Seek("receive_btn") self.timeout_btn = self.ui:Seek("timeout_btn") self.completed = self.ui:Seek("completed") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.receive_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:receive() end) self:initRewardIU() end function TaskOrderCell:showUI() local info = TaskOrderMgr:getOrderInfo(self.taskId) local createTime, timeLimit, outTime = info:getTimeLift() local currTime = os.time() local span = currTime - createTime local isClear = (span > timeLimit) and (span >= (timeLimit + outTime)) local isTomeOut = span >= (timeLimit + outTime) self.order_title:GetComponent("TextMeshProUGUI").text = info:getTitle() self.order_desc:GetComponent("TextMeshProUGUI").text = info:getDesc() self.progress_bar:GetComponent("Image").fillAmount = info:getFillAmount() self.progress_value:GetComponent("TextMeshProUGUI").text = info:getTimeLeftDesc() self.receive_btn:SetActive(not isTomeOut) self.receive_btn:GetComponent("Button").interactable = not isClear self.timeout_btn:SetActive(isTomeOut) self:showRewardIU(info) end --function TaskOrderCell:initTime() -- local info = TaskOrderMgr:getOrderInfo(self.taskId) -- self.createTime, self.timeLimit, self.outTime = info:getTimeLift() -- self.isClear = false -- self.isTimeOut = false --end -- --function TaskOrderCell:startTime() -- self.tmr = self:timer(self, -- function(dt) -- self:timeUpdate(dt) -- end -- ,60, 0) --end -- --function TaskOrderCell:stopTime() -- self:clear(true, self.tmr) --end --- 初始化奖励UI function TaskOrderCell:initRewardIU() self.coins = {} for i = 1, 3 do self.coins[i] = self.ui:Seek("reward_coin_" .. i) end self.coinIcons = {} for i = 1, #self.coins do self.coinIcons[i] = self.coins[i]:Seek("icon") end self.entities = {} for i = 1, 3 do self.entities[i] = self.ui:Seek("reward_entity_" .. i) end self.entityIcons = {} for i = 1, #self.entities do self.entityIcons[i] = self.entities[i]:Seek("icon") end end --- 展示奖励UI ---@param info TaskOrderInfo function TaskOrderCell:showRewardIU(info) local rewardIds = info:getRewardIds() local rewardValues = info:getRewardValues() local coinIndex = 1 local entityIndex = 1 for i, id in ipairs(rewardIds) do local has = rewardIds[i] ~= -1 if has then if id == 1 then coinIndex = self:showCoinReward(coinIndex, rewardIds[i], rewardValues[i]) elseif id == 2 then coinIndex = self:showCoinReward(coinIndex, rewardIds[i], rewardValues[i]) elseif id == 3 then entityIndex = self:showEntityReward(entityIndex, rewardIds[i], rewardValues[i]) elseif id == 4 then entityIndex = self:showEntityReward(entityIndex, rewardIds[i], rewardValues[i]) end end end for i = coinIndex + 1, #self.coins do self.coins[i]:SetActive(false) end for i = entityIndex + 1, #self.entities do self.entities[i]:SetActive(false) end end function TaskOrderCell:getCoinIcon(type) -- todo 图片 local base_path = "Assets/AssetsPackage/Res/modules/ui/common/images/" if type == 1 then local img_path = base_path .. "coin.png" return ResLoader.loadAsset(img_path,typeof(CS.UnityEngine.Sprite)) elseif type == 2 then local img_path = base_path .. "coin.png" return ResLoader.loadAsset(img_path,typeof(CS.UnityEngine.Sprite)) end end function TaskOrderCell:getEntityIcon(type, entityId) -- todo 图片 local base_path = "Assets/AssetsPackage/Res/modules/ui/common/images/" if type == 3 then local img_path = base_path .. "default_role_cell.png" return ResLoader.loadAsset(img_path,typeof(CS.UnityEngine.Sprite)) elseif type == 4 then local img_path = base_path .. "default_cuisine.png" return ResLoader.loadAsset(img_path,typeof(CS.UnityEngine.Sprite)) end end function TaskOrderCell:showCoinReward(index, type, value) self.coins[index]:SetActive(true) self.coins[index]:GetComponent("TextMeshProUGUI").text = "+" .. value self.coinIcons[index]:GetComponent("Image").sprite = self:getCoinIcon(type) return index + 1 end function TaskOrderCell:showEntityReward(index, type, value) self.entities[index]:SetActive(true) self.entityIcons[index]:GetComponent("Image").sprite = self:getEntityIcon(type, value) return index + 1 end --- 释放所有通过Seek获得的物体索引 function EmployeeCell:releaseUI() -- 遍历所有通过Seek获得的物体并将其置为空 self.order_title = nil self.order_desc = nil self.progress_bar = nil self.progress_value = nil self.receive_btn = nil self.timeout_btn = nil self.completed = nil self.coins = nil self.coinIcons = nil self.entities = nil self.entityIcons = nil -- 最后,将self.ui置为空 if self.ui then self.ui = nil end end function TaskOrderCell:receive() --local view = TaskOrderDetailUI.new(self.taskId):show():showMask():enableCloseWhenClickMask(function() local view = TaskOrderDetailUI.new(self.taskId):show():showMask():enableCloseWhenClickMask(function() end) end ---- 时间更新 --function TaskOrderCell:timeUpdate(seconds) -- if self.isTimeOut then -- return -- end -- -- if self.isClear then -- if os.time() - self.createTime then -- self.isTimeOut = true -- self:showUI() -- end -- return -- end -- -- if os.time() - self.createTime then -- self.isClear = true -- self:showUI() -- end --end return TaskOrderCellmainrequire("modules/role/customer/special/CustomerSpecial") require("modules/role/customer/special/SpecialCustomerMgr") require("modules/role/customer/special/RichCustomer") require("modules/role/customer/special/AdCustomer") require("modules/role/customer/special/ThiefCustomer") require("modules/role/customer/special/StenchCustomer") require("modules/role/customer/special/MagicianCustomer") require("modules/role/customer/special/SingingCustomer") require("modules/role/customer/special/MysteryCustomer") --require("modules/role/customer/special/GroundhogBrothers1") --require("modules/role/customer/special/GroundhogBrothers2") require("modules/role/customer/special/TaskOrderCustomer") ReceptionConst--[[ author:{zhangpeng} time:2025-05-16 17:16:18 ]] local ReceptionConst = defClassStatic("ReceptionConst") local LOGTAG = "ReceptionConst" -- 迎宾台id(只有一个) ReceptionConst.receptionDeskId = 105 function ReceptionConst:init() end return ReceptionConstUIRoot. ---@class UIRoot:LuaClass local UIRoot, super = defClass("UIRoot") local UnityEngine = CS.UnityEngine local UGUI = CS.UnityEngine.UI local LOGTAG = "UIRoot" ---@param go CS.UnityEngine.GameObject ---@param isGlobal boolean ---@param uiMask CS.UnityEngine.GameObject function UIRoot:ctor(go, isGlobal, uiMask) Msg.del(Msg.EXIT, nil, self) self.go = go self.isGlobalUI = isGlobal ---@type UILayer[] self.uiLayerStack = {} self.uiMask = uiMask ---@type {audio:table, timeline:table, videoplayer:table, action:table}|nil self.pauseList = nil end function UIRoot:getUILayerStack() return self.uiLayerStack or {} end function UIRoot:getUIMask() return self.uiMask end function UIRoot:addUILayer(uilayer) table.insert(self.uiLayerStack, uilayer) end function UIRoot:removeUILayer(uilayer) printInfo(LOGTAG, "removeUILayer") table.removeByValue(self.uiLayerStack, uilayer, true) if not next(self.uiLayerStack) and not self.isGlobalUI then self:exit() end end function UIRoot:getGameObject() return self.go end function UIRoot:getPauseList() return self.pauseList end function UIRoot:setPauseList(pauseList) self.pauseList = pauseList end function UIRoot:onExit() UILayerUtil:_removeUIRoot(self) super.onExit(self) end return UIRootCage`-- 牢笼 local Cage = defClass("Cage") -- 构造函数 function Cage:ctor(targetId, parent, worldPos) self.id = targetId self.go = GameObject.Instantiate(FishingGameMgr.resLink.cage, parent) self.go.transform.localPosition = worldPos or Vector3.zero self:init() self:initEvent() end -- 初始化 function Cage:init() end -- 初始化事件 function Cage:initEvent() self.collider = self.go:Seek("collider") Event.add(self.collider, Event.OnTriggerEnter2D, function(other) self:onTriggerEnter(other) end) end -- 当玩家触发碰撞时 function Cage:onTriggerEnter(other) if (not FishingGameMgr.isPlaying) or FishingGameMgr.isPaused then return end if other.gameObject:CompareTag("Player") then -- 改为胜利动画 --if FishingGameMgr:isReachTargetLevel() then -- 到达对应等级才能打开牢笼 -- FishingGameMgr:gameVictory() --else -- FishingGameMgr:hurt() -- 改为减少能量 -- 没有到达对应等级,玩家死亡 --end end end SpecialCustomerDetailUI--- 菜品详情界面 ---@class SpecialCustomerDetailUI : UILayer local SpecialCustomerDetailUI, super = defClass("SpecialCustomerDetailUI", UILayer) -- UIComponent local UIImage = CS.UnityEngine.UI.Image local TMProUGUI = CS.TMPro.TextMeshProUGUI function SpecialCustomerDetailUI:ctor(customerId, isNewCustomer) super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/customerui/customeruireslink") self.customerId = customerId self.isNewCustomer = isNewCustomer end function SpecialCustomerDetailUI:onLoad() self.ui = GameObject.Instantiate(self.R.special_customer_detail_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end --- 初始化 function SpecialCustomerDetailUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.new_customer = self.ui:Seek("new_customer") self.new_share = self.ui:Seek("new_share") self.share_value = self.ui:Seek("share_value") self.share_btn = self.ui:Seek("share_btn") self:initInfo() self:initAttribute() -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addButtonClickEvent(self.share_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:share() end) end --- 展示UI function SpecialCustomerDetailUI:showUI() local info = SpecialCustomerMgr:getSpecialCustomerInfo(self.customerId) local shareCoin = info:getShareCoin() local isVisited = info:isVisited() or self.isNewCustomer local showShareReward = isVisited and (not info:isShared()) and (shareCoin > 0) self.new_customer:SetActive(self.isNewCustomer) self.new_share:SetActive(showShareReward) self.share_value:SetActive(showShareReward) self.share_btn:SetActive(isVisited) if showShareReward then self.share_value[TMProUGUI].text = "+" .. shareCoin end self:showInfo(info) self:showAttribute(info) end function SpecialCustomerDetailUI:initInfo() self.lock = self.ui:Seek("lock") self.info = self.ui:Seek("info") self.customer_img = self.ui:Seek("customer_img") self.customer_spine_root = self.ui:Seek("customer_spine_root") self.customer_name = self.ui:Seek("customer_name") self.customer_desc = self.ui:Seek("customer_desc") end function SpecialCustomerDetailUI:showInfo(info) local isVisited = info:isVisited() or self.isNewCustomer self.lock:SetActive(not isVisited) self.info:SetActive(isVisited) if isVisited then --self.customer_img[UIImage].sprite = info:getIcon() --self.customer_img[UIImage]:SetNativeSize() GameObject.Instantiate(info:getSpineUI(), self.customer_spine_root.transform) self.customer_name[TMProUGUI].text = info:getName() self.customer_desc[TMProUGUI].text = info:getDesc() end end function SpecialCustomerDetailUI:initAttribute() self.tagCount = 4 self.tags = {} for i = 1, self.tagCount do self.tags[i] = self.ui:Seek("customer_tag_cell_" .. i) end self.line = self.ui:Seek("line") end function SpecialCustomerDetailUI:showAttribute(info) local idx = 1 --if info:getVisitNeedStar() > 0 then -- idx = self:showTag(idx, info:getVisitNeedStarDesc(), nil) --end ---- 招揽条件 --if info:getSolicitTagId() ~= -1 then -- idx = self:showTag(idx, info:getSolicitTagDesc(), nil) --end ---- 功能 --local funList = info:getFunTagIdList() --if #funList > 0 then -- for i, v in ipairs(funList) do -- idx = self:showTag(idx, info:getFunTagDesc(i), nil) -- end --end --for i = idx, self.tagCount do -- self.tags[i]:SetActive(false) --end end function SpecialCustomerDetailUI:showTag(idx, desc) self.tags[idx]:SetActive(true) self.tags[idx][TMProUGUI].text = desc idx = idx + 1 return idx end function SpecialCustomerDetailUI:share() local info = CustomerMgr:getCustomerInfo(self.customerId) local reward = info:getShareCoin() local isReward = info:isShared() and (reward > 0) -- todo 分享 -- 分享回调 if not isReward then return end CurrencyMgr:changeCoin(reward) self:showUI() end return SpecialCustomerDetailUIcommonuireslink%return { --BASIC --ASSET pop_up_ui = {"Assets/AssetsPackage/Res/modules/ui/common/prefabs/pop_up_ui.prefab", 0, 0}, entity_cell = {"Assets/AssetsPackage/Res/modules/ui/common/prefabs/entity_cell.prefab", 0, 0}, entity_detail_ui = {"Assets/AssetsPackage/Res/modules/ui/common/prefabs/entity_detail_ui.prefab", 0, 0}, entity_list_ui = {"Assets/AssetsPackage/Res/modules/ui/common/prefabs/entity_list_ui.prefab", 0, 0}, coin_fly_spine = {"Assets/AssetsPackage/Res/modules/ui/common/prefabs/coin_fly_spine.prefab", 0, 0}, } loginuireslink|return { --BASIC --ASSET login_ui = {"Assets/AssetsPackage/Res/modules/login/ui/prefabs/login_ui.prefab", 0, 0}, } FishCfgParse-- 捕鱼 鱼配置解析 local FishCfgParse = defClassStatic("FishCfgParse") local FishCfgData = require("data/config/fishCfg") local LOGTAG = "FishCfgParse" -- 初始化 function FishCfgParse:init() self.idDic = {} for _, v in pairs(FishCfgData) do self.idDic[v.id] = v end end -- 获取鱼配置 function FishCfgParse:getFishCfgById(id) return self.idDic[id] end -- 获取所有鱼的配置 function FishCfgParse:getAllFishCfg() return FishCfgData end -- 按等级获取所有鱼的配置 function FishCfgParse:getFishCfgByGrade(grade) local result = {} for _, v in pairs(FishCfgData) do if v.grade == grade then table.insert(result, v) end end return result end -- 获取鱼的配置列表 function FishCfgParse:getFishCfgListByIds(ids) local result = {} for _, id in ipairs(ids) do local cfg = self:getFishCfgById(id) table.insert(result, cfg) end return result end FishCfgParse:init() BaseModel  ---@class BaseModel:LuaClass local BaseModel = defClass("BaseModel") function BaseModel:ctor() self.bindInfoList = {} self:init() end --重写 function BaseModel:init() end --[[ 四种绑定方式 self:b("name", "pbName") self:b("name", "pbRole.name") self:b("role", "pbRole", roleModel) --未测试 self:b("roleList", "pbRoleList", {roleModel}) --未测试 ]] function BaseModel:bindKey(keyName, pbName, t) local moCls = t local isArr = false if t and t[1] then moCls = t[1] isArr = true end local bindInfo = {keyName = keyName, pbName = pbName, moCls = moCls, isArr = isArr} table.insert(self.bindInfoList, bindInfo) return bindInfo end BaseModel.b = BaseModel.bindKey function BaseModel:setByPB(pbData) for i, bindInfo in ipairs(self.bindInfoList) do local value = nil local list = string.split(bindInfo.pbName, ".") local count = #list for j, name in ipairs(list) do name = tonumber(name) or name if j == 1 then value = pbData[name] elseif j < count then value = value[name] or {} else value = value or {} value = value[name] end end if bindInfo.moCls then if bindInfo.isArr then self[bindInfo.keyName] = {} for k, pbData in ipairs(value) do table.insert(self[bindInfo.keyName], bindInfo.moCls.new():setByPB(pbData)) end else self[bindInfo.keyName] = bindInfo.moCls.new():setByPB(value) end else self[bindInfo.keyName] = value end end return self end function BaseModel:getToPB() local pbData = {} for i, bindInfo in ipairs(self.bindInfoList) do local value = nil if bindInfo.moCls then if bindInfo.isArr then value = {} for k, mo in ipairs(self[bindInfo.keyName]) do table.insert(value, mo:getToPB()) end else value = self[bindInfo.keyName]:getToPB() end else value = self[bindInfo.keyName] end local list = string.split(bindInfo.pbName, ".") local t = nil local count = #list for j, name in ipairs(list) do name = tonumber(name) or name if 1 == count then pbData[name] = value else if j == 1 then if pbData[name] == nil then pbData[name] = {} end t = pbData[name] elseif j < count then if t[name] == nil then t[name] = {} end t = t[name] else t[name] = value end end end end return pbData end return BaseModel EmployeInfop?--[[ 员工信息,存基础数据和升级后的数据,UI界面也可用 author:{zhangpeng} time:2025-05-21 10:16:39 ]] ---@class EmployeInfo local EmployeInfo = defClass("EmployeInfo") local LOGTAG = "EmployeInfo" local Sprite = typeof(CS.UnityEngine.Sprite) function EmployeInfo:ctor(employeeCfgId) self.id = employeeCfgId self:init() end function EmployeInfo:init() self:initData() -- 状态参数初始化 self:initStatus() end function EmployeInfo:initData() self.config = self:getEmployeeCfg() -- 获取员工配置 local employeeCfg = self:getEmployeeCfg() -- 员工名称 self.name = employeeCfg.name -- 员工描述 self.desc = employeeCfg.desc -- 资源链接 self.imageName = employeeCfg.res -- 购买价格 self.price = employeeCfg.price -- 员工类型 self.posts = employeeCfg.tabType -- 解锁id self.unlockCondId = employeeCfg.unlockCondId -- 员工等级 self.upgradeIds = employeeCfg.levelupIds -- 故事 self.storyNum = employeeCfg.storyNum -- 移动速度 self.movespeed = employeeCfg.movespeed self.storyIds = { [1] = employeeCfg.storyid_1, [2] = employeeCfg.storyid_2, [3] = employeeCfg.storyid_3 } -- 特殊属性 self:initSpecialAttr() end function EmployeInfo:initStatus() -- 员工等级 self.grade = UserDataMgr.employeeUserData:getEmployeeGrade() -- 当前熟练度 self.proficiency = 0 -- 获取员工等级配置 self.currGradeCfg = self:getEmployeeGradeCfg(self.grade) -- 当前皮肤 self.skin = UserDataMgr.employeeUserData:getUsedSkinId() -- 已获得皮肤 self.skins = UserDataMgr.employeeUserData:getEmployeeSkinList() -- 获取员工下一等级配置 if not self:isMaxGrade() then self.nextGradeCfg = self:getEmployeeGradeCfg(self.grade + 1) else self.nextGradeCfg = nil end -- 状态 self.hireState = UserDataMgr.employeeUserData:getEmployeeState(self.id) self.workState = EmployeConst.State.Idle self.workTime = 0 self.restTime = 0 end -- 获取员工配置 function EmployeInfo:getEmployeeCfg() return EmployeCfgParse:getEmployeCfg(self.id) end -- 获取员工等级配置 function EmployeInfo:getEmployeeGradeCfg(gradeValue) return EmployeLevelUpCfgParse:getEmployeLevelUpCfg(self.upgradeIds[gradeValue]) end -- 获取员工等级配置 function EmployeInfo:getEmployeeFunCfg(employeeFunId) return EmployeeFunCfgParse:getEmployeeFunCfg(employeeFunId) end -- 初始化特殊属性(根据不同角色类型初始化) function EmployeInfo:initSpecialAttr() if self.id == EmployeConst.Type.Waiter then -- 服务员 elseif self.id == EmployeConst.Type.Security then -- 保安 elseif self.id == EmployeConst.Type.Chef then -- 大厨 elseif self.id == EmployeConst.Type.Cashier then -- 收银员 elseif self.id == EmployeConst.Type.Fisherman then -- 渔夫 elseif self.id == EmployeConst.Type.Singer then -- 歌手 elseif self.id == EmployeConst.Type.Assistant then -- 助理 end end --- 获取名称 function EmployeInfo:getName() return self.name end function EmployeInfo:getDesc() return self.desc end --- 获取Icon名称 function EmployeInfo:getIconName() return self.config.res end --- 获取半身Icon名称 function EmployeInfo:getBustIconName() return self.config.artBustRes end --- 职位 function EmployeInfo:getPosts() return self.posts end -- 雇佣状态 function EmployeInfo:getHireState() return self.hireState end -- 工作状态 function EmployeInfo:getWorkState() return self.workState end -- 设置工作状态 function EmployeInfo:setWorkState(state) self.workState = state if state == EmployeConst.State.Busy then -- 工作中 self.workTime = os.time() self.restTime = 0 self:useTags() elseif state == EmployeConst.State.Idle then -- 空闲 self.workTime = 0 self.restTime = 0 elseif state == EmployeConst.State.Resting then -- 休息中 self.restTime = os.time() self.workTime = 0 self:closeTags() end end --- 职位名 function EmployeInfo:getPostsName() return EmployeConst.PostsDesc[self.posts] end --- 员工工作时间 function EmployeInfo:getWorkTime() return self.workTime end -- 设置员工工作时间 function EmployeInfo:setWorkTime(time) self.workTime = time end function EmployeInfo:getWorkTimeLimit() for i = 1, 3 do local type = self.currGradeCfg["typeId_" .. i] if type == -1 then return 0 end if type == EmployeConst.Features.Solicitation then return self.currGradeCfg["arg_" .. i .. "_1"] elseif type == EmployeConst.Features.Order then return self.currGradeCfg["arg_" .. i .. "_1"] elseif type == EmployeConst.Features.CookingSpeed then return self.currGradeCfg["arg_" .. i .. "_2"] elseif type == EmployeConst.Features.CollectCoins then return self.currGradeCfg["arg_" .. i .. "_1"] end end return 0 end --- 员工休息时间 function EmployeInfo:getRestTime() return self.restTime end -- 设置员工休息时间 function EmployeInfo:setRestTime(time) self.workTime = time end function EmployeInfo:getRestTimeLimit() for i = 1, 3 do local type = self.currGradeCfg["typeId_" .. i] if type == -1 then return 0 end if type == EmployeConst.Features.Solicitation then return self.currGradeCfg["arg_" .. i .. "_2"] elseif type == EmployeConst.Features.Order then return self.currGradeCfg["arg_" .. i .. "_2"] elseif type == EmployeConst.Features.CookingSpeed then return self.currGradeCfg["arg_" .. i .. "_3"] elseif type == EmployeConst.Features.CollectCoins then return self.currGradeCfg["arg_" .. i .. "_2"] end end return 0 end --- 图片路径 function EmployeInfo:getImgPath() return "Assets/AssetsPackage/Res/modules/roles/employe/images/" .. self:getIconName() .. ".png" end --- 获取半身图标路径 function EmployeInfo:getBustIconPath() return "Assets/AssetsPackage/Res/modules/roles/employe/images/bust/" .. self:getBustIconName() .. ".png" end function EmployeInfo:getImg(path) if ResLoader.hasAsset(path) then return ResLoader.loadAsset(path, Sprite) end return nil end --- 获取全身图标 function EmployeInfo:getIcon() return self:getImg(self:getImgPath()) end --- 获取半身图标 function EmployeInfo:getBustIcon() return self:getImg(self:getBustIconPath()) end --- 获取剪影图标 function EmployeInfo:getSilhouetteIcon() return self:getImg(self:getImgPath()) end --- 获取半身剪影图标 function EmployeInfo:getBustSilhouetteIcon() return self:getImg(self:getBustIconPath()) end ----------解锁---------- function EmployeInfo:unlock() self.hireState = EmployeConst.HireState.Unlocked UserDataMgr.employeeUserData:setEmployeeState(self.id, self.hireState) end --- 是否已解锁 function EmployeInfo:isUnlocked() --return self.hireState >= EmployeConst.HireState.Unlocked -- todo 解锁事件完成后设置主动解锁 return UnlockMgr:isUnlock(self.unlockCondId) end --- 获取解锁描述 function EmployeInfo:getUnlockDesc() return UnlockMgr:getUnlockDesc(self.unlockCondId) end ----------雇佣---------- --- 是否已雇佣 function EmployeInfo:isPurchased() return self.hireState >= EmployeConst.HireState.Purchased end --- 雇佣价格 function EmployeInfo:getHirePrice() return self.price end --- 雇佣缺少金币 为负数时不可购买 function EmployeInfo:hireShortage() return self.price - CurrencyMgr:getCoin() end --- 雇佣 function EmployeInfo:hire() self.hireState = EmployeConst.HireState.Purchased UserDataMgr.employeeUserData:setEmployeeState(self.id, self.hireState) self:updateLastGradeTime(self.id) end ----------故事---------- function EmployeInfo:getStoryNum() return self.storyNum end function EmployeInfo:getStoryId(num) return self.storyIds[num] end ----------属性---------- function EmployeInfo:getTabId(gradeConfig, num) if num == 1 then return gradeConfig.typeId_1 elseif num == 2 then return gradeConfig.typeId_2 elseif num == 3 then return gradeConfig.typeId_3 end return -1 end function EmployeInfo:getCurrTagId(num) return self:getTabId(self.currGradeCfg, num) end function EmployeInfo:getNextTagId(num) return self:getTabId(self.nextGradeCfg, num) end --- 当前等级升星描述 function EmployeInfo:getCurrStarDesc() return string.format("提高人气: %d", self.currGradeCfg.upStarNum) end --- 下一等级升星描述 function EmployeInfo:getNextStarDesc() return string.format("提高人气: %d", self.nextGradeCfg.upStarNum) end --- 当前标签描述 function EmployeInfo:getTagDesc(gradeConfig, tagIdx) local typeId = self:getTabId(gradeConfig, tagIdx) if typeId == -1 then return nil end local param1 = self:getCurrTagValueByIdx(tagIdx, 1) local param2 = self:getCurrTagValueByIdx(tagIdx, 2) local param3 = self:getCurrTagValueByIdx(tagIdx, 3) local str = self:getEmployeeFunCfg(typeId).desc return string.format(str, param1, param2, param3) end --- 获取指定索引参数 function EmployeInfo:getTagValueByIdx(gradeConfig, tagIdx, paramIdx) return gradeConfig["arg_" .. tagIdx .. "_" .. paramIdx] end --- 获取指定索引参数 function EmployeInfo:getCurrTagValueByIdx(tagIdx, paramIdx) return self:getTagValueByIdx(self.currGradeCfg, tagIdx, paramIdx) end --- 获取指定索引参数 function EmployeInfo:getNextTagValueByIdx(tagIdx, paramIdx) if not self.nextGradeCfg then return 0 end return self:getTagValueByIdx(self.nextGradeCfg, tagIdx, paramIdx) end --- 当前等级标签描述 function EmployeInfo:getCurrTagDesc(num) return self:getTagDesc(self.currGradeCfg, num) end --- 下一级标签描述 function EmployeInfo:getNextTagDesc(num) return self:getTagDesc(self.nextGradeCfg, num) end -- 使用标签效果 function EmployeInfo:useTags() for i = 1, 3 do --local type = self.currGradeCfg["typeId_" .. i] --if type == -1 then -- return 0 --end --if type == EmployeConst.Features.MovementSpeed then -- BonusMgr:addTempBonus(BonusConst.BonusType.MovementSpeed, self.currGradeCfg["arg_" .. i .. "_1"]) --elseif type == EmployeConst.Features.Order then -- return self.currGradeCfg["arg_" .. i .. "_2"] --elseif type == EmployeConst.Features.CookingSpeed then -- return self.currGradeCfg["arg_" .. i .. "_3"] --elseif type == EmployeConst.Features.CollectCoins then -- return self.currGradeCfg["arg_" .. i .. "_2"] --end end return 0 end -- 关闭标签效果 function EmployeInfo:closeTags() for i = 1, 3 do --local type = self.currGradeCfg["typeId_" .. i] --if type == -1 then -- return 0 --end --if type == EmployeConst.Features.Solicitation then -- return self.currGradeCfg["arg_" .. i .. "_2"] --elseif type == EmployeConst.Features.Order then -- return self.currGradeCfg["arg_" .. i .. "_2"] --elseif type == EmployeConst.Features.CookingSpeed then -- return self.currGradeCfg["arg_" .. i .. "_3"] --elseif type == EmployeConst.Features.CollectCoins then -- return self.currGradeCfg["arg_" .. i .. "_2"] --end end return 0 end ----------升级---------- --- 获取当前等级 function EmployeInfo:getGrade() return self.grade end --- 升级价格 function EmployeInfo:getUpgradePrice() return self.currGradeCfg.levelUpPrice end --- 获取最大等级 function EmployeInfo:getMaxGrade() return #self.upgradeIds end --- 获取当前熟练度 function EmployeInfo:getProficiency() return self.proficiency end --- 获取最大熟练度 function EmployeInfo:getMaxProficiency() return self.currGradeCfg.cdTime end --- 是否为最大熟练度 function EmployeInfo:isMaxProficiency() return self.proficiency >= self.currGradeCfg.cdTime end -- 获取最高熟练度描述 function EmployeInfo:getMaxProficiencyDesc() if self:getMaxProficiency() >= 3600 then if self:getMaxProficiency() % 60 == 0 then return ("升级需达到历练%d小时"):format(self:getMaxProficiency() / 3600) else return ("升级需达到历练%d小时%d分钟"):format(self:getMaxProficiency() / 3600, self:getMaxProficiency() % 3600 / 60) end else return ("升级需达到历练%d分钟"):format(self:getMaxProficiency() / 60) end return "" end --- 获取是否可升级 function EmployeInfo:getCanUpgrade() return self:getGrade() < self:getMaxGrade() and self:isMaxProficiency() end --- 升级 function EmployeInfo:upgrade() self.grade = self.grade + 1 self.currGradeCfg = self:getEmployeeGradeCfg(self.grade) UserDataMgr.employeeUserData:addEmployeeGrade(self.id, 1) self:updateLastGradeTime() if not self:isMaxGrade() then self.nextGradeCfg = self:getEmployeeGradeCfg(self.grade + 1) else self.nextGradeCfg = nil end end --- 是否为最大等级 function EmployeInfo:isMaxGrade() return self:getGrade() >= self:getMaxGrade() end -- 更新时间 function EmployeInfo:updateLastGradeTime() UserDataMgr.employeeUserData:updateEmployeeLastGradeTime(self.id) end -- 更新升级进度 function EmployeInfo:refreshProficiency() local curr = os.time() local lastTime = UserDataMgr.employeeUserData:getEmployeeLastGradeTime(self.id) if lastTime == -1 then self.proficiency = 0 return end self.proficiency = curr - lastTime if self.proficiency > self.currGradeCfg.cdTime then self.proficiency = self.currGradeCfg.cdTime end end --- 获取升级剩余时间描述 function EmployeInfo:getTimeLeftDesc() return self:timeFormat(self:getMaxProficiency() - self:getProficiency()) end --- 时间格式化 function EmployeInfo:timeFormat(num) if num > 3600 then return string.format("剩余时间:%02d时%02d分", math.floor(num / 3600), math.floor((num % 3600) / 60)) else return string.format("剩余时间:%02d分", math.floor((num % 3600) / 60)) end end ----------皮肤---------- -- 获取员工皮肤配置 function EmployeInfo:getEmployeeSkinCfg(skinId) return EmployeSkinCfgParse:getEmployeSkinCfg(skinId) end -- 获取员工皮肤配置集合 function EmployeInfo:getEmployeeSkinCfgs() return EmployeSkinCfgParse:getEmployeeSkinCfgByEmployeeCfgId(self.id) end --- 当前皮肤描述 function EmployeInfo:getSkinDesc(skinId) local config = self:getEmployeeSkinCfg(skinId) local str = EmployeConst.SkinTypeDesc[config.attType] return string.format(str, param1) end -- 皮肤是否已购买 function EmployeInfo:isSkinPurchase(skinId) local arr = self.skins for _, value in ipairs(arr) do if value == skinId then return true end end return false end -- 皮肤购买 function EmployeInfo:skinPurchase(skinId) table.insert(self.skins, skinId) UserDataMgr.employeeUserData:addEmployeeSkin(self.id, skinId) end -- 皮肤是否已安装 function EmployeInfo:isSkinInstall(skinId) return self.skin == skinId end -- 皮肤安装 function EmployeInfo:skinInstall(skinId) self.skin = skinId UserDataMgr.employeeUserData:useEmployeeSkin(self.id, skinId) end -- 皮肤取消安装 function EmployeInfo:skinUninstall() self.skin = -1 UserDataMgr.employeeUserData:unuseEmployeeSkin(self.id) end return EmployeInfo main--[[ luaide 模板位置位于 Template/FunTemplate/NewFileTemplate.lua 其中 Template 为配置路径 与luaide.luaTemplatesDir luaide.luaTemplatesDir 配置 https://www.showdoc.cc/web/#/luaide?page_id=713062580213505 author:{zhangpeng} time:2022-05-17 17:29:25 ]] require("common/core/base/utils/CSharpUtil") require("common/core/base/utils/ShortForUnity") require("common/core/base/utils/uAction") util = { spine = require("common/core/base/utils/SpineUtil"), animator = require("common/core/base/utils/AnimatorUtil"), async = require("common/core/base/utils/async"), string = require("common/core/base/utils/StringUtil"), time = require("common/core/base/utils/TimeUtil"), file = require("common/core/base/utils/FileUtil"), lua = require("common/core/base/utils/LuaUtil"), perf = require("common/core/base/utils/PerfUtil"), TableUtil = require("common/core/base/utils/TableUtil"), ugui = require("common/core/base/utils/UGuiUtil"), coord = require("common/core/base/utils/CoordUtil"), fsm = require("common/core/base/utils/luafsm"), pause = require("common/core/base/utils/PauseUtil"), serpent = require("common/core/base/utils/serpent"), common = require("common/core/base/utils/CommonUtils"), PositionConvert = require("common/core/base/utils/PositionConvert"), RichTextUtil = require("common/core/base/utils/RichTextUtil"), TakePhoto = require("common/core/base/utils/ScreenShotUtil"), BoundUtil = require("common/core/base/utils/BoundUtil"), GameNodeAdapt = require("common/core/base/utils/GameNodeAdapt"), particle = require("common/core/base/utils/ParticleUtil"), } ThemeUIConstS--[[ 建筑主题ui常量 author:{zhangpeng} time:2025-05-23 12:22:46 ]] local ThemeUIConst = defClassStatic("ThemeUIConst") -- 建筑主题 ThemeUIConst.themes = { -- 自然之韵 nature = 1, -- 原木风光 wood = 2, -- 现代简约 modern = 3, -- 现代精致 modern_pretty = 4, -- 粉红糖果 pink_candy = 5, } -- 主题名字 ThemeUIConst.themeNameList = { -- 自然之韵 [ThemeUIConst.themes.nature] = "自然之韵", -- 原木风光 [ThemeUIConst.themes.wood] = "原木风光", -- 现代简约 [ThemeUIConst.themes.modern] = "现代简约", -- 现代精致 [ThemeUIConst.themes.modern_pretty] = "现代精致", -- 粉红糖果 [ThemeUIConst.themes.pink_candy] = "粉红糖果", } -- init function ThemeUIConst:init() end ThemeUIConst:init() CuisineConst--[[ 菜品常量 author:{zhangpeng} time:2025-05-13 10:48:01 ]] ---@class CuisineConst local CuisineConst = defClassStatic("CuisineConst") -- icon/bubble, icon用在ui界面,bubble用在角色头顶和灶台上(非ui) CuisineConst.view = { icon = "icon_for_ui", -- ui界面用的图标 bubble = "bubble_for_ui", -- 角色头顶的气泡,非ui } -- 菜品类型(和灶台出餐类型保持一致) CuisineConst.CuisineType = { default = 0, -- 默认类型 dish = 1, -- 菜类 drink = 2, -- 饮料类 barbecue = 3, -- 烧烤类 } -- 气泡显示在角色头顶还是灶台上 CuisineConst.BubbleShowParentType = { customer = 10, -- 角色头顶 stove = 20, -- 灶台上 desk = 30, -- 餐桌上 } -- 气泡显示主体名字 CuisineConst.BubbleShowParentTypeName = { [CuisineConst.BubbleShowParentType.customer] = "角色", -- 角色头顶 [CuisineConst.BubbleShowParentType.stove] = "灶台", -- 灶台上 [CuisineConst.BubbleShowParentType.desk] = "餐桌", -- 餐桌上的食物 } -- 不同类型头顶气泡的缩放 CuisineConst.BubbleShowParentTypeScale = { [CuisineConst.BubbleShowParentType.customer] = 0.18, -- 角色头顶 [CuisineConst.BubbleShowParentType.stove] = 0.2, -- 灶台上 [CuisineConst.BubbleShowParentType.desk] = 0.2, -- 餐桌上 } -- 菜谱状态 CuisineConst.State = { -- 未解锁 Locked = 1, -- 已解锁 Unlocked = 2, -- 已购买 Purchased = 3 } -- 菜品消费后掉落类型 CuisineConst.DropType = { -- 金币 coin = 1, -- 星星 star = 2, -- 音符 note = 3, } function CuisineConst:init() end CuisineConst:init() helpTypeCfg--[[ from file:帮助分类.xlsx --]] local helpTypeCfg = { [1] = { id = 701, name = "餐厅", res = "bz_canting", }, [2] = { id = 702, name = "音乐烤吧", res = "bz_kaoba", }, [3] = { id = 703, name = "钓鱼", res = "bz_diaoyu", }, [4] = { id = 704, name = "顾客", res = "bz_guke", }, [5] = { id = 705, name = "扭蛋机", res = "bz_niudan", }, } return helpTypeCfg EmployeMgr&--[[ 员工管理类,负责员工的管理,包括员工的雇佣,升级 持有已解锁员工列表 author:{zhangpeng} time:2025-05-21 11:15:05 ]] local EmployeMgr = defClassStatic("EmployeMgr") local LOGTAG = "EmployeeMgr" -- init function EmployeMgr:init() local scene = RestaurantMgr:getRestaurantScene() self.employeeResLink = scene.employeR self.employees = {} self.infoList = {} self:initUserData() self:initEvent() self:setEvent() end -- 初始化用户数据 function EmployeMgr:initUserData() self.userData = UserDataMgr.employeeUserData end -- 初始化事件 function EmployeMgr:initEvent() self.employeeHireEvent = SimpleEvent.new("EmployeeHireEvent") -- 雇佣员工事件 end -- 设置事件 function EmployeMgr:setEvent() end -- 初始化地图上的员工 function EmployeMgr:initEmployeeOnMap() for id ,state in pairs(self.userData.employee_state) do if state == EmployeConst.HireState.Purchased then self:createEmployeOnMap(id, MapsMgr:getCurrentMapId()) end end end -- function EmployeMgr:startGame() for id ,state in pairs(self.userData.employee_state) do if state >= EmployeConst.HireState.Purchased then if id == EmployeConst.Type.Waiter then -- 服务员 self:startWaiterEmployeWork() elseif id == EmployeConst.Type.Security then -- 保安 elseif id == EmployeConst.Type.Chef then -- 大厨 self:startChefEmployeWork() elseif id == EmployeConst.Type.Cashier then -- 收银员 elseif id == EmployeConst.Type.Fisherman then -- 渔夫 elseif id == EmployeConst.Type.Singer then -- 歌手 self:startSingerEmployeWork() elseif id == EmployeConst.Type.Assistant then -- 助理 self:startAssistantEmployeWork() elseif id == EmployeConst.Type.BarbecueChef then -- 烤吧厨师 self:startBarbecueChefEmployeWork() end end end end -- 地图上创建一个员工 function EmployeMgr:createEmployeOnMap(employeeId,mapId) local employee = nil local map = MapsMgr:getMap(MapsConst.MapType.restaurant) local employees_root = map:getEmployeeRoot() local birthPoint = self:getBirthPoint(employeeId) if employeeId == EmployeConst.Type.Waiter then -- 服务员 employee = Waiter.new(self.employeeResLink, employeeId) elseif employeeId == EmployeConst.Type.Security then -- 保安 employee = Security.new(self.employeeResLink, employeeId) elseif employeeId == EmployeConst.Type.Chef then -- 大厨 employee = Chef.new(self.employeeResLink, employeeId) elseif employeeId == EmployeConst.Type.Fisherman then -- 渔夫 employee = Fisherman.new(self.employeeResLink, employeeId) elseif employeeId == EmployeConst.Type.Singer then -- 歌手 employee = Singer.new(self.employeeResLink, employeeId) elseif employeeId == EmployeConst.Type.Assistant then -- 餐厅助理 employee = Assistant.new(self.employeeResLink, employeeId) elseif employeeId == EmployeConst.Type.BarbecueChef then -- 烤吧厨师 employee = BarbecueChef.new(self.employeeResLink, employeeId) end employee:setEmployeParent(employees_root) employee:setCurPosition(birthPoint.transform.position) employee:setMapRoot(map.rootNode) local idlePoints = self:getIdlePoints(employeeId) if #idlePoints > 0 then employee.wanderPoints = idlePoints end -- 添加到员工列表 table.insert(self.employees, employee) return employee end -- 获取出生点 function EmployeMgr:getBirthPoint(employeeId) local map = MapsMgr:getMap(MapsMgr:getCurrentMapId()) local root_name = EmployeConst.EmployeeRootNames[employeeId] return map:getEmployeeNodes(root_name).birthPoint end -- 获取闲逛点列表 function EmployeMgr:getIdlePoints(employeeId) local map = MapsMgr:getMap(MapsMgr:getCurrentMapId()) local root_name = EmployeConst.EmployeeRootNames[employeeId] return map:getEmployeeNodes(root_name).wanderPoints end -- 添加一些测试员工 function EmployeMgr:addEmployeesToScene() end -- 解锁员工 function EmployeMgr:unlockEmployee(employeeId) local employeeInfo = self.infoList[employeeId] employeeInfo:unlock() end -- 升级员工 function EmployeMgr:upgradeEmployee(employeeId) local employeeInfo = self.infoList[employeeId] employeeInfo:upgrade() end -- 获取已解锁员工列表 function EmployeMgr:getUnlockedEmployees() return self.employees end -- 根据员工ID获取员工 function EmployeMgr:getEmployeeById(employeeId) return self.employees[employeeId] end -- 获取员工信息 function EmployeMgr:getEmployeeInfo(employeeId) if self.infoList[employeeId] then return self.infoList[employeeId] end local info = EmployeInfo.new(employeeId) self.infoList[employeeId] = info return info end -- 雇佣 function EmployeMgr:employeeHire(employeeId) local employeeInfo = self.infoList[employeeId] employeeInfo:hire() CurrencyMgr:changeCoin(-employeeInfo:getHirePrice()) self:createEmployeOnMap(employeeId, MapsMgr:getCurrentMapId()) -- 任务进度 UserDataMgr:addUnlockEmployeeCount(1) StoryDialogMgr:showRoleStoryDialogUI(employeeId, function() if employeeId == EmployeConst.Type.Waiter then self:startWaiterEmployeWork() elseif employeeId == EmployeConst.Type.Security then self:startSecurityEmployeWork() elseif employeeId == EmployeConst.Type.Chef then self:startChefEmployeWork() end end) end -- 视频雇佣 function EmployeMgr:employeeHireByVideo(employeeId, hireShortage) local employeeInfo = self.infoList[employeeId] employeeInfo:hire() CurrencyMgr:changeCoin(-employeeInfo:getHirePrice() + hireShortage) self:createEmployeOnMap(employeeId, MapsMgr:getCurrentMapId()) -- 任务进度 UserDataMgr:addUnlockEmployeeCount(1) StoryDialogMgr:showRoleStoryDialogUI(employeeId, function() if employeeId == EmployeConst.Type.Waiter then self:startWaiterEmployeWork() elseif employeeId == EmployeConst.Type.Security then self:startSecurityEmployeWork() elseif employeeId == EmployeConst.Type.Chef then self:startChefEmployeWork() end end) end -- 升级 function EmployeMgr:employeeUpgrade(employeeId) local info = self.infoList[employeeId] info:upgrade() end -- 购买皮肤 function EmployeMgr:employeeSkinPurchase(employeeId) local info = self.infoList[employeeId] info:skinPurchase() end -- 安装皮肤 function EmployeMgr:employeeSkinInstall(employeeId) local info = self.infoList[employeeId] info:skinInstall() end -- 安装皮肤 function EmployeMgr:employeeSkinUninstall(employeeId) local info = self.infoList[employeeId] info:skinUninstall() end -- 根据员工类型获取员工(同一类型Id的员工只有一个) function EmployeMgr:getEmployeesByType(employeeType) for _, employee in ipairs(self.employees) do if employee.employeCfgId == employeeType then return employee end end return nil end -- 是否有服务员 function EmployeMgr:hasWaiterEmploye() return self:getEmployeesByType(EmployeConst.Type.Waiter) ~= nil end -- 是否有厨师 function EmployeMgr:hasChefEmploye() return self:getEmployeesByType(EmployeConst.Type.Chef) ~= nil end -- 获取已购买员工列表 function EmployeMgr:getPurchasedEmployees() local purchasedEmployees = {} for _, employee in ipairs(self.employees) do local employeeInfo = self:getEmployeeInfo() if employeeInfo:isPurchased() then table.insert(purchasedEmployees, employee) end end return purchasedEmployees end -- 启动服务员工作 function EmployeMgr:startWaiterEmployeWork() local waiter = self:getEmployeesByType(EmployeConst.Type.Waiter) if not waiter then printWarn(LOGTAG, "没有服务员可以工作") return end waiter:action() end -- 启动厨师工作 function EmployeMgr:startChefEmployeWork() local chef = self:getEmployeesByType(EmployeConst.Type.Chef) if not chef then return end if chef:getState() == EmployeConst.State.Busy then return end chef:action() end -- 启动助理工作 function EmployeMgr:startAssistantEmployeWork() local assistant = self:getEmployeesByType(EmployeConst.Type.Assistant) if not assistant then return end assistant:action() end -- 启动歌手工作 function EmployeMgr:startSingerEmployeWork() local singer = self:getEmployeesByType(EmployeConst.Type.Singer) if not singer then return end singer:action() end -- 启动烤吧厨师工作 function EmployeMgr:startBarbecueChefEmployeWork() local barbecueChef = self:getEmployeesByType(EmployeConst.Type.BarbecueChef) if not barbecueChef then return end barbecueChef:action() end -- 启动保安工作 function EmployeMgr:startSecurityEmployeWork() local security = self:getEmployeesByType(EmployeConst.Type.Security) if not security then return end -- 判断保安是不是已经在工作了 if security:getState() == EmployeConst.State.Busy then return end -- 如果保安处于空闲状态,开始工作 if security:getState() == EmployeConst.State.Idle then -- 开始工作 security:action() end end return EmployeMgr RoleMsgdefGlobal("RoleMsg") ---@enum RoleMsg RoleMsg = { StartPathComplete = "RoleMsg.StartPathComplete", -- 查找路径完成 MovePathComplete = "RoleMsg.MovePathComplete", -- 移动路径完成 }FishingUserData*-- local FishingUserData = defClass("FishingUserData") function FishingUserData:ctor() self:init() end -- 初始化数据 function FishingUserData:init() -- 捕鱼玩法当日游戏次数 self.fishing_game_count_today = 0 -- 捕鱼玩法当日复活次数 self.fishing_revive_count_today = 0 -- 捕鱼玩法获取道具次数 self.fishing_get_prop_count = 0 -- 捕鱼玩法道具数量 self.fishing_prop_count = {} -- { [propType] = count } -- 捕鱼玩法鱼的数量 self.fishing_fish_count = {} -- { [fishId] = count } -- 捕鱼玩法摊主状态 self.fishing_vendor_state = {} -- { [vendorId] = state } state 1:未解锁, 2:已解锁, 3:已来访 -- 池塘贩卖机上次更新时间 self.pond_vending_last_update_time = 0 -- 用于记录摊主的更新时间 -- 池塘贩卖机当前贩卖物品 self.pond_vending_current_item = {} -- 用于记录当前贩卖的物品 { [fishId], count} -- 池塘贩卖机下次贩卖物品 self.pond_vending_next_item = {} -- 用于记录下次贩卖的物品 { [fishId], count} end -- 重置数据 function FishingUserData:resetData() self.fishing_prop_count = { [FishingConst.PropType.Dizzy] = 1, -- 眩晕 [FishingConst.PropType.Energy] = 1, -- 能量 [FishingConst.PropType.Shield] = 1, -- 护盾 } -- 重置道具数量 self:save() end -- 获取玩家数据 function FishingUserData:load() if UserDataMgr.isNewPlayer then -- 新玩家,初始化数据 self:resetData() else -- 加载已有数据 self:loadFromLocal() end self:onLoadComplete() end -- 加载完成回调 function FishingUserData:onLoadComplete() if UserDataMgr.isFirstLoginToday then -- 如果是今天第一次登录,重置当日游戏次数 self:setFishingGameCountToday(0, true) -- 重置当日复活次数 self:setFishingReviveCountToday(0, true) end self:save() end -- 从本地加载玩家数据 function FishingUserData:loadFromLocal() self.fishing_game_count_today = PlayerPrefsMgr:getInt(StrogeKeyDef.fishing_game_count_today, 0) self.fishing_revive_count_today = PlayerPrefsMgr:getInt(StrogeKeyDef.fishing_revive_count_today, 0) self.fishing_get_prop_count = PlayerPrefsMgr:getInt(StrogeKeyDef.fishing_get_prop_count, 0) self.fishing_prop_count = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.fishing_prop_count, "{}"))) self.fishing_fish_count = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.fishing_fish_count, "{}"))) self.fishing_vendor_state = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.fishing_vendor_state, "{}"))) self.pond_vending_last_update_time = PlayerPrefsMgr:getInt(StrogeKeyDef.pond_vending_last_update_time, 0) self.pond_vending_current_item = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.pond_vending_current_item, "{}"))) self.pond_vending_next_item = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.pond_vending_next_item, "{}"))) end -- 保存玩家数据 function FishingUserData:save() -- 保存玩家数据到本地 self:saveToLocal() end -- 保存玩家数据到本地 function FishingUserData:saveToLocal() PlayerPrefsMgr:setInt(StrogeKeyDef.fishing_game_count_today, self.fishing_game_count_today) PlayerPrefsMgr:setInt(StrogeKeyDef.fishing_revive_count_today, self.fishing_revive_count_today) PlayerPrefsMgr:setInt(StrogeKeyDef.fishing_get_prop_count, self.fishing_get_prop_count) PlayerPrefsMgr:setString(StrogeKeyDef.fishing_prop_count, json.encode(PlayerPrefsMgr:idToString(self.fishing_prop_count))) PlayerPrefsMgr:setString(StrogeKeyDef.fishing_fish_count, json.encode(PlayerPrefsMgr:idToString(self.fishing_fish_count))) PlayerPrefsMgr:setString(StrogeKeyDef.fishing_vendor_state, json.encode(PlayerPrefsMgr:idToString(self.fishing_vendor_state))) PlayerPrefsMgr:setInt(StrogeKeyDef.pond_vending_last_update_time, self.pond_vending_last_update_time) PlayerPrefsMgr:setString(StrogeKeyDef.pond_vending_current_item, json.encode(PlayerPrefsMgr:idToString(self.pond_vending_current_item))) PlayerPrefsMgr:setString(StrogeKeyDef.pond_vending_next_item, json.encode(PlayerPrefsMgr:idToString(self.pond_vending_next_item))) PlayerPrefsMgr:save() end -- 获取捕鱼玩法当日游戏次数 function FishingUserData:getFishingGameCountToday() return self.fishing_game_count_today or 0 end -- 设置捕鱼玩法当日游戏次数 function FishingUserData:setFishingGameCountToday(count, refuseSave) self.fishing_game_count_today = count if not refuseSave then self:save() end return self.fishing_game_count_today end -- 增加捕鱼玩法当日游戏次数 function FishingUserData:addFishingGameCountToday(count, refuseSave) local curr = self:getFishingGameCountToday() + count self:setFishingGameCountToday(curr, refuseSave) return curr end -- 获取捕鱼玩法当日复活次数 function FishingUserData:getFishingReviveCountToday() return self.fishing_revive_count_today or 0 end -- 设置捕鱼玩法当日复活次数 function FishingUserData:setFishingReviveCountToday(count, refuseSave) self.fishing_revive_count_today = count if not refuseSave then self:save() end return self.fishing_revive_count_today end -- 增加捕鱼玩法当日复活次数 function FishingUserData:addFishingReviveCountToday(count, refuseSave) local curr = self:getFishingReviveCountToday() + count self:setFishingReviveCountToday(curr, refuseSave) return curr end -- 获取捕鱼玩法获取道具次数 function FishingUserData:getFishingGetPropCount() return self.fishing_get_prop_count end -- 设置捕鱼玩法获取道具次数 function FishingUserData:setFishingGetPropCount(count, refuseSave) self.fishing_get_prop_count = count if not refuseSave then self:save() end return self.fishing_get_prop_count end -- 增加捕鱼玩法获取道具次数 function FishingUserData:addFishingGetPropCount(count, refuseSave) local curr = self:getFishingGetPropCount() + count self:setFishingGetPropCount(curr, refuseSave) return curr end -- 获取捕鱼玩法道具数量 function FishingUserData:getFishingPropCount(propType) return self.fishing_prop_count[propType] or 0 end -- 设置捕鱼玩法道具数量 function FishingUserData:setFishingPropCount(propType, count, refuseSave) self.fishing_prop_count[propType] = count if not refuseSave then self:save() end return self.fishing_prop_count[propType] end -- 增加捕鱼玩法道具数量 function FishingUserData:addFishingPropCount(propType, count, refuseSave) local curr = self:getFishingPropCount(propType) + count self:setFishingPropCount(propType, curr, refuseSave) return curr end -- 减少捕鱼玩法道具数量 function FishingUserData:reduceFishingPropCount(propType, count, refuseSave) local curr = self:getFishingPropCount(propType) - count if curr < 0 then curr = 0 end self:setFishingPropCount(propType, curr, refuseSave) return curr end -- 获取捕鱼玩法鱼的数量 function FishingUserData:getFishingFishCount(fishId) return self.fishing_fish_count[fishId] or 0 end -- 设置捕鱼玩法鱼的数量 function FishingUserData:setFishingFishCount(fishId, count, refuseSave) self.fishing_fish_count[fishId] = count if not refuseSave then self:save() end return self.fishing_fish_count[fishId] end -- 增加捕鱼玩法鱼的数量 function FishingUserData:addFishingFishCount(fishId, count, refuseSave) local curr = self:getFishingFishCount(fishId) + count self:setFishingFishCount(fishId, curr, refuseSave) return curr end -- 减少捕鱼玩法鱼的数量 function FishingUserData:reduceFishingFishCount(fishId, count, refuseSave) local curr = self:getFishingFishCount(fishId) - count if curr < 0 then curr = 0 end self:setFishingFishCount(fishId, curr, refuseSave) return curr end -- 获取捕鱼玩法摊主状态 function FishingUserData:getFishingVendorState(vendorId) return self.fishing_vendor_state[vendorId] or FishingConst.VendorState.Locked end -- 设置捕鱼玩法摊主状态 function FishingUserData:setFishingVendorState(vendorId, state, refuseSave) self.fishing_vendor_state[vendorId] = state if not refuseSave then self:save() end return self.fishing_vendor_state[vendorId] end -- 获取贩卖机更新时间 function FishingUserData:getPondVendingLastUpdateTime() return self.pond_vending_last_update_time or 0 end -- 设置贩卖机更新时间 function FishingUserData:setPondVendingLastUpdateTime(time, refuseSave) self.pond_vending_last_update_time = time if not refuseSave then self:save() end return self.pond_vending_last_update_time end -- 获取当前贩卖物品 function FishingUserData:getPondVendingCurrentItem() return self.pond_vending_current_item end -- 设置当前贩卖物品 function FishingUserData:setPondVendingCurrentItem(itemDic, refuseSave) self.pond_vending_current_item = itemDic if not refuseSave then self:save() end return self.pond_vending_current_item end -- 获取当前贩卖物品数量 function FishingUserData:getPondVendingCurrentItemCount(itemId) return self.pond_vending_current_item[itemId] or 0 end -- 减少当前贩卖物品数量 function FishingUserData:reducePondVendingCurrentItem(itemId, count, refuseSave) local curr = self.pond_vending_current_item[itemId] - count if curr < 0 then curr = 0 end return self:setPondVendingCurrentItem(itemId, curr, refuseSave) end -- 获取下次贩卖物品 function FishingUserData:getPondVendingNextItem() return self.pond_vending_next_item end -- 设置下次贩卖物品 function FishingUserData:setPondVendingNextItem(itemIdDic, refuseSave) self.pond_vending_next_item = itemIdDic if not refuseSave then self:save() end return self.pond_vending_next_item end -- 将字典的键从数字转换为字符串 function FishingUserData:idToString(dic) local put = {} for i, v in pairs(dic) do put[tostring(i)] = v end return put end -- 将字典的键从字符串转换为数字 function FishingUserData:idToNumber(dic) local put = {} for i, v in pairs(dic) do put[tonumber(i)] = v end return put end return FishingUserDatamainrequire("modules/currency/CurrencyConst") require("modules/currency/CurrencyMgr") require("modules/currency/CurrencyUserData") require("modules/currency/coin/main") CustomerParamCfgParseylocal CustomerParamCfgData = require("data/config/customerParamCfg") local CustomerParamCfgParse = defClassStatic("CustomerParamCfgParse") function CustomerParamCfgParse:init() end function CustomerParamCfgParse:getData() return CustomerParamCfgData end function CustomerParamCfgParse:getCustomerParamCfg(customerParamCfgId) for _, v in pairs(CustomerParamCfgData) do if v.id == customerParamCfgId then return v end end printError("CustomerParamCfgParse", string.format("没有找到顾客参数配置[%s]", customerParamCfgId)) return nil end CustomerParamCfgParse:init()mainHrequire("modules/login/LoginUI") require("modules/login/LoginScene") currencyreslinkreturn { --BASIC --ASSET coin_desk = {"Assets/AssetsPackage/Res/modules/common/prefab/currency/coin_desk.prefab", 0, 0}, coin_spine = {"Assets/AssetsPackage/Res/modules/common/effects/coin/prefab/coin_spine.prefab", 0, 0}, } main-- 管理 require("modules/unlock/mgr/UnlockMgr") -- 常量 require("modules/unlock/const/UnlockConst") -- 组件 require("modules/unlock/component/UnlockHintComponent")mainrequire("modules/building/fishingcenter/stall/StallConst") require("modules/building/fishingcenter/stall/StallDesk") require("modules/building/fishingcenter/stall/StallDeskMgr") require("modules/building/fishingcenter/stall/StallInfo")UIDialogo--[[ 尺寸:1240*640,根据文字长度按照此比例等比缩 author:{zhangpeng} time:2023-09-17 10:40:10 ]] local UIDialog, super = defClass("UIDialog", UILayer) local TMPUGUI = CS.TMPro.TextMeshProUGUI UIDialog.isPopup = true local prefab_path = "Assets/AssetsPackage/Res/common/ui/dailog/prefab/common_dailog.prefab" local LOG_TAG = "UIDialog" function UIDialog:ctor(style) super.ctor(self) self.uiStyle = style end function UIDialog:onLoad() do return end self:setPriority(UIOrderDef.UI_ORDER.SYS_PANEL) self.uiStyle = self.uiStyle or UITypeEnums.DialogType.Common local prefab = UITypeEnums.DialogPrefabs[self.uiStyle] local ui = CS.UnityEngine.GameObject.Instantiate(Res.loadAsset(prefab)) self:addChild(ui) -- close btn util.ugui.addClickEvent( ui:Seek("closebtn"), function() print("dialog click close") self:close() end ) self.ui = ui self:storeBtnsPosX() -- self:useTweenOnOpen(ui) local btnOK = self.ui:Seek("BtnOK") local btnCancel = self.ui:Seek("BtnCancel") btnOK:SetActive(false) btnCancel:SetActive(false) self:showMask() -- self:hideEar() end -- 单按钮弹窗 function UIDialog:showSingleBtn(text, cb) do return end self:storeBtnsPosX() local btnOK = self.ui:Seek("BtnOK") local btnCancel = self.ui:Seek("BtnCancel") self.btnOK = btnOK self.btnCancel = btnCancel self.btnOK.cb = cb local text_ui if self.btnOK:Seek("Text"):GetComponent(typeof(UGUI.Text)) then text_ui = self.btnOK:Seek("Text"):GetComponent(typeof(UGUI.Text)) elseif self.btnOK:Seek("Text")[TMPUGUI] then text_ui = self.btnOK:Seek("Text")[TMPUGUI] end text_ui.text = text util.ugui.addButtonClickEvent( self.btnOK, function() printInfo(LOG_TAG, "点击单按钮弹窗的确定") if self.btnOK.cb then self.btnOK.cb(self) end end ) btnCancel:SetActive(false) btnOK:SetActive(true) btnOK:SetPositionX((self.btnOKPosX + self.btnCancelPosX) * 0.5) return self end function UIDialog:showDoubleBtns(text1, text2, cb1, cb2) self:storeBtnsPosX() local btnOK = self.ui:Seek("BtnOK") btnOK:SetActive(true) btnOK:SetPositionX(self.btnOKPosX) btnOK:Seek("Text"):GetComponent(typeof(UGUI.Text)).text = text1 util.ugui.addButtonClickEvent( btnOK, function() if cb1 then cb1(self) end end ) local btnCancel = self.ui:Seek("BtnCancel") btnCancel:SetActive(true) btnCancel:SetPositionX(self.btnCancelPosX) btnCancel:Seek("Text"):GetComponent(typeof(UGUI.Text)).text = text2 util.ugui.addButtonClickEvent( btnCancel, function() if cb2 then cb2(self) end end ) return self end -- 设置确认按钮文本 function UIDialog:setOkBtnText(text) local text_ui if self.btnOK:Seek("Text"):GetComponent(typeof(UGUI.Text)) then text_ui = self.btnOK:Seek("Text"):GetComponent(typeof(UGUI.Text)) elseif self.btnOK:Seek("Text")[TMPUGUI] then text_ui = self.btnOK:Seek("Text")[TMPUGUI] end text_ui.text = text end -- 设置弹窗文本 function UIDialog:setContentText(text) do return end self.ui:Seek("ContentText"):GetComponent(typeof(UGUI.Text)).text = text self.content = text return self end -- 隐藏关闭按钮 function UIDialog:hideCloseBtn() do return end self.ui:Seek("closebtn"):SetActive(false) return self end -- 隐藏确认和取消按钮 function UIDialog:hideDoubleBtn() do return end self.ui:Seek("BtnOK"):SetActive(false) self.ui:Seek("BtnCancel"):SetActive(false) return self end function UIDialog:storeBtnsPosX() if self.ui then self.btnOKPosX = self.btnOKPosX or self.ui:Seek("BtnOK"):GetPositionX() self.btnCancelPosX = self.btnCancelPosX or self.ui:Seek("BtnCancel"):GetPositionX() end end function UIDialog:playSound(sound, cb) self.ui:PlaySound(sound, cb) return self end function UIDialog:hideEar() local ears = self.ui:SearchPattern("ear_\\d") for k,v in pairs(ears) do v:SetActive(false) end end -- 设置弹窗类型 function UIDialog:setStyle(style) self.uiStylePrefab = UITypeEnums.DialogPrefabs[style] -- if style == UITypeEnums.DialogType.Common then -- elseif style == UITypeEnums.DialogType.SkinPartAdUI then -- elseif style == UITypeEnums.DialogType.SkinPartUnlock then -- end end return UIDialogBuildingCfgParse--[[ luaide 模板位置位于 Template/FunTemplate/NewFileTemplate.lua 其中 Template 为配置路径 与luaide.luaTemplatesDir luaide.luaTemplatesDir 配置 https://www.showdoc.cc/web/#/luaide?page_id=713062580213505 author:{author} time:2025-05-14 10:22:25 ]] local BuildingCfgData = require("data/config/buildingCfg") local BuildingCfgParse = defClassStatic("BuildingCfgParse") local LOGTAG = "BuildingCfgParse" local building_list = {} local function initAllIds() for k, v in pairs(BuildingCfgData) do table.insert(building_list, v.buildingId) end end function BuildingCfgParse:init() initAllIds() end function BuildingCfgParse:getData() return BuildingCfgData end function BuildingCfgParse:getBuildingCfg(id) for _, v in pairs(BuildingCfgData) do if v.uid == id then return v end end return nil end -- 获取所有建筑配置 function BuildingCfgParse:getAllBuildingCfgs() return BuildingCfgData end -- 通过id获取建筑配置数据 function BuildingCfgParse:getBuildingCfgByIds(idList) if type(idList) == "number" then return { self:getBuildingCfg(idList) } end local list = {} for _, id in ipairs(idList) do local cfg = self:getBuildingCfg(id) if cfg then table.insert(list, cfg) else printError(LOGTAG, string.format("没有找到建筑配置[%s]", id)) end end return list end -- 获取同一类主题的建筑 function BuildingCfgParse:getThemeBuildings(mapId, themeId) local themeBuildings = {} for k,v in pairs(BuildingCfgData) do if (v.mapId == mapId) and (v.themeId == themeId) then table.insert(themeBuildings, v) end end return themeBuildings end -- 主题数据组织成tableview数据 function BuildingCfgParse:getThemeTableViewData(themeBuildings) local tableViewData = {} local rowIndex = 1 -- 每3个数据为一行 local rowData = {} for i, v in ipairs(themeBuildings) do table.insert(rowData, v) if i % 3 == 0 then local oneRow = { value = rowData, rowIndex = rowIndex } table.insert(tableViewData, oneRow) rowIndex = rowIndex + 1 rowData = {} end end -- 将分组后的数据转换为tableView数据 for _, buildings in pairs(rowData) do local oneRow = { value = buildings, rowIndex = rowIndex } table.insert(tableViewData, oneRow) rowIndex = rowIndex + 1 end self.tableViewData = tableViewData return self.tableViewData end -- 获取建筑列表里tableview需要的数据结构 -- @param buildingCategoryId 建筑分类id(餐厅/烤吧) function BuildingCfgParse:getDataForTableView(buildingCategoryId) local name = BuildingConst.buildingCategoryName[buildingCategoryId] printInfo(LOGTAG, "生成:[%s]的tableivew数据", name) local tableViewData = {} local rowIndex = 1 local needFilter = false if buildingCategoryId then needFilter = true end -- 按建筑类型分组 local buildingGroups = {} for i, v in ipairs(BuildingCfgData) do local category_id = BuildingTypesCfgParse:getCategoryIdByBuildingTypeId(v.buildingType) local insert = false if buildingCategoryId then if category_id == buildingCategoryId then insert = true end else insert = false end if insert then if not buildingGroups[v.buildingType] then buildingGroups[v.buildingType] = {} end table.insert(buildingGroups[v.buildingType], v) end end -- 将分组后的数据转换为tableView数据 local types = BuildingTypesCfgParse:getBuildingTypesByScene(buildingCategoryId) for i, v in pairs(types) do if v.scene == buildingCategoryId then local list = buildingGroups[v.typeId] if list then local oneRow = { value = buildingGroups[v.typeId], rowIndex = rowIndex } table.insert(tableViewData, oneRow) rowIndex = rowIndex + 1 end end end ---- 将分组后的数据转换为tableView数据 --for buildingType, buildings in pairs(buildingGroups) do -- local oneRow = { -- value = buildings, -- rowIndex = rowIndex -- } -- table.insert(tableViewData, oneRow) -- rowIndex = rowIndex + 1 --end self.tableViewData = tableViewData return self.tableViewData end BuildingCfgParse:init()main2require("modules/setting/data/SettingUserData")YooAssetLoaderExample-- YooAssetLoader Lua使用示例 -- 这个脚本演示了如何在Lua中使用YooAssetLoader加载各种资源 local YooAssetLoaderExample = {} -- 获取YooAssetLoader实例 local loader = CS.YooAssetLoader.Instance function YooAssetLoaderExample.TestLoadSprite() print("=== 测试加载Sprite ===") -- 同步加载Sprite local sprite = loader:LoadSprite("ui/icon/player_icon") if sprite then print("同步加载Sprite成功: " .. sprite.name) else print("同步加载Sprite失败") end -- 异步加载Sprite loader:LoadSpriteAsyncLua("ui/icon/enemy_icon", function(sprite) if sprite then print("异步加载Sprite成功: " .. sprite.name) else print("异步加载Sprite失败") end end) end function YooAssetLoaderExample.TestLoadPrefab() print("=== 测试加载Prefab ===") -- 同步加载Prefab local prefab = loader:LoadPrefab("ui/panels/LoginPanel") if prefab then print("同步加载Prefab成功: " .. prefab.name) -- 实例化Prefab local instance = CS.UnityEngine.Object.Instantiate(prefab) print("实例化Prefab成功: " .. instance.name) else print("同步加载Prefab失败") end -- 异步加载并实例化Prefab loader:LoadPrefabAsyncLua("ui/panels/SettingPanel", function(prefab) if prefab then print("异步加载Prefab成功: " .. prefab.name) local instance = CS.UnityEngine.Object.Instantiate(prefab) print("异步实例化Prefab成功: " .. instance.name) else print("异步加载Prefab失败") end end) end function YooAssetLoaderExample.TestLoadLuaScript() print("=== 测试加载Lua脚本 ===") -- 同步加载Lua脚本 local luaCode = loader:LoadLuaScript("main/config") if luaCode then print("同步加载Lua脚本成功,长度: " .. string.len(luaCode)) -- 可以执行加载的Lua代码 -- local func = load(luaCode) -- if func then func() end else print("同步加载Lua脚本失败") end -- 异步加载Lua脚本 loader:LoadLuaScriptAsyncLua("main/utils", function(luaCode) if luaCode then print("异步加载Lua脚本成功,长度: " .. string.len(luaCode)) else print("异步加载Lua脚本失败") end end) end function YooAssetLoaderExample.TestLoadTextAsset() print("=== 测试加载文本资源 ===") -- 同步加载文本内容 local textContent = loader:LoadText("config/game_config.json") if textContent then print("同步加载文本成功,长度: " .. string.len(textContent)) -- 可以解析JSON等 -- local config = CS.LitJson.JsonMapper.ToObject(textContent) else print("同步加载文本失败") end -- 异步加载文本内容 loader:LoadTextAsyncLua("config/localization.txt", function(textContent) if textContent then print("异步加载文本成功,长度: " .. string.len(textContent)) else print("异步加载文本失败") end end) end function YooAssetLoaderExample.TestLoadAudioClip() print("=== 测试加载音频 ===") -- 同步加载音频 local audioClip = loader:LoadAudioClip("audio/bgm/main_theme") if audioClip then print("同步加载音频成功: " .. audioClip.name .. ", 长度: " .. audioClip.length .. "秒") else print("同步加载音频失败") end -- 异步加载音频 loader:LoadAudioClipAsyncLua("audio/sfx/button_click", function(audioClip) if audioClip then print("异步加载音频成功: " .. audioClip.name .. ", 长度: " .. audioClip.length .. "秒") else print("异步加载音频失败") end end) end function YooAssetLoaderExample.TestLoadScene() print("=== 测试加载场景 ===") -- 异步加载场景 loader:LoadSceneAsyncLua("scenes/GameScene", function(success) if success then print("异步加载场景成功") else print("异步加载场景失败") end end) end function YooAssetLoaderExample.TestResourceManagement() print("=== 测试资源管理 ===") -- 检查资源是否存在 local exists = loader:CheckAssetExist("ui/icon/player_icon") print("资源是否存在: " .. tostring(exists)) -- 获取已加载资源数量 local assetCount = loader:GetLoadedAssetCount() local sceneCount = loader:GetLoadedSceneCount() print("已加载资源数量: " .. assetCount .. ", 场景数量: " .. sceneCount) -- 释放特定资源 loader:ReleaseAsset("ui/icon/player_icon") print("释放资源完成") -- 获取释放后的资源数量 local newAssetCount = loader:GetLoadedAssetCount() print("释放后资源数量: " .. newAssetCount) end function YooAssetLoaderExample.TestPreloadAssets() print("=== 测试预加载资源 ===") -- 定义要预加载的资源列表 local assetList = { "ui/icon/player_icon", "ui/icon/enemy_icon", "ui/panels/LoginPanel", "audio/bgm/main_theme" } -- 转换为C#数组 local csArray = CS.System.Array.CreateInstance(CS.System.String, #assetList) for i = 1, #assetList do csArray[i-1] = assetList[i] end -- 执行预加载 loader:PreloadAssets(csArray, function(success) if success then print("预加载全部成功") else print("预加载部分失败") end local loadedCount = loader:GetLoadedAssetCount() print("预加载完成,当前已加载资源数量: " .. loadedCount) end) end -- 运行所有测试 function YooAssetLoaderExample.RunAllTests() print("开始YooAssetLoader测试...") -- 注意:在实际使用中,需要确保YooAsset已经初始化完成 -- 可以通过检查YooAssets.GetPackage("bundles")是否为空来确认 YooAssetLoaderExample.TestLoadSprite() YooAssetLoaderExample.TestLoadPrefab() YooAssetLoaderExample.TestLoadLuaScript() YooAssetLoaderExample.TestLoadTextAsset() YooAssetLoaderExample.TestLoadAudioClip() YooAssetLoaderExample.TestLoadScene() YooAssetLoaderExample.TestResourceManagement() YooAssetLoaderExample.TestPreloadAssets() print("YooAssetLoader测试完成!") end -- 简化的使用示例 function YooAssetLoaderExample.SimpleExample() print("=== 简化使用示例 ===") -- 1. 加载UI图标 local icon = loader:LoadSprite("ui/icon/player_icon") if icon then -- 可以将icon设置给UI Image组件 print("加载图标成功: " .. icon.name) end -- 2. 异步加载UI面板并实例化 loader:LoadPrefabAsyncLua("ui/panels/MainPanel", function(prefab) if prefab then local panelInstance = CS.UnityEngine.Object.Instantiate(prefab) -- 可以对panelInstance进行进一步操作 print("创建面板成功: " .. panelInstance.name) end end) -- 3. 加载配置文件 local configText = loader:LoadText("config/game_config.json") if configText then -- 解析配置 print("加载配置成功") end -- 4. 动态加载Lua模块 loader:LoadLuaScriptAsyncLua("modules/battle_system", function(luaCode) if luaCode then -- 执行Lua代码 local battleSystem = load(luaCode) if battleSystem then battleSystem() print("加载战斗系统模块成功") end end end) end return YooAssetLoaderExample SceneCfgList--[[ 场景配置 author:{zhangpeng} time:2022-05-14 18:05:42 ]] local SceneCfgList = defClassStatic("SceneCfgList") -- 场景id SceneCfgList.SceneIndexs = { Start = 1, Shop = 2, Login = 3, WorldMap = 4, --测试 Test = 99 } -- 场景配置 SceneCfgList.SCENES = { LoginScene = {"modules/login/LoginScene", "登录场景"}, MainScene = {"modules/mainscene/MainScene", "主场景"}, LobbyScene = {"modules/lobby/LobbyScene", "主场景"}, TestScene = {"modules/test/TestScene", "测试场景"}, } -- 当前加载的场景信息 SceneCfgList.currLoadedSceneInfo = nil SqliteModel  ---@class SqliteModel:BaseModel local SqliteModel = defClass("SqliteModel", BaseModel) function SqliteModel:ctor(table) self.table = table self.bindInfoList = self.table.bindInfoList or {} local list = self.table:getColumnList() for i, col in ipairs(list) do if not col.isMetadata then self[col.name] = col.defaultValue else self[col.name] = nil end end self:init() end function SqliteModel:setSqliteData(data) local list = self.table:getColumnList() for i, col in ipairs(list) do if not col.isMetadata then if col.type == "string" then self[col.name] = data[col.name] or self[col.name] else self[col.name] = math.tointeger(data[col.name]) or data[col.name] or self[col.name] end else self[col.name] = nil end end end function SqliteModel:setSqliteDataWithKeysAndValues(keyList, valueList) local dict = self.table:getColumnDict() for i, key in pairs(keyList) do if dict[key] and not dict[key].isMetadata then if dict[key].type == "string" then self[key] = valueList[i] or self[key] else self[key] = math.tointeger(valueList[i]) or valueList[i] or self[key] end else self[key] = nil end end end function SqliteModel:setKey(key) if type(key) ~= "table" then key = {key} end local list = self.table.primaryList for i, col in ipairs(list) do self[col.name] = key[i] end end function SqliteModel:getKey() local key = {} local list = self.table.primaryList for i, col in ipairs(list) do table.insert(key, self[col.name]) end if #key == 1 then key = key[1] end return key end function SqliteModel:getKeyStr() local key = self:getKey() return self.table:keyToStr(key) end function SqliteModel:save() self.table:set(self) end -- sync必定save function SqliteModel:sync() if not self.table.useSync then self:save() return end self.table:sync(self) end function SqliteModel:refresh() local key = self:getKey() local data = self.table:get(key) if not data then return end self:setSqliteData(data) end --#region 设置metadata,既业务无关的数据,不可读,设置后在写入数据库后会置为空,防止业务模块中的缓存数据污染metadata function SqliteModel:setLastUpdateTime(time) self.lastUpdateTime = time end function SqliteModel:getLastUpdateTime() local key = self:getKey() local data = self.table:genQuery():where(self.table:getKeyCondition(key)):getFirst() return data.lastUpdateTime end function SqliteModel:setLastUpdateVersion(version) self.lastUpdateVersion = version end function SqliteModel:getLastUpdateVersion() local key = self:getKey() local data = self.table:genQuery():where(self.table:getKeyCondition(key)):getFirst() return data.lastUpdateVersion end function SqliteModel:setSyncState(syncState) self.syncState = syncState end function SqliteModel:getSyncState() local key = self:getKey() local data = self.table:genQuery():where(self.table:getKeyCondition(key)):getFirst() return data.syncState end function SqliteModel:clearMetadata() local list = self.table:getColumnList() for i, col in ipairs(list) do if col.isMetadata then self[col.name] = nil end end end --#endregion return SqliteModel BoothOwnerDetailUI~-- 池塘 摊主详情界面 local BoothOwnerDetailUI, super = defClass("BoothOwnerDetailUI", UILayer) -- 构造函数 function BoothOwnerDetailUI:ctor() super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/customerui/customeruireslink") end -- 当页面加载 function BoothOwnerDetailUI:onLoad() self.ui = GameObject.Instantiate(self.R.booth_owner_detail_ui) self:addChild(self.ui) self:initUI() self:showUI() end -- 初始化UI function BoothOwnerDetailUI:initUI() self.booth_owner_detail = self.ui:Seek("booth_owner_detail") self.booth_owner_name = self.booth_owner_detail:Seek("booth_owner_name") self.booth_owner_desc = self.booth_owner_detail:Seek("booth_owner_desc") self.booth_owner_icon = self.booth_owner_detail:Seek("booth_owner_icon") self.close_btn = self.booth_owner_detail:Seek("close_btn") util.ugui.addClickEvent(self.close_btn, function() UILayerUtil:closeUIByName("BoothOwnerDetailUI") end) end -- 展示UI function BoothOwnerDetailUI:showUI() self.booth_owner_name.text = "摊主名称" self.booth_owner_desc.text = "摊主描述信息" self.booth_owner_icon.sprite = ResLoader.loadSprite("path/to/booth_owner_icon") -- 替换为实际图标路径 end function BoothOwnerDetailUI:initInfo() self.customer_img = self.ui:Seek("icon") self.customer_name = self.ui:Seek("name") self.customer_desc = self.ui:Seek("desc") end function BoothOwnerDetailUI:initTag() self.favorite_items = self.ui:Seek("favorite_items") self.visit_need_star = self.ui:Seek("visit_need_star") end function BoothOwnerDetailUI:initIncome() self.favorite_items = self.ui:Seek("favorite_items") self.visit_need_star = self.ui:Seek("visit_need_star") end function BoothOwnerDetailUI:showInfo(info) local isVisited = info:isVisited() or self.isNewCustomer if isVisited then --self.customer_img[UIImage].sprite = info:getIcon() self.customer_img[UIImage]:SetNativeSize() self.customer_name[TMProUGUI].text = info:getName() self.customer_desc[TMProUGUI].text = info:getDesc() end end return BoothOwnerDetailUI StallInfos--[[ 摊位数据信息单独管理 author:{zhangpeng} time:2025-07-04 20:14:17 ]] local StallInfo, super = defClass("StallInfo") function StallInfo:ctor(stallId) self.id = stallId self.buildingType = self.id self:init() end function StallInfo:init() self:initDynamicData() end function StallInfo:initDynamicData() self.state = 1 end -- 设置摊主配置数据 function StallInfo:setVendorCfgData(vendorId) self.vendorCfg = VendorCfgParse:getVendorCfg(vendorId) end -- 获取摊主配置数据 function StallInfo:getVendorCfgData() return self.vendorCfg end return StallInfo CookingConst--[[ 灶台常量 author:{zhangpeng} time:2025-05-16 15:40:01 ]] local CookingConst = defClassStatic("CookingConst") -- 默认灶台配置id CookingConst.cookingBenchIds = { BuildingConst.buildingType.cookingbench1, BuildingConst.buildingType.cookingbench2, BuildingConst.buildingType.cookingbench3, BuildingConst.buildingType.waterbar, } -- 做菜类的灶台id CookingConst.dishIds = { BuildingConst.buildingType.cookingbench1, BuildingConst.buildingType.cookingbench2, BuildingConst.buildingType.cookingbench3, } -- 做饮料类的灶台id CookingConst.drinkIds = { BuildingConst.buildingType.waterbar, } -- 灶台类型 CookingConst.CookingType = { dish = 1, -- 菜类 drink = 2, -- 饮料类 } -- 出餐类型对应名字 CookingConst.CookingTypeName = { [CookingConst.CookingType.dish] = "菜类", [CookingConst.CookingType.drink] = "饮料类", } -- 灶台/饮料机出餐口数量 CookingConst.CookingBenchStoveCount = { [BuildingConst.buildingType.cookingbench1] = 1, [BuildingConst.buildingType.cookingbench2] = 1, [BuildingConst.buildingType.cookingbench3] = 1, [BuildingConst.buildingType.waterbar] = 1, } CookingConst.State = { -- 空闲 idle = 1, -- 占用 used = 2, } return CookingConstCuisineBubbleMgry--[[ 角色/灶台头顶气泡里的菜品图标创建等 author:{zhangpeng} time:2025-05-17 12:06:21 ]] local CuisineBubbleMgr = defClassStatic("CuisineBubbleMgr") local LOGTAG = "CuisineBubbleMgr" function CuisineBubbleMgr:init() end -- 创建菜品图标气泡 function CuisineBubbleMgr:createBubble(cuisineCfgId, parent, parentType) -- 确保参数存在 if not cuisineCfgId then printError(LOGTAG, "创建菜品气泡失败:菜品ID为空") return nil end -- 确保parent参数存在 if not parent then printError(LOGTAG, "创建菜品气泡失败:父节点为空") return nil end -- 添加父节点名称检查 local parentName = "未知" if parent.name then parentName = parent.name end printInfo(LOGTAG, string.format("创建菜品气泡,菜品ID[%s],父节点[%s],显示类型名[%s]", cuisineCfgId, parentName, CuisineConst.BubbleShowParentTypeName[parentType])) -- 创建气泡 local bubble = CuisineBubble.new(cuisineCfgId, parent, parentType) return bubble end return CuisineBubbleMgr WorldMapUI--[[ 世界地图界面 author:zhengnan time:2025-06-03 10:23:10 ]] local WorldMapUI = defClass("WorldMapUI", UILayer) local LOGTAG = "WorldMapUI" function WorldMapUI:ctor() self.super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/worldmapui/worldmapuireslink") end function WorldMapUI:onLoad() self.ui = GameObject.Instantiate(self.R.world_map_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() end function WorldMapUI:initUI() printInfo(LOGTAG, "initUI") -- close button self.close_btn = self.ui:Seek("btn_close") util.ugui.addButtonClickEvent(self.close_btn, function () printInfo(LOGTAG, "close button clicked") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) -- 餐厅 self.restaurant_btn = self.ui:Seek("btn_restaurant") util.ugui.addButtonClickEvent(self.restaurant_btn, function () printInfo(LOGTAG, "进入餐厅") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:switchMap(MapsConst.MapType.restaurant) end) -- 音乐烤吧 self.music_bar_btn = self.ui:Seek("btn_bbq") self.music_bar_lock = self.ui:Seek("btn_bbq_lock") local bbqNeedStar = SceneCfgParse:getSceneCfgById(MapsConst.MapType.music_bar).unlockStarNum self.music_bar_lock:SetActive(bbqNeedStar > CurrencyMgr:getStar()) util.ugui.addButtonClickEvent(self.music_bar_btn, function () printInfo(LOGTAG, "进入音乐烤吧") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) if bbqNeedStar > CurrencyMgr:getStar() then PopUpUI.new(string.format("人气必须达到%s", bbqNeedStar)):show():showMask():enableCloseWhenClickMask() printError(LOGTAG, "音乐烤吧未解锁,星星不足") return end self:switchMap(MapsConst.MapType.music_bar) end) -- 钓鱼岛 self.fishing_btn = self.ui:Seek("btn_fishing") self.fishing_lock = self.ui:Seek("btn_fishing_lock") local fishingNeedStar = SceneCfgParse:getSceneCfgById(MapsConst.MapType.fishing_island).unlockStarNum self.fishing_lock:SetActive(fishingNeedStar > CurrencyMgr:getStar()) util.ugui.addButtonClickEvent(self.fishing_btn, function () printInfo(LOGTAG, "进入钓鱼岛") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) if fishingNeedStar > CurrencyMgr:getStar() then PopUpUI.new(string.format("人气必须达到%s", fishingNeedStar)):show():showMask():enableCloseWhenClickMask() printError(LOGTAG, "钓鱼岛未解锁,星星不足") return end self:switchMap(MapsConst.MapType.fishing_island) end) -- 小屋 self.house_btn = self.ui:Seek("btn_house") self.house_lock = self.ui:Seek("btn_house_lock") local houseNeedStar = SceneCfgParse:getSceneCfgById(MapsConst.MapType.house).unlockStarNum self.house_lock:SetActive(houseNeedStar > CurrencyMgr:getStar()) util.ugui.addButtonClickEvent(self.house_btn, function () printInfo(LOGTAG, "进入小屋") AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) if houseNeedStar > CurrencyMgr:getStar() then PopUpUI.new(string.format("人气必须达到%s", houseNeedStar)):show():showMask():enableCloseWhenClickMask() printError(LOGTAG, "小屋未解锁,星星不足") return end self:switchMap(MapsConst.MapType.house) end) end -- 切换到不同地图 function WorldMapUI:switchMap(mapId) MapsMgr:switchMap(mapId) self:close() end return WorldMapUI BuyCommonCfgParse--[[ 购买通用配置解析 author:{zhangpeng} time:2025-05-20 16:17:24 ]] local BuyCommonCfgData = require("data/config/buyCommonCfg") local BuyCommonCfgParse = defClassStatic("BuyCommonCfgParse") local LOGTAG = "BuyCommonCfgParse" local buyCommon_list = {} local function initAllIds() for k, v in pairs(BuyCommonCfgData) do table.insert(buyCommon_list, v.id) end end function BuyCommonCfgParse:init() initAllIds() end function BuyCommonCfgParse:getBuyCommonCfg(id) for k,v in pairs(BuyCommonCfgData) do if v.id == id then return v end end printError(LOGTAG, string.format("没有找到购买通用配置[%s]", id)) return nil end -- 获取购买价格 function BuyCommonCfgParse:getPrice(id) local cfg = self:getBuyCommonCfg(id) if cfg.price == -1 then return 0 else return cfg.price end end BuyCommonCfgParse:init() uidialogreslinkreturn { --BASIC --ASSET dialog_box_simple = {"Assets/AssetsPackage/Res/modules/common/ui/dialog_box/dialog_box_simple_ui.prefab", 0, 0}, } EmployeLevelUpCfgParse---[[ 员工升级配置解析 author:{zhangpeng} time:2025-05-21 10:06:56 ]] local EmployeLevelUpCfg = require("data/config/employeLevelUpCfg") local EmployeLevelUpCfgParse = defClassStatic("EmployeLevelUpCfgParse") local LOG_TAG = "EmployeLevelUpCfgParse" local employe_level_up_list = {} local function initAllIds() for k, v in pairs(EmployeLevelUpCfg) do table.insert(employe_level_up_list, v.id) end end -- init function EmployeLevelUpCfgParse:init() initAllIds() end -- 根据员工升级配置id取一个员工升级配置的数据 function EmployeLevelUpCfgParse:getEmployeLevelUpCfg(employeLevelUpId) for k, v in pairs(EmployeLevelUpCfg) do if v.id == employeLevelUpId then return v end end end EmployeLevelUpCfgParse:init() taskuireslinkreturn { --BASIC --ASSET cell_daily = {"Assets/AssetsPackage/Res/modules/ui/task/prefabs/cell_daily.prefab", 0, 0}, task_ui_main = {"Assets/AssetsPackage/Res/modules/ui/task/prefabs/task_ui_main.prefab", 0, 0}, cell_ach = {"Assets/AssetsPackage/Res/modules/ui/task/prefabs/cell_ach.prefab", 0, 0}, task_list_ui = {"Assets/AssetsPackage/Res/modules/ui/task/prefabs/task_list_ui.prefab", 0, 0}, task_boot_detail_ui = {"Assets/AssetsPackage/Res/modules/ui/task/prefabs/task_boot_detail_ui.prefab", 0, 0}, } mainorequire("common/core/db/DBMgr") -- sqlite -- webgl不支持sqlite,不加载相关文件 if not Device.isWebGL() then require("common/core/db/dbsql/BaseModel") require("common/core/db/dbsql/SqliteModel") require("common/core/db/dbsql/SqliteUtil") require("common/core/db/dbsql/SqliteColumn") require("common/core/db/dbsql/SqliteFunc") require("common/core/db/dbsql/SqliteCondition") require("common/core/db/dbsql/SqliteQuery") require("common/core/db/dbsql/SqliteDatabase") require("common/core/db/dbsql/SqliteMgr") require("common/core/db/dbsql/SqliteTable") require("common/core/db/dbsql/SingleSqliteTable") require("common/core/db/dbsql/SyncMgr") end -- key:value require("common/core/db/dbkv/KVTable") require("common/core/db/dbkv/KVDatabase") require("common/core/db/dbkv/KVMgr") require("common/core/db/LocalStorageMgr")VideoPromotionUI---@class VideoPromotionUI : UILayer local VideoPromotionUI, super = defClass("VideoPromotionUI", UILayer) function VideoPromotionUI:ctor() super.ctor(self) self.R = ResLoader.loadResLink("modules/ui/customerui/customeruireslink") end function VideoPromotionUI:onLoad() self.ui = GameObject.Instantiate(self.R.video_promotion_ui) self:addChild(self.ui) self:useTweenOnOpen(self.ui) self:initUI() self:showUI() end function VideoPromotionUI:initUI() -- 初始化UI元素 self.close_btn = self.ui:Seek("close_btn") self.video_btn = self.ui:Seek("video_btn") --self.video_text = self.ui:Seek("video_text") -- 设置UI元素的属性和事件 util.ugui.addButtonClickEvent(self.close_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:close() end) util.ugui.addButtonClickEvent(self.video_btn, function() AudioMgr:playSoundEffectByName(AudioConst.audioClipName.click) self:clickVideo() end) end function VideoPromotionUI:showUI() -- 这里可以添加其他需要显示的内容 self.video_btn:GetComponent("Button").interactable = CustomerMgr:checkVideoSolicitationTime() end function VideoPromotionUI:clickVideo() if CustomerQueMgr:isToLimit() then PopUpUI.new("排队人有些多,先招待他们吧"):show():showMask():enableCloseWhenClickMask() return end printWarn("VideoPromotionUI", "VideoPromotionUI:clickVideo()") self:videoCallback() CustomerMgr:updateVideoSolicitationTime() end function VideoPromotionUI:videoCallback() CustomerMgr.customerSolicitation:solicitationList(15) UserDataMgr:addWatchAdCount(1) UserDataMgr:addPromoteVideoCount(1) self:close() end function VideoPromotionUI:badVideoCallback() self:close() end -- 获取视频按钮 function VideoPromotionUI:getVideoButton() return self.video_btn end return VideoPromotionUIsettinguireslinkreturn { --BASIC --ASSET setting_main_ui = {"Assets/AssetsPackage/Res/modules/ui/settingui/prefabs/setting_main_ui.prefab", 0, 0}, } main-- const require("modules/task/const/TaskConst") require("modules/task/const/TaskBootConst") -- mgr require("modules/task/mgr/TaskDailyMgr") require("modules/task/mgr/TaskAchMgr") require("modules/task/mgr/TaskOrderMgr") require("modules/task/mgr/TaskMgr") require("modules/task/mgr/TaskBootMgr") -- data require("modules/task/data/TaskOrderInfo") require("modules/task/data/TaskUserData")mainrequire("modules/common/mgr/StoryDialogMgr") require("modules/common/mgr/AudioMgr") require("modules/common/mgr/PlayerPrefsMgr") EmployeeUserData--- 用户数据 员工 ---@class EmployeeUserData : LuaClass local EmployeeUserData = defClass("EmployeeUserData") -- 构造函数 function EmployeeUserData:ctor() -- 初始化数据 self:init() end -- 初始化 function EmployeeUserData:init() -- 指定员工的状态 self.employee_state = {} -- {id: state} state: 1未解锁 2已解锁 3已雇佣 -- 指定员工的等级 self.employee_grade = {} -- {id: grade} -- 指定员工的已装皮肤 self.employee_skin = {} -- {id: skinId} -- 指定员工的已购皮肤 self.employee_skin_list = {} -- {id: [skinId]} -- 指定员工上次升级时间 self.employee_last_grade_time = {} -- {id: os.time()} end -- 重置数据 function EmployeeUserData:resetData() end -- 加载数据 function EmployeeUserData:load() -- 初始化数据 if UserDataMgr.isNewPlayer then self:resetData() else self:loadFromLocal() end self:onLoadComplete() end -- 加载本地数据 function EmployeeUserData:loadFromLocal() self.employee_state = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.EMPLOYEE_STATE, "{}"))) self.employee_grade = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.EMPLOYEE_GRADE, "{}"))) self.employee_skin = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.EMPLOYEE_SKIN, "{}"))) self.employee_skin_list = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.EMPLOYEE_SKIN_LIST, "{}"))) self.employee_last_grade_time = PlayerPrefsMgr:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.EMPLOYEE_LAST_GRADE_TIME, "{}"))) end -- 加载完成回调 function EmployeeUserData:onLoadComplete() -- 没有解锁条件的对象默认解锁 local data = EmployeCfgParse:getData() for _, v in pairs(data) do if v.unlockCondId == -1 and (self:getEmployeeState(v.id) < 2) then self:setEmployeeState(v.id, 2, true) -- 默认解锁 end end self:save() end -- 保存数据 function EmployeeUserData:save() self:saveToLocal() end -- 保存到本地 function EmployeeUserData:saveToLocal() PlayerPrefsMgr:setString(StrogeKeyDef.EMPLOYEE_STATE, json.encode(PlayerPrefsMgr:idToString(self.employee_state))) PlayerPrefsMgr:setString(StrogeKeyDef.EMPLOYEE_GRADE, json.encode(PlayerPrefsMgr:idToString(self.employee_grade))) PlayerPrefsMgr:setString(StrogeKeyDef.EMPLOYEE_SKIN, json.encode(PlayerPrefsMgr:idToString(self.employee_skin))) PlayerPrefsMgr:setString(StrogeKeyDef.EMPLOYEE_SKIN_LIST, json.encode(PlayerPrefsMgr:idToString(self.employee_skin_list))) PlayerPrefsMgr:setString(StrogeKeyDef.EMPLOYEE_LAST_GRADE_TIME, json.encode(PlayerPrefsMgr:idToString(self.employee_last_grade_time))) PlayerPrefsMgr:save() end -- 获取员工状态 function EmployeeUserData:getEmployeeState(employeeId) return self.employee_state[employeeId] or 1 end -- 设置员工状态 function EmployeeUserData:setEmployeeState(employeeId, state, refuseSave) self.employee_state[employeeId] = state if not refuseSave then self:save() end return state end -- 获取员工等级 function EmployeeUserData:getEmployeeGrade(employeeId) return self.employee_grade[employeeId] or 1 end -- 设置员工等级 function EmployeeUserData:setEmployeeGrade(employeeId, value, refuseSave) self.employee_grade[employeeId] = value if not refuseSave then self:save() end return value end -- 增加员工等级 function EmployeeUserData:addEmployeeGrade(employeeId, value, refuseSave) local curr = self:getEmployeeGrade(employeeId) + value return self:setEmployeeGrade(employeeId, curr, refuseSave) end -- 设置员工当前皮肤 function EmployeeUserData:setEmployeeSkin(employeeId, employeeSkinId, refuseSave) self.employee_skin[employeeId] = employeeSkinId if not refuseSave then self:save() end return employeeSkinId end -- 使用员工当前皮肤 function EmployeeUserData:useEmployeeSkin(employeeId, employeeSkinId, refuseSave) return self:setEmployeeSkin(employeeId, employeeSkinId, refuseSave) end -- 取消使用员工当前皮肤 function EmployeeUserData:unuseEmployeeSkin(employeeId, refuseSave) return self:setEmployeeSkin(employeeId, -1, refuseSave) end -- 获取员工当前使用的皮肤ID function EmployeeUserData:getUsedSkinId(employeeId) return self.employee_skin[employeeId] or -1 end -- 获取员工已获得皮肤id列表 function EmployeeUserData:getEmployeeSkinList(employeeId) return self.employee_skin_list[employeeId] or {} end -- 增加员工已获得皮肤 function EmployeeUserData:addEmployeeSkin(employeeId, employeeSkinId, refuseSave) local list = self.employee_skin_list[employeeId] if not list then list = {} self.employee_skin_list[employeeId] = list end table.insert(list, employeeSkinId) if not refuseSave then self:save() end return list end -- 获取员工上次升级时间 function EmployeeUserData:getEmployeeLastGradeTime(employeeId) return self.employee_last_grade_time[employeeId] or -1 end -- 设置员工上次升级时间 function EmployeeUserData:setEmployeeLastGradeTime(employeeId, time, refuseSave) self.employee_last_grade_time[employeeId] = time if not refuseSave then self:save() end return time end -- 更新员工上次升级时间(升级和雇佣后调用) function EmployeeUserData:updateEmployeeLastGradeTime(employeeId, refuseSave) return self:setEmployeeLastGradeTime(employeeId, os.time(), refuseSave) end -- 清除员工升级时间(已满级) function EmployeeUserData:clearEmployeeLastGradeTime(employeeId, refuseSave) return self:setEmployeeLastGradeTime(employeeId, nil, refuseSave) end return EmployeeUserData GachaUserData-- local GachaUserData, super = defClass("GachaUserData") local LOGTAG = "GachaUserData" function GachaUserData:ctor() self:init() end function GachaUserData:init() self.gacha_free_count = 0 -- 玩家免费扭蛋次数 self.gacha_free_today_count = 1 -- 今日免费次数 end function GachaUserData:resetData() end function GachaUserData:load() if UserDataMgr.isNewPlayer then self:resetData() else self:loadFromLocal() end self:onLoadComplete() end function GachaUserData:loadFromLocal() self.gacha_free_count = PlayerPrefsMgr:getInt(StrogeKeyDef.gacha_free_count, 0) self.gacha_free_today_count = PlayerPrefsMgr:getInt(StrogeKeyDef.gacha_free_today_count, 0) end function GachaUserData:onLoadComplete() if UserDataMgr.isFirstLoginToday then self.gacha_free_today_count = 1 -- 重置今日免费次数 end self:save() end function GachaUserData:save() self:saveToLocal() end function GachaUserData:saveToLocal() PlayerPrefsMgr:setInt(StrogeKeyDef.gacha_free_count, self.gacha_free_count) PlayerPrefsMgr:setInt(StrogeKeyDef.gacha_free_today_count, self.gacha_free_today_count) PlayerPrefsMgr:save() end function GachaUserData:getFreeCount() return self.gacha_free_count end function GachaUserData:setFreeCount(count, refuseSave) self.gacha_free_count = count if not refuseSave then self:save() end return self.gacha_free_count end -- 增加免费次数 function GachaUserData:addFreeCount(count, refuseSave) local curr = self:getFreeCount() + count return self:setFreeCount(curr, refuseSave) end -- 减少免费次数 function GachaUserData:reduceFreeCount(count, refuseSave) local curr = self:getFreeCount() - count return self:setFreeCount(curr, refuseSave) end function GachaUserData:getFreeTodayCount() return self.gacha_free_today_count end function GachaUserData:setFreeTodayCount(count, refuseSave) self.gacha_free_today_count = count if not refuseSave then self:save() end return self.gacha_free_today_count end return GachaUserData CoinDeskMgr--[[ 管理扔在桌子上的金币,点击桌子之后,金币飞到货币栏 author:{zhangpeng} time:2025-05-21 18:45:30 ]] local CoinDeskMgr = defClassStatic("CoinDeskMgr") local LOGTAG = "CoinDeskMgr" function CoinDeskMgr:init() self.coinId = 1 self.coinOnDeskList = {} self.commonResLink = ResLoader.loadResLink("modules/ui/commonui/commonuireslink") end -- 设置金币资源路径 function CoinDeskMgr:setCoinResLink(resLink) self.coinResLink = resLink end -- 获取金币资源路径 function CoinDeskMgr:getCoinResLink() local scene = RestaurantMgr:getRestaurantScene() self.coinResLink = scene.coinResLink return self.coinResLink end -- 创建金币 --@param deskId 桌子id function CoinDeskMgr:createCoin(deskId, coinNum, mapId) local coin = CoinOnDesk.new(deskId, coinNum, mapId) -- id coin.id = self.coinId self.coinId = self.coinId + 1 -- printInfo(LOGTAG, string.format("往桌子 %d 上添加金币: %d", deskId, coin.id)) coin:setCoinName(string.format("coin_%d", coin.id)) -- 加到队列列 table.insert(self.coinOnDeskList, coin) end -- 清除一个金币 function CoinDeskMgr:clearCoin(i) if not self.coinOnDeskList[i] then printError(LOGTAG, string.format("clearCoin: 索引 %d 超出范围,列表长度: %d", i, #self.coinOnDeskList)) return end -- 保存金币对象引用 local coin = self.coinOnDeskList[i] -- 先从列表中移除,避免异步回调中的索引问题 table.remove(self.coinOnDeskList, i) -- 增加货币 CurrencyMgr:changeCoin(coin.coinNum, true) -- 执行飞行动画 coin:flyToCurrencyBar(function() CurrencyMgr:refreshCoin(true) -- 销毁金币对象 if coin then coin:destroy() end printInfo(LOGTAG, string.format("金币[%d]已清除,剩余金币数量: %d", coin.id or 0, #self.coinOnDeskList)) end) end -- 清除指定桌子上的所有金币 function CoinDeskMgr:clearCoinsOnDesk(deskId) -- 收集需要清除的金币索引 local indicesToClear = {} for i = 1, #self.coinOnDeskList do local coin = self.coinOnDeskList[i] if coin and coin.desk and coin.desk:getBuildingId() == deskId then table.insert(indicesToClear, i) end end -- 倒序清除,避免索引偏移问题 for j = #indicesToClear, 1, -1 do local index = indicesToClear[j] if index <= #self.coinOnDeskList then self:clearCoin(index) end end printInfo(LOGTAG, string.format("清除桌子[%s]上的%d个金币", tostring(deskId), #indicesToClear)) end -- 获取指定桌子上的金币数量 function CoinDeskMgr:getCoinCountOnDesk(deskId) local count = 0 for i = 1, #self.coinOnDeskList do local coin = self.coinOnDeskList[i] if coin and coin.desk and coin.desk:getBuildingId() == deskId then count = count + 1 end end return count end -- 获取所有桌子上的金币列表 function CoinDeskMgr:getCoinsOnDeskList() return self.coinOnDeskList end -- 从列表中移除特定金币 function CoinDeskMgr:removeCoinFromList(targetCoin) if not targetCoin then printWarn(LOGTAG, "removeCoinFromList: targetCoin为nil") return false end for i = #self.coinOnDeskList, 1, -1 do local coin = self.coinOnDeskList[i] if coin == targetCoin then table.remove(self.coinOnDeskList, i) printInfo(LOGTAG, string.format("从列表中移除金币: %d 剩余金币数量: %d", coin.id or 0, #self.coinOnDeskList)) return true end end printWarn(LOGTAG, string.format("未找到要移除的金币: %d", targetCoin.id or 0)) return false end -- 飞到货币栏 function CoinDeskMgr:flyToCurrencyBar(startNode, callback) local mainViewUI = UILayerUtil:findUIByName("MainViewUI") local targetNode = mainViewUI:getCoinUI() -- 换个思路:在Canvas坐标系中做动画 self:flyInCanvasSpace(startNode, targetNode, callback) end -- 在Canvas坐标系中做飞行动画 function CoinDeskMgr:flyInCanvasSpace(startNode, targetNode, callback) -- 使用GuideCom中验证过的坐标转换方法 local startPos = self:getGoPositionInCanvasSpace(startNode) --local targetPos = self:getGoPositionInCanvasSpace(targetNode) -- 在Canvas上创建临时的金币UI对象 local flyUI = self:createFlyCoinSpineUI(startPos, targetNode) if not flyUI then if callback then callback() end return end -- 使用UI动画 flyUI:RunAction(ua.Sequence({ --ua.MoveTo(0.5, Vector3(targetPos.x, targetPos.y, 0)), ua.MoveTo(1.0, Vector3(0, 0, 0)), ua.cb(function() -- 销毁临时UI对象 GameObject.Destroy(flyUI) if callback then callback() end end) })) end -- 借用GuideCom中的坐标转换方法 function CoinDeskMgr:getGoPositionInCanvasSpace(goOrPos) if not _IsType(goOrPos, typeof(GameObject)) then return goOrPos end local pos = Vector2.zero local go = goOrPos if CS.LuaHelper.IsNull(go) then printWarn("CoinOnDesk", "getGoPositionInCanvasSpace, go is null") return pos end local isUIGo = util.ugui.isUIGameObject(go) if isUIGo then pos = go.transform:TransformPoint(Vector3.zero) else local uiCamSize = UILayerUtil.cameraCom.orthographicSize local mainCamSize = self.desk.scene.cams.scene.orthographicSize local k = uiCamSize / mainCamSize pos = go.transform:TransformPoint(Vector3.zero) * k end pos = util.ugui.worldSpaceToCanvasSpace(pos, UILayerUtil.canvas) return pos end -- 创建用于飞行的金币UI对象 function CoinDeskMgr:createFlyCoinUI(startPos) -- 在UI Canvas上创建一个Image对象 local canvas = UILayerUtil.canvas local flyGo = GameObject("FlyCoin") flyGo.transform:SetParent(canvas.transform, false) -- 添加Image组件 local image = flyGo:AddComponent(typeof(CS.UnityEngine.UI.Image)) image.sprite = CurrencyMgr:getCoinUIIcon() -- 设置初始位置和大小 local rectTransform = image.rectTransform rectTransform.anchorMin = Vector2(0.5, 0.5) rectTransform.anchorMax = Vector2(0.5, 0.5) rectTransform.pivot = Vector2(0.5, 0.5) rectTransform.anchoredPosition = startPos or Vector2.zero rectTransform.sizeDelta = Vector2(50, 50) -- 设置合适的大小 flyGo.transform:SetParent(targetNode.transform, true) return flyGo end -- 创建用于飞行的金币UI对象 function CoinDeskMgr:createFlyCoinSpineUI(startPos, targetNode) -- 在UI Canvas上创建一个Spine对象 local canvas = UILayerUtil.canvas local flyGo = GameObject.Instantiate(self.commonResLink.coin_fly_spine) flyGo.transform:SetParent(canvas.transform, false) -- 设置初始位置和大小 local rectTransform = flyGo.transform rectTransform.anchoredPosition = startPos flyGo.transform:SetParent(targetNode.transform, true) return flyGo end return CoinDeskMgr IronSourceMgrG --[[ author:{zhangpeng} time:2023-10-19 22:16:13 ]] local IronSourceMgr, super = defClassStatic("IronSourceMgr") local LOG_TAG = "IronSourceMgr" local IOC_IS_UTIL_CLASS_NAME = "IronSourceUtil" function IronSourceMgr:init() printInfo(LOG_TAG,"是否IOS设备:%s",Device.isIOS()) if Device.isIOS() then self:registIosLuaCallBack() end end -- 注册ios IS lua回调 function IronSourceMgr:registIosLuaCallBack() printInfo(LOG_TAG,"注册ios广告回调 registIosLuaCallBack") local param = { didReceiveRewardForPlacementCb = function() printInfo(LOG_TAG,"观看完一个激励视频 lua") end, hasAvailableAdWithAdInfoCb = function () -- UIComsTool:showToast("有可用激励视频",1) printInfo(LOG_TAG,"[激励视频] call lua hasAvailableAdWithAdInfoCb") end } luaoc.callStaticMethod(IOC_IS_UTIL_CLASS_NAME, "registLuaCallback", param) end function IronSourceMgr:loadBannerView() if Device.isIOS() then printInfo(LOG_TAG,"lua侧请求banner广告") local ok, result = luaoc.callStaticMethod(IOC_IS_UTIL_CLASS_NAME, "loadBannerView") return result elseif Device.isAndroid() then -- todo:: end end function IronSourceMgr:showBannerView() if Device.isIOS() then luaoc.callStaticMethod(IOC_IS_UTIL_CLASS_NAME, "showBannerView") elseif Device.isAndroid() then -- todo:: end end function IronSourceMgr:hideBannerView() end function IronSourceMgr:loadReviewVideo() if Device.isIOS() then luaoc.callStaticMethod(IOC_IS_UTIL_CLASS_NAME, "loadReviewVideo") end end -- 主动检测是否有可用的激励视频 function IronSourceMgr:haveRewardVideo() if Device.isIOS() then local ok,ret = luaoc.callStaticMethod(IOC_IS_UTIL_CLASS_NAME, "haveRewardVideo") return ret elseif Device.isAndroid() then -- todo: return true else return true end end function IronSourceMgr:showReviewVideo(placmentName,itemId) if Device.isIOS() then local param = { placementName = "RV_UNLOCK_SKIN" -- 这个参数根据实际情况配置 } luaoc.callStaticMethod(IOC_IS_UTIL_CLASS_NAME, "showReviewVideo", param) elseif Device.isAndroid() then end end IronSourceMgr:init()mainqrequire("modules/building/restaurant/order/OrderInfo") require("modules/building/restaurant/order/OrderConst")YooAssetAdapter<--[[ YooAssetLoader适配层 提供统一的资源加载接口,封装YooAssetLoader的具体实现 支持智能类型推断、缓存管理、错误处理等功能 author: zhangheng time: 2025-07-19 ]] ---@class YooAssetAdapter local YooAssetAdapter = defClassStatic("YooAssetAdapter") local LOGTAG = "YooAssetAdapter" -- ============================================================================ -- 私有函数和配置 -- ============================================================================ -- 获取YooAssetLoader实例 local function getYooAssetLoader() return CS.YooAssetLoader.Instance end -- 资源类型映射表 local ASSET_TYPE_MAP = { [".prefab"] = "GameObject", [".png"] = "Sprite", [".jpg"] = "Sprite", [".jpeg"] = "Sprite", [".wav"] = "AudioClip", [".mp3"] = "AudioClip", [".ogg"] = "AudioClip", [".bytes"] = "TextAsset", [".txt"] = "TextAsset", [".json"] = "TextAsset", [".mat"] = "Material", [".shader"] = "Shader", [".anim"] = "AnimationClip", [".controller"] = "RuntimeAnimatorController", [".asset"] = "SkeletonDataAsset" -- Spine资源 } -- 根据路径推断资源类型 local function inferAssetType(path) if not path then return nil end local ext = string.match(path:lower(), "%.([^%.]+)$") if ext then return ASSET_TYPE_MAP["." .. ext] end return nil end -- ============================================================================ -- 公共接口 -- ============================================================================ -- 检查YooAssetLoader是否可用 function YooAssetAdapter.isAvailable() local loader = getYooAssetLoader() return loader ~= nil and CS.YooAssetLoaderExtension.IsReady(loader) end -- 同步加载资源适配器 ---@param path string 资源路径 ---@param assetType string|System.Type|nil 资源类型 ---@return any|nil 加载的资源对象 function YooAssetAdapter.loadAssetSync(path, assetType) printDebug(LOGTAG, "同步加载资源:%s, 类型:%s", path, tostring(assetType)) -- 根据UseLocalRes判断加载方式 if CS.LuaHelper.UseLocalRes() then printDebug(LOGTAG, "使用本地资源加载:%s", path) return YooAssetAdapter.loadAssetInEditor(path, assetType) else printDebug(LOGTAG, "使用YooAssetLoader加载:%s", path) local loader = getYooAssetLoader() if not loader then printError(LOGTAG, "YooAssetLoader未初始化") return nil end -- 将assetType转换为字符串用于YooAssetLoader local assetTypeString = nil if assetType then if type(assetType) == "string" then assetTypeString = assetType else assetTypeString = convertSystemTypeToString(assetType) end end -- 根据资源类型选择合适的加载方法 if assetTypeString == "GameObject" or inferAssetType(path) == "GameObject" then return loader:LoadPrefab(path) elseif assetTypeString == "Sprite" or inferAssetType(path) == "Sprite" then return loader:LoadSprite(path) elseif assetTypeString == "AudioClip" or inferAssetType(path) == "AudioClip" then return loader:LoadAudioClip(path) elseif assetTypeString == "TextAsset" or inferAssetType(path) == "TextAsset" then return loader:LoadTextAsset(path) elseif assetTypeString == "Material" or inferAssetType(path) == "Material" then return loader:LoadMaterial(path) else -- 使用通用加载方法和扩展 if assetTypeString then return CS.YooAssetLoaderExtension.LoadAssetByTypeName(loader, path, assetTypeString) else -- 尝试推断类型 local inferredType = inferAssetType(path) if inferredType then return YooAssetAdapter.loadAssetSync(path, inferredType) else return CS.YooAssetLoaderExtension.LoadAssetByTypeName(loader, path, nil) end end end end end -- 异步加载资源适配器 ---@param path string 资源路径 ---@param assetType string|System.Type|nil 资源类型 ---@param progressCallback function|nil 进度回调 ---@param finishCallback function|nil 完成回调 function YooAssetAdapter.loadAssetAsync(path, assetType, progressCallback, finishCallback) printDebug(LOGTAG, "异步加载资源:%s, 类型:%s", path, tostring(assetType)) -- 根据UseLocalRes判断加载方式 if CS.LuaHelper.UseLocalRes() then printDebug(LOGTAG, "使用本地资源加载:%s", path) local asset = YooAssetAdapter.loadAssetInEditor(path, assetType) -- 模拟异步完成 if progressCallback then progressCallback(1) end if finishCallback then finishCallback(asset) end return else printDebug(LOGTAG, "使用YooAssetLoader异步加载:%s", path) local loader = getYooAssetLoader() if not loader then printError(LOGTAG, "YooAssetLoader未初始化") if finishCallback then finishCallback(nil) end return end -- 将assetType转换为字符串用于YooAssetLoader local assetTypeString = nil if assetType then if type(assetType) == "string" then assetTypeString = assetType else assetTypeString = convertSystemTypeToString(assetType) end end -- 进度回调包装 local wrappedCallback = function(asset) if finishCallback then finishCallback(asset) end end -- 根据资源类型选择合适的加载方法 if assetTypeString == "GameObject" or inferAssetType(path) == "GameObject" then loader:LoadPrefabAsyncLua(path, wrappedCallback) elseif assetTypeString == "Sprite" or inferAssetType(path) == "Sprite" then loader:LoadSpriteAsyncLua(path, wrappedCallback) elseif assetTypeString == "AudioClip" or inferAssetType(path) == "AudioClip" then loader:LoadAudioClipAsyncLua(path, wrappedCallback) elseif assetTypeString == "TextAsset" or inferAssetType(path) == "TextAsset" then loader:LoadTextAsyncLua(path, wrappedCallback) else -- 使用通用异步加载方法和扩展 if assetTypeString then CS.YooAssetLoaderExtension.LoadAssetByTypeNameAsyncLua(loader, path, assetTypeString, wrappedCallback) else -- 尝试推断类型 local inferredType = inferAssetType(path) if inferredType then YooAssetAdapter.loadAssetAsync(path, inferredType, progressCallback, finishCallback) else CS.YooAssetLoaderExtension.LoadAssetByTypeNameAsyncLua(loader, path, nil, wrappedCallback) end end end end end -- 检查资源是否存在 ---@param path string 资源路径 ---@return boolean 是否存在 function YooAssetAdapter.hasAsset(path) -- 根据UseLocalRes判断检查方式 if CS.LuaHelper.UseLocalRes() then printDebug(LOGTAG, "使用本地资源检查:%s", path) return YooAssetAdapter.hasAssetInEditor(path) else printDebug(LOGTAG, "使用YooAssetLoader检查:%s", path) local loader = getYooAssetLoader() if not loader then return false end return loader:CheckAssetExist(path) end end -- 释放资源 ---@param path string 资源路径 function YooAssetAdapter.releaseAsset(path) local loader = getYooAssetLoader() if loader then loader:ReleaseAsset(path) end end -- 预加载资源列表 ---@param assetList string[] 资源路径列表 ---@param callback function|nil 完成回调 function YooAssetAdapter.preloadAssets(assetList, callback) if not assetList or #assetList == 0 then if callback then callback(true) end return end printInfo(LOGTAG, "预加载资源列表,数量:%d", #assetList) local loader = getYooAssetLoader() if loader then -- 转换为C#数组 local csArray = CS.System.Array.CreateInstance(CS.System.String, #assetList) for i = 1, #assetList do csArray[i-1] = assetList[i] end loader:PreloadAssets(csArray, function(success) printInfo(LOGTAG, "批量预加载完成,成功:%s", tostring(success)) if callback then callback(success) end end) else printError(LOGTAG, "YooAssetLoader未初始化,无法预加载") if callback then callback(false) end end end -- 清理所有YooAssetLoader资源 function YooAssetAdapter.clearAll() local loader = getYooAssetLoader() if loader then loader:ReleaseAllAssets() printInfo(LOGTAG, "YooAssetLoader资源清理完成") end end -- 获取YooAssetLoader统计信息 ---@return table 统计信息 function YooAssetAdapter.getStats() local loader = getYooAssetLoader() if not loader then return { assetCount = 0, sceneCount = 0, isReady = false } end return { assetCount = loader:GetLoadedAssetCount(), sceneCount = loader:GetLoadedSceneCount(), isReady = CS.YooAssetLoaderExtension.IsReady(loader), details = CS.YooAssetLoaderExtension.GetDetailedStats(loader) } end -- 获取支持的资源类型列表 ---@return string[] 支持的文件扩展名列表 function YooAssetAdapter.getSupportedTypes() local types = {} for ext, typeName in pairs(ASSET_TYPE_MAP) do table.insert(types, ext .. " -> " .. typeName) end return types end -- 根据路径推断资源类型(公共接口) ---@param path string 资源路径 ---@return string|nil 推断的资源类型 function YooAssetAdapter.inferAssetType(path) return inferAssetType(path) end -- 打印适配器状态信息 function YooAssetAdapter.printStatus() printInfo(LOGTAG, "=== YooAssetAdapter 状态 ===") local loader = getYooAssetLoader() if not loader then printError(LOGTAG, "YooAssetLoader未初始化") return end local stats = YooAssetAdapter.getStats() printInfo(LOGTAG, "就绪状态: %s", tostring(stats.isReady)) printInfo(LOGTAG, "已加载资源数量: %d", stats.assetCount) printInfo(LOGTAG, "已加载场景数量: %d", stats.sceneCount) printInfo(LOGTAG, "详细信息: %s", stats.details) printInfo(LOGTAG, "支持的资源类型:") local supportedTypes = YooAssetAdapter.getSupportedTypes() for i, typeInfo in ipairs(supportedTypes) do printInfo(LOGTAG, " %s", typeInfo) end printInfo(LOGTAG, "========================") end -- ============================================================================ -- 类型转换工具函数 -- ============================================================================ -- 将资源类型转换为System.Type对象 ---@param assetType string|System.Type|nil 资源类型 ---@return System.Type|nil 转换后的System.Type对象 local function convertAssetTypeToSystemType(assetType) if not assetType then return nil end local csType = nil if type(assetType) == "string" then -- 如果是字符串,尝试构造类型名 -- 首先尝试UnityEngine命名空间 csType = CS.System.Type.GetType("UnityEngine." .. assetType .. ", UnityEngine") if not csType then -- 尝试直接使用字符串作为类型名 csType = CS.System.Type.GetType(assetType) end if not csType then -- 尝试添加CS.UnityEngine前缀 csType = CS.System.Type.GetType("CS.UnityEngine." .. assetType) end elseif type(assetType) == "userdata" then -- 检查是否是System.Type对象 if assetType.GetType then csType = assetType elseif assetType.GetType and assetType:GetType() == CS.System.Type then csType = assetType end end return csType end -- 将System.Type对象转换为字符串类型名 ---@param systemType System.Type|nil System.Type对象 ---@return string|nil 类型名字符串 local function convertSystemTypeToString(systemType) if not systemType then return nil end if type(systemType) == "userdata" and systemType.Name then return systemType.Name end return tostring(systemType) end -- 创建资源类型辅助函数 ---@param typeName string 类型名称 ---@return string 标准化的类型名称 function YooAssetAdapter.createAssetType(typeName) return typeName end -- 从System.Type创建资源类型 ---@param systemType System.Type System.Type对象 ---@return System.Type 原System.Type对象 function YooAssetAdapter.createAssetTypeFromSystemType(systemType) return systemType end -- ============================================================================ -- 编辑器兼容性支持 -- ============================================================================ -- 编辑器模式下加载资源 ---@param path string 资源路径 ---@param assetType string|System.Type|nil 资源类型 ---@return any|nil 加载的资源对象 function YooAssetAdapter.loadAssetInEditor(path, assetType) printWarn(LOGTAG, "使用编辑器模式加载资源:%s", path) local assetPath = path -- string.format("Assets/AssetsPackage/%s", path) -- 使用AssetDatabase加载资源 if assetType then local csType = convertAssetTypeToSystemType(assetType) if csType then return CS.UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath, csType) else printError(LOGTAG, "无法解析资源类型: %s", tostring(assetType)) return nil end else return CS.UnityEditor.AssetDatabase.LoadMainAssetAtPath(assetPath) end end -- 检查编辑器模式下资源是否存在 ---@param path string 资源路径 ---@return boolean 是否存在 function YooAssetAdapter.hasAssetInEditor(path) -- 处理资源路径格式转换(与loadAssetInEditor保持一致) local assetPath = path -- 如果路径不以Assets/开头,尝试添加Assets/前缀 if not string.match(assetPath, "^[Aa]ssets/") then -- 处理AssetsPackage路径 if string.match(assetPath, "^[Aa]ssetsPackage/") then assetPath = "Assets/" .. assetPath else -- 尝试在常见目录中查找 local possiblePaths = { "Assets/AssetsPackage/" .. assetPath, "Assets/Resources/" .. assetPath, "Assets/" .. assetPath } for _, testPath in ipairs(possiblePaths) do local testAsset = CS.UnityEditor.AssetDatabase.LoadMainAssetAtPath(testPath) if testAsset then return true end end return false end end local asset = CS.UnityEditor.AssetDatabase.LoadMainAssetAtPath(assetPath) return asset ~= nil end return YooAssetAdapter UILayerUtil&5 ---@class UILayerUtil:LuaStaticClass local UILayerUtil = defClassStatic("UILayerUtil") local LOGTAG = "UILayerUtil" local UnityEngine = CS.UnityEngine local UGUI = CS.UnityEngine.UI UILayerUtil.SORTING_ORDER = { LOCAL_UI = 10000, GLOBAL_UI = 30000 -- 32767是允许的最大值 } local UIConfig = { safeWidth = 1098, safeHeight = 2048, } local LOCAL_UI_ROOT_NAME = "__ROOT_UI__" local GLOBAL_UIROOT_NAME = "UILayerUtil.uiroot" local UI_MASK_NAME = "__UIMASK__" function UILayerUtil:init() if self:isInited() then return end local needCreate = false self.layerIndex = 0 self.topMaskLayer = nil -- 最上层带遮罩的uilayer local goname = "UINodeGameObject" local uinode = GameObject.Find(goname) if not uinode then needCreate = true uinode = GameObject.Instantiate(Resources.Load("ui/uinode")) uinode:SetName(goname) GameObject.DontDestroyOnLoad(uinode) end ---@type UIRoot[] self._uirootList = {} self.uinode = uinode self.canvas = uinode:Seek("UICanvas") self.prefab = uinode:Seek("UIPrefab") self.mask = uinode:Seek("UIMask") self.camera = uinode:Seek("UICamera") self.eventSystem = uinode:Seek("UIEventSystem") self:calcCanvasScalerFactor(self.canvas) self.mask:SetActive(false) if needCreate then self.uiroot = UnityEngine.GameObject.Instantiate(self.prefab) self.uiroot:SetParent(self.prefab.transform.parent.gameObject, false) self.uiroot:SetName(GLOBAL_UIROOT_NAME) else self.uiroot = self.prefab.transform.parent.gameObject:Child(GLOBAL_UIROOT_NAME) end -- 相机渲染类型改成Overlay local cam = self.camera:GetComponent(typeof(UnityEngine.Camera)) local data = CS.UnityEngine.Rendering.Universal.CameraExtensions.GetUniversalAdditionalCameraData(cam) data.renderType = CS.UnityEngine.Rendering.Universal.CameraRenderType.Overlay self.cameraCom = cam local canvasCom = self.canvas:GetComponent(typeof(UnityEngine.Canvas)) canvasCom.sortingOrder = self.SORTING_ORDER.GLOBAL_UI -- 局部ui在全局ui下方 if needCreate then -- clear all children local trans = self.canvas.transform local Destroy = UnityEngine.GameObject.Destroy for i = 0, trans.childCount - 1 do local child = trans:GetChild(i).gameObject if child ~= self.prefab and child ~= self.uiroot and child ~= self.mask then Destroy(child) end end end end function UILayerUtil:tryAddAppExitMsg() if rawget(Msg, "APP_EXIT") then Msg.add(Msg.APP_EXIT, function () self:CloseAllGlobal() self.camera:SetActive(false) end) end self.tryAddAppExitMsg = function () end end function UILayerUtil:getLocalUI() local uinode = UnityEngine.GameObject.Find(LOCAL_UI_ROOT_NAME) local uiroot,uirootObj if not uinode then uinode = UnityEngine.GameObject.Instantiate(UnityEngine.Resources.Load("ui/uinodelocal")) uinode:SetName(LOCAL_UI_ROOT_NAME) uiroot = uinode:Seek("UIPrefab") uinode:Seek("UIMask"):SetActive(false) local canvas = uinode:Seek("UICanvas") self:calcCanvasScalerFactor(canvas) local canvasCom = canvas:GetComponent(typeof(UnityEngine.Canvas)) canvasCom.sortingOrder = self.SORTING_ORDER.LOCAL_UI canvasCom.worldCamera = self.cameraCom canvas.transform.sizeDelta = self.canvas.transform.sizeDelta -- fix issue,sizeDelta 的值不正确 local uiMask = uinode:Seek("UIMask") uiMask:SetActive(true) uiMask:SetScalef(0) local img = uiMask[UGUI.Image] if img then local color = img.color color.a = 0 img.color = color end uirootObj = UILayerUtil:_getOrCreateUIRoot(uiroot, false, uiMask) else uiroot = uinode:Seek("UIPrefab") uirootObj = UILayerUtil:_getOrCreateUIRoot(uiroot) end return uirootObj, uinode end ---@private function UILayerUtil:refreshMask() local globalStack = self:getGlobalUI():getUILayerStack() or {} local localStack = self:getLocalUI():getUILayerStack() or {} local found = false self.topMaskLayer = nil for i = #globalStack, 1, -1 do local ui = globalStack[i] local mask = ui[UI_MASK_NAME] if mask then if found then mask:SetActive(false) else mask:SetActive(true) found = true self.topMaskLayer = ui end end end for i = #localStack, 1, -1 do local ui = localStack[i] local mask = ui[UI_MASK_NAME] if mask then if found then mask:SetActive(false) else mask:SetActive(true) found = true self.topMaskLayer = ui end end end local localMask = self:getLocalUI():getUIMask() if not localMask then return end if found then localMask:SetScalef(1) else localMask:SetScalef(0) end end function UILayerUtil:setCurScene(scene) self.scene = scene end function UILayerUtil:getCurScene() return self.scene end function UILayerUtil:canShowPopup() return self._canShowPopup ~= false end function UILayerUtil:setCanShowPopup(b) self._canShowPopup = b end function UILayerUtil:genEmptyUILayerProxy(uiLayer) local proxy = { __isProxy = true, } setmetatable(proxy,{ __index = function(t,k) local emptyFunc = function(...) print("proxy",uiLayer.__cls_name,k,...) return proxy end return emptyFunc end, }) return proxy end function UILayerUtil:CloseAllLocal(dontDestroy) local stack = self:getLocalUI():getUILayerStack() for i = #stack, 1, -1 do local layer = stack[i] if layer then layer:close(dontDestroy) end end end function UILayerUtil:PrintLocalUIList() local stack = self:getGlobalUI():getUILayerStack() for _, layer in ipairs(stack) do printInfo(LOGTAG, "UILayer inst tag:%d SiblingIndex:%d", layer.tag, layer.__uiContainer.transform:GetSiblingIndex()) end end function UILayerUtil:getLocalByTag(tag) local stack = self:getLocalUI():getUILayerStack() for _, ui in ipairs(stack) do if ui.__tag == tag then return ui end end end function UILayerUtil:getGlobalByTag(tag) local stack = self:getGlobalUI():getUILayerStack() for _, ui in ipairs(stack) do if ui.__tag == tag then return ui end end end --#region pause resume function UILayerUtil:onAppPause() local rootUIObj = self:getLocalUI() if rootUIObj:getPauseList() then return end local pauseList = {audio = {}, timeline = {}, videoplayer = {}, action = {} } pauseList.audio = util.pause.pauseAudio(rootUIObj:getGameObject()) pauseList.timeline = util.pause.pauseTimeline(rootUIObj:getGameObject()) pauseList.videoplayer = util.pause.pauseVideoPlayer(rootUIObj:getGameObject()) pauseList.action = util.pause.pauseAction(rootUIObj:getGameObject()) rootUIObj:setPauseList(pauseList) end function UILayerUtil:onAppResume() local rootUIObj = self:getLocalUI() if not rootUIObj:getPauseList() then return end local pauseList = rootUIObj:getPauseList() rootUIObj:setPauseList(nil) util.pause.resumeAudio(pauseList.audio) util.pause.resumeTimeline(pauseList.timeline) util.pause.resumeVideoPlayer(pauseList.videoplayer) util.pause.resumeAction(pauseList.action) end function UILayerUtil:stopLocalUI() local rootGo = self:getLocalUI():getGameObject() util.pause.stopAll(rootGo) end --#endregion function UILayerUtil:calcCanvasScalerFactor(canvasGo) local BEST_SCREEN_RATIO = UIConfig.safeWidth / UIConfig.safeHeight local screenWidth = UnityEngine.Screen.width local screenHeight = UnityEngine.Screen.height -- 如果配置了四个方向都支持,启动的时候可能是竖屏,导致算出来的缩放比例不对 -- if screenWidth < screenHeight then -- screenWidth,screenHeight = screenHeight,screenWidth -- end local CURRENT_SCREEN_RATIO = screenWidth / screenHeight local canvas = canvasGo:GetComponent(typeof(UnityEngine.Canvas)) if not self.scaleFactor then if CURRENT_SCREEN_RATIO > BEST_SCREEN_RATIO then self.scaleFactor = screenHeight / UIConfig.safeHeight else self.scaleFactor = screenWidth / UIConfig.safeWidth end end canvas.scaleFactor = self.scaleFactor end ---获取全局的 uiroot 和 uinode ---@return UIRoot ---@return CS.UnityEngine.GameObject|CS.UnityEngine.Object function UILayerUtil:getGlobalUI() if not self.uirootObj then self.uirootObj = self:_getOrCreateUIRoot(self.uiroot, true, nil) end return self.uirootObj, self.uinode end function UILayerUtil:_getOrCreateUIRoot(go, isGlobalUI, uiMask) for index, value in ipairs(self._uirootList) do if value.go == go then return value end end local uirootObj = UIRoot.new(go, isGlobalUI, uiMask) table.insert(self._uirootList, uirootObj) return uirootObj end function UILayerUtil:_removeUIRoot(uirootObj) table.removeByValue(self._uirootList, uirootObj, true) end function UILayerUtil:CloseAllGlobal() printInfo(LOGTAG, "CloseAllGlobal") local stack = self:getGlobalUI():getUILayerStack() or {} for i = #stack, 1, -1 do local layer = stack[i] layer:close() end for index, uirootObj in ipairs(self._uirootList) do uirootObj:exit() end end function UILayerUtil:isInited() return not CS.LuaHelper.IsNull(self.uiroot) end --#region UILayer api function UILayerUtil:_addLayerIndex() self.layerIndex = self.layerIndex + 1 return self.layerIndex end function UILayerUtil:_getPrefabGameObject() return UnityEngine.GameObject.Instantiate(self.prefab) end function UILayerUtil:_getMaskGameObject() return UnityEngine.GameObject.Instantiate(self.mask) end function UILayerUtil:_getEventSystem() return self.eventSystem[UnityEngine.EventSystems.EventSystem] end --#endregion function UILayerUtil:getCamera() return self.cameraCom end function UILayerUtil:getCanvas() return self.canvas end function UILayerUtil:isGlobalUIGo(go) return go:SeekInParentHierarchy(GLOBAL_UIROOT_NAME) end function UILayerUtil:isUIGo(go) return go:SeekInParentHierarchy("UICanvas") end -- 获取屏幕ui大小 function UILayerUtil:getCanvasSize() if not self.uinode then return CS.UnityEngine.Vector2(CS.UnityEngine.Screen.width, CS.UnityEngine.Screen.height) end local canvas = self.uinode:Seek("UICanvas") return canvas.transform.sizeDelta end function UILayerUtil:findUILayerOfGo(go) local function findUILayerIsGo(_go) for index, value in ipairs(self._uirootList) do local stack = value:getUILayerStack() for _, layer in ipairs(stack) do if layer:getUIContainer() == _go then return layer end end end return nil end local curr = go while curr do local layer = findUILayerIsGo(curr) if layer then return layer end curr = curr:GetParent() end end function UILayerUtil:setMaskAlpha(alpha) self._maskAlpha = alpha end function UILayerUtil:getMaskAlpha() return self._maskAlpha or 0.63 end --- 根据UI界面名称关闭对应的UI ---@param uiName string UI界面的类名 ---@return boolean 是否成功关闭UI function UILayerUtil:closeUIByName(uiName) if not uiName then printInfo(LOGTAG, "closeUIByName: uiName is nil") return false end -- 先检查全局UI栈 local globalStack = self:getGlobalUI():getUILayerStack() or {} for i = #globalStack, 1, -1 do local layer = globalStack[i] if layer.__cls_name == uiName then layer:close() return true end end -- 再检查局部UI栈 local localStack = self:getLocalUI():getUILayerStack() or {} for i = #localStack, 1, -1 do local layer = localStack[i] if layer.__cls_name == uiName then layer:close() return true end end printInfo(LOGTAG, "closeUIByName: UI '%s' not found", uiName) return false end -- 根据名字查找UI function UILayerUtil:findUIByName(uiName) local globalStack = self:getGlobalUI():getUILayerStack() or {} local localStack = self:getLocalUI():getUILayerStack() or {} for _, layer in ipairs(globalStack) do if layer.__cls_name == uiName then return layer end end for _, layer in ipairs(localStack) do if layer.__cls_name == uiName then return layer end end return nil end -- 判断某一个界面是否打开 function UILayerUtil:isUIOpen(uiName) local globalStack = self:getGlobalUI():getUILayerStack() or {} local localStack = self:getLocalUI():getUILayerStack() or {} for _, layer in ipairs(globalStack) do if layer.__cls_name == uiName then return true end end for _, layer in ipairs(localStack) do if layer.__cls_name == uiName then return true end end return false end UILayerUtil:init() PassivityConst#--[[ author:{zhangpeng} time:2025-05-16 17:29:36 ]] local PassivityConst = defClassStatic("PassivityConst") local LOGTAG = "PassivityConst" -- 闲逛建筑id PassivityConst.ids = { BuildingConst.buildingType.outdoormovie,-- 露天电影院 BuildingConst.buildingType.barrel, -- 酒桶 BuildingConst.buildingType.fruitstand, -- 水果台 } -- 闲逛建筑同时可接待顾客数量 PassivityConst.acceptCount ={ [BuildingConst.buildingType.outdoormovie] = 3, -- 露天电影院可同时接待3个 [BuildingConst.buildingType.barrel] = 1, -- 酒桶可同时接待1个 [BuildingConst.buildingType.fruitstand] = 1, -- 水果台可同时接待1个 } -- 闲逛建筑接待一个顾客花费的时间 PassivityConst.acceptTime ={ [BuildingConst.buildingType.outdoormovie] = 2, -- 露天电影院接待一个顾客花费2秒 [BuildingConst.buildingType.barrel] = 2, -- 酒桶接待一个顾客花费2秒 [BuildingConst.buildingType.fruitstand] = 2, -- 水果台接待一个顾客花费2秒 } return PassivityConst TouchListener ---@class TouchListener:LuaClass local TouchListener = defClass("TouchListener") local globalIndex = 0 function TouchListener:ctor(gameObject, touchCom) self.gameObject = gameObject self.priority = 0 self._dead = false self._bSwallow = true -- 吞没事件 self._enabled = true self._actived = false self.touchCom = touchCom self._globalIndex = globalIndex globalIndex = globalIndex + 1 self:onInit() end function TouchListener:disable() self._enabled = false return self end function TouchListener:enable() self._enabled = true return self end function TouchListener:isEnable() return self._enabled and self.gameObject.activeInHierarchy end function TouchListener:setPriority(priority) self.priority = priority self.touchCom.sortDirtry = true return self end --#region begin:override me function TouchListener:onInit() end ---@param point CS.UnityEngine.Vector3 function TouchListener:onBegan(point) end ---@param point CS.UnityEngine.Vector3 function TouchListener:onMoved(point) end ---@param point CS.UnityEngine.Vector3 function TouchListener:onEnd(point) end --#endregion function TouchListener:setBeganCb(cb) self._beganCb = cb return self end function TouchListener:setMovedCb(cb) self._movedCb = cb return self end function TouchListener:setEndCb(cb) self._endCb = cb return self end function TouchListener:destroy() self._dead = true end function TouchListener:isDead() return self._dead or CS.LuaHelper.IsNull(self.gameObject) end function TouchListener:setSwallow(isSwallow) self._bSwallow = isSwallow return self end mainlocal platform = CS.UnityEngine.Application.platform local platformFolderName = "pc/" if platform == CS.UnityEngine.RuntimePlatform.IPhonePlayer then platformFolderName = "ios/" elseif platform == CS.UnityEngine.RuntimePlatform.Android then platformFolderName = "android/" elseif platform == CS.UnityEngine.RuntimePlatform.OSXEditor or platform == CS.UnityEngine.RuntimePlatform.WindowsEditor then platformFolderName = "pc/" end -- 是否加载广告,支付,登录相关(调试阶段先不加载) local debug_use_sdk = false if debug_use_sdk then require("common/platform/base/XSdkConstants") require("common/platform/base/XSdkBase") require("common/platform/" .. platformFolderName .. "XSdk") require("common/platform/base/common/PlatformUtil") require("common/platform/base/applovin/ApplovinConst") require("common/platform/base/applovin/ApplovinMgr") require("common/platform/base/firebase/FirebaseEventEnum") require("common/platform/base/firebase/FirebaseAnalyticsUtil") require("common/platform/base/facebook/login/FacebookLoginUtil") require("common/platform/base/apple/login/AppleLoginMgr") require("common/platform/base/payment/PaymentErrorCode") require("common/platform/base/payment/ApplePaymentMgr") require("common/platform/base/payment/GooglePaymentMgr") require("common/platform/base/payment/PaymentMgr") require("common/platform/base/device/DeviceVibration") end SimpleEventw-- 简单事件 local SimpleEvent = defClass("SimpleEvent") local LOGTAG = "SimpleEvent" -- 构造函数 function SimpleEvent:ctor(position, disposable) self.position = position self.disposable = disposable or false -- 是否自动释放 self:init() end -- 初始化任务事件 function SimpleEvent:init() self.eventList = {} -- 任务事件列表 end -- 添加任务事件 function SimpleEvent:addEvent(func) if type(func) == "function" then local id = 1 --local count = 1 --for i, v in ipairs(self.eventList) do -- count = count + 1 --end local count = table.count(self.eventList) + 1 for i = 1, count do if not self.eventList[i] then id = i break end end self.eventList[id] = func if self.disposable then return -1 end return id else printError(LOGTAG .. self.position, "添加任务事件失败,func不是函数类型") end return -1 end -- 删除事件 function SimpleEvent:removeEventById(id) if not id or (id == -1) then return end if self.eventList[id] then self.eventList[id] = nil else printError(LOGTAG .. self.position, "删除事件失败,id不存在") end end -- 删除事件 function SimpleEvent:removeEvent(func) if not func or type(func) ~= "function" then return end for id, event in pairs(self.eventList) do if event == func then self.eventList[id] = nil return end end printError(LOGTAG .. self.position, "删除事件失败,func不存在") end -- 清空事件列表 function SimpleEvent:clearEvents() for id, _ in pairs(self.eventList) do self.eventList[id] = nil end end -- 事件触发 function SimpleEvent:triggerEvent(...) for id, event in pairs(self.eventList) do if event then event(id, ...) end if self.disposable then self.eventList[id] = nil -- 如果是一次性事件,触发后删除 end end end return SimpleEvent SceneInfo--[[ author:{zhangpeng} time:2023-03-21 20:26:17 ]] local SceneInfo = defClass("SceneInfo") function SceneInfo:ctor(cfg, args) self:setArgs(args) self:setSceneCfg(cfg) local metatable = getmetatable(self) metatable.__tostring = function (t) return string.format("SceneInfo. path:%s, name:%s", t.path, t.name) end end function SceneInfo:setSceneCfg(cfg) self.cfg = DeepCopy(cfg) self.path = cfg[1] -- 场景代码入口 self.name = cfg[2] or "" -- 场景名字 self.enterTransCls = cfg.enterTransCls end function SceneInfo:getCfg() return self.cfg end function SceneInfo:setArgs(args) self.args = args end function SceneInfo:getArgs() return self.args end function SceneInfo:getPath() return self.path end function SceneInfo:getFileName() local list = string.split(self:getPath(), "/") return list[#list] end function SceneInfo:getName() return self.name end function SceneInfo:getTag() return self.tag end function SceneInfo:setSkipHotUpdate(bool) self.isSkipHotUpdate = bool end function SceneInfo:getEnterTransCls() return self.enterTransCls end function SceneInfo:setEnterTransCls(transCls) self.enterTransCls = transCls end return SceneInfo EmployeeSkinCell--- 皮肤 ---@class EmployeeSkinCell local EmployeeSkinCell = defClass("EmployeeSkinCell") function EmployeeSkinCell:ctor(employeeCfg, go, clickAction) self.ui = go self.employeeCfgId = employeeCfg.id self.config = employeeCfg self.action = clickAction self:initUI() end function EmployeeSkinCell:initUI() self.skin_icon = self.ui:Seek("skin_icon") --self.skin_icon:GetComponent("Image").sprite = ResLoader.loadAsset(self.config.res, typeof(CS.UnityEngine.Sprite)) util.ugui.addButtonClickEvent(self.skin_icon, function() self:click() end) end function EmployeeSkinCell:click() self.action(self.employeeCfg) end return EmployeeSkinCellTaskOrderUserData?local TaskOrderUserData = defClass("TaskOrderUserData") local LOGTAG = "TaskOrderUserData" -- 构造函数 function TaskOrderUserData:ctor() self:init() end -- 初始化 function TaskOrderUserData:init() -- 任务订单创建时间 {task_order_id: create_time} self.task_order_create_times = {} -- 任务订单是否重置过 {task_order_id: is_reset} self.task_order_is_reset = {} -- 任务订单通知提示 {task_order_id: need_prompt} self.task_order_notification_prompts = {} end -- 重置数据 function TaskOrderUserData:resetData() self:save() end -- 加载数据 function TaskOrderUserData:load() if UserDataMgr.isNewPlayer then self:resetData() else self:loadFromLocal() end self:loadComplete() end function TaskOrderUserData:loadComplete() end -- 从本地加载数据 function TaskOrderUserData:loadFromLocal() self.task_order_create_times = self:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.task_order_create_times, "{}"))) self.task_order_is_reset = self:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.task_order_is_reset, "{}"))) self.task_order_notification_prompts = self:idToNumber(json.decode(PlayerPrefsMgr:getString(StrogeKeyDef.task_order_notification_prompts, "{}"))) end -- 保存数据 function TaskOrderUserData:save() self:saveToLocal() end -- 保存到本地 function TaskOrderUserData:saveToLocal() PlayerPrefsMgr:setString(StrogeKeyDef.task_order_create_times, json.encode(self:idToString(self.task_order_create_times))) PlayerPrefsMgr:setString(StrogeKeyDef.task_order_is_reset, json.encode(self:idToString(self.task_order_is_reset))) PlayerPrefsMgr:setString(StrogeKeyDef.task_order_notification_prompts, json.encode(self:idToString(self.task_order_notification_prompts))) end function TaskOrderUserData:getTaskOrderCreateTimes() return self.task_order_create_times end function TaskOrderUserData:getTaskOrderCreateTime(taskOrderId) return self.task_order_create_times[taskOrderId] end function TaskOrderUserData:setTaskOrderId(taskOrderId) self.task_order_create_times[taskOrderId] = os.time() -- 记录创建时间 end function TaskOrderUserData:getTaskOrderIsReset(taskOrderId) return self.task_order_is_reset[taskOrderId] or false -- 获取是否重置过 end function TaskOrderUserData:resetTaskOrderId(taskOrderId) self.task_order_is_reset[taskOrderId] = true -- 记录是否重置过 self.task_order_create_times[taskOrderId] = os.time() -- 重置时更新创建时间 end function TaskOrderUserData:setTaskOrderPrompt(taskOrderId, needPrompt) self.task_order_notification_prompts[taskOrderId] = needPrompt -- 记录是否需要通知 end function TaskOrderUserData:cleanTaskOrderId(taskOrderId) self.task_order_create_times[taskOrderId] = nil self.task_order_notification_prompts[taskOrderId] = nil end function TaskOrderUserData:taskOrderIdsLength() local count = 0 for _ in pairs(self.task_order_create_times) do count = count + 1 end return count end -- 字典中的id转字符串 function TaskOrderUserData:idToString(dic) local put = {} for i, v in pairs(dic) do put[tostring(i)] = v end return put end -- 字典中的id转数字 function TaskOrderUserData:idToNumber(dic) local put = {} for i, v in pairs(dic) do put[tonumber(i)] = v end return put end return TaskOrderUserDatauitoastreslinkureturn { --BASIC --ASSET toast = {"Assets/AssetsPackage/Res/modules/common/ui/toast/toast.prefab", 0, 0}, } main5print("commmon.managers.cmd.http.main.lua loaded") RichCustomer3 --[[ 富二代/骆驼 闲逛的过程中,点击掉落金币 ]] local RichCustomer, super = defClass("RichCustomer", CustomerSpecial) local LOGTAG = "RichCustomer" -- 状态 RichCustomer.State = { Idle = 1, -- 空闲 Entrance = 2, -- 入场 Wandering = 3, -- 闲逛 Leaving = 4, -- 离开 } --- 构造函数 function RichCustomer:ctor(reslink) super.ctor(self, reslink, CustomerConst.CustomerSpecialType.RichCustomer) self:init() end --- 初始化 function RichCustomer:init() self:initDisplay("rich_customer") self:initProperties() self:startRichLogic() end --- 初始化属性 function RichCustomer:initProperties() self.info = SpecialCustomerMgr:getSpecialCustomerInfo(self.specialCustomerTypeId) self.state = RichCustomer.State.Idle -- 初始状态 self.activeTime = self.info:getActiveTime() -- 活动时间 self.clickTimes = self.info:getClickTimes() -- 点击次数 self.currClickTimes = 0 -- 当前点击次数 end --- 开始富二代逻辑 function RichCustomer:startRichLogic() printInfo(LOGTAG, "富二代开始活动,活动时间:%d秒", self.info:getMoveSpeed()) -- 入场 self:enter() -- 添加点击事件 self:addClickEvent(function() self:click() end) -- 初始化点击进度 self:initClickProgress() end --- 入店 function RichCustomer:enter() printInfo(LOGTAG, "富二代入店") local scene = RestaurantMgr:getRestaurantScene() local enterPoint = self:getSpecialCustomerEnterPoint() self:setCurPosition(enterPoint.transform.position) self:walkToPos(enterPoint.transform.position, super.tempSpeed, function() self.state = RichCustomer.State.Wandering self:startTimer() end) end --- 离开 function RichCustomer:exit() printInfo(LOGTAG, "富二代离开") self.state = RichCustomer.State.Leaving self:walkToPos(self:getSpecialCustomerExitPoint().transform.position, super.tempSpeed, function() self:destroy() end) end --- 时间 function RichCustomer:timeupdate(seconds) self.time = self.time + seconds if self.time >= self.activeTime then self:exit() end end --- 撒钱 function RichCustomer:throw() printInfo(LOGTAG, "富二代撒钱") -- todo:: 撒钱 end --- 交互 function RichCustomer:click() if self.state ~= RichCustomer.State.Wandering then return end if self.currClickTimes < self.clickTimes then self:throw() end end return RichCustomer DollCfgParse--[[ 玩偶配置解析 author:{zhangpeng} time:2025-07-09 14:42:20 ]] local dollCfg = require("data/config/dollCfg") local DollCfgParse = defClassStatic("DollCfgParse") function DollCfgParse:init() self.dollCfg = {} for k,v in pairs(dollCfg) do self.dollCfg[v.id] = v end end function DollCfgParse:getDollCfg(id) return self.dollCfg[id] end function DollCfgParse:getData() return dollCfg end DollCfgParse:init()