diff --git a/exported_done/aip_dou_totem/aip_dou_totem.scml b/exported/aip_dou_totem/aip_dou_totem.scml similarity index 86% rename from exported_done/aip_dou_totem/aip_dou_totem.scml rename to exported/aip_dou_totem/aip_dou_totem.scml index f6ec2306..2c8d7082 100644 --- a/exported_done/aip_dou_totem/aip_dou_totem.scml +++ b/exported/aip_dou_totem/aip_dou_totem.scml @@ -8,6 +8,12 @@ + + + + + + @@ -15,11 +21,22 @@ + + - + + + + + + + + + + @@ -36,8 +53,24 @@ - - + + + + + + + + + + + + + + + + + + @@ -85,10 +118,10 @@ - + - + diff --git a/exported/aip_dou_totem/fire_marker_left/fire_marker_left.png b/exported/aip_dou_totem/fire_marker_left/fire_marker_left.png new file mode 100644 index 00000000..293bb3ff Binary files /dev/null and b/exported/aip_dou_totem/fire_marker_left/fire_marker_left.png differ diff --git a/exported/aip_dou_totem/fire_marker_right/fire_marker_right.png b/exported/aip_dou_totem/fire_marker_right/fire_marker_right.png new file mode 100644 index 00000000..293bb3ff Binary files /dev/null and b/exported/aip_dou_totem/fire_marker_right/fire_marker_right.png differ diff --git a/exported_done/aip_dou_totem/totem/full.png b/exported/aip_dou_totem/totem/full.png similarity index 100% rename from exported_done/aip_dou_totem/totem/full.png rename to exported/aip_dou_totem/totem/full.png diff --git a/exported_done/aip_dou_totem/totem/half.png b/exported/aip_dou_totem/totem/half.png similarity index 100% rename from exported_done/aip_dou_totem/totem/half.png rename to exported/aip_dou_totem/totem/half.png diff --git a/exported_done/aip_dou_totem/totem/half.sai2 b/exported/aip_dou_totem/totem/half.sai2 similarity index 100% rename from exported_done/aip_dou_totem/totem/half.sai2 rename to exported/aip_dou_totem/totem/half.sai2 diff --git a/exported_done/aip_dou_totem/totem/head.png b/exported/aip_dou_totem/totem/head.png similarity index 100% rename from exported_done/aip_dou_totem/totem/head.png rename to exported/aip_dou_totem/totem/head.png diff --git a/exported_done/aip_dou_totem/totem/left.png b/exported/aip_dou_totem/totem/left.png similarity index 100% rename from exported_done/aip_dou_totem/totem/left.png rename to exported/aip_dou_totem/totem/left.png diff --git a/exported_done/aip_dou_totem/totem/nest.png b/exported/aip_dou_totem/totem/nest.png similarity index 100% rename from exported_done/aip_dou_totem/totem/nest.png rename to exported/aip_dou_totem/totem/nest.png diff --git a/exported_done/aip_dou_totem/totem/nest.sai2 b/exported/aip_dou_totem/totem/nest.sai2 similarity index 100% rename from exported_done/aip_dou_totem/totem/nest.sai2 rename to exported/aip_dou_totem/totem/nest.sai2 diff --git a/exported_done/aip_dou_totem/totem/right.png b/exported/aip_dou_totem/totem/right.png similarity index 100% rename from exported_done/aip_dou_totem/totem/right.png rename to exported/aip_dou_totem/totem/right.png diff --git a/exported_done/aip_dou_totem/totem/totem.sai2 b/exported/aip_dou_totem/totem/totem.sai2 similarity index 100% rename from exported_done/aip_dou_totem/totem/totem.sai2 rename to exported/aip_dou_totem/totem/totem.sai2 diff --git a/exported_done/aip_dou_totem/totem/up.png b/exported/aip_dou_totem/totem/up.png similarity index 100% rename from exported_done/aip_dou_totem/totem/up.png rename to exported/aip_dou_totem/totem/up.png diff --git a/exported/aip_fake_fly_totem/aip_fake_fly_totem.scml b/exported/aip_fake_fly_totem/aip_fake_fly_totem.scml new file mode 100644 index 00000000..54df2f5e --- /dev/null +++ b/exported/aip_fake_fly_totem/aip_fake_fly_totem.scml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/exported/aip_fake_fly_totem/body/lighthouse_ground-1.png b/exported/aip_fake_fly_totem/body/lighthouse_ground-1.png new file mode 100644 index 00000000..304e09ae Binary files /dev/null and b/exported/aip_fake_fly_totem/body/lighthouse_ground-1.png differ diff --git a/exported/aip_fake_fly_totem/body/totem.png b/exported/aip_fake_fly_totem/body/totem.png new file mode 100644 index 00000000..7c9b82f4 Binary files /dev/null and b/exported/aip_fake_fly_totem/body/totem.png differ diff --git a/exported/aip_fake_fly_totem/body/totem.sai2 b/exported/aip_fake_fly_totem/body/totem.sai2 new file mode 100644 index 00000000..d1f6c841 Binary files /dev/null and b/exported/aip_fake_fly_totem/body/totem.sai2 differ diff --git a/exported/aip_legion/aip_legion.scml b/exported/aip_legion/aip_legion.scml new file mode 100644 index 00000000..d2615c1e --- /dev/null +++ b/exported/aip_legion/aip_legion.scml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/exported/aip_legion/body/full.png b/exported/aip_legion/body/full.png new file mode 100644 index 00000000..ffd295c0 Binary files /dev/null and b/exported/aip_legion/body/full.png differ diff --git a/exported/aip_legion/body/full.sai2 b/exported/aip_legion/body/full.sai2 new file mode 100644 index 00000000..7d0b70a7 Binary files /dev/null and b/exported/aip_legion/body/full.sai2 differ diff --git a/exported/aip_rubik/aip_rubik.scml b/exported/aip_rubik/aip_rubik.scml new file mode 100644 index 00000000..7ecc5593 --- /dev/null +++ b/exported/aip_rubik/aip_rubik.scml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/exported/aip_rubik/body/full.png b/exported/aip_rubik/body/full.png new file mode 100644 index 00000000..d6bbf217 Binary files /dev/null and b/exported/aip_rubik/body/full.png differ diff --git a/exported/aip_rubik/body/full.sai2 b/exported/aip_rubik/body/full.sai2 new file mode 100644 index 00000000..456b7629 Binary files /dev/null and b/exported/aip_rubik/body/full.sai2 differ diff --git a/exported/aip_rubik/fire_marker/fire_marker.png b/exported/aip_rubik/fire_marker/fire_marker.png new file mode 100644 index 00000000..293bb3ff Binary files /dev/null and b/exported/aip_rubik/fire_marker/fire_marker.png differ diff --git a/exported/aip_rubik_fire/aip_rubik_fire.scml b/exported/aip_rubik_fire/aip_rubik_fire.scml new file mode 100644 index 00000000..fd99b005 --- /dev/null +++ b/exported/aip_rubik_fire/aip_rubik_fire.scml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/exported/aip_rubik_fire/body/00000002 b/exported/aip_rubik_fire/body/00000002 new file mode 100644 index 00000000..7599f92d Binary files /dev/null and b/exported/aip_rubik_fire/body/00000002 differ diff --git a/exported/aip_rubik_fire/body/fire.png b/exported/aip_rubik_fire/body/fire.png new file mode 100644 index 00000000..735c16a2 Binary files /dev/null and b/exported/aip_rubik_fire/body/fire.png differ diff --git a/exported/aip_rubik_fire/body/light.png b/exported/aip_rubik_fire/body/light.png new file mode 100644 index 00000000..64679311 Binary files /dev/null and b/exported/aip_rubik_fire/body/light.png differ diff --git a/exported/aip_rubik_fire/body/next.png b/exported/aip_rubik_fire/body/next.png new file mode 100644 index 00000000..f364b244 Binary files /dev/null and b/exported/aip_rubik_fire/body/next.png differ diff --git a/exported/aip_rubik_ghost/aip_rubik_ghost.scml b/exported/aip_rubik_ghost/aip_rubik_ghost.scml new file mode 100644 index 00000000..37c38ea6 --- /dev/null +++ b/exported/aip_rubik_ghost/aip_rubik_ghost.scml @@ -0,0 +1,572 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/exported/aip_rubik_ghost/body/body.png b/exported/aip_rubik_ghost/body/body.png new file mode 100644 index 00000000..2eb35cb5 Binary files /dev/null and b/exported/aip_rubik_ghost/body/body.png differ diff --git a/exported/aip_rubik_ghost/body/full.sai2 b/exported/aip_rubik_ghost/body/full.sai2 new file mode 100644 index 00000000..db579742 Binary files /dev/null and b/exported/aip_rubik_ghost/body/full.sai2 differ diff --git a/exported/aip_rubik_ghost/body/hair1.png b/exported/aip_rubik_ghost/body/hair1.png new file mode 100644 index 00000000..93c801a4 Binary files /dev/null and b/exported/aip_rubik_ghost/body/hair1.png differ diff --git a/exported/aip_rubik_ghost/body/hair2.png b/exported/aip_rubik_ghost/body/hair2.png new file mode 100644 index 00000000..ead8694e Binary files /dev/null and b/exported/aip_rubik_ghost/body/hair2.png differ diff --git a/exported/aip_rubik_ghost/body/hair3.png b/exported/aip_rubik_ghost/body/hair3.png new file mode 100644 index 00000000..6e19f5c8 Binary files /dev/null and b/exported/aip_rubik_ghost/body/hair3.png differ diff --git a/exported/aip_rubik_ghost/body/head.png b/exported/aip_rubik_ghost/body/head.png new file mode 100644 index 00000000..7ef9b773 Binary files /dev/null and b/exported/aip_rubik_ghost/body/head.png differ diff --git a/exported/aip_rubik_ghost/body/jaw.png b/exported/aip_rubik_ghost/body/jaw.png new file mode 100644 index 00000000..78a02b38 Binary files /dev/null and b/exported/aip_rubik_ghost/body/jaw.png differ diff --git a/exported/aip_rubik_ghost/body/tail.png b/exported/aip_rubik_ghost/body/tail.png new file mode 100644 index 00000000..c4c7de9d Binary files /dev/null and b/exported/aip_rubik_ghost/body/tail.png differ diff --git a/exported/aip_rubik_heart/aip_rubik_heart.scml b/exported/aip_rubik_heart/aip_rubik_heart.scml new file mode 100644 index 00000000..a95b0edc --- /dev/null +++ b/exported/aip_rubik_heart/aip_rubik_heart.scml @@ -0,0 +1,455 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/exported/aip_rubik_heart/body/down.png b/exported/aip_rubik_heart/body/down.png new file mode 100644 index 00000000..a6b7ec89 Binary files /dev/null and b/exported/aip_rubik_heart/body/down.png differ diff --git a/exported/aip_rubik_heart/body/full.sai2 b/exported/aip_rubik_heart/body/full.sai2 new file mode 100644 index 00000000..e1d7bb6d Binary files /dev/null and b/exported/aip_rubik_heart/body/full.sai2 differ diff --git a/exported/aip_rubik_heart/body/left.png b/exported/aip_rubik_heart/body/left.png new file mode 100644 index 00000000..9c1fbd25 Binary files /dev/null and b/exported/aip_rubik_heart/body/left.png differ diff --git a/exported/aip_rubik_heart/body/right.png b/exported/aip_rubik_heart/body/right.png new file mode 100644 index 00000000..6c3eb148 Binary files /dev/null and b/exported/aip_rubik_heart/body/right.png differ diff --git a/exported/aip_rubik_heart/body/up.png b/exported/aip_rubik_heart/body/up.png new file mode 100644 index 00000000..1098c8b4 Binary files /dev/null and b/exported/aip_rubik_heart/body/up.png differ diff --git a/exported/aip_wizard_hat/aip_wizard_hat.scml b/exported/aip_wizard_hat/aip_wizard_hat.scml new file mode 100644 index 00000000..614a65cf --- /dev/null +++ b/exported/aip_wizard_hat/aip_wizard_hat.scml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/exported/aip_wizard_hat/swap_hat/aip_joker_face-0.sai2 b/exported/aip_wizard_hat/swap_hat/aip_joker_face-0.sai2 new file mode 100644 index 00000000..063f990b Binary files /dev/null and b/exported/aip_wizard_hat/swap_hat/aip_joker_face-0.sai2 differ diff --git a/exported/aip_wizard_hat/swap_hat/aip_joker_face-3.sai2 b/exported/aip_wizard_hat/swap_hat/aip_joker_face-3.sai2 new file mode 100644 index 00000000..b111cb84 Binary files /dev/null and b/exported/aip_wizard_hat/swap_hat/aip_joker_face-3.sai2 differ diff --git a/exported/aip_wizard_hat/swap_hat/baseline-0.png b/exported/aip_wizard_hat/swap_hat/baseline-0.png new file mode 100644 index 00000000..87cf80ad Binary files /dev/null and b/exported/aip_wizard_hat/swap_hat/baseline-0.png differ diff --git a/exported/aip_wizard_hat/swap_hat/swap_hat-0.png b/exported/aip_wizard_hat/swap_hat/swap_hat-0.png new file mode 100644 index 00000000..c22041a3 Binary files /dev/null and b/exported/aip_wizard_hat/swap_hat/swap_hat-0.png differ diff --git a/exported/aip_wizard_hat/swap_hat/swap_hat-1.png b/exported/aip_wizard_hat/swap_hat/swap_hat-1.png new file mode 100644 index 00000000..83622457 Binary files /dev/null and b/exported/aip_wizard_hat/swap_hat/swap_hat-1.png differ diff --git a/exported/aip_wizard_hat/swap_hat/swap_hat-2.png b/exported/aip_wizard_hat/swap_hat/swap_hat-2.png new file mode 100644 index 00000000..c22041a3 Binary files /dev/null and b/exported/aip_wizard_hat/swap_hat/swap_hat-2.png differ diff --git a/exported/aip_wizard_hat/swap_hat/swap_hat-3.png b/exported/aip_wizard_hat/swap_hat/swap_hat-3.png new file mode 100644 index 00000000..0cc2358e Binary files /dev/null and b/exported/aip_wizard_hat/swap_hat/swap_hat-3.png differ diff --git a/images/aipWidgets/aipRubik.sai2 b/images/aipWidgets/aipRubik.sai2 new file mode 100644 index 00000000..03c0e901 Binary files /dev/null and b/images/aipWidgets/aipRubik.sai2 differ diff --git a/images/inventoryimages/aip_fake_fly_totem.png b/images/inventoryimages/aip_fake_fly_totem.png new file mode 100644 index 00000000..20a429e1 Binary files /dev/null and b/images/inventoryimages/aip_fake_fly_totem.png differ diff --git a/images/inventoryimages/aip_fake_fly_totem.tex b/images/inventoryimages/aip_fake_fly_totem.tex new file mode 100644 index 00000000..e6912428 Binary files /dev/null and b/images/inventoryimages/aip_fake_fly_totem.tex differ diff --git a/images/inventoryimages/aip_fake_fly_totem.xml b/images/inventoryimages/aip_fake_fly_totem.xml new file mode 100644 index 00000000..ff74faed --- /dev/null +++ b/images/inventoryimages/aip_fake_fly_totem.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/inventoryimages/aip_legion.png b/images/inventoryimages/aip_legion.png new file mode 100644 index 00000000..35c9d28e Binary files /dev/null and b/images/inventoryimages/aip_legion.png differ diff --git a/images/inventoryimages/aip_legion.tex b/images/inventoryimages/aip_legion.tex new file mode 100644 index 00000000..3b943457 Binary files /dev/null and b/images/inventoryimages/aip_legion.tex differ diff --git a/images/inventoryimages/aip_legion.xml b/images/inventoryimages/aip_legion.xml new file mode 100644 index 00000000..b54ca2a2 --- /dev/null +++ b/images/inventoryimages/aip_legion.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/inventoryimages/aip_wizard_hat.png b/images/inventoryimages/aip_wizard_hat.png new file mode 100644 index 00000000..c7bf1ac0 Binary files /dev/null and b/images/inventoryimages/aip_wizard_hat.png differ diff --git a/images/inventoryimages/aip_wizard_hat.tex b/images/inventoryimages/aip_wizard_hat.tex new file mode 100644 index 00000000..fe8cec57 Binary files /dev/null and b/images/inventoryimages/aip_wizard_hat.tex differ diff --git a/images/inventoryimages/aip_wizard_hat.xml b/images/inventoryimages/aip_wizard_hat.xml new file mode 100644 index 00000000..9ddf523a --- /dev/null +++ b/images/inventoryimages/aip_wizard_hat.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/modmain.lua b/modmain.lua index 90bfcb67..e84134c0 100644 --- a/modmain.lua +++ b/modmain.lua @@ -89,8 +89,12 @@ PrefabFiles = { "aip_suwu", "aip_suwu_mound", "aip_breadfruit_tree", - -- "aip_rubik_fire", - -- "aip_rubik", + "aip_rubik_fire", + "aip_rubik", + "aip_legion", + "aip_rubik_ghost", + "aip_rubik_heart", + "aip_wizard_hat", -- Orbit "aip_orbit", @@ -165,6 +169,8 @@ modimport("scripts/shadowPackageAction.lua") modimport("scripts/widgetHooker.lua") modimport("scripts/recpiesHooker.lua") modimport("scripts/flyWrapper.lua") +modimport("scripts/houseWrapper.lua") +modimport("scripts/sgHooker.lua") ------------------------------------- 测试专用 ------------------------------------- if GetModConfigData("dev_mode") == "enabled" then diff --git a/scripts/aipUtils.lua b/scripts/aipUtils.lua index ac4e5c83..f0e4e53f 100644 --- a/scripts/aipUtils.lua +++ b/scripts/aipUtils.lua @@ -62,8 +62,10 @@ end function _G.aipTableSlice(tbl, start, len) local list = {} + local finalStart = start or 1 + local finalLen = len or #tbl - for i = start, math.min(len + start - 1, #tbl) do + for i = finalStart, math.min(finalLen + finalStart - 1, #tbl) do table.insert(list, tbl[i]) end return list @@ -138,7 +140,11 @@ function _G.aipCommonStr(showType, split, ...) parsed = parsed .. "}" end - str = str .. "[" .. vType .. ": " .. tostring(parsed) .. "]" .. split + if vType == "string" then + str = str.."'"..tostring(parsed).."'"..split + else + str = str .. "[" .. vType .. ": " .. tostring(parsed) .. "]" .. split + end else -- 显示文字 str = str .. tostring(parsed) .. split @@ -236,11 +242,17 @@ function _G.aipDiffAngle(a1, a2) return math.min(diff1, diff2) end --- 返回两点之间的距离(无视 Y 坐标) -function _G.aipDist(p1, p2) +-- 返回两点之间的距离(默认无视 Y 坐标) +function _G.aipDist(p1, p2, includeY) local dx = p1.x - p2.x local dz = p1.z - p2.z - return math.pow(dx*dx+dz*dz, 0.5) + local dy = p1.y - p2.y + + if includeY then + return math.pow(dx*dx+dy*dy+dz*dz, 1/3) + else + return math.pow(dx*dx+dz*dz, 0.5) + end end function _G.aipRandomEnt(ents) @@ -500,9 +512,13 @@ function _G.aipFindEnt(...) end -- 是暗影生物 -_G.aipShadowTags = { "shadow", "shadowminion", "shadowchesspiece", "stalker", "stalkerminion" } +_G.aipShadowTags = { "shadow", "shadowminion", "shadowchesspiece", "stalker", "stalkerminion", "aip_shadowcreature" } function _G.aipIsShadowCreature(inst) + if not inst then + return false + end + for i, tag in ipairs(_G.aipShadowTags) do if inst:HasTag(tag) then return true @@ -518,18 +534,22 @@ function _G.aipRPC(funcName, ...) end -- 添加 aipc_buffer -function _G.patchBuffer(inst, name, duration, fn) +function _G.patchBuffer(inst, name, duration, fn, showFX) if inst.components.aipc_buffer == nil then inst:AddComponent("aipc_buffer") end - inst.components.aipc_buffer:Patch(name, duration, fn) + inst.components.aipc_buffer:Patch(name, duration, fn, showFX) end -- 存在 aipc_buffer function _G.hasBuffer(inst, name) - if inst.components.aipc_buffer ~= nil then - return inst.components.aipc_buffer.buffers[name] ~= nil + -- if inst.components.aipc_buffer ~= nil then + -- return inst.components.aipc_buffer.buffers[name] ~= nil + -- end + + if inst.replica.aipc_buffer ~= nil then + return inst.replica.aipc_buffer:HasBuffer(name) end return false diff --git a/scripts/brains/aip_rubik_ghost_brain.lua b/scripts/brains/aip_rubik_ghost_brain.lua new file mode 100644 index 00000000..c8985a4a --- /dev/null +++ b/scripts/brains/aip_rubik_ghost_brain.lua @@ -0,0 +1,55 @@ +require "behaviours/wander" +require "behaviours/chaseandattack" + +local NOTAGS = { "FX", "NOCLICK", "DECOR", "playerghost", "INLIMBO" } + +local MAX_CHASE_DIST = 15 +local MAX_CHASE_TIME = 8 + +local MAX_WANDER_DIST = 20 + +local RUN_AWAY_DIST = 3 +local STOP_RUN_AWAY_DIST = 5 +local STOP_RUN_AWAY_DIST_MAX = 8 + +local RubikGhostBrain = Class(Brain, function(self, inst) + Brain._ctor(self, inst) + self.mytarget = nil +end) + +function RubikGhostBrain:OnStop() +end + +function RubikGhostBrain:OnStart() + local root = PriorityNode({ + -- 寻找目标攻击 + WhileNode( + function() + return self.inst.components.combat.target == nil or not self.inst.components.combat:InCooldown() + end, + "AttackMomentarily", + ChaseAndAttack(self.inst, MAX_CHASE_TIME, MAX_CHASE_DIST) + ), + + -- 躲闪 + WhileNode( + function() + return self.inst.components.combat.target and self.inst.components.combat:InCooldown() + end, + "Dodge", + RunAway( + self.inst, + function() return self.inst.components.combat.target end, + RUN_AWAY_DIST, + math.random(STOP_RUN_AWAY_DIST, STOP_RUN_AWAY_DIST_MAX) + ) + ), + + -- 漫步 + Wander(self.inst, function() return self.inst.components.knownlocations:GetLocation("home") end, MAX_WANDER_DIST), + }, .25) + + self.bt = BT(self.inst, root) +end + +return RubikGhostBrain \ No newline at end of file diff --git a/scripts/components/aipc_aura.lua b/scripts/components/aipc_aura.lua index b08f8652..ddb5d362 100644 --- a/scripts/components/aipc_aura.lua +++ b/scripts/components/aipc_aura.lua @@ -7,6 +7,7 @@ local Aura = Class(function(self, inst) self.bufferFn = nil self.mustTags = nil self.noTags = nil + self.showFX = true self:Start() end) @@ -16,7 +17,7 @@ local function SearchToAddBuffer(inst, self) local ents = TheSim:FindEntities(x, y, z, self.range, self.mustTags, self.noTags) for i, ent in ipairs(ents) do - patchBuffer(ent, self.bufferName, self.bufferDuration, self.bufferFn) + patchBuffer(ent, self.bufferName, self.bufferDuration, self.bufferFn, self.showFX) end end diff --git a/scripts/components/aipc_buffer.lua b/scripts/components/aipc_buffer.lua index 78e5fcde..8929e802 100644 --- a/scripts/components/aipc_buffer.lua +++ b/scripts/components/aipc_buffer.lua @@ -19,6 +19,7 @@ local function DoEffect(inst, self) end self.buffers = aipFilterKeysTable(self.buffers, rmNames) + self:SyncBuffer() if allRemove then inst:RemoveComponent("aipc_buffer") @@ -41,11 +42,31 @@ local Buffer = Class(function(self, inst) self.inst:AddChild(self.fx) end) -function Buffer:Patch(name, duration, fn) +function Buffer:Patch(name, duration, fn, showFX) self.buffers[name] = { duration = duration or 2, - fn = fn + fn = fn, + showFX = showFX, } + + self:SyncBuffer() +end + +function Buffer:SyncBuffer(ames) + local names = "" + local showFX = false + for name, ent in pairs(self.buffers) do + names = names.."|"..name + showFX = showFX or ent.showFX + end + + if showFX then + self.fx:Show() + else + self.fx:Hide() + end + + self.inst.replica.aipc_buffer:SyncBuffer(names) end return Buffer \ No newline at end of file diff --git a/scripts/components/aipc_buffer_replica.lua b/scripts/components/aipc_buffer_replica.lua new file mode 100644 index 00000000..b8657248 --- /dev/null +++ b/scripts/components/aipc_buffer_replica.lua @@ -0,0 +1,23 @@ +local Buffer = Class(function(self, inst) + self.inst = inst + self.bufferNames = net_string(inst.GUID, "aipc_buffer", "aipc_buffer_dirty") +end) + +function Buffer:SyncBuffer(names) + self.bufferNames:set(names) +end + +function Buffer:HasBuffer(tgt) + local names = self.bufferNames:value() + local nameList = string.split(names, '|') + + for i, name in ipairs(nameList) do + if name == tgt then + return true + end + end + + return false +end + +return Buffer \ No newline at end of file diff --git a/scripts/components/aipc_float.lua b/scripts/components/aipc_float.lua new file mode 100644 index 00000000..81adfaee --- /dev/null +++ b/scripts/components/aipc_float.lua @@ -0,0 +1,51 @@ +-- 一个只会飞行到目标地点的投掷物 +local Float = Class(function(self, inst) + self.inst = inst + self.targetPos = nil + self.speed = 6 + self.ySpeed = 6 +end) + +-- 转向目标点 +function Float:RotateToTarget(dest) + local angle = aipGetAngle(self.inst:GetPosition(), dest) + self.inst.Transform:SetRotation(angle) + self.inst:FacePoint(dest) +end + +-- 重置坐标 +function Float:GoToPoint(pt) + self.targetPos = pt + self.inst.Physics:Teleport(pt.x, pt.y, pt.z) + + self.inst:StartUpdatingComponent(self) +end + +-- 飞向目标 +function Float:MoveToPoint(pt) + self.targetPos = pt +end + +function Float:OnUpdate(dt) + local pos = self.inst:GetPosition() + if not self.targetPos then + return + end + + -- 水平方向的距离 + local dist = aipDist(pos, self.targetPos) + local speed = self.speed + if dist < 0.3 then + speed = 0.5 + end + + -- 朝一个方向飞去 + self:RotateToTarget(self.targetPos) + self.inst.Physics:SetMotorVel( + speed, + (self.targetPos.y - pos.y) * self.ySpeed, + 0 + ) +end + +return Float \ No newline at end of file diff --git a/scripts/components/aipc_flyer_sc.lua b/scripts/components/aipc_flyer_sc.lua index 58a3f4a4..c2a279f5 100644 --- a/scripts/components/aipc_flyer_sc.lua +++ b/scripts/components/aipc_flyer_sc.lua @@ -36,8 +36,13 @@ local function IsNearDanger(inst) inst, 10, function(target) - return (target.components.combat ~= nil and target.components.combat.target == inst) or - (target:HasTag("monster") and not target:HasTag("player")) + return ( + target.components.combat ~= nil and + target.components.combat.target == inst and + target.prefab ~= "shadowtentacle" + ) or ( + target:HasTag("monster") and not target:HasTag("player") + ) end, nil, nil, diff --git a/scripts/components/aipc_projectile.lua b/scripts/components/aipc_projectile.lua index 11195b64..c7be868e 100644 --- a/scripts/components/aipc_projectile.lua +++ b/scripts/components/aipc_projectile.lua @@ -60,7 +60,7 @@ function Projectile:OnUpdate(dt) -- 目标已经失效 self:OnFinish() return - else + elseif self.target ~= nil then -- 目标留存 self.targetPos = self.target:GetPosition() end diff --git a/scripts/components/aipc_rubik.lua b/scripts/components/aipc_rubik.lua new file mode 100644 index 00000000..25d245f2 --- /dev/null +++ b/scripts/components/aipc_rubik.lua @@ -0,0 +1,549 @@ +local dev_mode = aipGetModConfig("dev_mode") == "enabled" + +local createProjectile = require("utils/aip_vest_util").createProjectile + +------------------------------- 方法 ------------------------------- +local FX_OFFSET = 2 +local FX_HEIGHT = 7 + +local function getPos(idx) + local mathIdx = idx - 1 + local y = math.floor(mathIdx / 9) + local x = math.floor(math.mod(mathIdx, 9) / 3) + local z = math.mod(mathIdx, 3) + + return x - 1, y - 1, z - 1 +end + +local function getPosVec(idx) + local x, y, z = getPos(idx) + return Vector3(x, y, z) +end + +local function getIndex(pt) + local x = pt.x + 1 + local y = pt.y + 1 + local z = pt.z + 1 + return y * 9 + x * 3 + z + 1 +end + +local function getSameCount(x1, y1, z1, x2, y2, z2) + local sameX = x1 == x2 + local sameY = y1 == y2 + local sameZ = z1 == z2 + local sameCnt = (sameX and 1 or 0) + (sameY and 1 or 0) + (sameZ and 1 or 0) + return sameCnt +end + +local function isCorner(x, y, z) + return math.abs(x) == 1 and math.abs(y) == 1 and math.abs(z) == 1 +end + +-- 获取两点相同的轴 +local function getSameAxis(p1, p2) + local axises = {} + local xyz = {'x','y','z'} + for i, axis in ipairs(xyz) do + if p1[axis] == p2[axis] then + table.insert(axises, axis) + end + end + return axises +end + +-- 是一条边的中间 +local function isEdgeCenter(x, y, z) + return math.abs(x) + math.abs(y) + math.abs(z) == 2 +end + +-- 是一个面的中间 +local function isFaceCenter(x, y, z) + return math.abs(x) + math.abs(y) + math.abs(z) == 1 +end + +-- 按照一个轴反转角度 +local function revertPT(axis, x, y, z) + if type(x) == "table" then + z = x.z + y = x.y + x = x.x + end + + local pt = Vector3(x, y, z) + pt[axis] = -pt[axis] + return pt +end + +-- 设置得到一个新的 +local function setPT(axis, pt, val) + local clone = Vector3(pt.x, pt.y, pt.z) + clone[axis] = val + return clone +end + +-- 偏移得到一个新的 +local function offsetPT(axis, pt, offset) + local val = pt[axis] + offset + return setPT(axis, pt, val) +end + +-- 根据一个轴的名字,获取剩余两个轴的名字 +local function getAxisRest(axis) + local tbl = { + x = { 'y', 'z' }, + y = { 'z', 'x' }, + z = { 'x', 'y' }, + } + return tbl[axis] +end + +-- 按照一个角度旋转 +local function rotateFace(originFxs, originMatrix, fixedAxis, fixedAxisPos, reverse) + local angle = PI / 2 + local restAxises = getAxisRest(fixedAxis) + + if reverse then + angle = -angle + end + + local next_fxs = aipTableSlice(originFxs) + local next_matrix = aipTableSlice(originMatrix) + + for x = -1, 1 do -- x y 只是代指两个轴,不是真的 x y + for y = -1, 1 do + -- 需要取整 + local nextX = math.floor(x * math.cos(angle) - y * math.sin(angle) + 0.1) + local nextY = math.floor(x * math.sin(angle) + y * math.cos(angle) + 0.1) + local xyz = Vector3() + xyz[fixedAxis] = fixedAxisPos + xyz[restAxises[1]] = x + xyz[restAxises[2]] = y + + -- 目标节点 + local nextXYZ = Vector3(xyz.x, xyz.y, xyz.z) + nextXYZ[restAxises[1]] = nextX + nextXYZ[restAxises[2]] = nextY + + -- aipPrint("ROTATE:", xyz.x, xyz.y, xyz.z, ">", nextXYZ.x, nextXYZ.y, nextXYZ.z) + + -- 交换 颜色 和 实体 + local oriIdx = getIndex(xyz) + local tgtIdx = getIndex(nextXYZ) + + next_fxs[tgtIdx] = originFxs[oriIdx] + next_matrix[tgtIdx] = originMatrix[oriIdx] + end + end + + return { + fxs = next_fxs, + matrix = next_matrix, + } +end + +-- 获取旋转后的起点和终点距离 +local function rotateDist(fxs, next_fxs, startPT, endPT) + local startRotatedIdx = aipTableIndex(next_fxs, fxs[getIndex(startPT)]) + local startRotatedPos = getPosVec(startRotatedIdx) + return aipDist(startRotatedPos, endPT, true) +end + +-- 播放动画 +local function playAnimation(fx, motion) + if fx then + fx.AnimState:PlayAnimation(motion, true) + + if motion == "optional" then + fx.rotate = true + end + end +end + +-- 根据值获得轴的名字 +local function getAxisByVal(pt, val) + if pt.x == val then + return 'x' + elseif pt.y == val then + return 'y' + elseif pt.z == val then + return 'z' + end +end + +-- 检查两个点是否在轴上(如果在轴上就不用旋转了) +local function isOnAxis(axis, pt) + if axis == 'x' then + return pt.y == 0 and pt.z == 0 + elseif axis == 'y' then + return pt.x == 0 and pt.z == 0 + elseif axis == 'z' then + return pt.x == 0 and pt.y == 0 + end +end + +------------------------------- 组件 ------------------------------- +local Rubik = Class(function(self, inst) + self.inst = inst + + self.start = false + self.selectIndex = nil + self.fxs = {} + + self.matrix = {} + self.randomTimes = 10 -- 随机剩余次数 + self.summonning = false + + local colors = {"red","green","blue"} + for y = 0, 2 do + local color = colors[y + 1] + + for x = 0, 2 do + for z = 0, 2 do + local idx = 1 + z + x * 3 + y * 9 + local finalColor = color + if isFaceCenter(x-1,y-1,z-1) or (x==1 and y==1 and z==1) then + finalColor = "yellow" + end + self.matrix[idx] = finalColor + end + end + end +end) + +----------------------------- 创造怪物 ----------------------------- +function Rubik:CreateMonster(count) + local pt = self.inst:GetPosition() + local dist = 7 + + -- 创建心脏 + local heart = aipSpawnPrefab(self.inst, "aip_rubik_heart") + heart.aipGhosts = {} + + -- 创建怪物 + for i = 1, count do + local angle = 2 * PI / count * i + local tgtPT = Vector3( + pt.x + math.cos(angle) * dist, 0, pt.z + math.sin(angle) * dist + ) + + createProjectile( + self.inst, tgtPT, function(proj) + local effect = aipSpawnPrefab(proj, "aip_shadow_wrapper", nil, 0.1) + effect.DoShow() + + local ghost = aipSpawnPrefab(proj, "aip_rubik_ghost") + if ghost.components.knownlocations then + ghost.components.knownlocations:RememberLocation("home", pt) + end + + ghost.aipHeart = heart + table.insert(heart.aipGhosts, ghost) + end, { 0, 0, 0, 5 }, 10 + ) + end +end + +------------------------------- 检查 ------------------------------- +-- 检查是否可以触发火坑 +function Rubik:SummonBoss() + local passedLevelCount = 0 + + for y = -1, 1 do + local commonColor = nil + local passed = true + + for x = -1, 1 do + for z = -1, 1 do + local index = getIndex(Vector3(x, y ,z)) + local color = self.matrix[index] + + if color ~= "yellow" then + -- 如果没有颜色就初始化一个颜色 + if commonColor == nil then + commonColor = color + end + + -- 如果颜色不对,则不过了 + if commonColor ~= color then + passed = false + end + end + end + end + + if passed then + passedLevelCount = passedLevelCount + 1 + end + end + + -- 动画效果,每个火都要回到火坑里 + local x, y, z = self.inst.Transform:GetWorldPosition() + local pt = Vector3(x, y + 1.5, z) + + for i, fx in ipairs(self.fxs) do + fx.components.aipc_float:MoveToPoint(pt) + end + + self.summonning = true + self.selectIndex = nil + + self.inst:DoTaskInTime(0.5, function() + if self.inst.components.fueled ~= nil then + self.inst.components.fueled:MakeEmpty() + end + + -- 爆炸效果 + local effect = aipSpawnPrefab(self.inst, "aip_shadow_wrapper", nil, 0.1) + effect.DoShow(2) + + -- 创建怪物 + self:CreateMonster(1 + (3 - passedLevelCount) * 2) + + -- 我们直接重新替换一个新的 + local replacedRubik = aipReplacePrefab(self.inst, "aip_rubik") + if replacedRubik.components.fueled ~= nil then + replacedRubik.components.fueled:MakeEmpty() + end + end) +end + +------------------------------- 位置 ------------------------------- +function Rubik:SyncPos(motion) + -- 我们总是延迟一丢丢 + self.inst:DoTaskInTime(0.1, function() + local x, y, z = self.inst.Transform:GetWorldPosition() + + for i, fx in ipairs(self.fxs) do + local ox, oy, oz = getPos(i) + + local scaleOffset = 1 + (oy - 1) / 6 + + local tgtX = x + ox * FX_OFFSET * scaleOffset + local tgtY = y + oy * FX_OFFSET + FX_HEIGHT + local tgtZ = z + oz * FX_OFFSET * scaleOffset + + local pt = Vector3(tgtX, tgtY, tgtZ) + + if i == 2 then + fx.components.aipc_float.debug = true + end + + if motion then + fx.components.aipc_float:MoveToPoint(pt) + else + fx.components.aipc_float:GoToPoint(pt) + end + end + end) +end + +function Rubik:SyncHighlight() + for i, fx in ipairs(self.fxs) do + local ox, oy, oz = getPos(i) + + playAnimation(fx, "idle") + fx.rotate = false + + if ox == 0 and oy == 0 and oz == 0 then + fx:AddTag("FX") + else + fx:RemoveTag("FX") + end + end + + if self.selectIndex then + -- 选中动画 + local selected = self.fxs[self.selectIndex] + playAnimation(selected, "selected") + + -- 可选动画 + local sx, sy, sz = getPos(self.selectIndex) + + + ------------ 角落点击 ------------ + if isCorner(sx, sy, sz) then + local ptXY = revertPT('x', revertPT('y', sx, sy, sz)) + ptXY.lock = 'z' + local ptYZ = revertPT('y', revertPT('z', sx, sy, sz)) + ptYZ.lock = 'x' + local ptXZ = revertPT('z', revertPT('x', sx, sy, sz)) + ptXZ.lock = 'y' + + local ptList = { ptXY, ptYZ, ptXZ } + for i, pt in ipairs(ptList) do + local restAxises = getAxisRest(pt.lock) + + for i, restAxis in ipairs(restAxises) do + local finalPT = setPT(restAxis, pt, 0) + local idx = getIndex(finalPT) + playAnimation(self.fxs[idx], "optional") + end + end + end + + ------------ 边缘中间 ------------ + if isEdgeCenter(sx, sy, sz) then + -- 找到数值为 0 的轴 + local pt = Vector3(sx, sy, sz) + local fixAxis = getAxisByVal(pt, 0) + local restAxises = getAxisRest(fixAxis) + + -- 同轴对面 + for i, restAxis in ipairs(restAxises) do + local finalPT = setPT(restAxis, pt, 0) + local idx = getIndex(finalPT) + playAnimation(self.fxs[idx], "optional") + end + + -- 同面旋转 + local pt1 = setPT(fixAxis, pt, -1) -- 先找到角落 + local pt2 = setPT(fixAxis, pt, 1) + local ptList = { pt1, pt2 } + for i, pt in ipairs(ptList) do + for i, restAxis in ipairs(restAxises) do + local finalPT = setPT(restAxis, pt, 0) + local idx = getIndex(finalPT) + playAnimation(self.fxs[idx], "optional") + end + end + end + + ------------ 中心点击 ------------ + if isFaceCenter(sx, sy, sz) then + local pt = Vector3(sx, sy, sz) + local fixAxis = getAxisByVal(pt, 1) or getAxisByVal(pt, -1) + local restAxises = getAxisRest(fixAxis) + + for i, restAxis in ipairs(restAxises) do + local pt1 = setPT(restAxis, pt, -1) + local pt2 = setPT(restAxis, pt, 1) + local ptList = { pt1, pt2 } + for i, finalPT in ipairs(ptList) do + local idx = getIndex(finalPT) + playAnimation(self.fxs[idx], "optional") + end + end + end + end +end + +------------------------------- 开关 ------------------------------- +function Rubik:Start() + self.start = true + + if #self.fxs == 0 then + for i, color in ipairs(self.matrix) do + local fx = SpawnPrefab("aip_rubik_fire_"..color) + fx.aipRubik = self.inst + self.fxs[i] = fx + end + end + + self:SyncPos() + self:SyncHighlight() + + -- 随机旋转 + self:TryRandom() +end + +function Rubik:TryRandom() + if self.randomTimes > 0 then + self.randomTimes = self.randomTimes - 1 + + local axises = { "x", "y", "z" } + local axis = dev_mode and 'x' or axises[math.random(#axises)] + + local rndPos = 0 + repeat + rndPos = math.random(-1, 1) + until(rndPos ~= self.randomPos) + + local walkingInfo = rotateFace(self.fxs, self.matrix, axis, rndPos) + self.fxs = walkingInfo.fxs + self.matrix = walkingInfo.matrix + + self.selectIndex = nil + self:SyncPos(true) + self:SyncHighlight() + + -- 记录这次的旋转 + self.randomPos = rndPos + + -- 过一段时间再旋转一次 + self.inst:DoTaskInTime(0.3, function() + self:TryRandom() + end) + end +end + +function Rubik:Stop() + self.start = false + + for i, fx in ipairs(self.fxs) do + fx:Remove() + end + + self.fxs = {} +end + +------------------------------- 选择 ------------------------------- +function Rubik:Select(fire) + -- 如果正在初始化旋转则不让选择 + if self.randomTimes > 0 or self.summonning then + return + end + + local prevIndex = self.selectIndex + + self.selectIndex = aipTableIndex(self.fxs, fire) + + -- 如果是中间那个 或者 相同元素,就不能选中 + if self.selectIndex == 14 or prevIndex == self.selectIndex then + self.selectIndex = nil + end + + ----------------------- 旋转判断 ----------------------- + if prevIndex and self.selectIndex and fire.rotate then + local pStart = getPosVec(prevIndex) + local pEnd = getPosVec(self.selectIndex) + + -- aipTypePrint("旋转吧:", pStart, '>>>', pEnd) + + -- 遍历每个轴,找到可以旋转的那个 + local axises = getSameAxis(pStart, pEnd) + for i, axis in ipairs(axises) do + -- aipPrint("检测轴:", axis, not isOnAxis(axis, pStart), not isOnAxis(axis, pEnd)) + if not isOnAxis(axis, pStart) and not isOnAxis(axis, pEnd) then + local fixedAxisPos = pEnd[axis] -- 固定轴上的固定点 + + -- 顺、逆时针 都转一圈 + local walkingInfo = rotateFace(self.fxs, self.matrix, axis, fixedAxisPos) + local reverseWalkingInfo = rotateFace(self.fxs, self.matrix, axis, fixedAxisPos, true) + + -- 顺时针的距离计算 + local rotatedDist = rotateDist(self.fxs, walkingInfo.fxs, pStart, pEnd) + + -- 逆时针的距离计算 + local reverseRotatedDist = rotateDist(self.fxs, reverseWalkingInfo.fxs, pStart, pEnd) + + -- 同步回去 + if rotatedDist < reverseRotatedDist then + self.fxs = walkingInfo.fxs + self.matrix = walkingInfo.matrix + else + self.fxs = reverseWalkingInfo.fxs + self.matrix = reverseWalkingInfo.matrix + end + self:SyncPos(true) + + -- 转过了,不用选中了 + self.selectIndex = nil + end + end + end + + --------------------- 更新点击状态 --------------------- + self:SyncHighlight() +end + +return Rubik \ No newline at end of file diff --git a/scripts/components/world_common_store.lua b/scripts/components/world_common_store.lua index 0a5ff097..bb2047db 100644 --- a/scripts/components/world_common_store.lua +++ b/scripts/components/world_common_store.lua @@ -1,3 +1,5 @@ +local open_beta = aipGetModConfig("open_beta") == "open" + local dev_mode = aipGetModConfig("dev_mode") == "enabled" local function findFarAwayOcean(pos) @@ -57,6 +59,9 @@ local CommonStore = Class(function(self, inst) -- 记录所有的箱子 self.chests = {} + -- 当前的豆酱图腾 + self.douTotem = nil + -- 记录所有的飞行点 self.flyTotems = {} @@ -90,6 +95,41 @@ function CommonStore:CreateCoookieKing(pos) return nil end +-- 获取一下豆酱图腾 +function CommonStore:FindDouTotem() + if not self.douTotem then + self.douTotem = TheSim:FindFirstEntityWithTag("aip_dou_totem_final") + end + return self.douTotem +end + +-- 创建 魔方,在墓地附近寻找 +function CommonStore:CreateRubik() + -- 存在且没有坐标就跳过 + local ent = TheSim:FindFirstEntityWithTag("aip_rubik") + if ent ~= nil then + return ent + end + + -- 寻找一个墓地 + local grave = TheSim:FindFirstEntityWithTag("grave") + local pos = nil + if grave ~= nil then + pos = grave:GetPosition() + end + + if not pos then + pos = aipGetSecretSpawnPoint(Vector3(0, 0, 0), 0, 1000) + end + + pos = aipGetSecretSpawnPoint(pos, 0, 50, 5) + + local rubik = aipSpawnPrefab(nil, "aip_rubik", pos.x, pos.y, pos.z) + rubik.components.fueled:MakeEmpty() + + return rubik +end + function CommonStore:CreateSuWuMound(pos) -- 存在且没有坐标就跳过 local ent = TheSim:FindFirstEntityWithTag("aip_suwu_mound") @@ -120,8 +160,6 @@ function CommonStore:PostWorld() aipSpawnPrefab(nil, "aip_dou_totem_broken", tgt.x, tgt.y, tgt.z) end end - - end) --------------------------- 创建饼干 --------------------------- @@ -129,9 +167,57 @@ function CommonStore:PostWorld() self:CreateCoookieKing() end) - --------------------------- 开发模式 --------------------------- - if dev_mode then + --------------------------- 创建魔方 --------------------------- + if open_beta then + self.inst:DoTaskInTime(5, function() + self:CreateRubik() + end) end + + --------------------------- 开发模式 --------------------------- + -- if dev_mode then + -- self.inst:DoTaskInTime(2, function() + -- if ThePlayer and false then + -- aipSpawnPrefab(ThePlayer, "aip_rubik") + + -- -- 避免和神话书说&小房子冲突 + -- local px = 1900 + -- local py = 0 + -- local pz = 1900 + + -- ThePlayer.Physics:Teleport(px, py, pz) + + -- -- local tile = TheWorld.Map:GetTileAtPoint(px, py, pz) + -- -- aipPrint("Tile Type:", tile) + + -- -- if tile == GROUND.INVALID then + -- -- local tileX, tileY = TheWorld.Map:GetTileCoordsAtPoint(px, py, pz) + -- -- TheWorld.Map:SetTile(tileX, tileY, GROUND.DIRT) + -- -- TheWorld.Map:RebuildLayer(GROUND.DIRT, tileX, tileY) + + -- -- ThePlayer.Physics:Teleport(px, py, pz) + -- -- end + + + -- -- aipPrint("Next Tile Type:", TheWorld.Map:GetTileAtPoint(px, py, pz)) + + -- -- for px = 0, 1000 do + -- -- local py = 0 + -- -- local pz = 0 + -- -- local tile = TheWorld.Map:GetTileAtPoint(px, py, pz) + -- -- aipPrint("Tile Type:", px, tile) + -- -- end + -- end + -- end) + + -- -- self.inst:DoPeriodicTask(1, function() + -- -- if ThePlayer then + -- -- local x, y, z = ThePlayer.Transform:GetWorldPosition() + -- -- local tile = TheWorld.Map:GetTileAtPoint(x,y,z) + -- -- aipPrint("Player Tile Type:", tile) + -- -- end + -- -- end) + -- end end return CommonStore diff --git a/scripts/componentsHooker.lua b/scripts/componentsHooker.lua index 1118ffb9..edde487d 100644 --- a/scripts/componentsHooker.lua +++ b/scripts/componentsHooker.lua @@ -1,6 +1,8 @@ local _G = GLOBAL local language = _G.aipGetModConfig("language") +env.AddReplicableComponent("aipc_buffer") + ----------------------------------- 通用组件行为 ----------------------------------- -- 服务端组件 local function triggerComponentAction(player, item, target, targetPoint) diff --git a/scripts/dev.lua b/scripts/dev.lua index 35d39092..0037acfb 100644 --- a/scripts/dev.lua +++ b/scripts/dev.lua @@ -17,8 +17,8 @@ function PlayerPrefabPostInit(inst) -- 开发模式下移除失眠效果 inst:RemoveTag("insomniac") - inst.components.aipc_timer:Interval(1, function() - if inst.components.health.currenthealth < 50 then + inst.components.aipc_timer:Interval(0.3, function() + if not inst.components.health:IsDead() and inst.components.health.currenthealth < 50 then inst.components.health:DoDelta(50) end if inst.components.sanity.current < 30 then diff --git a/scripts/houseWrapper.lua b/scripts/houseWrapper.lua new file mode 100644 index 00000000..4f0dc4c0 --- /dev/null +++ b/scripts/houseWrapper.lua @@ -0,0 +1,94 @@ +local _G = GLOBAL + +local dev_mode = _G.aipGetModConfig("dev_mode") == "enabled" + +if not dev_mode then + return +end + +-- IMPASSABLE = 1 不可过边界(深水) +-- GRASS = 6, 草地 +-- FOREST = 7, 森林 +-- OCEAN_COASTAL = 201 海洋 +-- OCEAN_COASTAL_SHORE = 202 海岸线 +-- INVALID = 255 无效(黑色的空间) + + +-- local aipHouses = {} + +local RANGE = 1900 + +-- 是否在特殊空间里 +local function inPlace(x, z, offset) + offset = offset or 50 + return x >= RANGE - offset and x <= RANGE + offset and + z >= RANGE - offset and z <= RANGE + offset +end + +AddPrefabPostInit("world", function(inst) + local map = _G.getmetatable(inst.Map).__index + if map then + + local old_IsAboveGroundAtPoint = map.IsAboveGroundAtPoint + map.IsAboveGroundAtPoint = function(self, x, y, z, ...) + if inPlace(x, z) then + -- for k, v in pairs(aipHouses) do + -- if v[3] ~= nil then + -- if z >= v[2] - 6.5 and z <= v[2] + 6.5 and x >= v[1] - + -- 5.5 and x <= v[1] + 5 then + -- return true + -- end + -- else + -- if v and z >= v[2] - 12 and z <= v[2] + 12 and x >= v[1] - + -- 8 and x <= v[1] + 8 then + -- if TheSim:WorldPointInPoly(x, z, { + -- {v[1] - 6.2, v[2] + 10}, + -- {v[1] - 6.2, v[2] - 10.6}, + -- {v[1] + 7.8, v[2] - 12}, {v[1] + 7.8, v[2] + 12} + -- }) then return true end + -- end + -- end + -- end + return true + end + return old_IsAboveGroundAtPoint(self, x, y, z, ...) + end + + local old_IsVisualGroundAtPoint = map.IsVisualGroundAtPoint + map.IsVisualGroundAtPoint = function(self, x, y, z, ...) + if inPlace(x, z) then + -- for k, v in pairs(aipHouses) do + -- if v[3] ~= nil then + -- if z >= v[2] - 6.5 and z <= v[2] + 6.5 and x >= v[1] - + -- 5.5 and x <= v[1] + 5 then + -- return true + -- end + -- else + -- if v and z >= v[2] - 12 and z <= v[2] + 12 and x >= v[1] - + -- 8 and x <= v[1] + 8 then + -- if TheSim:WorldPointInPoly(x, z, { + -- {v[1] - 6.2, v[2] + 10}, + -- {v[1] - 6.2, v[2] - 10.6}, + -- {v[1] + 7.8, v[2] - 12}, {v[1] + 7.8, v[2] + 12} + -- }) then return true end + -- end + -- end + -- end + return true + end + return old_IsVisualGroundAtPoint(self, x, y, z, ...) + end + + local old_GetTileCenterPoint = map.GetTileCenterPoint + map.GetTileCenterPoint = function(self, x, y, z) + if inPlace(x, z, 0) then + return math.floor(x / 4) * 4 + 2, 0, math.floor(z / 4) * 4 + 2 + end + if z then + return old_GetTileCenterPoint(self, x, y, z) + else + return old_GetTileCenterPoint(self, x, y) + end + end + end +end) diff --git a/scripts/prefabs/aip_aura.lua b/scripts/prefabs/aip_aura.lua index 89e56223..0340e767 100644 --- a/scripts/prefabs/aip_aura.lua +++ b/scripts/prefabs/aip_aura.lua @@ -20,14 +20,16 @@ local function getFn(data) inst.entity:AddAnimState() inst.entity:AddNetwork() - inst.AnimState:SetBank(data.name) - inst.AnimState:SetBuild(data.name) + if data.assets ~= nil then + inst.AnimState:SetBank(data.name) + inst.AnimState:SetBuild(data.name) - inst.AnimState:PlayAnimation("idle", data.onAnimOver == nil) + inst.AnimState:PlayAnimation("idle", data.onAnimOver == nil) - inst.AnimState:SetOrientation(ANIM_ORIENTATION.OnGround) - inst.AnimState:SetLayer(LAYER_WORLD_BACKGROUND) - inst.AnimState:SetSortOrder(2) + inst.AnimState:SetOrientation(ANIM_ORIENTATION.OnGround) + inst.AnimState:SetLayer(LAYER_WORLD_BACKGROUND) + inst.AnimState:SetSortOrder(2) + end if data.scale ~= nil then inst.Transform:SetScale(data.scale, data.scale, data.scale) @@ -54,6 +56,9 @@ local function getFn(data) inst.components.aipc_aura.bufferFn = data.bufferFn inst.components.aipc_aura.mustTags = data.mustTags inst.components.aipc_aura.noTags = data.noTags + if data.showFX ~= nil then + inst.components.aipc_aura.showFX = data.showFX + end end if data.onAnimOver ~= nil then @@ -89,6 +94,14 @@ local list = { inst:Remove() end, }, + { -- 预见光环:可以直接看到诡影脚步 + name = "aip_aura_see", + range = 1, + bufferName = "seeFootPrint", + showFX = false, + mustTags = { "_health" }, + noTags = { "INLIMBO", "NOCLICK", "ghost" }, + }, } diff --git a/scripts/prefabs/aip_cookiecutter_king.lua b/scripts/prefabs/aip_cookiecutter_king.lua index 6fe90983..313b34cf 100644 --- a/scripts/prefabs/aip_cookiecutter_king.lua +++ b/scripts/prefabs/aip_cookiecutter_king.lua @@ -18,7 +18,7 @@ local LANG_MAP = { TALK_KING_HUNGER_AIP_MUD_CRAB = "(Live Mud crab!)", TALK_KING_FIND_ME = "(Keep in touch!)", - TALK_KING_FINISH = "(Nice food. Next challenge is in dev)", + TALK_KING_FINISH = "(Nice food. Wish you like rubik)", TALK_KING_88 = "(Bye!)", }, chinese = { @@ -35,7 +35,7 @@ local LANG_MAP = { TALK_KING_HUNGER_AIP_MUD_CRAB = "(活泥蟹!)", TALK_KING_FIND_ME = "(保持联系,投石问路)", - TALK_KING_FINISH = "(多谢招待,下一个挑战还在开发中)", + TALK_KING_FINISH = "(多谢招待,希望你喜欢拼图)", TALK_KING_88 = "(再见)", }, } @@ -270,6 +270,9 @@ local function startEater(inst) -- 给予子卿 inst.aipVest.components.lootdropper:SpawnLootPrefab("aip_suwu") end + + -- 给予棱镜石 + inst.aipVest.components.lootdropper:SpawnLootPrefab("aip_legion") end ) end diff --git a/scripts/prefabs/aip_dou_totem.lua b/scripts/prefabs/aip_dou_totem.lua index da25df64..a89ae26e 100644 --- a/scripts/prefabs/aip_dou_totem.lua +++ b/scripts/prefabs/aip_dou_totem.lua @@ -1,8 +1,14 @@ +local dev_mode = aipGetModConfig("dev_mode") == "enabled" -- 配置 local language = aipGetModConfig("language") local additional_survival = aipGetModConfig("additional_survival") == "open" +local WarnRange = 8 -- 创造攻击玩家的范围 + + +local FueledTime = dev_mode and 8 or 40 + local LANG_MAP = { english = { BROEKN_NAME = "Wreckage", @@ -12,10 +18,11 @@ local LANG_MAP = { NAME = "IOT Totem", DESC = "Seems magic somewhere", TALK_WELCOME = "Are you ready?", + TALK_ANGER = "'Thank you' for fuel me!", TALK_FIRST = "Start your challenge", TOTEM_POS = "First Place", TOTEM_BALLOON = "Ruo Guang", - TOTEM_SNAKE = "", -- 抓捕玩具蛇 + TOTEM_RUBIK = "The Matrix", }, chinese = { BROEKN_NAME = "一片残骸", @@ -25,9 +32,11 @@ local LANG_MAP = { NAME = "联结图腾", DESC = "有一丝魔法气息", TALK_WELCOME = "想得到我的秘密,你做好准备了吗?", + TALK_ANGER = "“感谢”你的馈赠", TALK_FIRST = "开始你的挑战!", TOTEM_POS = "伊始之地", TOTEM_BALLOON = "若光小驻", + TOTEM_RUBIK = "薄暮矩阵", }, } @@ -42,6 +51,7 @@ STRINGS.NAMES.AIP_DOU_TOTEM = LANG.NAME STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_DOU_TOTEM = LANG.DESC STRINGS.AIP_DOU_TOTEM_TALK_WELCOME = LANG.TALK_WELCOME STRINGS.AIP_DOU_TOTEM_TALK_FIRST = LANG.TALK_FIRST +STRINGS.AIP_DOU_TOTEM_TALK_ANGER = LANG.TALK_ANGER ---------------------------------- 资源 ---------------------------------- local assets = { @@ -87,12 +97,15 @@ end local function createFlyTotems(inst) local startTotem = false local balloonTotem = false + local rubikTotem = false for i, totem in ipairs(TheWorld.components.world_common_store.flyTotems) do if totem.markType == "START" then startTotem = true elseif totem.markType == "BALLOON" then balloonTotem = true + elseif totem.markType == "RUBIK" then + rubikTotem = true end end @@ -116,6 +129,17 @@ local function createFlyTotems(inst) ) end end + + if rubikTotem == false then + local rubik = TheSim:FindFirstEntityWithTag("aip_rubik") + if rubik then + createFlyTotem( + aipGetSecretSpawnPoint(rubik:GetPosition(), 4, 8, 5), + LANG.TOTEM_RUBIK, + "RUBIK" + ) + end + end end -- 创建挑战点 @@ -123,6 +147,74 @@ local function createChallenge() aipGetTopologyPoint("lunacyarea", "moon_fissure") end +---------------------------------- 燃料 ---------------------------------- +local function ontakefuel(inst) + inst.SoundEmitter:PlaySound("dontstarve/common/nightmareAddFuel") +end + +local function onupdatefueled(inst) + if inst.components.burnable ~= nil then + inst.components.burnable:SetFXLevel(inst.components.fueled:GetCurrentSection(), inst.components.fueled:GetSectionPercent()) + end +end + +local function onfuelchange(newsection, oldsection, inst) + if newsection <= 0 then + inst.components.burnable:Extinguish() + else + if not inst.components.burnable:IsBurning() then + inst.components.burnable:Ignite() + end + + inst.components.burnable:SetFXLevel(newsection, inst.components.fueled:GetSectionPercent()) + end +end + +---------------------------------- 玩家 ---------------------------------- +local function killTimer(inst) + if inst.aipGhostTimer ~= nil then + inst.aipGhostTimer:Cancel() + end + + inst.aipGhostTimer = nil +end + +-- 玩家靠近 +local function onNear(inst, player) + killTimer(inst) + + inst.aipGhostTimer = inst:DoPeriodicTask(8, function() + -- 点燃时,源源不断创造触手攻击玩家 + if inst.components.fueled ~= nil and not inst.components.fueled:IsEmpty() then + local players = aipFindNearPlayers(inst, WarnRange) + local player = aipRandomEnt(players) + + if player ~= nil then + local tentacle = aipSpawnPrefab(player, "shadowtentacle") + tentacle.components.combat:SetTarget(player) + + -- 触手不会降低玩家理智 + if tentacle.components.sanityaura ~= nil then + tentacle.components.sanityaura.aura = 0 + end + + tentacle.SoundEmitter:PlaySound("dontstarve/characters/walter/slingshot/shadowTentacleAttack_1") + tentacle.SoundEmitter:PlaySound("dontstarve/characters/walter/slingshot/shadowTentacleAttack_2") + + -- 警告玩家 + if inst.components.talker then + inst.components.talker:Say(STRINGS.AIP_DOU_TOTEM_TALK_ANGER) + end + end + end + end) +end + +-- 玩家远离 +local function onFar(inst) + killTimer(inst) +end + ---------------------------------- 实体 ---------------------------------- local function makeTotemFn(name, animation, nextPrefab, nextPrefabAnimation) -- 建筑到下一个级别 @@ -183,6 +275,8 @@ local function makeTotemFn(name, animation, nextPrefab, nextPrefabAnimation) -- 最后一个级别做额外事情 if nextPrefab == nil then + inst:AddTag("aip_dou_totem_final") + -- 会添加对话能力 inst:AddComponent("talker") inst.components.talker.fontsize = 30 @@ -197,6 +291,35 @@ local function makeTotemFn(name, animation, nextPrefab, nextPrefabAnimation) return inst end + -- 最后一个级别做额外事情 + if nextPrefab == nil then + -- 可以点燃 + inst:AddComponent("burnable") + inst.components.burnable:AddBurnFX("nightlight_flame", Vector3(0, 0, 0), "fire_marker_left") + inst.components.burnable:AddBurnFX("nightlight_flame", Vector3(0, 0, 0), "fire_marker_right") + inst.components.burnable.canlight = false + + -- 使用燃料 + inst:AddComponent("fueled") + inst.components.fueled.maxfuel = FueledTime + inst.components.fueled.accepting = true + inst.components.fueled.fueltype = FUELTYPE.NIGHTMARE + inst.components.fueled:SetSections(3) + inst.components.fueled:SetTakeFuelFn(ontakefuel) + inst.components.fueled:SetUpdateFn(onupdatefueled) + inst.components.fueled:SetSectionCallback(onfuelchange) + inst.components.fueled:InitializeFuelLevel(0) + + inst.components.fueled.rate = 0 -- 永不熄灭 + inst.components.fueled.bonusmult = (1 / TUNING.LARGE_FUEL) * (FueledTime / 4) + + -- 玩家召唤触手打玩家 + inst:AddComponent("playerprox") + inst.components.playerprox:SetDist(WarnRange, WarnRange) + inst.components.playerprox:SetOnPlayerNear(onNear) + inst.components.playerprox:SetOnPlayerFar(onFar) + end + inst:AddComponent("inspectable") if nextPrefab ~= nil then diff --git a/scripts/prefabs/aip_dragon_footprint.lua b/scripts/prefabs/aip_dragon_footprint.lua index a063c792..f32c03f6 100644 --- a/scripts/prefabs/aip_dragon_footprint.lua +++ b/scripts/prefabs/aip_dragon_footprint.lua @@ -49,7 +49,15 @@ local function PrintFootPrint(inst) local player = ThePlayer if player ~= nil and player.replica.sanity ~= nil then local ptg = player.replica.sanity:GetPercent() - vest.AnimState:OverrideMultColour(1, 1, 1, 1 - ptg * 0.8) + + -- 如果有帽子光环就可以直接看到 + if hasBuffer(player, "seeFootPrint") then + ptg = 0 + end + + local mulPtg = 1 - ptg * 0.8 + + vest.AnimState:OverrideMultColour(mulPtg, mulPtg, mulPtg, mulPtg) end -- 每次打印脚印后就更新一下记录 diff --git a/scripts/prefabs/aip_dress_template.lua b/scripts/prefabs/aip_dress_template.lua index 7d4580d7..2b207888 100644 --- a/scripts/prefabs/aip_dress_template.lua +++ b/scripts/prefabs/aip_dress_template.lua @@ -101,6 +101,11 @@ local function template(name, config) inst:AddTag("hat") inst:AddTag("waterproofer") + -- 额外功能 + if config.preInst then + config.preInst(inst) + end + inst.entity:SetPristine() if not TheWorld.ismastersim then @@ -151,7 +156,7 @@ local function template(name, config) end end - -- 护甲 + -- 额外功能 if config.postInst then config.postInst(inst) end diff --git a/scripts/prefabs/aip_fly_totem.lua b/scripts/prefabs/aip_fly_totem.lua index 12753b83..99629f34 100644 --- a/scripts/prefabs/aip_fly_totem.lua +++ b/scripts/prefabs/aip_fly_totem.lua @@ -6,6 +6,12 @@ local LANG_MAP = { NAME = "Fly Totem", RECDESC = "Unicom's flight site", DESC = "To Infinity... and Beyond", + + FAKE_NAME = "Inferior Fly Totem", + FAKE_RECDESC = "Not an outstanding counterfeit ", + FAKE_DESC = "Things that need to be recharged to activate", + FAKE_NO_POWER = "Need recharge on the source", + UNNAMED = "[UNNAMED]", CURRENT = "I'm already here!", INVLIDATE = "Target seems disappeared", @@ -16,6 +22,12 @@ local LANG_MAP = { NAME = "飞行图腾", RECDESC = "联通的飞行站点", DESC = "飞向宇宙,浩瀚无垠!", + + FAKE_NAME = "劣质的飞行图腾", + FAKE_RECDESC = "并不杰出的仿冒品", + FAKE_DESC = "需要充能才能启动的玩意儿", + FAKE_NO_POWER = "它的魔力来源已经消耗殆尽了", + UNNAMED = "[未命名]", CURRENT = "我就在这里!", INVLIDATE = "目的地不见了", @@ -30,6 +42,12 @@ local LANG = LANG_MAP[language] or LANG_MAP.english STRINGS.NAMES.AIP_FLY_TOTEM = LANG.NAME STRINGS.RECIPE_DESC.AIP_FLY_TOTEM = LANG.RECDESC STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_FLY_TOTEM = LANG.DESC + +STRINGS.NAMES.AIP_FAKE_FLY_TOTEM = LANG.FAKE_NAME +STRINGS.RECIPE_DESC.AIP_FAKE_FLY_TOTEM = LANG.FAKE_RECDESC +STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_FAKE_FLY_TOTEM = LANG.FAKE_DESC +STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_FAKE_FLY_TOTEM_NO_POWER = LANG.FAKE_NO_POWER + STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_FLY_TOTEM_UNNAMED = LANG.UNNAMED STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_FLY_TOTEM_CURRENT = LANG.CURRENT STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_FLY_TOTEM_INVLIDATE = LANG.INVLIDATE @@ -41,7 +59,9 @@ require "prefabutil" local assets = { Asset("ANIM", "anim/aip_fly_totem.zip"), + Asset("ANIM", "anim/aip_fake_fly_totem.zip"), Asset("ATLAS", "images/inventoryimages/aip_fly_totem.xml"), + Asset("ATLAS", "images/inventoryimages/aip_fake_fly_totem.xml"), } local prefabs = { @@ -81,6 +101,21 @@ local function canBeActOn(inst, doer) end local function onOpenPicker(inst, doer) + -- 如果是假图腾就检查是否豆酱图腾由能量 + if inst.aipFake == true and TheWorld.components.world_common_store then + local douTotem = TheWorld.components.world_common_store:FindDouTotem() + + -- 没有的话就不让飞行了 + if not douTotem or not douTotem.components.fueled or douTotem.components.fueled:IsEmpty() then + if doer.components.talker then + doer.components.talker:Say( + STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_FAKE_FLY_TOTEM_NO_POWER + ) + end + return + end + end + -- 加一个切割前缀强制服务器触发 doer.player_classified.aip_fly_picker:set(tostring(os.time()).."|"..inst.aipId) end @@ -101,6 +136,18 @@ local function onLoad(inst, data) end end +---------------------------------- 燃料 ---------------------------------- +local function ontakefuel(inst) + if TheWorld.components.world_common_store then + local douTotem = TheWorld.components.world_common_store:FindDouTotem() + + -- 给链接图腾补充燃料 + if douTotem and douTotem.components.fueled then + douTotem.components.fueled:DoDelta(5) + end + end +end + ---------------------------------- 方法 ---------------------------------- -- 玩家靠近时创建一个小豆酱 local function onnear(inst, player) @@ -153,86 +200,105 @@ local function startSpell(inst, targetTotem) local aura = aipSpawnPrefab(inst, "aip_aura_send") aura.OnRemoveEntity = function() startTranslate(inst, targetTotem) + + -- 如果是劣质图腾则消耗一下使用寿命 + if inst.aipFake == true and TheWorld.components.world_common_store then + local douTotem = TheWorld.components.world_common_store:FindDouTotem() + douTotem.components.fueled:DoDelta(-1, inst) + end end end ---------------------------------- 实体 ---------------------------------- -local function fn() - local inst = CreateEntity() - - inst.entity:AddTransform() - inst.entity:AddAnimState() - inst.entity:AddSoundEmitter() - -- inst.entity:AddMiniMapEntity() - inst.entity:AddNetwork() - - MakeObstaclePhysics(inst, .2) - - -- inst.MiniMapEntity:SetIcon("sign.png") +local function genTotem(buildName, fake) + local function fn() + local inst = CreateEntity() - inst.AnimState:SetBank("aip_fly_totem") - inst.AnimState:SetBuild("aip_fly_totem") - inst.AnimState:PlayAnimation("idle") + inst.entity:AddTransform() + inst.entity:AddAnimState() + inst.entity:AddSoundEmitter() + -- inst.entity:AddMiniMapEntity() + inst.entity:AddNetwork() - MakeSnowCoveredPristine(inst) + MakeObstaclePhysics(inst, .2) - inst:AddTag("structure") - inst:AddTag("aip_fly_totem") + -- inst.MiniMapEntity:SetIcon("sign.png") - --Sneak these into pristine state for optimization - inst:AddTag("_writeable") + inst.AnimState:SetBank(buildName) + inst.AnimState:SetBuild(buildName) + inst.AnimState:PlayAnimation("idle") - -- 添加飞行图腾 - inst:AddComponent("aipc_action_client") - inst.components.aipc_action_client.canBeActOn = canBeActOn + MakeSnowCoveredPristine(inst) - inst.entity:SetPristine() + inst:AddTag("structure") + inst:AddTag("aip_fly_totem") - if not TheWorld.ismastersim then - return inst - end + --Sneak these into pristine state for optimization + inst:AddTag("_writeable") - --Remove these tags so that they can be added properly when replicating components below - inst:RemoveTag("_writeable") + -- 添加飞行图腾 + inst:AddComponent("aipc_action_client") + inst.components.aipc_action_client.canBeActOn = canBeActOn - inst:AddComponent("aipc_action") - inst.components.aipc_action.onDoAction = onOpenPicker + inst.entity:SetPristine() - inst:AddComponent("inspectable") - inst:AddComponent("writeable") - inst:AddComponent("lootdropper") + if not TheWorld.ismastersim then + return inst + end - inst:AddComponent("workable") - inst.components.workable:SetWorkAction(ACTIONS.HAMMER) - inst.components.workable:SetWorkLeft(4) - inst.components.workable:SetOnFinishCallback(onhammered) - inst.components.workable:SetOnWorkCallback(onhit) + --Remove these tags so that they can be added properly when replicating components below + inst:RemoveTag("_writeable") + + inst:AddComponent("aipc_action") + inst.components.aipc_action.onDoAction = onOpenPicker + + inst:AddComponent("inspectable") + inst:AddComponent("writeable") + inst:AddComponent("lootdropper") + + inst:AddComponent("workable") + inst.components.workable:SetWorkAction(ACTIONS.HAMMER) + inst.components.workable:SetWorkLeft(4) + inst.components.workable:SetOnFinishCallback(onhammered) + inst.components.workable:SetOnWorkCallback(onhit) + + -- 玩家靠近 + inst:AddComponent("playerprox") + inst.components.playerprox:SetDist(10, 13) + inst.components.playerprox:SetOnPlayerNear(onnear) + + -- 如果伪劣产品,我们可以充能 + if fake then + inst:AddComponent("fueled") + inst.components.fueled.accepting = true + inst.components.fueled.fueltype = FUELTYPE.NIGHTMARE + inst.components.fueled:SetTakeFuelFn(ontakefuel) + end - -- 玩家靠近 - inst:AddComponent("playerprox") - inst.components.playerprox:SetDist(10, 13) - inst.components.playerprox:SetOnPlayerNear(onnear) + MakeSnowCovered(inst) - MakeSnowCovered(inst) + MakeSmallBurnable(inst, TUNING.LARGE_BURNTIME) + MakeSmallPropagator(inst) - MakeSmallBurnable(inst, TUNING.LARGE_BURNTIME) - MakeSmallPropagator(inst) + inst.aipId = tostring(os.time())..tostring(math.random()) - inst.aipId = tostring(os.time())..tostring(math.random()) + inst.aipStartSpell = startSpell + inst.aipFake = fake - inst.aipStartSpell = startSpell + inst.OnSave = onSave + inst.OnLoad = onLoad - inst.OnSave = onSave - inst.OnLoad = onLoad + MakeHauntableWork(inst) + inst:ListenForEvent("onbuilt", onbuilt) - MakeHauntableWork(inst) - inst:ListenForEvent("onbuilt", onbuilt) + -- 全局注册飞行图腾 + table.insert(TheWorld.components.world_common_store.flyTotems, inst) + inst:ListenForEvent("onremove", onRemove) - -- 全局注册飞行图腾 - table.insert(TheWorld.components.world_common_store.flyTotems, inst) - inst:ListenForEvent("onremove", onRemove) + return inst + end - return inst + return fn end -------------------------------- 起飞特效 -------------------------------- @@ -307,7 +373,9 @@ local function flyEffectFn() end -return Prefab("aip_fly_totem", fn, assets, prefabs), +return Prefab("aip_fly_totem", genTotem("aip_fly_totem"), assets, prefabs), MakePlacer("aip_fly_totem_placer", "aip_fly_totem", "aip_fly_totem", "idle"), + Prefab("aip_fake_fly_totem", genTotem("aip_fake_fly_totem", true), assets, prefabs), + MakePlacer("aip_fake_fly_totem_placer", "aip_fake_fly_totem", "aip_fake_fly_totem", "idle"), Prefab("aip_eagle_effect", effectFn, { Asset("ANIM", "anim/lavaarena_attack_buff_effect.zip") }), Prefab("aip_fly_totem_effect", flyEffectFn, { Asset("ANIM", "anim/aip_fly_totem_effect.zip") }) diff --git a/scripts/prefabs/aip_legion.lua b/scripts/prefabs/aip_legion.lua new file mode 100644 index 00000000..f8375a3f --- /dev/null +++ b/scripts/prefabs/aip_legion.lua @@ -0,0 +1,111 @@ +local open_beta = aipGetModConfig("open_beta") == "open" + +local language = aipGetModConfig("language") + +-- 文字描述 +local LANG_MAP = { + english = { + NAME = "Legion Stone", + DESC = "Last piece of Magic Rubik", + EMPTY = "This Rubik is powerless", + }, + chinese = { + NAME = "棱镜石", + DESC = "魔力方阵的最后一片", + EMPTY = "这个方阵没有魔力了", + }, +} + +if not open_beta then + LANG_MAP.english.EMPTY = "Special but useless" + LANG_MAP.chinese.EMPTY = "奇特的石头,似乎没有什么用处" +end + +local LANG = LANG_MAP[language] or LANG_MAP.english + +STRINGS.NAMES.AIP_LEGION = LANG.NAME +STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_LEGION = LANG.DESC +STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_LEGION_EMPTY = LANG.EMPTY + +-- 资源 +local assets = { + Asset("ANIM", "anim/aip_legion.zip"), + Asset("ATLAS", "images/inventoryimages/aip_legion.xml"), +} + +----------------------------------------------------------- +local function canActOn(inst, doer, target) + return target.prefab == "aip_rubik" +end + +local function onDoTargetAction(inst, doer, target) + -- server only + if + not TheWorld.ismastersim or -- 不是主机 + target.prefab ~= "aip_rubik" -- 不是魔方 + then + return + end + + if + not target.components.fueled or -- 没有燃料 + target.components.fueled:IsEmpty() -- 燃料耗尽 + then + if doer.components.talker ~= nil then + doer.components.talker:Say( + STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_LEGION_EMPTY + ) + end + return + end + + -- 召唤 BOSS + if target.components.aipc_rubik then + target.components.aipc_rubik:SummonBoss() + end + + inst:Remove() +end + +----------------------------------------------------------- +local function fn() + local inst = CreateEntity() + + inst.entity:AddTransform() + inst.entity:AddAnimState() + inst.entity:AddNetwork() + + MakeInventoryPhysics(inst) + + inst.AnimState:SetBank("aip_legion") + inst.AnimState:SetBuild("aip_legion") + inst.AnimState:PlayAnimation("idle") + + MakeInventoryFloatable(inst, "med", nil, 0.68) + + inst.entity:SetPristine() + + inst:AddComponent("aipc_action_client") + inst.components.aipc_action_client.canActOn = canActOn + + if not TheWorld.ismastersim then + return inst + end + + inst:AddComponent("aipc_action") + inst.components.aipc_action.onDoTargetAction = onDoTargetAction + + inst:AddComponent("inspectable") + + inst:AddComponent("inventoryitem") + inst.components.inventoryitem.atlasname = "images/inventoryimages/aip_legion.xml" + + inst:AddComponent("tradable") + inst.components.tradable.goldvalue = 3 + + MakeHauntableLaunch(inst) + + return inst +end + +return Prefab("aip_legion", fn, assets) diff --git a/scripts/prefabs/aip_mini_doujiang.lua b/scripts/prefabs/aip_mini_doujiang.lua index 850ef837..eb2216a6 100644 --- a/scripts/prefabs/aip_mini_doujiang.lua +++ b/scripts/prefabs/aip_mini_doujiang.lua @@ -28,6 +28,7 @@ local LANG_MAP = { WAIT_NEXT = "I've seen an Gaint. Maybe you have interested", CARDS = "Reciprocity~", WRONG_GIFT = "It's meaningless", + TOTEM_RUBIK = "Shadow Rubik", }, chinese = { NAME = "若光", @@ -41,6 +42,7 @@ local LANG_MAP = { WAIT_NEXT = "我看到过一个大家伙,你也去看看吧", CARDS = "礼尚往来~", WRONG_GIFT = "我不需要它!", + TOTEM_RUBIK = "暗影魔方", }, } @@ -156,6 +158,23 @@ local function aipThrowBallBack(inst, ball) inst.components.timer:StartTimer("aip_mini_dou_no_talk", 5) end +-- 创建魔方挑战 +local function createChallenge() + local rubikTotem = false + + for i, totem in ipairs(TheWorld.components.world_common_store.flyTotems) do + if totem.markType == "RUBIK" then + rubikTotem = true + end + end + + if rubikTotem == false then + local flyTotem = aipSpawnPrefab(nil, "aip_fly_totem", 3000, 0, 3000) + flyTotem.components.writeable:SetText(LANG.TOTEM_RUBIK) + flyTotem.markType = "RUBIK" + end +end + -- 判断是否要给奖励 local function aipPlayEnd(inst, throwTimes) local rewardTimes = dev_mode and 2 or 4 diff --git a/scripts/prefabs/aip_olden_tea.lua b/scripts/prefabs/aip_olden_tea.lua index 83adeab4..06ca5932 100644 --- a/scripts/prefabs/aip_olden_tea.lua +++ b/scripts/prefabs/aip_olden_tea.lua @@ -118,7 +118,7 @@ local function onDoEat(inst, doer) if doer.components.timer ~= nil then doer.components.timer:StopTimer("aip_olden_tea") - doer.components.timer:StartTimer("aip_olden_tea", dev_mode and 10 or 60) + doer.components.timer:StartTimer("aip_olden_tea", dev_mode and 300 or 60) -- 喝茶时说一句话 if doer.components.talker ~= nil then diff --git a/scripts/prefabs/aip_rubik.lua b/scripts/prefabs/aip_rubik.lua index 83620e60..c6a4a6cb 100644 --- a/scripts/prefabs/aip_rubik.lua +++ b/scripts/prefabs/aip_rubik.lua @@ -1,3 +1,5 @@ +local dev_mode = aipGetModConfig("dev_mode") == "enabled" + local language = aipGetModConfig("language") -- 文字描述 @@ -19,8 +21,7 @@ STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_RUBIK = LANG.DESC -- 资源 local assets = { - Asset("ANIM", "anim/aip_22_fish.zip"), - Asset("ATLAS", "images/inventoryimages/aip_22_fish.xml"), + Asset("ANIM", "anim/aip_rubik.zip"), } local prefabs = { @@ -29,35 +30,41 @@ local prefabs = { "aip_rubik_fire_red", } -------------------------------- 事件 ------------------------------- +------------------------------- 燃烧 ------------------------------- +local function onextinguish(inst) + if inst.components.fueled ~= nil then + inst.components.fueled:InitializeFuelLevel(0) + end + inst:RemoveTag("shadow_fire") + inst.components.aipc_rubik:Stop() +end + +local function onignite(inst) + inst:AddTag("shadow_fire") + inst.components.aipc_rubik:Start() +end + +------------------------------- 燃料 ------------------------------- +local function ontakefuel(inst) + inst.SoundEmitter:PlaySound("dontstarve/common/nightmareAddFuel") +end -------------------------------- 魔方 ------------------------------- -local function CreateFire(inst, colorName, x, y, z) - local tgt = SpawnPrefab("aip_rubik_fire_"..colorName) - inst:AddChild(tgt) - tgt.Transform:SetPosition(x, y, z) +local function onupdatefueled(inst) + if inst.components.burnable ~= nil then + inst.components.burnable:SetFXLevel(inst.components.fueled:GetCurrentSection(), inst.components.fueled:GetSectionPercent()) + end end -local function initRubik(inst) - local offset = 2 - local height = 4 - local colors = {"green", "red","blue"} - - for oy = 1, 3 do - local scaleOffset = 1 -- + (3 - oy) / 6 - - for ox = -1, 1 do - for oz = -1, 1 do - CreateFire( - inst, - colors[oy], - ox * offset * scaleOffset, - oy * offset + height, - oz * offset * scaleOffset - ) - end - end - end +local function onfuelchange(newsection, oldsection, inst) + if newsection <= 0 then + inst.components.burnable:Extinguish() + else + if not inst.components.burnable:IsBurning() then + inst.components.burnable:Ignite() + end + + inst.components.burnable:SetFXLevel(newsection, inst.components.fueled:GetSectionPercent()) + end end ------------------------------- 实体 ------------------------------- @@ -66,14 +73,18 @@ local function fn() inst.entity:AddTransform() inst.entity:AddAnimState() + inst.entity:AddSoundEmitter() inst.entity:AddNetwork() - MakeInventoryPhysics(inst) + MakeObstaclePhysics(inst, .2) - inst.AnimState:SetBank("aip_22_fish") - inst.AnimState:SetBuild("aip_22_fish") + inst.AnimState:SetBank("aip_rubik") + inst.AnimState:SetBuild("aip_rubik") inst.AnimState:PlayAnimation("idle") + inst:AddTag("wildfireprotected") + inst:AddTag("aip_rubik") + inst.entity:SetPristine() if not TheWorld.ismastersim then @@ -81,12 +92,25 @@ local function fn() end inst:AddComponent("inspectable") - - inst:AddComponent("inventoryitem") - inst.components.inventoryitem.atlasname = "images/inventoryimages/aip_22_fish.xml" - inst.components.inventoryitem.imagename = "aip_22_fish" - - initRubik(inst) + inst:AddComponent("aipc_rubik") + + -- 可以点燃 + inst:AddComponent("burnable") + inst.components.burnable:AddBurnFX("nightlight_flame", Vector3(0, 0, 0), "fire_marker") + inst.components.burnable.canlight = false + inst:ListenForEvent("onextinguish", onextinguish) + inst:ListenForEvent("onignite", onignite) + + -- 使用燃料 + inst:AddComponent("fueled") + inst.components.fueled.maxfuel = TUNING.NIGHTLIGHT_FUEL_MAX + inst.components.fueled.accepting = true + inst.components.fueled.fueltype = FUELTYPE.NIGHTMARE + inst.components.fueled:SetSections(4) + inst.components.fueled:SetTakeFuelFn(ontakefuel) + inst.components.fueled:SetUpdateFn(onupdatefueled) + inst.components.fueled:SetSectionCallback(onfuelchange) + inst.components.fueled:InitializeFuelLevel(dev_mode and TUNING.NIGHTLIGHT_FUEL_START or 0) return inst end diff --git a/scripts/prefabs/aip_rubik_fire.lua b/scripts/prefabs/aip_rubik_fire.lua index 6c789371..6f79316f 100644 --- a/scripts/prefabs/aip_rubik_fire.lua +++ b/scripts/prefabs/aip_rubik_fire.lua @@ -1,3 +1,42 @@ +local colors = { + red = { 1, 0, 0 }, + green = { 0, 1, 0 }, + blue = { 0, 0, 1 }, + yellow = { 1, 1, 0 }, +} + +-- string.upper(name) + +------------------------------- 描述 ------------------------------- +local language = aipGetModConfig("language") + +local LANG_MAP = { + english = { + NAME = "Fire", + DESC = "Examine close fire to move", + }, + chinese = { + NAME = "火焰", + DESC = "检查相邻火焰移动", + }, +} + +local LANG = LANG_MAP[language] or LANG_MAP.english + +for colorName, rgb in pairs(colors) do + local name = "AIP_RUBIK_FIRE_"..string.upper(colorName) + STRINGS.NAMES[name] = LANG.NAME + STRINGS.CHARACTERS.GENERIC.DESCRIBE[name] = LANG.DESC +end + +------------------------------- 事件 ------------------------------- +local function onSelect(inst, viewer) + if inst.aipRubik ~= nil and inst.aipRubik.components.aipc_rubik ~= nil then + inst.aipRubik.components.aipc_rubik:Select(inst) + end +end + +------------------------------- 实体 ------------------------------- local MakeTorchFire = require("prefabs/torchfire_common") local ANIM_HAND_TEXTURE = "fx/animhand.tex" @@ -7,6 +46,7 @@ local SHADER = "shaders/vfx_particle.ksh" local REVEAL_SHADER = "shaders/vfx_particle_reveal.ksh" local assets = { + Asset("ANIM", "anim/aip_rubik_fire.zip"), Asset("IMAGE", ANIM_HAND_TEXTURE), Asset("IMAGE", ANIM_SMOKE_TEXTURE), Asset("SHADER", SHADER), @@ -176,6 +216,18 @@ local function genRubik(colorName, rgb) -------------------------------------------------------------------------- local function common_postinit(inst) + inst.entity:AddAnimState() + inst.AnimState:SetBank("aip_rubik_fire") + inst.AnimState:SetBuild("aip_rubik_fire") + inst.AnimState:PlayAnimation("idle") + + MakeTinyFlyingCharacterPhysics(inst, 1, 0) + -- MakeTinyFlyingCharacterPhysics(inst, 0, 0) + -- MakeInventoryPhysics(inst, 1, .5) + RemovePhysicsColliders(inst) + + inst:RemoveTag("FX") + --Dedicated server does not need to spawn local particle fx if TheNet:IsDedicated() then return @@ -186,7 +238,8 @@ local function genRubik(colorName, rgb) ----------------------------------------------------- local effect = inst.entity:AddVFXEffect() - effect:InitEmitters(3) + -- effect:InitEmitters(3) + effect:InitEmitters(1) --FIRE effect:SetRenderResources(0, ANIM_SMOKE_TEXTURE, REVEAL_SHADER) @@ -203,32 +256,31 @@ local function genRubik(colorName, rgb) effect:SetKillOnEntityDeath(0, true) effect:SetFollowEmitter(0, true) - --SMOKE - effect:SetRenderResources(1, ANIM_SMOKE_TEXTURE, REVEAL_SHADER) --REVEAL_SHADER --particle_add - effect:SetMaxNumParticles(1, 32) - effect:SetRotationStatus(1, true) - effect:SetMaxLifetime(1, SMOKE_MAX_LIFETIME) - effect:SetColourEnvelope(1, COLOUR_ENVELOPE_NAME_SMOKE) - effect:SetScaleEnvelope(1, SCALE_ENVELOPE_NAME_SMOKE) - effect:SetBlendMode(1, BLENDMODE.AlphaBlended) --AlphaBlended Premultiplied - effect:EnableBloomPass(1, true) - effect:SetUVFrameSize(1, 1, 1) - effect:SetSortOrder(1, 0) - effect:SetSortOffset(1, 1) - - --HAND - effect:SetRenderResources(2, ANIM_HAND_TEXTURE, REVEAL_SHADER) --REVEAL_SHADER --particle_add - effect:SetMaxNumParticles(2, 32) - effect:SetRotationStatus(2, true) - effect:SetMaxLifetime(2, HAND_MAX_LIFETIME) - effect:SetColourEnvelope(2, COLOUR_ENVELOPE_NAME_HAND) - effect:SetScaleEnvelope(2, SCALE_ENVELOPE_NAME_HAND) - effect:SetBlendMode(2, BLENDMODE.AlphaBlended) --AlphaBlended Premultiplied - effect:EnableBloomPass(2, true) - effect:SetUVFrameSize(2, .25, 1) - effect:SetSortOrder(2, 0) - effect:SetSortOffset(2, 1) - --effect:SetDragCoefficient(2, 50) + -- --SMOKE + -- effect:SetRenderResources(1, ANIM_SMOKE_TEXTURE, REVEAL_SHADER) --REVEAL_SHADER --particle_add + -- effect:SetMaxNumParticles(1, 32) + -- effect:SetRotationStatus(1, true) + -- effect:SetMaxLifetime(1, SMOKE_MAX_LIFETIME) + -- effect:SetColourEnvelope(1, COLOUR_ENVELOPE_NAME_SMOKE) + -- effect:SetScaleEnvelope(1, SCALE_ENVELOPE_NAME_SMOKE) + -- effect:SetBlendMode(1, BLENDMODE.AlphaBlended) --AlphaBlended Premultiplied + -- effect:EnableBloomPass(1, true) + -- effect:SetUVFrameSize(1, 1, 1) + -- effect:SetSortOrder(1, 0) + -- effect:SetSortOffset(1, 1) + + -- --HAND + -- effect:SetRenderResources(2, ANIM_HAND_TEXTURE, REVEAL_SHADER) --REVEAL_SHADER --particle_add + -- effect:SetMaxNumParticles(2, 32) + -- effect:SetRotationStatus(2, true) + -- effect:SetMaxLifetime(2, HAND_MAX_LIFETIME) + -- effect:SetColourEnvelope(2, COLOUR_ENVELOPE_NAME_HAND) + -- effect:SetScaleEnvelope(2, SCALE_ENVELOPE_NAME_HAND) + -- effect:SetBlendMode(2, BLENDMODE.AlphaBlended) --AlphaBlended Premultiplied + -- effect:EnableBloomPass(2, true) + -- effect:SetUVFrameSize(2, .25, 1) + -- effect:SetSortOrder(2, 0) + -- effect:SetSortOffset(2, 1) ----------------------------------------------------- @@ -256,35 +308,33 @@ local function genRubik(colorName, rgb) end fire_num_particles_to_emit = fire_num_particles_to_emit + fire_particles_per_tick * math.random() * 3 - --SMOKE - while smoke_num_particles_to_emit > 1 do - emit_smoke_fn(effect, sphere_emitter) - smoke_num_particles_to_emit = smoke_num_particles_to_emit - 1 - end - smoke_num_particles_to_emit = smoke_num_particles_to_emit + smoke_particles_per_tick - - --HAND - while hand_num_particles_to_emit > 1 do - emit_hand_fn(effect, sphere_emitter) - hand_num_particles_to_emit = hand_num_particles_to_emit - 1 - end - hand_num_particles_to_emit = hand_num_particles_to_emit + hand_particles_per_tick + -- --SMOKE + -- while smoke_num_particles_to_emit > 1 do + -- emit_smoke_fn(effect, sphere_emitter) + -- smoke_num_particles_to_emit = smoke_num_particles_to_emit - 1 + -- end + -- smoke_num_particles_to_emit = smoke_num_particles_to_emit + smoke_particles_per_tick + + -- --HAND + -- while hand_num_particles_to_emit > 1 do + -- emit_hand_fn(effect, sphere_emitter) + -- hand_num_particles_to_emit = hand_num_particles_to_emit - 1 + -- end + -- hand_num_particles_to_emit = hand_num_particles_to_emit + hand_particles_per_tick end) end local function master_postinit(inst) - inst.fx_offset = -100 + inst:AddComponent("inspectable") + inst.components.inspectable.descriptionfn = onSelect + + inst:AddComponent("aipc_float") end return MakeTorchFire("aip_rubik_fire_"..colorName, assets, nil, common_postinit, master_postinit) end local prefabList = {} -local colors = { - red = { 1, 0, 0 }, - green = { 0, 1, 0 }, - blue = { 0, 0, 1 }, -} for colorName, rgb in pairs(colors) do table.insert(prefabList, genRubik(colorName, rgb)) diff --git a/scripts/prefabs/aip_rubik_ghost.lua b/scripts/prefabs/aip_rubik_ghost.lua new file mode 100644 index 00000000..60a49e55 --- /dev/null +++ b/scripts/prefabs/aip_rubik_ghost.lua @@ -0,0 +1,176 @@ +-- 开发模式 +local dev_mode = aipGetModConfig("dev_mode") == "enabled" + +local brain = require("brains/aip_rubik_ghost_brain") + +local assets = { + Asset("ANIM", "anim/aip_rubik_ghost.zip"), +} + +local language = aipGetModConfig("language") + +local LANG_MAP = { + english = { + NAME = "Mr. Skits", + }, + chinese = { + NAME = "诙谐梦魇", + }, +} + +local LANG = LANG_MAP[language] or LANG_MAP.english + +-- 文字描述 +STRINGS.NAMES.AIP_RUBIK_GHOST = LANG.NAME + +local sounds = { + attack = "dontstarve/sanity/creature2/attack", + attack_grunt = "dontstarve/sanity/creature2/attack_grunt", + death = "dontstarve/sanity/creature2/die", + idle = "dontstarve/sanity/creature2/idle", + taunt = "dontstarve/sanity/creature2/taunt", + appear = "dontstarve/sanity/creature2/appear", + disappear = "dontstarve/sanity/creature2/dissappear", +} + +------------------------------- 厉火 ------------------------------- +local BaseHealth = dev_mode and 100 or TUNING.WORM_HEALTH +local MultipleHealth = BaseHealth * 0.5 + +local BaseDamage = dev_mode and 4 or 40 +local MultipleDamage = BaseDamage * 0.5 + +local BASE_SCALE = 1 +local MULTIPLE_SCALE = 0.2 + +local function refreshGrow(inst, nextLevel) + local currentLevel = inst.aipGrow or 0 + + -- 每次死亡一个单位就飞过来充能一个单位 + inst.aipGrow = nextLevel + local scale = BASE_SCALE + nextLevel * MULTIPLE_SCALE + inst.Transform:SetScale(scale, scale, scale) + + -- 更新生命值 + local healthPTG = inst.components.health:GetPercent() + inst.components.health:SetMaxHealth(BaseHealth + nextLevel * MultipleHealth) + inst.components.health:SetPercent(healthPTG) + + -- 更新攻击力 + inst.components.combat:SetDefaultDamage(BaseDamage + nextLevel * MultipleDamage) +end + +------------------------------- 事件 ------------------------------- +local RETARGET_CANT_TAGS = {} +local RETARGET_ONEOF_TAGS = {"character"} +local function Retarget(inst) + local newtarget = FindEntity( + inst, + dev_mode and TUNING.BAT_TARGET_DIST or TUNING.PIG_TARGET_DIST, + function(guy) + return inst.components.combat:CanTarget(guy) + end, + nil, + RETARGET_CANT_TAGS, + RETARGET_ONEOF_TAGS + ) + + return newtarget +end + +local function OnAttacked(inst, data) + inst.components.combat:SetTarget(data.attacker) + inst.components.combat:ShareTarget(data.attacker, 30, function(dude) + return dude:HasTag("aip_rubik_ghost") and not dude.components.health:IsDead() + end, 99) +end + +local function OnDead(inst) + if inst.aipHeart and inst.aipHeart.aipGhosts then + for i, ghost in ipairs(inst.aipHeart.aipGhosts) do + if ghost ~= inst and ghost.components.health and not ghost.components.health:IsDead() then + -- 飞行灵魂给其他鬼魂 + local proj = aipSpawnPrefab(inst, "aip_projectile") + proj.components.aipc_info_client:SetByteArray( -- 调整颜色 + "aip_projectile_color", { 0, 0, 0, 5 } + ) + proj.components.aipc_projectile.speed = 10 + proj.components.aipc_projectile:GoToTarget(ghost, function() + if ghost.components and ghost.components.health and not ghost.components.health:IsDead() then + refreshGrow(ghost, (ghost.aipGrow or 1) + 1) + end + end) + end + end + end +end + +------------------------------- 实体 ------------------------------- +local function fn() + local inst = CreateEntity() + + inst.entity:AddTransform() + inst.entity:AddAnimState() + inst.entity:AddSoundEmitter() + inst.entity:AddNetwork() + + MakeFlyingCharacterPhysics(inst, 1, .5) + + inst.Transform:SetTwoFaced() + -- inst.Transform:SetScale(2, 2, 2) + + inst:AddTag("shadowcreature") + inst:AddTag("gestaltnoloot") + inst:AddTag("monster") + inst:AddTag("hostile") + inst:AddTag("shadow") + inst:AddTag("notraptrigger") + + inst:AddTag("aip_rubik_ghost") + + inst.AnimState:SetBank("aip_rubik_ghost") + inst.AnimState:SetBuild("aip_rubik_ghost") + inst.AnimState:PlayAnimation("idle_loop", true) + inst.AnimState:SetMultColour(1, 1, 1, .7) + + inst.entity:SetPristine() + + if not TheWorld.ismastersim then + return inst + end + + inst:AddComponent("locomotor") -- locomotor must be constructed before the stategraph + inst.components.locomotor.walkspeed = TUNING.BEEQUEEN_SPEED + inst.components.locomotor:SetTriggersCreep(false) + inst.components.locomotor.pathcaps = { ignorecreep = true } + + inst.sounds = sounds + + inst:SetStateGraph("SGaip_rubik_ghost") + inst:SetBrain(brain) + + inst:AddComponent("health") + inst.components.health:SetMaxHealth(BaseHealth) + + inst:AddComponent("knownlocations") + + inst:AddComponent("combat") + inst.components.combat.hiteffectsymbol = "body" + inst.components.combat:SetDefaultDamage(BaseDamage) + inst.components.combat:SetAttackPeriod(TUNING.BEEQUEEN_ATTACK_PERIOD) + -- inst.components.combat:SetRange(TUNING.BEE_ATTACK_RANGE) + inst.components.combat:SetRetargetFunction(1, Retarget) + + inst:ListenForEvent("attacked", OnAttacked) + inst:ListenForEvent("death", OnDead) + + inst.persists = false + + inst:DoTaskInTime(0.1, function() + refreshGrow(inst, 0) + end) + + return inst +end + +return Prefab("aip_rubik_ghost", fn, assets) diff --git a/scripts/prefabs/aip_rubik_heart.lua b/scripts/prefabs/aip_rubik_heart.lua new file mode 100644 index 00000000..4adddb65 --- /dev/null +++ b/scripts/prefabs/aip_rubik_heart.lua @@ -0,0 +1,188 @@ +-- 开发模式 +local dev_mode = aipGetModConfig("dev_mode") == "enabled" + +local BaseHealth = dev_mode and 100 or TUNING.LEIF_HEALTH + +local assets = { + Asset("ANIM", "anim/aip_rubik_heart.zip"), +} + +local language = aipGetModConfig("language") + +local LANG_MAP = { + english = { + NAME = "Skits Heart", + DESC = "A beating heart", + }, + chinese = { + NAME = "诙谐之心", + DESC = "一颗跳动的心脏", + }, +} + +local LANG = LANG_MAP[language] or LANG_MAP.english + +-- 文字描述 +STRINGS.NAMES.AIP_RUBIK_HEART = LANG.NAME +STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_RUBIK_HEART = LANG.DESC + +------------------------------- 掉落 ------------------------------- +local loot = { + "aip_fake_fly_totem_blueprint", + "aip_wizard_hat", +} + +local createProjectile = require("utils/aip_vest_util").createProjectile + +------------------------------- 事件 ------------------------------- +-- 移除无效死鬼 +local function refreshGhosts(inst) + inst.aipGhosts = aipFilterTable(inst.aipGhosts or {}, function(ghost) + return ghost and ghost:IsValid() and ghost.components.health and not ghost.components.health:IsDead() + end) +end + +local function onHit(inst) + inst.SoundEmitter:PlaySound("dontstarve/creatures/leif/livingtree_hit") + + -- 只有没有其他动作的时候被打才会表现动画 + if inst.AnimState:IsCurrentAnimation("idle") then + inst.AnimState:PlayAnimation("hit") + inst.AnimState:PushAnimation("idle", true) + end + + if + inst.components.health ~= nil and + inst.components.health:GetPercent() < 0.5 + then + refreshGhosts(inst) + + -- 血少了,开始吸血玩家 + if inst.components.timer ~= nil and not inst.components.timer:TimerExists("aip_eat_snity") then + inst.components.timer:StartTimer("aip_eat_snity", 5) + inst.AnimState:PlayAnimation("spell") + + inst:DoTaskInTime(.5, function() -- 延迟施法 + if inst.components.health:IsDead() then + return + end + + local players = aipFindNearPlayers(inst, 10) + + for i, player in ipairs(players) do + if player.components.sanity ~= nil and player.components.sanity:GetPercent() > 0 then + player.components.sanity:DoDelta(-30) + + createProjectile(player, inst, function() + if not inst.components.health:IsDead() then + inst.components.health:DoDelta(50) + end + end, { 0, 0, 0, 5 }) + end + end + end) + elseif #inst.aipGhosts <= 0 and not inst.components.timer:TimerExists("aip_spawn_ghost") then + inst.components.timer:StartTimer("aip_spawn_ghost", 10) + + -- 如果发现没有生物了,我们至少召唤一个 + local homePos = inst:GetPosition() + createProjectile( + inst, aipGetSpawnPoint(homePos, 5), function(proj) + local effect = aipSpawnPrefab(proj, "aip_shadow_wrapper", nil, 0.1) + effect.DoShow() + + -- 有血我们才创建 + if not inst.components.health:IsDead() then + local ghost = aipSpawnPrefab(proj, "aip_rubik_ghost") + if ghost.components.knownlocations then + ghost.components.knownlocations:RememberLocation("home", homePos) + end + + ghost.aipHeart = inst + table.insert(inst.aipGhosts, ghost) + end + end, { 0, 0, 0, 5 } + ) + end + end +end + +local function OnDead(inst) + if inst.aipGhosts then + -- 不用再通知其他鬼魂了 + local tmpGhosts = inst.aipGhosts + inst.aipGhosts = {} + + for i, ghost in ipairs(tmpGhosts) do + ghost.aipHeartDead = true + if ghost.components.health and not ghost.components.health:IsDead() then + ghost.components.health:Kill() + end + end + end + + inst:DoTaskInTime(0.1, function() + inst.AnimState:PlayAnimation("dead") + inst:ListenForEvent("animover", function() + aipSpawnPrefab(inst, "aip_shadow_wrapper", nil, 4).DoShow() + local pt = inst:GetPosition() + pt.y = 4 + inst.components.lootdropper:DropLoot(pt) + inst:Remove() + end) + end) +end + +------------------------------- 实体 ------------------------------- +local function fn() + local inst = CreateEntity() + + inst.entity:AddTransform() + inst.entity:AddAnimState() + inst.entity:AddSoundEmitter() + inst.entity:AddDynamicShadow() + inst.entity:AddNetwork() + + inst.DynamicShadow:SetSize(.8, .5) + + MakeFlyingCharacterPhysics(inst, 0, 0) + + inst:AddTag("aip_shadowcreature") -- 标记的暗影生物,因为默认的不允许攻击 + inst:AddTag("gestaltnoloot") + inst:AddTag("monster") + inst:AddTag("hostile") + inst:AddTag("shadow") + inst:AddTag("notraptrigger") + + inst.AnimState:SetBank("aip_rubik_heart") + inst.AnimState:SetBuild("aip_rubik_heart") + inst.AnimState:PlayAnimation("idle", true) + + inst.entity:SetPristine() + + if not TheWorld.ismastersim then + return inst + end + + inst:AddComponent("inspectable") + + inst:AddComponent("timer") + + inst:AddComponent("health") + inst.components.health:SetMaxHealth(BaseHealth) + inst.components.health.nofadeout = true + + inst:AddComponent("combat") + inst.components.combat:SetOnHit(onHit) + + inst:AddComponent("lootdropper") + inst.components.lootdropper:SetLoot(loot) + + inst:ListenForEvent("death", OnDead) + + inst.persists = false + + return inst +end + +return Prefab("aip_rubik_heart", fn, assets) diff --git a/scripts/prefabs/aip_shadow_wrapper.lua b/scripts/prefabs/aip_shadow_wrapper.lua index 5143ae29..21118384 100755 --- a/scripts/prefabs/aip_shadow_wrapper.lua +++ b/scripts/prefabs/aip_shadow_wrapper.lua @@ -62,7 +62,10 @@ function fn() end -- Play show - inst.DoShow = function() + inst.DoShow = function(scale) + scale = scale or 1 + inst.Transform:SetScale(scale, scale, scale) + inst.SoundEmitter:PlaySound("dontstarve/maxwell/shadowmax_despawn") inst.AnimState:PlayAnimation("end") diff --git a/scripts/prefabs/aip_wizard_hat.lua b/scripts/prefabs/aip_wizard_hat.lua new file mode 100644 index 00000000..9c122063 --- /dev/null +++ b/scripts/prefabs/aip_wizard_hat.lua @@ -0,0 +1,72 @@ +-- 配置 +local dress_uses = aipGetModConfig("dress_uses") +local language = aipGetModConfig("language") + +-- 默认参数 +local PERISH_MAP = { + ["less"] = 0.5, + ["normal"] = 1, + ["much"] = 2, +} + +local LANG_MAP = { + english = { + NAME = "Haunted Wizard Hat", + DESC = "Ghosts can't scare me anymore", + }, + chinese = { + NAME = "闹鬼巫师帽", + DESC = "鬼已经吓不到我了", + }, +} + +TUNING.AIP_WIZARD_FUEL = TUNING.YELLOWAMULET_FUEL * PERISH_MAP[dress_uses] + +local LANG = LANG_MAP[language] or LANG_MAP.english + +-- 文字描述 +STRINGS.NAMES.AIP_WIZARD_HAT = LANG.NAME +STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_WIZARD_HAT = LANG.DESC + +local function onremovebody(body) + body._aipHat._aipEye = nil +end + +-- 配方 +local tempalte = require("prefabs/aip_dress_template") + +-- 可以获得直接看到诡影的能力,同时被攻击没有硬直 +return tempalte("aip_wizard_hat", { + dapperness = TUNING.DAPPERNESS_LARGE, + fueled = { + level = TUNING.AIP_WIZARD_FUEL, + }, + waterproofer = true, + onEquip = function(inst, owner) + if inst._aipAura ~= nil then + inst._aipAura:Remove() + end + + inst._aipAura = SpawnPrefab("aip_aura_see") + inst:AddChild(inst._aipAura) + -- if inst._aipEye ~= nil then + -- inst._aipEye:Remove() + -- end + -- inst._aipEye = SpawnPrefab("redlanternbody") + -- inst._aipEye._aipHat = inst + -- inst:ListenForEvent("onremove", onremovebody, inst._aipEye) + + -- inst._aipEye.entity:SetParent(owner.entity) + -- inst._aipEye.entity:AddFollower() + -- inst._aipEye.Follower:FollowSymbol(owner.GUID, "hair", 0, 0, 1) + end, + onUnequip = function(inst, owner) + if inst._aipAura ~= nil then + inst._aipAura:Remove() + end + inst._aipAura = nil + end, + preInst = function(inst) + inst:AddTag("aip_no_shadow_stun") + end, +}) diff --git a/scripts/recpiesHooker.lua b/scripts/recpiesHooker.lua index c32381ba..047ec910 100644 --- a/scripts/recpiesHooker.lua +++ b/scripts/recpiesHooker.lua @@ -20,6 +20,16 @@ AddRecipe( "aip_score_ball.tex" ) +-- 劣质的飞行图腾 +AddRecipe( + "aip_fake_fly_totem", + { Ingredient("boards", 1), Ingredient("rope", 1), Ingredient("nightmarefuel", 1) }, + _G.RECIPETABS.TOWN, _G.TECH.LOST, + "aip_fake_fly_totem_placer", nil, nil, nil, nil, + "images/inventoryimages/aip_fake_fly_totem.xml", + "aip_fake_fly_totem.tex" +) + -- 飞行图腾。只有蓝图可以做出来,现在不提供 AddRecipe( "aip_fly_totem", diff --git a/scripts/sgHooker.lua b/scripts/sgHooker.lua new file mode 100644 index 00000000..3536ecbe --- /dev/null +++ b/scripts/sgHooker.lua @@ -0,0 +1,20 @@ +local _G = GLOBAL + +-- 硬直劫持 +AddStategraphPostInit("wilson", function(sg) + local originAttackedFn = sg.events.attacked.fn + + sg.events.attacked = _G.EventHandler('attacked', function(inst, data, ...) + if -- 暗影生物打不出硬直 + inst.replica.inventory:EquipHasTag("aip_no_shadow_stun") and + data.attacker and + (_G.aipIsShadowCreature(data.attacker) or data.attacker:HasTag("ghost")) + then + if not inst.sg:HasStateTag('frozen') and not inst.sg:HasStateTag('sleeping') then + return + end + end + + return originAttackedFn(inst, data, ...) + end) +end) \ No newline at end of file diff --git a/scripts/stategraphs/SGaip_dragon_tail.lua b/scripts/stategraphs/SGaip_dragon_tail.lua index e2049a7a..d024acaa 100644 --- a/scripts/stategraphs/SGaip_dragon_tail.lua +++ b/scripts/stategraphs/SGaip_dragon_tail.lua @@ -106,4 +106,4 @@ local states = { CommonStates.AddWalkStates(states, nil, nil, true) -return StateGraph("SGaip_dragon", states, events, "appear") \ No newline at end of file +return StateGraph("SGaip_dragon_tail", states, events, "appear") \ No newline at end of file diff --git a/scripts/stategraphs/SGaip_rubik_ghost.lua b/scripts/stategraphs/SGaip_rubik_ghost.lua new file mode 100644 index 00000000..82710f68 --- /dev/null +++ b/scripts/stategraphs/SGaip_rubik_ghost.lua @@ -0,0 +1,88 @@ +require("stategraphs/commonstates") + + +local function FinishExtendedSound(inst, soundid) + inst.SoundEmitter:KillSound("sound_"..tostring(soundid)) + inst.sg.mem.soundcache[soundid] = nil +end + +local function PlayExtendedSound(inst, soundname) + if inst.sg.mem.soundcache == nil then + inst.sg.mem.soundcache = {} + inst.sg.mem.soundid = 0 + else + inst.sg.mem.soundid = inst.sg.mem.soundid + 1 + end + inst.sg.mem.soundcache[inst.sg.mem.soundid] = true + inst.SoundEmitter:PlaySound(inst.sounds[soundname], "sound_"..tostring(inst.sg.mem.soundid)) + inst:DoTaskInTime(5, FinishExtendedSound, inst.sg.mem.soundid) +end + +local events = { + EventHandler("death", function(inst) + inst.sg:GoToState("death") + end), + CommonHandlers.OnAttack(), + CommonHandlers.OnLocomote(false, true), +} + +local states = { + State{ + name = "idle", + tags = { "idle", "canrotate" }, + + onenter = function(inst) + inst.components.locomotor:StopMoving() + if not inst.AnimState:IsCurrentAnimation("idle_loop") then + inst.AnimState:PlayAnimation("idle_loop", true) + end + end, + }, + + State{ + name = "attack", + tags = { "attack", "busy" }, + + onenter = function(inst, target) + inst.sg.statemem.target = target + inst.Physics:Stop() + inst.components.combat:StartAttack() + inst.AnimState:PlayAnimation("attack") + -- PlayExtendedSound(inst, "attack_grunt") + inst.sg.statemem.target = inst.components.combat.target + end, + + timeline = {-- 1 秒 30 次渲染/1000 帧,340 帧时造成伤害。 + TimeEvent(0*FRAMES, function(inst) PlayExtendedSound(inst, "attack") end), + -- 30*340/1000 + TimeEvent(10.2*FRAMES, function(inst) inst.components.combat:DoAttack(inst.sg.statemem.target) end), + }, + events = { + EventHandler("animover", function(inst) inst.sg:GoToState("idle") end), + }, + }, + + State{ + name = "death", + tags = { "busy" }, + + onenter = function(inst) + PlayExtendedSound(inst, "death") + + inst.AnimState:PlayAnimation("death", false) + + + inst.Physics:Stop() + RemovePhysicsColliders(inst) + inst:AddTag("NOCLICK") + inst.persists = false + end, + events = { + -- EventHandler("animover", function(inst) inst:Remove() end), + }, + }, +} + +CommonStates.AddWalkStates(states, nil, nil, true) + +return StateGraph("SGaip_rubik_ghost", states, events, "idle") \ No newline at end of file diff --git a/scripts/utils/aip_vest_util.lua b/scripts/utils/aip_vest_util.lua index f8b79441..39917678 100644 --- a/scripts/utils/aip_vest_util.lua +++ b/scripts/utils/aip_vest_util.lua @@ -69,8 +69,33 @@ function createGroudVest(bank, build, animate) return inst end +-- 飞到目标点或者物 +function createProjectile(source, target, fn, color, speed) + local proj = aipSpawnPrefab(source, "aip_projectile") + + -- 设置颜色 + if color ~= nil then + proj.components.aipc_info_client:SetByteArray( -- 调整颜色 + "aip_projectile_color", color + ) + end + + if speed ~= nil then + proj.components.aipc_projectile.speed = 10 + end + + if target ~= nil and target.prefab ~= nil then + proj.components.aipc_projectile:GoToTarget(target, fn) + else + proj.components.aipc_projectile:GoToPoint(target, fn) + end + + return proj +end + return { createClientVest = createClientVest, createEffectVest = createEffectVest, createGroudVest = createGroudVest, + createProjectile = createProjectile, } \ No newline at end of file diff --git a/scripts/widgetHooker.lua b/scripts/widgetHooker.lua index dd786231..a7b0852a 100644 --- a/scripts/widgetHooker.lua +++ b/scripts/widgetHooker.lua @@ -1,8 +1,10 @@ local _G = GLOBAL -local DestinationScreen = require("widgets/aip_dest_screen") local PlayerHud = _G.require("screens/playerhud") +------------------------------- 飞行 ------------------------------- +local DestinationScreen = require("widgets/aip_dest_screen") + function PlayerHud:OpenAIPDestination(inst, currentTotemId) self.aipDestScreen = DestinationScreen(self.owner, currentTotemId) self:OpenScreenUnderPause(self.aipDestScreen) @@ -14,4 +16,4 @@ function PlayerHud:CloseAIPDestination() self.aipDestScreen:Close() self.aipDestScreen = nil end -end \ No newline at end of file +end diff --git a/scripts/widgets/aip_rubik_screen.lua b/scripts/widgets/aip_rubik_screen.lua new file mode 100644 index 00000000..73c4544b --- /dev/null +++ b/scripts/widgets/aip_rubik_screen.lua @@ -0,0 +1,95 @@ +local Screen = require "widgets/screen" +local Widget = require "widgets/widget" +local Text = require "widgets/text" +local TextEdit = require "widgets/textedit" +local Menu = require "widgets/menu" +local UIAnim = require "widgets/uianim" +local ImageButton = require "widgets/imagebutton" + +local function oncancel(doer, widget) + if not widget.isopen then + return + end + + doer.HUD:CloseAIPRubik() +end + +local RubikScreen = Class(Screen, function(self, owner) + Screen._ctor(self, "AIP_RubikScreen") + + self.owner = owner + + self.isopen = false + + ----------------------------------- 以下直接抄的木板代码 ----------------------------------- + self._scrnw, self._scrnh = TheSim:GetScreenSize() + + self:SetScaleMode(SCALEMODE_PROPORTIONAL) + self:SetMaxPropUpscale(MAX_HUD_SCALE) + self:SetPosition(0, 0, 0) + self:SetVAnchor(ANCHOR_MIDDLE) + self:SetHAnchor(ANCHOR_MIDDLE) + + self.scalingroot = self:AddChild(Widget("writeablewidgetscalingroot")) + self.scalingroot:SetScale(TheFrontEnd:GetHUDScale()) + self.inst:ListenForEvent("continuefrompause", function() + if self.isopen then + self.scalingroot:SetScale(TheFrontEnd:GetHUDScale()) + end + end, TheWorld) + self.inst:ListenForEvent("refreshhudsize", function(hud, scale) + if self.isopen then + self.scalingroot:SetScale(scale) + end + end, owner.HUD.inst) + + self.root = self.scalingroot:AddChild(Widget("writeablewidgetroot")) + local scale = 0.8 + self.root:SetScale(scale, scale, scale) + + -- 不可见的透明背景:mask + self.black = self.root:AddChild(Image("images/global.xml", "square.tex")) + self.black:SetVRegPoint(ANCHOR_MIDDLE) + self.black:SetHRegPoint(ANCHOR_MIDDLE) + self.black:SetVAnchor(ANCHOR_MIDDLE) + self.black:SetHAnchor(ANCHOR_MIDDLE) + self.black:SetScaleMode(SCALEMODE_FILLSCREEN) + self.black:SetTint(0, 0, 0, 0) + self.black.OnMouseButton = function() oncancel(self.owner, self) end + + -- 木板 UI + self.bganim = self.root:AddChild(UIAnim()) + self.bganim:SetHAnchor(ANCHOR_RIGHT) + self.bganim:SetVAnchor(ANCHOR_BOTTOM) + self.bganim:SetScale(0.5, 0.5, 0.5) + + self.bganim:GetAnimState():SetBank("ui_board_5x3") + self.bganim:GetAnimState():SetBuild("ui_board_5x3") + + self.bganim:SetPosition(-300, 150, 0) + + ----------------------------------- 创建魔方 UI 内容物 ----------------------------------- + + + ----------------------------------- 以下直接抄的木板代码 ----------------------------------- + self.root:SetPosition(0, 0, 0) + + self.isopen = true + self:Show() + + self.bganim:GetAnimState():PlayAnimation("open") +end) + +function RubikScreen:Close() + if self.isopen then + self.bganim:GetAnimState():PlayAnimation("close") + + self.black:Kill() + + self.isopen = false + + self.inst:DoTaskInTime(.3, function() TheFrontEnd:PopScreen(self) end) + end +end + +return RubikScreen \ No newline at end of file diff --git a/scripts/writeablesWrapper.lua b/scripts/writeablesWrapper.lua index e535d055..eca000c5 100644 --- a/scripts/writeablesWrapper.lua +++ b/scripts/writeablesWrapper.lua @@ -13,6 +13,8 @@ kinds["aip_fly_totem"] = { acceptbtn = { text = _G.STRINGS.SIGNS.MENU.ACCEPT, cb = nil, control = _G.CONTROL_ACCEPT }, } +kinds["aip_fake_fly_totem"] = kinds["aip_fly_totem"] + local originMakescreen = writeables.makescreen writeables.makescreen = function(inst, doer, ...) diff --git a/tools-scripts/dist.js b/tools-scripts/dist.js index 3128394b..b94c91af 100644 --- a/tools-scripts/dist.js +++ b/tools-scripts/dist.js @@ -99,8 +99,8 @@ async function doJob() { text = text.replace(src, tgt); fs.writeFileSync(filepath, text, 'utf8'); } - replaceText('package/modinfo.lua', /"\(DEV MODE\)",/g, outputMark ? '"(OUTPUT)",' : ''); - replaceText('package/modinfo.lua', /name = "Additional Item Package DEV"/g, `name = "Additional Item Package${outputMark ? ' (OUTPUT)' : ''}"`); + replaceText('package/modinfo.lua', /"\(DEV MODE\)",/g, outputMark ? '"(内测)",' : ''); + replaceText('package/modinfo.lua', /name = "Additional Item Package DEV"/g, `name = "Additional Item Package${outputMark ? ' (测试)' : ''}"`); replaceText('package/modmain.lua', /TUNING.ZOMBIEJ_ADDTIONAL_PACKAGE = "Additional Item Package DEV"/g, 'TUNING.ZOMBIEJ_ADDTIONAL_PACKAGE = "Additional Item Package"'); // 压缩一下 LUA 代码 @@ -128,7 +128,7 @@ async function doJob() { fs.copySync('package', relativePath); console.log(chalk.cyan("Add name mark...")); - replaceText(path.join(relativePath, '/modinfo.lua'), /name = "Additional Item Package"/g, 'name = "Additional Item Package (output)"'); + replaceText(path.join(relativePath, '/modinfo.lua'), /name = "Additional Item Package"/g, 'name = "Additional Item Package (内测)"'); // 创建 zip 包 const parentFolder = path.dirname(relativePath);