快乐守护者 Posted January 8 Report Posted January 8 (edited) English: Could you please tell me how to use the mod to generate a "Hefty Red meat-Carrot stew" at the coordinate position "512081, 112, 512051" with the "/zuofan" command? I have successfully generated items at the coordinate position "512081, 112, 512051" using the "/zuofan" command through "wiki.vintagestory.at and ChatGPT", but I can only generate blocks or empty "clay pots", and I can't generate a "Dark brown ceramic pot of meat stew" like a "clay pot with Hefty Red meat-Carrot stew". Here is my code. ————————————————————————————————————————————————————————————————————————————————————————————————— 中文: 请问,如何通过模组实现用“/zuofan”指令在“512081, 112, 512051”这个坐标位置生成一份"豪华红肉-胡萝卜炖"。 我通过“wiki.vintagestory.at和ChatGPT”成功实现了用“/zuofan”指令在“512081, 112, 512051”这个坐标位置生成物品,但是只能生成方块或者空的"陶锅",无法生成"装着炖肉的深棕陶锅"这种有食物的陶锅比如装着"豪华红肉-胡萝卜炖"的陶锅。下面是我的代码。 ————————————————————————————————————————————————————————————————————————————————————————————————— using System; using Vintagestory.API.Common; using Vintagestory.API.MathTools; using Vintagestory.API.Server; using Vintagestory.GameContent; public class ZengruiModSystem : ModSystem { ICoreServerAPI sapi; public override void StartServerSide(ICoreServerAPI api) { sapi = api; // 指令 1:获取玩家脚下坐标 api.ChatCommands .Create("huodezuobiao") .WithDescription("显示玩家脚下方块的绝对坐标") .RequiresPrivilege(Privilege.chat) .HandleWith(OnGetPosition); // 指令 2:在固定坐标放置陶锅,并填充食物 api.ChatCommands .Create("zuofan") .WithDescription("在固定坐标放置红肉炖胡萝卜或陶锅") .RequiresPrivilege(Privilege.chat) .HandleWith(OnPlaceFixedBlock); } // ----------------------------- // /huodezuobiao 的回调 private TextCommandResult OnGetPosition(TextCommandCallingArgs args) { var player = args.Caller.Player; if (player == null) return TextCommandResult.Error("未找到玩家。"); // 直接获取玩家所在块坐标 BlockPos pos = player.Entity.Pos.AsBlockPos; return TextCommandResult.Success($"你脚下的绝对坐标为: X={pos.X}, Y={pos.Y}, Z={pos.Z}"); } // ----------------------------- // /zengruizuorouduncai 的回调 private TextCommandResult OnPlaceFixedBlock(TextCommandCallingArgs args) { // 固定坐标 BlockPos pos = new BlockPos(512081, 112, 512051); // 强制加载区块 sapi.World.BlockAccessor.GetChunkAtBlockPos(pos); // 使用陶锅的 ID (1093) 来获取陶锅方块 Block crockBlock = sapi.World.GetBlock(1093); // 请确保陶锅的ID正确 if (crockBlock == null) { return TextCommandResult.Error("未找到陶锅方块,请检查 block id"); } // 放置陶锅(只放在目标方块上) sapi.World.BlockAccessor.SetBlock(crockBlock.BlockId, pos); // 获取该位置的 BlockEntity var be = sapi.World.BlockAccessor.GetBlockEntity(pos) as BlockEntityCrock; if (be != null) { // 访问陶锅的 Inventory var inv = be.Inventory; if (inv != null) { // 获取 "生红肉" 和 "胡萝卜" 的物品实例 Item meatItem = sapi.World.GetItem(new AssetLocation("game", "redmeat-raw")); Item carrotItem = sapi.World.GetItem(new AssetLocation("game", "vegetable-carrot")); if (meatItem == null || carrotItem == null) { return TextCommandResult.Error("未能找到生红肉或胡萝卜的物品,请检查物品代码"); } // 创建物品堆栈,假设数量为 1(你可以根据需要调整) ItemStack meatStack = new ItemStack(meatItem, 1); ItemStack carrotStack = new ItemStack(carrotItem, 1); // 直接设置陶锅的槽位内容 inv[0].Itemstack = meatStack; // 放入红肉 inv[1].Itemstack = carrotStack; // 放入胡萝卜 // 确保 BlockEntity 更新 be.MarkDirty(true); } } return TextCommandResult.Success($"已在 {pos.X} {pos.Y} {pos.Z} 放置陶锅,并添加了红肉和胡萝卜"); } } ————————————————————————————————————————————————————————————————————————————————————————————————— English: ChatGPT said that this piece of code of mine can generate a pottery pot filled with food, but the actual result in the world is that it generates an empty pottery pot. ————————————————————————————————————————————————————————————————————————————————————————————————— 中文: ChatGPT说我这段代码可以生成装有食物的陶锅,但是实际效果是生成一个空的陶锅。 Edited January 9 by 快乐守护者
Solution Spoonail Posted January 12 Solution Report Posted January 12 I made a few changes and got it working: In `StartServerSide`: Spoiler api.ChatCommands .Create("zuofan") .WithDescription("Places a stew pot at player pos +3z") .RequiresPrivilege(Privilege.chat) .HandleWith(args => { // Places the pot at player pos +3z instead of the fixed position var pos = args.Caller.Pos.AddCopy(0, 0, 3); return OnPlaceFixedBlock(args, pos.AsBlockPos); }); The handler and a method for Ground Storage: Spoiler private TextCommandResult OnPlaceFixedBlock(TextCommandCallingArgs args, BlockPos pos) { var crockCode = "claypot-blue-cooked"; // Get the crock block using AssetLocation var crockBlock = sapi.World.GetBlock(new AssetLocation(crockCode)); //var crockBlock = sapi.World.GetBlock(1093); var crockStack = new ItemStack(crockBlock, 1); // To set meal into a container, you need to call `IBlockMealContainer.SetContents` IBlockMealContainer mealContainer = crockBlock as IBlockMealContainer; if (mealContainer == null) { return TextCommandResult.Error("Specified block code is not a meal container"); } // Get item instances for raw red meat and carrots Item meatItem = sapi.World.GetItem( new AssetLocation("game", "redmeat-raw") ); Item carrotItem = sapi.World.GetItem( new AssetLocation("game", "vegetable-carrot") ); if (meatItem == null || carrotItem == null) { return TextCommandResult.Error( "Failed to find raw red meat or carrot items. Please check the item codes." ); } // Create item stacks (quantity can be adjusted as needed) ItemStack meatStack = new ItemStack(meatItem, 1); ItemStack carrotStack = new ItemStack(carrotItem, 1); // Call `IBlockMealContainer.SetContents` mealContainer.SetContents( "meatystew", crockStack, [ meatStack.Clone(), meatStack.Clone(), carrotStack.Clone(), carrotStack.Clone() ] ); // In recent VS versions, meal containers are placed in Ground Storages instead of being placed directly // So we follow the way PlaceOneItemStackAsGS(crockStack, pos); return TextCommandResult.Success( $"Placed a crock at {pos.X} {pos.Y} {pos.Z} and added red meat and carrots." ); } /// <summary> /// Create a Ground Storage, place the gs at `pos` in the world and set `itemStack` into it /// </summary> /// <param name="itemStack"></param> /// <param name="pos"></param> public void PlaceOneItemStackAsGS(ItemStack itemStack, BlockPos pos) { Block blockgs = sapi.World.GetBlock(new AssetLocation("groundstorage")); sapi.World.BlockAccessor.SetBlock(blockgs.Id, pos); BlockEntityGroundStorage begs = sapi.World.BlockAccessor.GetBlockEntity(pos) as BlockEntityGroundStorage; var dummySlot = new DummySlot(itemStack); begs.DetermineStorageProperties(dummySlot); CollectibleBehaviorGroundStorable behavior = itemStack.Collectible.GetBehavior<CollectibleBehaviorGroundStorable>(); var groundStorageProperties = ((behavior != null) ? behavior.StorageProps : null); begs.ForceStorageProps(groundStorageProperties); begs.Inventory[0].Itemstack = itemStack.Clone(); begs.Inventory[0].MarkDirty(); } Changes and reasons: - Spawning the pot at fixed position was hard to debug, so changed the command to spawn it at player pos +3z - So after running the command, look south to find the spawned pot - Getting block/item by raw ID doesn't work well. Use it with `AssetLocation` always. - Actually `GetBlock(1093)` gave me one of variants of brick blocks - Meat stew requires at least 2 meat, so changed the ingredient to 2 red meat and 2 carrots - To set meal into a pot properly, you need to call `IBlockMealContainer.SetContents` - In recent VS versions, meal containers are placed in Ground Storages instead of being placed directly, so changed the code to follow the way - Create a pot stack, set meal content to it, create a Ground Storage, place the gs and set the pot stack into it 1
快乐守护者 Posted January 16 Author Report Posted January 16 On 1/12/2026 at 7:59 PM, Spoonail said: I made a few changes and got it working: In `StartServerSide`: Reveal hidden contents api.ChatCommands .Create("zuofan") .WithDescription("Places a stew pot at player pos +3z") .RequiresPrivilege(Privilege.chat) .HandleWith(args => { // Places the pot at player pos +3z instead of the fixed position var pos = args.Caller.Pos.AddCopy(0, 0, 3); return OnPlaceFixedBlock(args, pos.AsBlockPos); }); The handler and a method for Ground Storage: Reveal hidden contents private TextCommandResult OnPlaceFixedBlock(TextCommandCallingArgs args, BlockPos pos) { var crockCode = "claypot-blue-cooked"; // Get the crock block using AssetLocation var crockBlock = sapi.World.GetBlock(new AssetLocation(crockCode)); //var crockBlock = sapi.World.GetBlock(1093); var crockStack = new ItemStack(crockBlock, 1); // To set meal into a container, you need to call `IBlockMealContainer.SetContents` IBlockMealContainer mealContainer = crockBlock as IBlockMealContainer; if (mealContainer == null) { return TextCommandResult.Error("Specified block code is not a meal container"); } // Get item instances for raw red meat and carrots Item meatItem = sapi.World.GetItem( new AssetLocation("game", "redmeat-raw") ); Item carrotItem = sapi.World.GetItem( new AssetLocation("game", "vegetable-carrot") ); if (meatItem == null || carrotItem == null) { return TextCommandResult.Error( "Failed to find raw red meat or carrot items. Please check the item codes." ); } // Create item stacks (quantity can be adjusted as needed) ItemStack meatStack = new ItemStack(meatItem, 1); ItemStack carrotStack = new ItemStack(carrotItem, 1); // Call `IBlockMealContainer.SetContents` mealContainer.SetContents( "meatystew", crockStack, [ meatStack.Clone(), meatStack.Clone(), carrotStack.Clone(), carrotStack.Clone() ] ); // In recent VS versions, meal containers are placed in Ground Storages instead of being placed directly // So we follow the way PlaceOneItemStackAsGS(crockStack, pos); return TextCommandResult.Success( $"Placed a crock at {pos.X} {pos.Y} {pos.Z} and added red meat and carrots." ); } /// <summary> /// Create a Ground Storage, place the gs at `pos` in the world and set `itemStack` into it /// </summary> /// <param name="itemStack"></param> /// <param name="pos"></param> public void PlaceOneItemStackAsGS(ItemStack itemStack, BlockPos pos) { Block blockgs = sapi.World.GetBlock(new AssetLocation("groundstorage")); sapi.World.BlockAccessor.SetBlock(blockgs.Id, pos); BlockEntityGroundStorage begs = sapi.World.BlockAccessor.GetBlockEntity(pos) as BlockEntityGroundStorage; var dummySlot = new DummySlot(itemStack); begs.DetermineStorageProperties(dummySlot); CollectibleBehaviorGroundStorable behavior = itemStack.Collectible.GetBehavior<CollectibleBehaviorGroundStorable>(); var groundStorageProperties = ((behavior != null) ? behavior.StorageProps : null); begs.ForceStorageProps(groundStorageProperties); begs.Inventory[0].Itemstack = itemStack.Clone(); begs.Inventory[0].MarkDirty(); } Changes and reasons: - Spawning the pot at fixed position was hard to debug, so changed the command to spawn it at player pos +3z - So after running the command, look south to find the spawned pot - Getting block/item by raw ID doesn't work well. Use it with `AssetLocation` always. - Actually `GetBlock(1093)` gave me one of variants of brick blocks - Meat stew requires at least 2 meat, so changed the ingredient to 2 red meat and 2 carrots - To set meal into a pot properly, you need to call `IBlockMealContainer.SetContents` - In recent VS versions, meal containers are placed in Ground Storages instead of being placed directly, so changed the code to follow the way - Create a pot stack, set meal content to it, create a Ground Storage, place the gs and set the pot stack into it English: Thank you very much! These two methods have perfectly solved my problem. Now I can generate a pot of food at the specified coordinates "512081, 112, 512051" by using the "/zuofan" command. Here is my code. 原文: 非常感谢!这两个方法完美的解决了我的问题。我现在能通过“/zuofan”指令在指定的坐标“512081, 112, 512051”上生成一锅食物了。下面是我的代码。 ———————————————————————————————————————————————————————————————— using System; using Vintagestory.API.Common; using Vintagestory.API.MathTools; using Vintagestory.API.Server; using Vintagestory.GameContent; public class ZengruiModSystem : ModSystem { private ICoreServerAPI sapi; public override void StartServerSide(ICoreServerAPI api) { sapi = api; // 指令:获取玩家脚下坐标 api.ChatCommands .Create("huodezuobiao") .WithDescription("显示玩家脚下方块的绝对坐标") .RequiresPrivilege(Privilege.chat) .HandleWith(OnGetPosition); // 指令:在固定坐标放置一锅红肉炖胡萝卜 api.ChatCommands .Create("zuofan") .WithDescription("在固定坐标放置一锅红肉炖胡萝卜(陶锅)") .RequiresPrivilege(Privilege.chat) .HandleWith(OnPlaceCookedCrock); } // -------------------------------------------------- // /huodezuobiao private TextCommandResult OnGetPosition(TextCommandCallingArgs args) { var player = args.Caller.Player; if (player == null) { return TextCommandResult.Error("未找到玩家"); } BlockPos pos = player.Entity.Pos.AsBlockPos; return TextCommandResult.Success( $"你脚下的绝对坐标为: X={pos.X}, Y={pos.Y}, Z={pos.Z}" ); } // -------------------------------------------------- // /zuofan private TextCommandResult OnPlaceCookedCrock(TextCommandCallingArgs args) { // ★ 固定坐标(你指定的) BlockPos pos = new BlockPos(512081, 112, 512051); // 确保区块已加载 sapi.World.BlockAccessor.GetChunkAtBlockPos(pos); // -------------------------------------------------- // 1. 获取“陶锅物品对应的方块” // 注意:这是陶锅“物品”,不是直接放在世界里的方块 Block crockBlock = sapi.World.GetBlock(new AssetLocation("game", "claypot-blue-cooked")); if (crockBlock == null) { return TextCommandResult.Error("未找到陶锅方块(claypot-blue-cooked)"); } // 创建一个陶锅 ItemStack ItemStack crockStack = new ItemStack(crockBlock, 1); // -------------------------------------------------- // 2. 通过 IBlockMealContainer 给陶锅“生成一锅食物” IBlockMealContainer mealContainer = crockBlock as IBlockMealContainer; if (mealContainer == null) { return TextCommandResult.Error("该方块不是食物容器(IBlockMealContainer)"); } // 获取食材物品 Item meatItem = sapi.World.GetItem(new AssetLocation("game", "redmeat-raw")); Item carrotItem = sapi.World.GetItem(new AssetLocation("game", "vegetable-carrot")); if (meatItem == null || carrotItem == null) { return TextCommandResult.Error("未能找到红肉或胡萝卜物品"); } // 创建食材 ItemStack ItemStack meatStack = new ItemStack(meatItem, 1); ItemStack carrotStack = new ItemStack(carrotItem, 1); // ★ 核心:生成一锅“红肉炖胡萝卜” // "meatystew" 是食谱代码 mealContainer.SetContents( "meatystew", crockStack, new ItemStack[] { meatStack.Clone(), meatStack.Clone(), carrotStack.Clone(), carrotStack.Clone() } ); // -------------------------------------------------- // 3. 把“装好食物的陶锅”作为物品,放进地面存放(Ground Storage) PlaceOneItemStackAsGroundStorage(crockStack, pos); return TextCommandResult.Success( $"已在 {pos.X}, {pos.Y}, {pos.Z} 放置一锅红肉炖胡萝卜" ); } // -------------------------------------------------- // 在指定坐标创建 GroundStorage,并放入一个物品 private void PlaceOneItemStackAsGroundStorage(ItemStack itemStack, BlockPos pos) { // 获取地面存放方块 Block groundStorageBlock = sapi.World.GetBlock(new AssetLocation("game", "groundstorage")); sapi.World.BlockAccessor.SetBlock(groundStorageBlock.BlockId, pos); // 获取地面存放的 BlockEntity BlockEntityGroundStorage begs = sapi.World.BlockAccessor.GetBlockEntity(pos) as BlockEntityGroundStorage; if (begs == null) return; // 使用 DummySlot 让系统判断存放属性 ItemSlot dummySlot = new DummySlot(itemStack); begs.DetermineStorageProperties(dummySlot); // 强制应用 GroundStorage 的存放属性 CollectibleBehaviorGroundStorable behavior = itemStack.Collectible.GetBehavior<CollectibleBehaviorGroundStorable>(); if (behavior != null) { begs.ForceStorageProps(behavior.StorageProps); } // 放入物品 begs.Inventory[0].Itemstack = itemStack.Clone(); begs.Inventory[0].MarkDirty(); // 通知世界刷新 begs.MarkDirty(true); } } 1
Recommended Posts