Compare commits

..

9 Commits

Author SHA1 Message Date
ae8d901b9f add printLatestCommits.sh 2026-05-31 03:30:21 +02:00
a08f8db72f add veryaged to skipVariants 2026-05-31 03:29:33 +02:00
2106e78ec8 add 1.22 compat - use pin as tool in recipes 2026-05-31 03:17:55 +02:00
eac0c23179 add readme 2026-05-31 02:00:34 +02:00
fc8f2eb160 fix warning and error about crafting aged wood variant 2026-05-31 01:51:59 +02:00
a4a3d72275 update building for new env vars 2026-05-31 01:45:14 +02:00
f7f32a07e9 make pin board woodtyped 2026-03-19 02:19:51 +01:00
44ea336e61 fix sounds 2026-03-19 02:08:38 +01:00
e48580f10a blockinfo 2026-03-19 02:06:07 +01:00
14 changed files with 174 additions and 77 deletions

View File

@@ -1,5 +1,5 @@
{ {
"total": 18477, "total": 22680,
"sessions": [ "sessions": [
{ {
"begin": "2026-03-17T23:54:43+01:00", "begin": "2026-03-17T23:54:43+01:00",
@@ -25,6 +25,36 @@
"begin": "2026-03-18T23:14:52+01:00", "begin": "2026-03-18T23:14:52+01:00",
"end": "2026-03-19T01:17:26+01:00", "end": "2026-03-19T01:17:26+01:00",
"duration": 7354 "duration": 7354
},
{
"begin": "2026-05-31T01:21:35+02:00",
"end": "2026-05-31T01:44:30+02:00",
"duration": 1374
},
{
"begin": "2026-05-31T01:44:30+02:00",
"end": "2026-05-31T01:51:55+02:00",
"duration": 445
},
{
"begin": "2026-05-31T01:52:40+02:00",
"end": "2026-05-31T02:00:30+02:00",
"duration": 470
},
{
"begin": "2026-05-31T02:24:20+02:00",
"end": "2026-05-31T02:49:01+02:00",
"duration": 1481
},
{
"begin": "2026-05-31T03:09:33+02:00",
"end": "2026-05-31T03:10:03+02:00",
"duration": 29
},
{
"begin": "2026-05-31T03:10:07+02:00",
"end": "2026-05-31T03:16:52+02:00",
"duration": 404
} }
] ]
} }

4
.vscode/tasks.json vendored
View File

