Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Brewing Stands #916

Merged
merged 26 commits into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e77a34e
implement campfires
xNatsuri Aug 20, 2024
436d7a9
Revert "implement campfires"
xNatsuri Aug 22, 2024
7c38d90
register and send Potion and PotionContainerChange recipes
xNatsuri Aug 22, 2024
75cbcac
Revert "register and send Potion and PotionContainerChange recipes"
xNatsuri Aug 22, 2024
2c0efa3
Merge branch 'df-mc:master' into master
xNatsuri Aug 31, 2024
6709c00
Merge branch 'df-mc:master' into master
xNatsuri Sep 1, 2024
0aa713a
implement brewing stands
xNatsuri Sep 1, 2024
bac251a
remove debug code
xNatsuri Sep 1, 2024
41f96d5
Merge branch 'df-mc:master' into master
xNatsuri Sep 2, 2024
136e2e8
Merge branch 'df-mc:master' into master
xNatsuri Sep 7, 2024
6316bd1
Merge branch 'master' of https://github.com/xNatsuri/dragonfly into b…
xNatsuri Sep 7, 2024
44d918f
update to latest df version
xNatsuri Sep 7, 2024
fdaf4a0
working brewing stands (temp fix to import cycle)
xNatsuri Sep 7, 2024
3fcde10
validate reagents
xNatsuri Sep 7, 2024
4f99f3d
validate reagents
xNatsuri Sep 7, 2024
1be2783
Merge branch 'df-mc:master' into brewing-stand
xNatsuri Nov 16, 2024
91b07ba
server: Latest dragonfly & fix recipe registering
TwistedAsylumMC Nov 16, 2024
958d469
remove comment and finish documentation.
xNatsuri Nov 17, 2024
1ea16ca
recipe/vanilla.go: Fix staticcheck
TwistedAsylumMC Nov 17, 2024
77a3854
Merge branch 'df-mc:master' into brewing-stand
xNatsuri Nov 17, 2024
3875ba7
requested changes
xNatsuri Nov 17, 2024
35e52da
add a check if input is a potion or a splash potion
xNatsuri Nov 17, 2024
7a41522
add lingering potions to accepted items
xNatsuri Nov 17, 2024
517d398
block/brewer.go: stop early on invalid hopper inputs
DaPigGuy Nov 17, 2024
6cb8043
block/brewer.go: allow hoppers to insert empty bottles
DaPigGuy Nov 17, 2024
1c12c01
block/brewer.go: change order of conditions
DaPigGuy Nov 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
253 changes: 253 additions & 0 deletions server/block/brewer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
package block

import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/item/inventory"
"github.com/df-mc/dragonfly/server/item/recipe"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/sound"
"sync"
"time"
)

// brewer is a struct that may be embedded by blocks that can brew potions, such as brewing stands.
type brewer struct {
mu sync.Mutex

viewers map[ContainerViewer]struct{}
inventory *inventory.Inventory

duration time.Duration
fuelAmount int32
fuelTotal int32
}

// newBrewer creates a new initialised brewer. The inventory is properly initialised.
func newBrewer() *brewer {
b := &brewer{viewers: make(map[ContainerViewer]struct{})}
b.inventory = inventory.New(5, func(slot int, _, item item.Stack) {
b.mu.Lock()
defer b.mu.Unlock()
for viewer := range b.viewers {
viewer.ViewSlotChange(slot, item)
}
})
return b
}

// InsertItem ...
func (b *brewer) InsertItem(h Hopper, pos cube.Pos, w *world.World) bool {
for sourceSlot, sourceStack := range h.inventory.Slots() {
var slot int

if sourceStack.Empty() {
continue
}

if h.Facing == cube.FaceDown {
if !recipe.ValidBrewingReagent(sourceStack.Item()) {
// This item is not a valid brewing reagent.
continue
}
slot = 0
} else if _, ok := sourceStack.Item().(item.BlazePowder); ok {
slot = 4
} else {
_, okPotion := sourceStack.Item().(item.Potion)
_, okSplash := sourceStack.Item().(item.SplashPotion)
_, okLingering := sourceStack.Item().(item.LingeringPotion)
_, okBottle := sourceStack.Item().(item.GlassBottle)
if !okPotion && !okSplash && !okLingering && !okBottle {
continue
}
for brewingSlot, brewingStack := range b.inventory.Slots() {
if brewingSlot == 0 || brewingSlot == 4 {
continue
}
if brewingStack.Count() == brewingStack.MaxCount() || !brewingStack.Comparable(sourceStack) {
continue
}

slot = brewingSlot
break
}
// Could not find an empty slot
if slot == 0 {
continue
}
}

stack := sourceStack.Grow(-sourceStack.Count() + 1)
it, _ := b.Inventory(w, pos).Item(slot)

if !sourceStack.Comparable(it) {
// The items are not the same.
continue
}
if it.Count() == it.MaxCount() {
// The item has the maximum count that the stack is able to hold.
continue
}
if !it.Empty() {
stack = it.Grow(1)
}

_ = b.Inventory(w, pos).SetItem(slot, stack)
_ = h.inventory.SetItem(sourceSlot, sourceStack.Grow(-1))
return true

}
return false
}

