Skip to content

Commit bc82851

Browse files
authored
Implement inventory system (Part I) (#51)
* hmmm * 添加 Inventory 的各种 SlotArea * 添加 LeftClick 逻辑 * 添加 RightClick 逻辑 * 重构,添加 crafting 流程 * hmmm * 实现 crafting 配方匹配 * 添加 data * 完成 Crafting Table * 更新 crafting * 分离 Block / Item 逻辑 * 修复关闭窗口奔溃的问题 * 添加 BlockEntity * 实现 Chest * hmmm * 添加 Inventory 的各种 SlotArea * 添加 LeftClick 逻辑 * 添加 RightClick 逻辑 * 重构,添加 crafting 流程 * hmmm * 实现 crafting 配方匹配 * 添加 data * 完成 Crafting Table * 更新 crafting * 分离 Block / Item 逻辑 * 修复关闭窗口奔溃的问题 * 添加 BlockEntity * 实现 Chest * 修复 Chest Window无法打开的 bug * rebase * Fix a mistake in ChunkColumnGrain * clean code style * 完成 Large Chest * 添加 Furnace 基架 * 添加 Furnace 逻辑 * 完成 Furnace * 修改 ITickable
1 parent 026f18f commit bc82851

File tree

112 files changed

+4841
-218
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+4841
-218
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ paket-files/
259259
# Python Tools for Visual Studio (PTVS)
260260
__pycache__/
261261
*.pyc
262-
*.txt
262+
src/**/*.txt
263263

264264
# vscode
265265
.vscode/

data/crafting.txt

Lines changed: 971 additions & 0 deletions
Large diffs are not rendered by default.

data/furnace.txt

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
#*****************#
2+
# Furnace Recipes #
3+
#*****************#
4+
#
5+
#
6+
#******************************************************#
7+
# Basic Notation Help
8+
#
9+
# **** Item Definition ****
10+
# An Item is defined by an Item Type, an amount (and damage)
11+
# The damage is optional, and if not specified it's assumed to be 0
12+
#
13+
# Cactus Green example:
14+
# 351 ^ 2 ( , 1 )
15+
# ItemType ^ Damage ( , Amount )
16+
# or simple use the item name (marked in items.ini):
17+
# CactusGreen ( , 1 )
18+
#
19+
#
20+
# **** Recipe and result ****
21+
#
22+
# Cobble @ 200 = Stone -> Produces 1 smooth stone from 1 cobblestone in 200 ticks (10 seconds)
23+
#
24+
# Write in full:
25+
# Cobble ^ 0 , 1 @ 200 = 1 ^ 1 , 1
26+
# ItemType ^ Damage , Amount @ ticks = ItemType ^ Damage , Amount
27+
#
28+
#
29+
# **** Fuel ****
30+
#
31+
# !17:1 = 300 -> 1 Wood burns for 300 ticks (15 s)
32+
#
33+
# ! Wood , 1 = 300
34+
# Fuel ItemType , Amount = ticks
35+
#
36+
#******************************************************#
37+
38+
39+
40+
41+
42+
#--------------------------
43+
# Smelting recipes
44+
45+
#Beef = Steak
46+
#BlackTerracotta = BlackGlazedTerracotta
47+
#BlueTerracotta = BlueGlazedTerracotta
48+
#BrownTerracotta = BrownGlazedTerracotta
49+
#Cactus = CactusGreen
50+
#ChainmailBoots = IronNugget
51+
#ChainmailChestplate = IronNugget
52+
#ChainmailHelmet = IronNugget
53+
#ChainmailLeggings = IronNugget
54+
#Chicken = CookedChicken
55+
#ChorusFruit = PoppedChorusFruit
56+
#Clay = Brick
57+
#ClayBlock = HardenedClay
58+
#CoalOre = Coal
59+
#Cobblestone = Stone
60+
#CrackedStonebrick = Stonebrick
61+
#CyanTerracotta = CyanGlazedTerracotta
62+
#DiamondOre = Diamond
63+
#EmeraldOre = Emerald
64+
#Fish = CookedFish
65+
#GoldOre = GoldIngot
66+
#GoldAxe = GoldNugget
67+
#GoldBoots = GoldNugget
68+
#GoldChestplate = GoldNugget
69+
#GoldHorseArmor = GoldNugget
70+
#GoldHelmet = GoldNugget
71+
#GoldHoe = GoldNugget
72+
#GoldPants = GoldNugget
73+
#GoldPickaxe = GoldNugget
74+
#GoldShovel = GoldNugget
75+
#GoldSword = GoldNugget
76+
#GrayTerracotta = GrayGlazedTerracotta
77+
#GreenTerracotta = GreenGlazedTerracotta
78+
#IronOre = IronIngot
79+
#IronAxe = IronNugget
80+
#IronBoots = IronNugget
81+
#IronChestplate = IronNugget
82+
#IronHorseArmor = IronNugget
83+
#IronHelmet = IronNugget
84+
#IronHoe = IronNugget
85+
#IronLeggings = IronNugget
86+
#IronPickaxe = IronNugget
87+
#IronShovel = IronNugget
88+
#IronSword = IronNugget
89+
#LapisOre = LapisLazuli
90+
#LightBlueTerracotta = LightBlueGlazedTerracotta
91+
#LightGrayTerracotta = LightGrayGlazedTerracotta
92+
#LimeTerracotta = LimeGlazedTerracotta
93+
Wood^-1 = Coal^Charcoal
94+
Wood2^-1 = Coal^Charcoal
95+
#MagentaTerracotta = MagentaGlazedTerracotta
96+
#Mutton = CookedMutton
97+
#NetherQuartzOre = NetherQuartz
98+
#Netherrack = NetherBrick
99+
#OrangeTerracotta = OrangeGlazedTerracotta
100+
#PinkTerracotta = PinkGlazedTerracotta
101+
#Porkchop = CookedPorkchop
102+
#Potato = BakedPotato
103+
#PurpleTerracotta = PurpleGlazedTerracotta
104+
#Rabbit = CookedRabbit
105+
#RedTerracotta = RedGlazedTerracotta
106+
#RedstoneOre = Redstone
107+
#Salmon = CookedSalmon
108+
#Sand = Glass
109+
#StoneBrick = CrackedStoneBricks
110+
#WetSponge = Sponge
111+
#WhiteTerracotta = WhiteGlazedTerracotta
112+
#YellowTerracotta = YellowGlazedTerracotta
113+
114+
115+
116+
117+
#--------------------------
118+
# Fuels
119+
120+
#! AcaciaBoat = 400 # -> 20 sec
121+
#! AcaciaDoor = 200 # -> 10 sec
122+
#! AcaciaFence = 300 # -> 15 sec
123+
#! AcaciaFenceGate = 300 # -> 15 sec
124+
#! AcaciaStairs = 300 # -> 15 sec
125+
#! Banner = 300 # -> 15 sec
126+
#! BirchBoat = 400 # -> 20 sec
127+
#! BirchDoor = 200 # -> 10 sec
128+
#! BirchFence = 300 # -> 15 sec
129+
#! BirchFenceGate = 300 # -> 15 sec
130+
#! BirchStairs = 300 # -> 15 sec
131+
#! BlazeRod = 2400 # -> 120 sec
132+
#! Boat = 400 # -> 20 sec
133+
#! Bookshelf = 300 # -> 15 sec
134+
#! Bow = 300 # -> 15 sec
135+
#! Bowl = 100 # -> 5 sec
136+
#! BrownMushroomBlock = 300 # -> 15 sec
137+
#! Carpet = 67 # -> 3.35 sec
138+
#! CharCoal = 1600 # -> 80 sec
139+
#! Chest = 300 # -> 15 sec
140+
! Coal^-1 = 1600 # -> 80 sec
141+
! BlockOfCoal = 16000 # -> 800 sec
142+
#! CraftingTable = 300 # -> 15 sec
143+
#! DarkOakBoat = 400 # -> 20 sec
144+
#! DarkOakDoor = 200 # -> 10 sec
145+
#! DarkOakFence = 300 # -> 15 sec
146+
#! DarkOakFenceGate = 300 # -> 15 sec
147+
#! DarkOakStairs = 300 # -> 15 sec
148+
#! DaylightSensor = 300 # -> 15 sec
149+
#! Fence = 300 # -> 15 sec
150+
#! FenceGate = 300 # -> 15 sec
151+
#! FishingRod = 300 # -> 15 sec
152+
#! Jukebox = 300 # -> 15 sec
153+
#! JungleBoat = 400 # -> 20 sec
154+
#! JungleDoor = 200 # -> 10 sec
155+
#! JungleFence = 300 # -> 15 sec
156+
#! JungleFenceGate = 300 # -> 15 sec
157+
#! JungleStairs = 300 # -> 15 sec
158+
#! Ladder = 300 # -> 15 sec
159+
#! Lavabucket = 20000 # -> 1000 sec
160+
! Wood^-1 = 300 # -> 15 sec
161+
! Wood2^-1 = 300 # -> 15 sec
162+
#! NoteBlock = 300 # -> 15 sec
163+
#! OakStairs = 300 # -> 15 sec
164+
#! Planks = 300 # -> 15 sec
165+
#! RedMushroomBlock = 300 # -> 15 sec
166+
#! Sapling = 100 # -> 5 sec
167+
#! Sign = 200 # -> 10 sec
168+
#! SpruceBoat = 400 # -> 20 sec
169+
#! SpruceDoor = 200 # -> 10 sec
170+
#! SpruceFence = 300 # -> 15 sec
171+
#! SpruceFenceGate = 300 # -> 15 sec
172+
#! SpruceStairs = 300 # -> 15 sec
173+
#! Stick = 100 # -> 5 sec
174+
#! Trapdoor = 300 # -> 15 sec
175+
#! TrappedChest = 300 # -> 15 sec
176+
#! WoodenAxe = 200 # -> 10 sec
177+
#! WoodenButton = 100 # -> 5 sec
178+
#! WoodenDoor = 200 # -> 10 sec
179+
#! WoodenHoe = 200 # -> 10 sec
180+
#! WoodenPressurePlate = 300 # -> 15 sec
181+
#! WoodenPickaxe = 200 # -> 10 sec
182+
#! WoodenSlab = 150 # -> 7.5 sec
183+
#! WoodenShovel = 200 # -> 10 sec
184+
#! WoodenSword = 200 # -> 10 sec
185+
#! Wool = 100 # -> 5 sec
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace MineCase.Algorithm
6+
{
7+
public class FindCraftingRecipeResult
8+
{
9+
public Slot Result { get; set; }
10+
11+
public Slot[,] AfterTake { get; set; }
12+
}
13+
14+
public class CraftingRecipeMatcher
15+
{
16+
private List<CraftingRecipe> _recipes;
17+
18+
public CraftingRecipeMatcher(List<CraftingRecipe> recipes)
19+
{
20+
_recipes = recipes;
21+
}
22+
23+
public FindCraftingRecipeResult FindRecipe(Slot[,] craftingGrid)
24+
{
25+
int gridLeft = 2, gridTop = 2;
26+
int gridRight = 0, gridBottom = 0;
27+
28+
for (int x = 0; x < craftingGrid.GetUpperBound(0) + 1; x++)
29+
{
30+
for (int y = 0; y < craftingGrid.GetUpperBound(1) + 1; y++)
31+
{
32+
if (!craftingGrid[x, y].IsEmpty)
33+
{
34+
gridRight = Math.Max(x, gridRight);
35+
gridBottom = Math.Max(y, gridBottom);
36+
gridLeft = Math.Min(x, gridLeft);
37+
gridTop = Math.Min(y, gridTop);
38+
}
39+
}
40+
}
41+
42+
var gridWidth = gridRight - gridLeft + 1;
43+
var gridHeight = gridBottom - gridTop + 1;
44+
45+
if (gridWidth <= 0 || gridHeight <= 0) return null;
46+
var newGrid = CropCraftingGrid(craftingGrid, gridLeft, gridTop, gridWidth, gridHeight);
47+
var result = FindRecipeCropped(newGrid);
48+
if (result != null)
49+
{
50+
result.AfterTake = RestoreCroppedCraftingGrid(result.AfterTake, gridLeft, gridTop, craftingGrid.GetUpperBound(0) + 1, craftingGrid.GetUpperBound(1) + 1);
51+
return result;
52+
}
53+
54+
return null;
55+
}
56+
57+
private FindCraftingRecipeResult FindRecipeCropped(Slot[,] craftingGrid)
58+
{
59+
var gridWidth = craftingGrid.GetUpperBound(0) + 1;
60+
var gridHeight = craftingGrid.GetUpperBound(1) + 1;
61+
62+
foreach (var recipe in _recipes)
63+
{
64+
var maxOfX = gridWidth - recipe.Width;
65+
var maxOfY = gridHeight - recipe.Height;
66+
67+
for (int x = 0; x <= maxOfX; x++)
68+
{
69+
for (int y = 0; y <= maxOfY; y++)
70+
{
71+
var result = MatchRecipe(craftingGrid, recipe, x, y);
72+
if (result != null) return result;
73+
}
74+
}
75+
}
76+
77+
return null;
78+
}
79+
80+
private FindCraftingRecipeResult MatchRecipe(Slot[,] craftingGrid, CraftingRecipe recipe, int xOffset, int yOffset)
81+
{
82+
var gridWidth = craftingGrid.GetUpperBound(0) + 1;
83+
var gridHeight = craftingGrid.GetUpperBound(1) + 1;
84+
85+
var hasMatched = new bool[gridWidth, gridHeight];
86+
var after = (Slot[,])craftingGrid.Clone();
87+
for (int i = 0; i < recipe.Inputs.Length; i++)
88+
{
89+
ref var recipeSlot = ref recipe.Inputs[i];
90+
91+
// Anywhere, 稍后处理
92+
if (recipeSlot.X < 0 || recipeSlot.Y < 0) continue;
93+
94+
var x = recipeSlot.X + xOffset;
95+
var y = recipeSlot.Y + yOffset;
96+
ref var gridSlot = ref craftingGrid[x, y];
97+
if (recipeSlot.X >= gridWidth || recipeSlot.Y >= gridHeight ||
98+
recipeSlot.Slot.BlockId != gridSlot.BlockId ||
99+
recipeSlot.Slot.ItemCount > gridSlot.ItemCount ||
100+
(recipeSlot.Slot.ItemDamage >= 0 && recipeSlot.Slot.ItemDamage != gridSlot.ItemDamage))
101+
return null;
102+
hasMatched[recipeSlot.X + xOffset, recipeSlot.Y + yOffset] = true;
103+
after[x, y].ItemCount -= recipeSlot.Slot.ItemCount;
104+
after[x, y].MakeEmptyIfZero();
105+
}
106+
107+
// 处理 Anywhere
108+
for (int i = 0; i < recipe.Inputs.Length; i++)
109+
{
110+
ref var recipeSlot = ref recipe.Inputs[i];
111+
112+
// Anywhere, 稍后处理
113+
if (recipeSlot.X >= 0 && recipeSlot.Y >= 0) continue;
114+
115+
int startX = 0, endX = gridWidth - 1;
116+
int startY = 0, endY = gridHeight - 1;
117+
if (recipeSlot.X >= 0)
118+
startX = endX = recipeSlot.X;
119+
if (recipeSlot.Y >= 0)
120+
startY = endY = recipeSlot.Y;
121+
bool found = false;
122+
for (int x = startX; x <= endX; x++)
123+
{
124+
for (int y = startY; y <= endY; y++)
125+
{
126+
if (hasMatched[x, y]) continue;
127+
ref var gridSlot = ref craftingGrid[x, y];
128+
if (gridSlot.BlockId == recipeSlot.Slot.BlockId &&
129+
(recipeSlot.Slot.ItemDamage < 0 || gridSlot.ItemDamage == recipeSlot.Slot.ItemDamage))
130+
{
131+
hasMatched[x, y] = true;
132+
found = true;
133+
after[x, y].ItemCount -= recipeSlot.Slot.ItemCount;
134+
break;
135+
}
136+
}
137+
138+
if (found) break;
139+
}
140+
141+
if (!found) return null;
142+
}
143+
144+
// 最后检查一下
145+
for (int x = 0; x < gridWidth; x++)
146+
{
147+
for (int y = 0; y < gridHeight; y++)
148+
{
149+
if (!hasMatched[x, y] && !craftingGrid[x, y].IsEmpty)
150+
return null;
151+
}
152+
}
153+
154+
return new FindCraftingRecipeResult
155+
{
156+
Result = recipe.Output,
157+
AfterTake = after
158+
};
159+
}
160+
161+
private static Slot[,] CropCraftingGrid(Slot[,] sourceGrid, int gridLeft, int gridTop, int gridWidth, int gridHeight)
162+
{
163+
var slots = new Slot[gridWidth, gridHeight];
164+
for (int y = 0; y < gridHeight; y++)
165+
{
166+
for (int x = 0; x < gridWidth; x++)
167+
{
168+
slots[x, y] = sourceGrid[x + gridLeft, y + gridTop];
169+
}
170+
}
171+
172+
NormalizeSlots(slots);
173+
return slots;
174+
}
175+
176+
private Slot[,] RestoreCroppedCraftingGrid(Slot[,] craftingGrid, int gridLeft, int gridTop, int originWidth, int originHeight)
177+
{
178+
var slots = new Slot[originWidth, originHeight];
179+
for (int y = 0; y < craftingGrid.GetUpperBound(1) + 1; y++)
180+
{
181+
for (int x = 0; x < craftingGrid.GetUpperBound(0) + 1; x++)
182+
{
183+
slots[x + gridLeft, y + gridTop] = craftingGrid[x, y];
184+
}
185+
}
186+
187+
NormalizeSlots(slots);
188+
return slots;
189+
}
190+
191+
private static void NormalizeSlots(Slot[,] slots)
192+
{
193+
for (int x = 0; x < slots.GetUpperBound(0) + 1; x++)
194+
{
195+
for (int y = 0; y < slots.GetUpperBound(1) + 1; y++)
196+
{
197+
slots[x, y].MakeEmptyIfZero();
198+
}
199+
}
200+
}
201+
}
202+
}

0 commit comments

Comments
 (0)