@@ -4,9 +4,9 @@
{ {
"label": "check env", "label": "check env",
"type": "shell", "type": "shell",
"command": "[ -n \"$VINTAGE_STORY\" ] || { echo 'ERROR: VINTAGE_STORY is not set'; exit 1; }; [ -n \"$VINTAGE_STORY_DATA\" ] || { echo 'ERROR: VINTAGE_STORY_DATA is not set'; exit 1; }", "command": "if [ \"${input:buildConfig}\" = \"Version22\" ]; then [ -n \"$VINTAGE_STORY_22\" ] || { echo 'ERROR: VINTAGE_STORY_22 is not set'; exit 1; }; [ -n \"$VINTAGE_STORY_DATA_22\" ] || { echo 'ERROR: VINTAGE_STORY_DATA_22 is not set'; exit 1; }; else [ -n \"$VINTAGE_STORY_21\" ] || { echo 'ERROR: VINTAGE_STORY_21 is not set'; exit 1; }; [ -n \"$VINTAGE_STORY_DATA_21\" ] || { echo 'ERROR: VINTAGE_STORY_DATA_21 is not set'; exit 1; }; fi",
"windows": { "windows": {
"command": "if (-not $env:VINTAGE_STORY) { Write-Host 'ERROR: VINTAGE_STORY is not set'; exit 1 }; if (-not $env:VINTAGE_STORY_DATA) { Write-Host 'ERROR: VINTAGE_STORY_DATA is not set'; exit 1 }" "command": "if ('${input:buildConfig}' -eq 'Version22') { if (-not $env:VINTAGE_STORY_22) { Write-Host 'ERROR: VINTAGE_STORY_22 is not set'; exit 1 }; if (-not $env:VINTAGE_STORY_DATA_22) { Write-Host 'ERROR: VINTAGE_STORY_DATA_22 is not set'; exit 1 } } else { if (-not $env:VINTAGE_STORY_21) { Write-Host 'ERROR: VINTAGE_STORY_21 is not set'; exit 1 }; if (-not $env:VINTAGE_STORY_DATA_21) { Write-Host 'ERROR: VINTAGE_STORY_DATA_21 is not set'; exit 1 } }"
}, },
"problemMatcher": [] "problemMatcher": []
}, },

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Text;
using Vintagestory.API.Client; using Vintagestory.API.Client;
using Vintagestory.API.Common; using Vintagestory.API.Common;
using Vintagestory.API.Datastructures; using Vintagestory.API.Datastructures;
@@ -110,6 +111,46 @@ public class BlockEntityButterflyPinBoard : BlockEntityDisplay
} }
} }
public override void GetBlockInfo(IPlayer forPlayer, StringBuilder dsc)
{
int highlightedSlot = -1;
BlockSelection? selection = forPlayer?.CurrentBlockSelection;
if (selection?.Position != null && selection.Position.Equals(Pos))
highlightedSlot = GetSlotIndex(selection.HitPosition);
AppendContentsBlockInfo(dsc, highlightedSlot);
}
public bool AppendContentsBlockInfo(StringBuilder builder, int highlightedSlot)
{
int initialLength = builder.Length;
for (int slotIndex = 0; slotIndex < ActiveSlotCount; slotIndex++)
{
ItemStack? stack = inventory[slotIndex].Itemstack;
if (stack == null)
continue;
if (builder.Length > initialLength)
builder.AppendLine();
else if (initialLength > 0)
builder.AppendLine();
string itemName = stack.GetName();
if (slotIndex == highlightedSlot)
{
builder.Append("<font color=\"#00ffff\">");
builder.Append(itemName);
builder.Append("</font>");
continue;
}
builder.Append(itemName);
}
return builder.Length > initialLength;
}
public int GetSlotIndex(Vec3d hitPosition) public int GetSlotIndex(Vec3d hitPosition)
{ {
Vec3d local = ToBaseOrientation(hitPosition); Vec3d local = ToBaseOrientation(hitPosition);

View File

@@ -1,5 +1,4 @@
using ButterflyPins.BlockEntities; using ButterflyPins.BlockEntities;
using System;
using Vintagestory.API.Common; using Vintagestory.API.Common;
using Vintagestory.API.MathTools; using Vintagestory.API.MathTools;
@@ -7,8 +6,6 @@ namespace ButterflyPins.Blocks;
public class BlockButterflyPinBoard : Block public class BlockButterflyPinBoard : Block
{ {
private const string ButterflyPinPrefix = "clothes-butterflypin-";
public override bool OnBlockInteractStart(IWorldAccessor world, IPlayer byPlayer, BlockSelection blockSel) public override bool OnBlockInteractStart(IWorldAccessor world, IPlayer byPlayer, BlockSelection blockSel)
{ {
if (blockSel == null) if (blockSel == null)
@@ -38,32 +35,4 @@ public class BlockButterflyPinBoard : Block
base.OnBlockBroken(world, pos, byPlayer, dropQuantityMultiplier); base.OnBlockBroken(world, pos, byPlayer, dropQuantityMultiplier);
} }
public override bool ConsumeCraftingIngredients(ItemSlot[] slots, ItemSlot outputSlot, GridRecipe matchingRecipe)
{
bool preservedPin = false;
foreach (ItemSlot slot in slots)
{
if (slot.Empty)
continue;
if (!preservedPin && IsButterflyPin(slot.Itemstack))
{
preservedPin = true;
continue;
}
slot.TakeOut(1);
slot.MarkDirty();
}
return true;
}
private static bool IsButterflyPin(ItemStack stack)
{
AssetLocation? code = stack.Collectible?.Code;
return code != null && code.Path.StartsWith(ButterflyPinPrefix, StringComparison.Ordinal);
}
} }

View File

@@ -6,6 +6,8 @@
<OutputPath>bin\$(Configuration)\Mods\mod</OutputPath> <OutputPath>bin\$(Configuration)\Mods\mod</OutputPath>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<Configurations>Debug;Release;Version22</Configurations> <Configurations>Debug;Release;Version22</Configurations>
<VINTAGE_STORY>$(VINTAGE_STORY_21)</VINTAGE_STORY>
<VINTAGE_STORY_DATA>$(VINTAGE_STORY_DATA_21)</VINTAGE_STORY_DATA>
<VS_CONFIGLIB Condition="'$(VS_CONFIGLIB)' == ''">$([System.Environment]::GetEnvironmentVariable('VS_CONFIGLIB'))</VS_CONFIGLIB> <VS_CONFIGLIB Condition="'$(VS_CONFIGLIB)' == ''">$([System.Environment]::GetEnvironmentVariable('VS_CONFIGLIB'))</VS_CONFIGLIB>
<ConfigLibAvailable Condition="'$(VS_CONFIGLIB)' != '' and Exists('$(VS_CONFIGLIB)')">true</ConfigLibAvailable> <ConfigLibAvailable Condition="'$(VS_CONFIGLIB)' != '' and Exists('$(VS_CONFIGLIB)')">true</ConfigLibAvailable>
<DefineConstants Condition="'$(ConfigLibAvailable)' == 'true'">$(DefineConstants);CONFIGLIB</DefineConstants> <DefineConstants Condition="'$(ConfigLibAvailable)' == 'true'">$(DefineConstants);CONFIGLIB</DefineConstants>

View File

@@ -7,6 +7,7 @@
{ "name": "Lockable" } { "name": "Lockable" }
], ],
"variantgroups": [ "variantgroups": [
{ "code": "wood", "loadFromProperties": "block/wood" },
{ "code": "side", "loadFromProperties": "abstract/horizontalorientation" } { "code": "side", "loadFromProperties": "abstract/horizontalorientation" }
], ],
"attributes": { "attributes": {
@@ -21,14 +22,17 @@
"reinforcable": true "reinforcable": true
}, },
"creativeinventory": { "creativeinventory": {
"general": ["pinboard2x2-south"], "general": ["pinboard2x2-oak-north"],
"decorative": ["pinboard2x2-south"] "decorative": ["pinboard2x2-oak-north"]
},
"textures": {
"wood": { "base": "game:block/wood/debarked/{wood}" }
}, },
"shapebytype": { "shapebytype": {
"*-north": { "base": "dusiulkaspinboard:block/pinboard-2x2", "rotateY": 180 }, "*-*-north": { "base": "dusiulkaspinboard:block/pinboard-2x2", "rotateY": 180 },
"*-east": { "base": "dusiulkaspinboard:block/pinboard-2x2", "rotateY": 90 }, "*-*-east": { "base": "dusiulkaspinboard:block/pinboard-2x2", "rotateY": 90 },
"*-south": { "base": "dusiulkaspinboard:block/pinboard-2x2", "rotateY": 0 }, "*-*-south": { "base": "dusiulkaspinboard:block/pinboard-2x2", "rotateY": 0 },
"*-west": { "base": "dusiulkaspinboard:block/pinboard-2x2", "rotateY": 270 } "*-*-west": { "base": "dusiulkaspinboard:block/pinboard-2x2", "rotateY": 270 }
}, },
"blockmaterial": "Wood", "blockmaterial": "Wood",
"drawtype": "json", "drawtype": "json",
@@ -47,10 +51,10 @@
"y2": 0.9375, "y2": 0.9375,
"z2": 1.0, "z2": 1.0,
"rotateYByType": { "rotateYByType": {
"*-north": 180, "*-*-north": 180,
"*-east": 90, "*-*-east": 90,
"*-south": 0, "*-*-south": 0,
"*-west": 270 "*-*-west": 270
} }
}, },
"selectionbox": { "selectionbox": {
@@ -61,10 +65,10 @@
"y2": 0.9375, "y2": 0.9375,
"z2": 1.0, "z2": 1.0,
"rotateYByType": { "rotateYByType": {
"*-north": 180, "*-*-north": 180,
"*-east": 90, "*-*-east": 90,
"*-south": 0, "*-*-south": 0,
"*-west": 270 "*-*-west": 270
} }
}, },
"combustibleProps": { "combustibleProps": {
@@ -72,9 +76,9 @@
"burnDuration": 20 "burnDuration": 20
}, },
"sounds": { "sounds": {
"place": "block/planks", "place": "game:block/planks",
"break": "block/planks", "break": "game:block/planks",
"hit": "block/planks" "hit": "game:block/planks"
}, },
"materialDensity": 400, "materialDensity": 400,
"guiTransform": { "guiTransform": {

View File

@@ -7,6 +7,7 @@
{ "name": "Lockable" } { "name": "Lockable" }
], ],
"variantgroups": [ "variantgroups": [
{ "code": "wood", "loadFromProperties": "block/wood" },
{ "code": "side", "loadFromProperties": "abstract/horizontalorientation" } { "code": "side", "loadFromProperties": "abstract/horizontalorientation" }
], ],
"attributes": { "attributes": {
@@ -21,14 +22,17 @@
"reinforcable": true "reinforcable": true
}, },
"creativeinventory": { "creativeinventory": {
"general": ["pinboard3x3-south"], "general": ["pinboard3x3-oak-north"],
"decorative": ["pinboard3x3-south"] "decorative": ["pinboard3x3-oak-north"]
},
"textures": {
"wood": { "base": "game:block/wood/debarked/{wood}" }
}, },
"shapebytype": { "shapebytype": {
"*-north": { "base": "dusiulkaspinboard:block/pinboard-3x3", "rotateY": 180 }, "*-*-north": { "base": "dusiulkaspinboard:block/pinboard-3x3", "rotateY": 180 },
"*-east": { "base": "dusiulkaspinboard:block/pinboard-3x3", "rotateY": 90 }, "*-*-east": { "base": "dusiulkaspinboard:block/pinboard-3x3", "rotateY": 90 },
"*-south": { "base": "dusiulkaspinboard:block/pinboard-3x3", "rotateY": 0 }, "*-*-south": { "base": "dusiulkaspinboard:block/pinboard-3x3", "rotateY": 0 },
"*-west": { "base": "dusiulkaspinboard:block/pinboard-3x3", "rotateY": 270 } "*-*-west": { "base": "dusiulkaspinboard:block/pinboard-3x3", "rotateY": 270 }
}, },
"blockmaterial": "Wood", "blockmaterial": "Wood",
"drawtype": "json", "drawtype": "json",
@@ -47,10 +51,10 @@
"y2": 0.9375, "y2": 0.9375,
"z2": 1.0, "z2": 1.0,
"rotateYByType": { "rotateYByType": {
"*-north": 180, "*-*-north": 180,
"*-east": 90, "*-*-east": 90,
"*-south": 0, "*-*-south": 0,
"*-west": 270 "*-*-west": 270
} }
}, },
"selectionbox": { "selectionbox": {
@@ -61,10 +65,10 @@
"y2": 0.9375, "y2": 0.9375,
"z2": 1.0, "z2": 1.0,
"rotateYByType": { "rotateYByType": {
"*-north": 180, "*-*-north": 180,
"*-east": 90, "*-*-east": 90,
"*-south": 0, "*-*-south": 0,
"*-west": 270 "*-*-west": 270
} }
}, },
"combustibleProps": { "combustibleProps": {
@@ -72,9 +76,9 @@
"burnDuration": 25 "burnDuration": 25
}, },
"sounds": { "sounds": {
"place": "block/planks", "place": "game:block/planks",
"break": "block/planks", "break": "game:block/planks",
"hit": "block/planks" "hit": "game:block/planks"
}, },
"materialDensity": 400, "materialDensity": 400,
"guiTransform": { "guiTransform": {

View File

@@ -1,4 +1,4 @@
{ {
"block-pinboard2x2-*": "Butterfly Pin Board (2x2)", "block-pinboard2x2-*-*": "Butterfly Pin Board (2x2)",
"block-pinboard3x3-*": "Butterfly Pin Board (3x3)" "block-pinboard3x3-*-*": "Butterfly Pin Board (3x3)"
} }

View File

@@ -1,13 +1,15 @@
{ {
"ingredientPattern": "_S_\tSBS\t_S_", "ingredientPattern": "_S_\tSBS\t_S_",
"ingredients": { "ingredients": {
"S": { "type": "item", "code": "game:stick" }, "S": { "type": "item", "code": "game:plank-*", "name": "wood", "skipVariants": ["aged", "veryaged"] },
"B": { "B": {
"type": "item", "type": "item",
"code": "game:*-butterflypin-*" "code": "game:clothes-butterflypin-*",
"isTool": true,
"toolDurabilityCost": 0
} }
}, },
"width": 3, "width": 3,
"height": 3, "height": 3,
"output": { "type": "block", "code": "pinboard2x2-north" } "output": { "type": "block", "code": "pinboard2x2-{wood}-north" }
} }

View File

@@ -1,13 +1,15 @@
{ {
"ingredientPattern": "S_S\t_B_\tS_S", "ingredientPattern": "S_S\t_B_\tS_S",
"ingredients": { "ingredients": {
"S": { "type": "item", "code": "game:stick" }, "S": { "type": "item", "code": "game:plank-*", "name": "wood", "skipVariants": ["aged", "veryaged"] },
"B": { "B": {
"type": "item", "type": "item",
"code": "game:*-butterflypin-*" "code": "game:clothes-butterflypin-*",
"isTool": true,
"toolDurabilityCost": 0
} }
}, },
"width": 3, "width": 3,
"height": 3, "height": 3,
"output": { "type": "block", "code": "pinboard3x3-north" } "output": { "type": "block", "code": "pinboard3x3-{wood}-north" }
} }

View File

@@ -7,7 +7,7 @@
"OrekiWoof" "OrekiWoof"
], ],
"description": "ButterflyPins mod.", "description": "ButterflyPins mod.",
"version": "1.0.0", "version": "1.0.1",
"dependencies": { "dependencies": {
"game": "1.21.0" "game": "1.21.0"
}, },