// ExtractItem ...
func (b *brewer) ExtractItem(h Hopper, pos cube.Pos, w *world.World) bool {
for sourceSlot, sourceStack := range b.inventory.Slots() {
if sourceStack.Empty() {
continue
}

if sourceSlot == 0 || sourceSlot == 4 {
continue
}

_, err := h.inventory.AddItem(sourceStack.Grow(-sourceStack.Count() + 1))
if err != nil {
// The hopper is full.
continue
}

_ = b.Inventory(w, pos).SetItem(sourceSlot, sourceStack.Grow(-1))
return true
}
return false
}

// Duration returns the remaining duration of the brewing process.
func (b *brewer) Duration() time.Duration {
b.mu.Lock()
defer b.mu.Unlock()
return b.duration
}

// Fuel returns the fuel and maximum fuel of the brewer.
func (b *brewer) Fuel() (fuel, maxFuel int32) {
b.mu.Lock()
defer b.mu.Unlock()
return b.fuelAmount, b.fuelTotal
}

// Inventory returns the inventory of the brewer.
func (b *brewer) Inventory(*world.World, cube.Pos) *inventory.Inventory {
return b.inventory
}

// AddViewer adds a viewer to the brewer, so that it is updated whenever the inventory of the brewer is changed.
func (b *brewer) AddViewer(v ContainerViewer, _ *world.World, _ cube.Pos) {
b.mu.Lock()
defer b.mu.Unlock()
b.viewers[v] = struct{}{}
}

// RemoveViewer removes a viewer from the brewer, so that slot updates in the inventory are no longer sent to
// it.
func (b *brewer) RemoveViewer(v ContainerViewer, _ *world.World, _ cube.Pos) {
b.mu.Lock()
defer b.mu.Unlock()
delete(b.viewers, v)
}

// setDuration sets the brew duration of the brewer to the given duration.
func (b *brewer) setDuration(duration time.Duration) {
b.mu.Lock()
defer b.mu.Unlock()
b.duration = duration
}

// setFuel sets the fuel of the brewer to the given fuel and maximum fuel.
func (b *brewer) setFuel(fuel, maxFuel int32) {
b.mu.Lock()
defer b.mu.Unlock()
b.fuelAmount, b.fuelTotal = fuel, maxFuel
}

// tickBrewing ticks the brewer, ensuring the necessary items exist in the brewer, and then processing all inputted
// items for the necessary duration.
func (b *brewer) tickBrewing(block string, pos cube.Pos, w *world.World) {
b.mu.Lock()

// Get each item in the brewer. We don't need to validate errors here since we know the bounds of the brewer.
left, _ := b.inventory.Item(1)
middle, _ := b.inventory.Item(2)
right, _ := b.inventory.Item(3)

// Keep track of our past durations, since if any of them change, we need to be able to tell they did and then
// update the viewers on the change.
prevDuration := b.duration
prevFuelAmount := b.fuelAmount
prevFuelTotal := b.fuelTotal

// If we need fuel, try and burn some.
fuel, _ := b.inventory.Item(4)

if _, ok := fuel.Item().(item.BlazePowder); ok && b.fuelAmount <= 0 {
defer b.inventory.SetItem(4, fuel.Grow(-1))
b.fuelAmount, b.fuelTotal = 20, 20
}

// Now get the ingredient item.
ingredient, _ := b.inventory.Item(0)

// Check each input and see if it is affected by the ingredient.
leftOutput, leftAffected := recipe.Perform(block, left.Item(), ingredient.Item())
middleOutput, middleAffected := recipe.Perform(block, middle.Item(), ingredient.Item())
rightOutput, rightAffected := recipe.Perform(block, right.Item(), ingredient.Item())

// Ensure that we have enough fuel to continue.
if b.fuelAmount > 0 {
// Now make sure that we have at least one potion that is affected by the ingredient.
if leftAffected || middleAffected || rightAffected {
// Tick our duration. If we have no brew duration, set it to the default of twenty seconds.
if b.duration == 0 {
b.duration = time.Second * 20
}
b.duration -= time.Millisecond * 50

// If we have no duration, we are done.
if b.duration <= 0 {
// Create the output items.
if leftAffected {
defer b.inventory.SetItem(1, leftOutput[0])
}
if middleAffected {
defer b.inventory.SetItem(2, middleOutput[0])
}
if rightAffected {
defer b.inventory.SetItem(3, rightOutput[0])
}

// Reduce the ingredient by one.
defer b.inventory.SetItem(0, ingredient.Grow(-1))
w.PlaySound(pos.Vec3Centre(), sound.PotionBrewed{})

// Decrement the fuel, and reset the duration.
b.fuelAmount--
b.duration = 0
}
} else {
// None of the potions are affected by the ingredient, so reset the duration.
b.duration = 0
}
} else {
// We don't have enough fuel, so reset our progress.
b.duration, b.fuelAmount, b.fuelTotal = 0, 0, 0
}

// Update the viewers on the new durations.
for v := range b.viewers {
v.ViewBrewingUpdate(prevDuration, b.duration, prevFuelAmount, b.fuelAmount, prevFuelTotal, b.fuelTotal)
}

b.mu.Unlock()
}
Loading
Loading