Skip to content

Commit 7634200

Browse files
committed
fix
1 parent ed1f633 commit 7634200

5 files changed

Lines changed: 223 additions & 67 deletions

File tree

src/main/java/com/github/gtexpert/gtbm/integration/forestry/util/WidgetBeeStatus.java

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,24 @@ public class WidgetBeeStatus extends Widget {
3939
private final IBeeRoot beeRoot;
4040
private final ApiaryModifiers modifiers;
4141
private final IntSupplier euPerTickSupplier;
42+
private final IntSupplier cycleTickSupplier;
4243

4344
// Client-side synced data
4445
private List<String> clientTooltip = new ArrayList<>();
4546
private List<Short> clientErrorIds = new ArrayList<>();
4647

4748
// Server-side change tracking
48-
private int lastTooltipHash = Integer.MIN_VALUE;
4949
private int lastErrorHash = Integer.MIN_VALUE;
50+
private int syncTickCounter;
5051

5152
public WidgetBeeStatus(int x, int y, IBeeHousing housing, IBeeRoot beeRoot, ApiaryModifiers modifiers,
52-
IntSupplier euPerTickSupplier) {
53+
IntSupplier euPerTickSupplier, IntSupplier cycleTickSupplier) {
5354
super(new Position(x, y), new Size(16, 16));
5455
this.housing = housing;
5556
this.beeRoot = beeRoot;
5657
this.modifiers = modifiers;
5758
this.euPerTickSupplier = euPerTickSupplier;
59+
this.cycleTickSupplier = cycleTickSupplier;
5860
}
5961

6062
// ---- Server-side: sync ----
@@ -81,10 +83,15 @@ public void detectAndSendChanges() {
8183
}
8284

8385
// Sync tooltip lines
84-
List<String> tooltip = buildTooltipLines();
85-
int tooltipHash = tooltip.hashCode();
86-
if (tooltipHash != lastTooltipHash) {
87-
lastTooltipHash = tooltipHash;
86+
// When active: sync every second (20 ticks) for smooth countdown
87+
// When idle: sync every 2 seconds (40 ticks) to catch changes
88+
syncTickCounter++;
89+
boolean isActive = housing.getBeekeepingLogic() != null && !housing.getBeeInventory().getQueen().isEmpty();
90+
int syncInterval = isActive ? 20 : 40;
91+
92+
if (syncTickCounter >= syncInterval) {
93+
syncTickCounter = 0;
94+
List<String> tooltip = buildTooltipLines();
8895
writeUpdateInfo(SYNC_TOOLTIP, buf -> {
8996
buf.writeVarInt(tooltip.size());
9097
for (String line : tooltip) {
@@ -112,25 +119,57 @@ private List<String> buildTooltipLines() {
112119
lines.add("gtbm.bee.label.temperature\t" + temp.getName().toLowerCase());
113120
lines.add("gtbm.bee.label.humidity\t" + hum.getName().toLowerCase());
114121

115-
// Bee stats
122+
// Bee stats - read actual values from Forestry config + lifespan modifier
123+
int baseCycle = forestry.apiculture.ModuleApiculture.ticksPerBeeWorkCycle;
124+
int effectiveTicksPerHealth = Math.round(baseCycle * modifiers.lifespan);
125+
final int BREEDING_TIME_TICKS = 100;
126+
116127
IBeeHousingInventory inv = housing.getBeeInventory();
117128
if (beeRoot != null && !inv.getQueen().isEmpty()) {
118129
IBee bee = beeRoot.getMember(inv.getQueen());
119-
if (bee != null && bee.isAnalyzed()) {
120-
IBeeGenome genome = bee.getGenome();
121-
lines.add("gtbm.bee.label.production\t" +
122-
String.format("%.0f%%", 100F * modifiers.production * genome.getSpeed()));
123-
lines.add("gtbm.bee.label.flowering\t" +
124-
String.format("%.0f%%", modifiers.flowering * genome.getFlowering()));
125-
lines.add("gtbm.bee.label.lifespan\t" +
126-
String.format("%.0f%%", 100F * modifiers.lifespan * genome.getLifespan()));
127-
var t = genome.getTerritory();
128-
lines.add("gtbm.bee.label.territory\t" + String.format("%.0f x %.0f x %.0f",
129-
t.getX() * modifiers.territory, t.getY() * modifiers.territory,
130-
t.getZ() * modifiers.territory));
130+
if (bee != null) {
131+
EnumBeeType type = beeRoot.getType(inv.getQueen());
132+
if (type == EnumBeeType.QUEEN) {
133+
// Precise remaining time using cycle tick counter
134+
int cycleTick = cycleTickSupplier.getAsInt();
135+
int remainingInCycle = Math.max(0, effectiveTicksPerHealth - cycleTick);
136+
int remainingTotal = Math.max(0, bee.getHealth() - 1) * effectiveTicksPerHealth + remainingInCycle;
137+
lines.add("gtbm.bee.label.health\t" + bee.getHealth() + " / " + bee.getMaxHealth() + " (" +
138+
formatTime(remainingTotal / 20) + ")");
139+
140+
// Next product: remaining ticks in current cycle
141+
lines.add("gtbm.bee.label.cycle\t" + (remainingInCycle / 20) + "s");
142+
} else if (type == EnumBeeType.PRINCESS) {
143+
int remainingSeconds = BREEDING_TIME_TICKS / 20;
144+
lines.add("gtbm.bee.label.breeding\t" + remainingSeconds + "s");
145+
}
146+
147+
if (bee.isAnalyzed()) {
148+
IBeeGenome genome = bee.getGenome();
149+
lines.add("gtbm.bee.label.production\t" +
150+
String.format("%.0f%%", 100F * modifiers.production * genome.getSpeed()));
151+
lines.add("gtbm.bee.label.flowering\t" +
152+
String.format("%.0f%%", modifiers.flowering * genome.getFlowering()));
153+
lines.add("gtbm.bee.label.lifespan\t" +
154+
String.format("%.0f%%", 100F * modifiers.lifespan * genome.getLifespan()));
155+
var t = genome.getTerritory();
156+
lines.add("gtbm.bee.label.territory\t" + String.format("%.0f x %.0f x %.0f",
157+
t.getX() * modifiers.territory, t.getY() * modifiers.territory,
158+
t.getZ() * modifiers.territory));
159+
}
131160
}
132161
}
133162

163+
// Active upgrade effects summary
164+
boolean hasUpgradeEffects = modifiers.energy != 1 || modifiers.lifespan != 1 || modifiers.production != 1 ||
165+
modifiers.mutation != 1 || modifiers.territory != 1 || modifiers.flowering != 1 ||
166+
modifiers.geneticDecay != 1 || modifiers.humidity != 0 || modifiers.temperature != 0 ||
167+
modifiers.isSealed || modifiers.isSelfLighted || modifiers.isSunlightSimulated ||
168+
modifiers.isAutomated || modifiers.isCollectingPollen || modifiers.biomeOverride != null;
169+
if (hasUpgradeEffects) {
170+
lines.add("gtbm.bee.label.upgrade_note\t");
171+
}
172+
134173
return lines;
135174
}
136175

@@ -227,4 +266,11 @@ public void drawInForeground(int mouseX, int mouseY) {
227266
drawHoveringText(ItemStack.EMPTY, tooltip, 200, mouseX, mouseY);
228267
}
229268
}
269+
270+
private static String formatTime(int totalSeconds) {
271+
if (totalSeconds >= 60) {
272+
return String.format("%dm %ds", totalSeconds / 60, totalSeconds % 60);
273+
}
274+
return totalSeconds + "s";
275+
}
230276
}

src/main/java/com/github/gtexpert/gtbm/integration/gendustry/metatileentities/IndustrialApiaryLogic.java

Lines changed: 104 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,17 @@
2020

2121
public class IndustrialApiaryLogic extends RecipeLogicEnergy {
2222

23-
private static final int BASE_EU_PER_TICK = 32;
24-
2523
private IBeeRoot beeRoot;
2624
private IBeekeepingLogic beekeepingLogic;
2725
private boolean needsMovePrincess;
2826

27+
public int getCycleTickCounter() {
28+
// Estimate current cycle position from progressTime
29+
if (maxProgressTime <= 0) return 0;
30+
int effectiveTicks = getEffectiveTicksPerHealth();
31+
return effectiveTicks > 0 ? (progressTime % effectiveTicks) : 0;
32+
}
33+
2934
public IndustrialApiaryLogic(@NotNull MetaTileEntity metaTileEntity, RecipeMap<?> recipeMap,
3035
Supplier<IEnergyContainer> energyContainer) {
3136
super(metaTileEntity, recipeMap, energyContainer);
@@ -91,20 +96,58 @@ public void updateModifiers() {
9196
}
9297
}
9398

99+
/**
100+
* Base power consumption matching Gendustry:
101+
* Gendustry uses 2 MJ/t internally = 20 RF/t displayed.
102+
* We use the displayed RF value directly as EU/t.
103+
*/
104+
private static final int BASE_EU_PER_TICK = 20;
105+
94106
public int getEUPerTick() {
95107
ApiaryModifiers mods = getApiary().getModifiers();
96-
if (mods == null) return BASE_EU_PER_TICK;
97-
return (int) (BASE_EU_PER_TICK * mods.energy);
108+
float energyMod = (mods != null) ? mods.energy : 1.0F;
109+
return Math.max(1, Math.round(BASE_EU_PER_TICK * energyMod));
110+
}
111+
112+
/**
113+
* Get the actual ticks per work cycle from Forestry's config.
114+
* Default: 550. Configurable in Forestry between 250-850.
115+
*/
116+
private int getTicksPerWorkCycle() {
117+
return forestry.apiculture.ModuleApiculture.ticksPerBeeWorkCycle;
118+
}
119+
120+
/**
121+
* Get the effective ticks per health point, accounting for lifespan modifier.
122+
* Higher lifespan modifier = queen lives longer = more ticks per health.
123+
* Forestry's age() logic: ageModifier = 1/lifespanModifier, health decreases by ageModifier per cycle.
124+
* So effective ticks per health = ticksPerWorkCycle * lifespanModifier.
125+
*/
126+
private int getEffectiveTicksPerHealth() {
127+
int baseCycle = getTicksPerWorkCycle();
128+
ApiaryModifiers mods = getApiary().getModifiers();
129+
float lifespanMod = (mods != null) ? mods.lifespan : 1.0F;
130+
return Math.round(baseCycle * lifespanMod);
98131
}
99132

100133
// ---- Core tick logic ----
101134

102135
@Override
103136
public void update() {
104-
if (metaTileEntity.getWorld() == null || metaTileEntity.getWorld().isRemote) return;
137+
if (metaTileEntity.getWorld() == null || metaTileEntity.getWorld().isRemote) {
138+
// Handle wasActiveAndNeedsUpdate on client too
139+
if (wasActiveAndNeedsUpdate) {
140+
wasActiveAndNeedsUpdate = false;
141+
setActive(false);
142+
}
143+
return;
144+
}
105145

106146
initBeekeepingLogic();
107-
if (beekeepingLogic == null) return;
147+
if (beekeepingLogic == null) {
148+
if (isActive) setActive(false);
149+
return;
150+
}
108151

109152
MetaTileEntityIndustrialApiary apiary = getApiary();
110153
needsMovePrincess = false;
@@ -116,19 +159,22 @@ public void update() {
116159
int euPerTick = getEUPerTick();
117160
boolean hasPower = getEnergyStored() >= euPerTick;
118161

162+
// canWork() calls errorLogic.clearErrors() internally, then sets bee-related errors.
163+
// We must set power error AFTER canWork() to avoid it being cleared.
164+
boolean canWork = beekeepingLogic.canWork();
119165
apiary.getErrorLogic().setCondition(!hasPower, EnumErrorCode.NO_POWER);
120166

121-
// Beekeeping tick
122-
boolean canWork = beekeepingLogic.canWork();
123167
boolean isWorking = hasPower && workingEnabled && canWork;
124168

125169
if (isWorking) {
126170
beekeepingLogic.doWork();
127171
drawEnergy(euPerTick, false);
172+
recipeEUt = euPerTick;
173+
} else {
174+
recipeEUt = 0;
128175
}
129176

130-
// GT energy indicator: only show when actively working but can't draw power
131-
// (matches GT default: only during active recipe processing)
177+
// GT energy indicator
132178
if (canWork && workingEnabled && !hasPower) {
133179
hasNotEnoughEnergy = true;
134180
} else if (hasNotEnoughEnergy && getEnergyInputPerSecond() > 19L * euPerTick) {
@@ -140,27 +186,53 @@ public void update() {
140186
doMovePrincess();
141187
}
142188

143-
// Update GT active state
144-
boolean hasQueen = !apiary.getQueen().isEmpty();
145-
boolean shouldBeActive = isWorking || (hasQueen && canWork && workingEnabled);
146-
147-
if (isActive != shouldBeActive) {
148-
setActive(shouldBeActive);
189+
// Update GT active state: only active when actually working
190+
if (isActive != isWorking) {
191+
setActive(isWorking);
192+
}
193+
if (wasActiveAndNeedsUpdate) {
194+
wasActiveAndNeedsUpdate = false;
195+
setActive(false);
149196
}
150197

151-
// Progress for GUI
152-
if (hasQueen && beekeepingLogic.getBeeProgressPercent() > 0) {
153-
progressTime = beekeepingLogic.getBeeProgressPercent();
154-
maxProgressTime = 100;
198+
// Progress (smooth tick counter for TOP/GUI)
199+
if (isWorking && !apiary.getQueen().isEmpty()) {
200+
// Initialize max progress (only once per queen lifecycle)
201+
if (maxProgressTime <= 0) {
202+
IBeeRoot root = getBeeRoot();
203+
if (root != null) {
204+
EnumBeeType type = root.getType(apiary.getQueen());
205+
if (type == EnumBeeType.QUEEN) {
206+
IBee bee = root.getMember(apiary.getQueen());
207+
if (bee != null && bee.getMaxHealth() > 0 && bee.getHealth() > 0) {
208+
int effectiveTicks = getEffectiveTicksPerHealth();
209+
maxProgressTime = bee.getMaxHealth() * effectiveTicks;
210+
progressTime = (bee.getMaxHealth() - bee.getHealth()) * effectiveTicks;
211+
}
212+
} else if (type == EnumBeeType.PRINCESS) {
213+
maxProgressTime = 100;
214+
progressTime = beekeepingLogic.getBeeProgressPercent();
215+
}
216+
}
217+
}
218+
219+
// Smooth increment every tick, stop at max
220+
if (maxProgressTime > 0 && progressTime < maxProgressTime) {
221+
progressTime++;
222+
}
155223
} else {
156224
progressTime = 0;
157225
maxProgressTime = 0;
158226
}
159227
}
160228

229+
/**
230+
* Progress for GUI ProgressWidget (0.0 → 1.0).
231+
* Uses the same smooth tick-based values as TOP.
232+
*/
161233
public float getBeeProgress() {
162-
if (beekeepingLogic == null || getApiary().getQueen().isEmpty()) return 0;
163-
return beekeepingLogic.getBeeProgressPercent() / 100F;
234+
if (maxProgressTime <= 0) return 0;
235+
return (float) progressTime / maxProgressTime;
164236
}
165237

166238
// ---- Princess auto-move ----
@@ -194,6 +266,16 @@ protected void updateRecipeProgress() {}
194266

195267
@Override
196268
public NBTTagCompound serializeNBT() {
269+
// Parent serialization accesses itemOutputs/fluidOutputs when progressTime > 0.
270+
// Initialize them to empty to prevent NPE while preserving progress for TOP.
271+
if (itemOutputs == null) {
272+
itemOutputs = net.minecraft.util.NonNullList.create();
273+
}
274+
if (fluidOutputs == null) {
275+
fluidOutputs = java.util.Collections.emptyList();
276+
}
277+
recipeEUt = getEUPerTick();
278+
197279
NBTTagCompound tag = super.serializeNBT();
198280
if (beekeepingLogic != null) {
199281
NBTTagCompound beeTag = new NBTTagCompound();

0 commit comments

Comments
 (0)