32
README.md Normal file
View File

@@ -0,0 +1,32 @@
# Butterfly Pins
## Requirements for the solution
- .NET 8 SDK for 1.21
- .NET 10 SDK when building `Version22` for 1.22
- Vintage Story install - 1.21 or 1.22, with environment variables describing the paths
- env vars described below
### Required env vars
- `VINTAGE_STORY_21`: path to the 1.21 game install (on Windows default is `%appdata%/VintageStory`)
- `VINTAGE_STORY_DATA_21`: path to the 1.21 game data directory (on Windows default is `%appdata%/VintageStoryData`)
- `VINTAGE_STORY_22`: path to the 1.22 game install (`Version22` configuration)
- `VINTAGE_STORY_DATA_22`: path to the 1.22 game data directory (`Version22` configuration)
- `VS_CONFIGLIB`: path to `configlib.dll`. If not set, will compile with a warning and without configlib support
## Running
If you know what you're doing, `dotnet build` etc obviously will work. This was created with the official mod template.
### VS Code with `C# Dev Kit` extension
- select appropriate solution configuration: `F1` (run command) -> `.NET: Select a configuration`
- for 1.21 select `Debug` or `Release`, for 1.22 select `Version22`
- run action `Start debugging` or `Start without debugging` (might be `f5` and `ctrl+f5` by default)
### Visual Studio
- use configuration picker at the top - `Debug` or `Release` for 1.21, `Version22` for 1.22
- set ButterflyPins project as the startup project
- run

