2020
2121public 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