diff --git a/exported/aip_crayon_wall/aip_crayon_wall.scml b/exported/aip_crayon_wall/aip_crayon_wall.scml new file mode 100644 index 00000000..1695dbf2 --- /dev/null +++ b/exported/aip_crayon_wall/aip_crayon_wall.scml @@ -0,0 +1,804 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/exported/aip_crayon_wall/wall_segment/wall_segment-0.png b/exported/aip_crayon_wall/wall_segment/wall_segment-0.png new file mode 100644 index 00000000..19c648ea Binary files /dev/null and b/exported/aip_crayon_wall/wall_segment/wall_segment-0.png differ diff --git a/exported/aip_crayon_wall/wall_segment/wall_segment-3.png b/exported/aip_crayon_wall/wall_segment/wall_segment-3.png new file mode 100644 index 00000000..ab20fe25 Binary files /dev/null and b/exported/aip_crayon_wall/wall_segment/wall_segment-3.png differ diff --git a/exported/aip_crayon_wall/wall_segment/wall_segment-4.png b/exported/aip_crayon_wall/wall_segment/wall_segment-4.png new file mode 100644 index 00000000..883a65b7 Binary files /dev/null and b/exported/aip_crayon_wall/wall_segment/wall_segment-4.png differ diff --git a/exported/aip_crayon_wall/wall_segment/wall_segment-5.png b/exported/aip_crayon_wall/wall_segment/wall_segment-5.png new file mode 100644 index 00000000..8a1cea6c Binary files /dev/null and b/exported/aip_crayon_wall/wall_segment/wall_segment-5.png differ diff --git a/exported/aip_crayon_wall/wall_segment/wall_segment-6.png b/exported/aip_crayon_wall/wall_segment/wall_segment-6.png new file mode 100644 index 00000000..de55dc63 Binary files /dev/null and b/exported/aip_crayon_wall/wall_segment/wall_segment-6.png differ diff --git a/images/inventoryimages/aip_crayon_wall_item.png b/images/inventoryimages/aip_crayon_wall_item.png new file mode 100644 index 00000000..fdef0a84 Binary files /dev/null and b/images/inventoryimages/aip_crayon_wall_item.png differ diff --git a/images/inventoryimages/aip_crayon_wall_item.tex b/images/inventoryimages/aip_crayon_wall_item.tex new file mode 100644 index 00000000..3cf77db0 Binary files /dev/null and b/images/inventoryimages/aip_crayon_wall_item.tex differ diff --git a/images/inventoryimages/aip_crayon_wall_item.xml b/images/inventoryimages/aip_crayon_wall_item.xml new file mode 100644 index 00000000..0cea0454 --- /dev/null +++ b/images/inventoryimages/aip_crayon_wall_item.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/inventoryimages/aip_food_surprise_stew.xml b/images/inventoryimages/aip_food_surprise_stew.xml index be936ce2..8a47c962 100644 --- a/images/inventoryimages/aip_food_surprise_stew.xml +++ b/images/inventoryimages/aip_food_surprise_stew.xml @@ -1 +1 @@ - + \ No newline at end of file diff --git a/modmain.lua b/modmain.lua index fef66368..531327f9 100644 --- a/modmain.lua +++ b/modmain.lua @@ -1,8 +1,11 @@ local _G = GLOBAL -_G.STRINGS.AIP = {} - --- 资源 +_G.STRINGS.AIP = {} + +-- 注册蜡笔墙修理材料,让原版 REPAIR 动作可以识别同类墙物品。 +_G.MATERIALS.AIP_CRAYON_WALL = "aip_crayon_wall" + +-- 资源 Assets = { Asset("ATLAS", "images/inventoryimages/popcorngun.xml"), Asset("ATLAS", "images/inventoryimages/incinerator.xml"), @@ -118,6 +121,7 @@ PrefabFiles = { "aip_woodener", "aip_glass_chest", "aip_protected_mark", + "aip_crayon_wall", -- 诡影迷踪 "aip_dou_totem", diff --git a/scripts/prefabs/aip_crayon_wall.lua b/scripts/prefabs/aip_crayon_wall.lua new file mode 100644 index 00000000..ee9be02e --- /dev/null +++ b/scripts/prefabs/aip_crayon_wall.lua @@ -0,0 +1,330 @@ +require "prefabutil" + +local language = aipGetModConfig("language") + +local LANG_MAP = { + english = { + NAME = "Crayon Wall", + DESC = "Bright, waxy, and surprisingly stubborn.", + }, + chinese = { + NAME = "蜡笔墙", + DESC = "看起来很鲜艳,也很结实。", + }, +} + +local LANG = LANG_MAP[language] or LANG_MAP.english + +STRINGS.NAMES.AIP_CRAYON_WALL = LANG.NAME +STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_CRAYON_WALL = LANG.DESC +STRINGS.NAMES.AIP_CRAYON_WALL_ITEM = LANG.NAME +STRINGS.CHARACTERS.GENERIC.DESCRIBE.AIP_CRAYON_WALL_ITEM = LANG.DESC + +local assets = { + Asset("ANIM", "anim/aip_crayon_wall.zip"), + Asset("ATLAS", "images/inventoryimages/aip_crayon_wall_item.xml"), +} + +local prefabs = { + "collapse_small", +} + +local BUILD = "aip_crayon_wall" +local ITEM = "aip_crayon_wall_item" +local MAX_HEALTH = TUNING.WOODWALL_HEALTH +local MAX_LOOTS = 2 +local REPAIR_MATERIAL = "aip_crayon_wall" +local PLAYER_TAGS = { "player" } + +local WALL_ANIMS = { + { threshold = 0, anim = "broken" }, + { threshold = 0.4, anim = "onequarter" }, + { threshold = 0.5, anim = "half" }, + { threshold = 0.99, anim = "threequarter" }, + { threshold = 1, anim = "full" }, +} + +-- 根据墙体血量选择展示阶段。 +local function ResolveAnim(percent) + for _, data in ipairs(WALL_ANIMS) do + if percent <= data.threshold then + return data.anim + end + end + + return "full" +end + +-- 同步路径墙状态,避免玩家和生物穿过实体。 +local function OnIsPathFindingDirty(inst) + if inst._ispathfinding:value() then + if inst._pfpos == nil and inst:GetCurrentPlatform() == nil then + inst._pfpos = inst:GetPosition() + TheWorld.Pathfinder:AddWall(inst._pfpos:Get()) + end + elseif inst._pfpos ~= nil then + TheWorld.Pathfinder:RemoveWall(inst._pfpos:Get()) + inst._pfpos = nil + end +end + +-- 延后初始化路径墙,等待部署位置稳定。 +local function InitializePathFinding(inst) + inst:ListenForEvent("onispathfindingdirty", OnIsPathFindingDirty) + OnIsPathFindingDirty(inst) +end + +-- 激活碰撞和寻路阻挡。 +local function MakeObstacle(inst) + inst.Physics:SetActive(true) + inst._ispathfinding:set(true) +end + +-- 关闭碰撞和寻路阻挡。 +local function ClearObstacle(inst) + inst.Physics:SetActive(false) + inst._ispathfinding:set(false) +end + +-- 移除实体时清理路径墙。 +local function OnRemove(inst) + inst._ispathfinding:set_local(false) + OnIsPathFindingDirty(inst) +end + +-- 被修复到有血量时恢复阻挡。 +local function OnRepaired(inst) + MakeObstacle(inst) + inst.SoundEmitter:PlaySound("dontstarve/common/place_structure_wood") +end + +-- 校验破损墙修复时不会把玩家卡在墙内。 +local function CanRepair(inst) + if inst.Physics:IsActive() then + return true + end + + local x, y, z = inst.Transform:GetWorldPosition() + if TheWorld.Map:IsAboveGroundAtPoint(x, y, z) then + return true + end + + if TheWorld.Map:IsVisualGroundAtPoint(x, y, z) then + for _, player in ipairs(TheSim:FindEntities(x, 0, z, 1, PLAYER_TAGS)) do + if player ~= inst and player.entity:IsVisible() and player.components.placer == nil and player.entity:GetParent() == nil then + local px, _, pz = player.Transform:GetWorldPosition() + if math.floor(x) == math.floor(px) and math.floor(z) == math.floor(pz) then + return false + end + end + end + end + + return true +end + +-- 部署墙物品时在网格中心生成墙体。 +local function OnDeployWall(inst, pt) + local wall = SpawnPrefab(BUILD) + + if wall ~= nil then + local x = math.floor(pt.x) + .5 + local z = math.floor(pt.z) + .5 + wall.Physics:SetCollides(false) + wall.Physics:Teleport(x, 0, z) + wall.Physics:SetCollides(true) + inst.components.stackable:Get():Remove() + + wall.SoundEmitter:PlaySound("dontstarve/common/place_structure_wood") + end +end + +-- 受击时播放当前血量阶段的轻微晃动。 +local function OnHit(inst) + inst.SoundEmitter:PlaySound("dontstarve/common/destroy_wood") + + if not inst.components.health:IsDead() then + local anim = ResolveAnim(inst.components.health:GetPercent()) + inst.AnimState:PlayAnimation(anim.."_hit") + inst.AnimState:PushAnimation(anim, false) + end +end + +-- 血量变化时更新墙体阶段。 +local function OnHealthChange(inst, old_percent, new_percent) + local anim = ResolveAnim(new_percent) + + if new_percent > 0 then + if old_percent <= 0 then + MakeObstacle(inst) + end + + inst.AnimState:PlayAnimation(anim.."_hit") + inst.AnimState:PushAnimation(anim, false) + else + if old_percent > 0 then + ClearObstacle(inst) + end + + inst.AnimState:PlayAnimation(anim) + end +end + +-- 锤掉时按剩余血量返还少量材料。 +local function OnHammered(inst) + local loot_count = math.max(1, math.floor(MAX_LOOTS * inst.components.health:GetPercent())) + + for _ = 1, loot_count do + inst.components.lootdropper:SpawnLootPrefab("log") + end + + local fx = SpawnPrefab("collapse_small") + fx.Transform:SetPosition(inst.Transform:GetWorldPosition()) + fx:SetMaterial("wood") + + inst:Remove() +end + +-- 阻止墙主动保持攻击目标。 +local function KeepTarget() + return false +end + +-- 读档时修正死亡墙的碰撞状态。 +local function OnLoad(inst) + if inst.components.health:IsDead() then + ClearObstacle(inst) + end +end + +-- 创建可部署的蜡笔墙物品。 +local function ItemFn() + local inst = CreateEntity() + + inst.entity:AddTransform() + inst.entity:AddAnimState() + inst.entity:AddNetwork() + + MakeInventoryPhysics(inst) + + inst:AddTag("wallbuilder") + + inst.AnimState:SetBank(BUILD) + inst.AnimState:SetBuild(BUILD) + inst.AnimState:PlayAnimation("item") + + MakeInventoryFloatable(inst) + + inst.entity:SetPristine() + + if not TheWorld.ismastersim then + return inst + end + + inst:AddComponent("stackable") + inst.components.stackable.maxsize = TUNING.STACK_SIZE_MEDITEM + + inst:AddComponent("inspectable") + inst:AddComponent("inventoryitem") + inst.components.inventoryitem.atlasname = "images/inventoryimages/aip_crayon_wall_item.xml" + + inst:AddComponent("repairer") + inst.components.repairer.repairmaterial = REPAIR_MATERIAL + inst.components.repairer.healthrepairvalue = MAX_HEALTH / 6 + + -- 掉落物可以被点燃,但不会作为火源向周围传播。 + MakeSmallBurnable(inst, TUNING.MED_BURNTIME) + + inst:AddComponent("fuel") + inst.components.fuel.fuelvalue = TUNING.SMALL_FUEL + + inst:AddComponent("deployable") + inst.components.deployable.ondeploy = OnDeployWall + inst.components.deployable:SetDeployMode(DEPLOYMODE.WALL) + + MakeHauntableLaunch(inst) + + return inst +end + +-- 创建部署后的蜡笔墙实体。 +local function WallFn() + local inst = CreateEntity() + + inst.entity:AddTransform() + inst.entity:AddAnimState() + inst.entity:AddSoundEmitter() + inst.entity:AddNetwork() + + -- 参考原版墙体,根据视角切换到对应方向动画。 + inst.Transform:SetEightFaced() + + inst:SetDeploySmartRadius(0.5) + + MakeObstaclePhysics(inst, .5) + inst.Physics:SetDontRemoveOnSleep(true) + + inst:AddTag("wall") + inst:AddTag("wood") + inst:AddTag("noauradamage") + inst:AddTag("electricdamageimmune") + + inst.AnimState:SetBank(BUILD) + inst.AnimState:SetBuild(BUILD) + inst.AnimState:PlayAnimation("half") + + inst._pfpos = nil + inst._ispathfinding = net_bool(inst.GUID, "_ispathfinding", "onispathfindingdirty") + MakeObstacle(inst) + inst:DoTaskInTime(0, InitializePathFinding) + inst.OnRemoveEntity = OnRemove + + inst.entity:SetPristine() + + if not TheWorld.ismastersim then + return inst + end + + inst.scrapbook_specialinfo = "WALLS" + inst.scrapbook_anim = "half" + + inst:AddComponent("inspectable") + inst:AddComponent("lootdropper") + + inst:AddComponent("repairable") + inst.components.repairable.repairmaterial = REPAIR_MATERIAL + inst.components.repairable.onrepaired = OnRepaired + inst.components.repairable.testvalidrepairfn = CanRepair + + inst:AddComponent("combat") + inst.components.combat:SetKeepTargetFunction(KeepTarget) + inst.components.combat.onhitfn = OnHit + + inst:AddComponent("health") + inst.components.health:SetMaxHealth(MAX_HEALTH) + inst.components.health:SetCurrentHealth(MAX_HEALTH * .5) + inst.components.health.ondelta = OnHealthChange + inst.components.health.nofadeout = true + inst.components.health.canheal = false + + -- 蜡笔墙可以被点燃,但不会作为火源向周围传播。 + MakeMediumBurnable(inst) + inst.components.burnable.flammability = .5 + inst.components.burnable.nocharring = true + + inst:AddComponent("workable") + inst.components.workable:SetWorkAction(ACTIONS.HAMMER) + inst.components.workable:SetWorkLeft(3) + inst.components.workable:SetOnFinishCallback(OnHammered) + inst.components.workable:SetOnWorkCallback(OnHit) + + MakeHauntableWork(inst) + + inst.OnLoad = OnLoad + + return inst +end + +return Prefab(BUILD, WallFn, assets, prefabs), + Prefab(ITEM, ItemFn, assets, { BUILD, ITEM.."_placer" }), + MakePlacer(ITEM.."_placer", BUILD, BUILD, "half", false, false, true, nil, nil, "eight") diff --git a/scripts/recpiesHooker.lua b/scripts/recpiesHooker.lua index a23df96f..b32cd6d3 100644 --- a/scripts/recpiesHooker.lua +++ b/scripts/recpiesHooker.lua @@ -151,6 +151,11 @@ rec("aip_glass_chest", TECH.MAGIC_TWO, { CRAFTING_FILTERS.MAGIC }, rec("aip_igloo", TECH.SCIENCE_TWO, { CRAFTING_FILTERS.STRUCTURES }, {Ingredient("ice", 21), Ingredient("carrot", 1), Ingredient("twigs", 2)}, "aip_igloo_placer") + +-- 蜡笔墙 +rec("aip_crayon_wall_item", TECH.SCIENCE_TWO, { CRAFTING_FILTERS.STRUCTURES }, + { Ingredient("petals", 4), Ingredient("charcoal", 2), Ingredient("glommerfuel", 1) }, + { numtogive = 8 }) -- 诙谐面具 recDress("aip_joker_face", TECH.SCIENCE_TWO, { CRAFTING_FILTERS.CLOTHING },