View File

@@ -4,11 +4,16 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory> <RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
<Configurations>Debug;Release;Version22</Configurations> <Configurations>Debug;Release;Version22</Configurations>
<VINTAGE_STORY>$(VINTAGE_STORY_21)</VINTAGE_STORY>
<VS_CONFIGLIB Condition="'$(VS_CONFIGLIB)' == ''">$([System.Environment]::GetEnvironmentVariable('VS_CONFIGLIB'))</VS_CONFIGLIB> <VS_CONFIGLIB Condition="'$(VS_CONFIGLIB)' == ''">$([System.Environment]::GetEnvironmentVariable('VS_CONFIGLIB'))</VS_CONFIGLIB>
<ConfigLibAvailable Condition="'$(VS_CONFIGLIB)' != '' and Exists('$(VS_CONFIGLIB)')">true</ConfigLibAvailable> <ConfigLibAvailable Condition="'$(VS_CONFIGLIB)' != '' and Exists('$(VS_CONFIGLIB)')">true</ConfigLibAvailable>
<DefineConstants Condition="'$(ConfigLibAvailable)' == 'true'">$(DefineConstants);CONFIGLIB</DefineConstants> <DefineConstants Condition="'$(ConfigLibAvailable)' == 'true'">$(DefineConstants);CONFIGLIB</DefineConstants>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Version22'">
<VINTAGE_STORY>$(VINTAGE_STORY_22)</VINTAGE_STORY>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Cake.Frosting" Version="5.0.0" /> <PackageReference Include="Cake.Frosting" Version="5.0.0" />
<PackageReference Include="Cake.Json" Version="7.0.1" /> <PackageReference Include="Cake.Json" Version="7.0.1" />

6
printLatestCommits.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
count="${1:-10}"
git -C "$script_dir" log -n "$count" --pretty=format:%s | awk '{ lines[NR] = $0 } END { for (i = NR; i >= 1; i--) print lines[i] }'