public override void OnBlockRemoved() { base.OnBlockRemoved(); if (Api is ICoreClientAPI) { RemoveRenderedFruits(); } int dropCount = 0; for (int i = 0; i < fruitPoints.Length; i++) { FruitData val = fruitPoints[i]; if (val.variant < 0 || val.currentStage == 0) continue; Item item = Api.World.GetItem(new AssetLocation(fruitCodeBases[val.variant] + val.currentStage)); if (item == null) continue; if (item.Attributes != null && item.Attributes["onGround"].AsBool(false)) continue; // ignore fruits already on the ground if (val.currentStage == this.ripeStage) { dropCount++; } else if (Math.Abs(val.currentStage - this.ripeStage) == 1 && Api.World.Rand.NextDouble() > 0.5) dropCount++; } if (dropCount > 0) { ItemStack stack = new ItemStack(Api.World.GetItem(dropCode), dropCount); Api.World.SpawnItemEntity(stack, Blockentity.Pos.ToVec3d().Add(0.5, 0.25, 0.5)); } }
public virtual void RemoveRenderedFruits() { if (positions == null || fruitCodeBases == null) return; for (int i = 0; i < fruitPoints.Length; i++) { FruitData val = fruitPoints[i]; if (val.variant >= 0 && val.currentStage > 0) manager.RemoveFruit(fruitCodeBases[val.variant] + val.currentStage, positions[i]); } }
/// <summary> /// Add a fruit to render, with its germination date in gametime (allows for it to be grown, transitioned etc) /// </summary> public void AddFruit(AssetLocation code, Vec3d position, FruitData data) { if (Api.Side == EnumAppSide.Client) { Item fruit = Api.World.GetItem(code); if (fruit != null) { Renderer.AddFruit(fruit, position, data); } } }
public void InitializeArrays() { // If initialising on fresh creation, both arrays will be null (otherwise fruitPoints was already initialised in FromTreeAttributes) if (fruitPoints == null) { // This code path is called on block placement (i.e. when previous stage crop grows to final stage) // This initialises each fruit point with a different germination time // (if called after FromTreeAttributes, fruitPoints will already have been populated, and GetGerminationDate() will be called in the next CheckForGrowth() call instead) fruitPoints = new FruitData[positionsCount]; int randomSelector = Math.Abs(this.Blockentity.Pos.GetHashCode()) % fruitCodeBases.Length; for (int i = 0; i < positionsCount; i++) { int fruitVariant = i; if (i >= fruitCodeBases.Length) fruitVariant = randomSelector++ % fruitCodeBases.Length; fruitPoints[i] = new FruitData(fruitVariant, GetGerminationDate(), this, null); } } positions = new Vec3d[positionsCount]; Vec3f temp = new Vec3f(); float[] matrix = null; if (Blockentity.Block.RandomizeRotations) { // For performance, only call the hash function once per blockEntity loaded int randomSelector = GameMath.MurmurHash3(-Blockentity.Pos.X, Blockentity.Block.RandomizeAxes == EnumRandomizeAxes.XYZ ? Blockentity.Pos.Y : 0, Blockentity.Pos.Z); matrix = randomRotMatrices[GameMath.Mod(randomSelector, randomRotations.Length)]; } for (int i = 0; i < positionsCount; i++) { if (Api.Side == EnumAppSide.Client) { positions[i] = new Vec3d(points[i * 3], points[i * 3 + 1], points[i * 3 + 2]); if (matrix != null) { Mat4f.MulWithVec3_Position(matrix, (float)positions[i].X, (float)positions[i].Y, (float)positions[i].Z, temp); positions[i].X = temp.X; positions[i].Y = temp.Y; positions[i].Z = temp.Z; } } else { positions[i] = new Vec3d((i + 1) / positionsCount, (i + 1) / positionsCount, (i + 1) / positionsCount); //dummy vector for serverside - exact positions don't matter because they won't be rendered } positions[i].Add(Blockentity.Pos); } }
public override void ToTreeAttributes(ITreeAttribute tree) { base.ToTreeAttributes(tree); tree.SetInt("count", positionsCount); for (int i = 0; i < positionsCount; i++) { FruitData val = fruitPoints[i]; if (val == null) continue; tree.SetDouble("td" + i, val.transitionDate); tree.SetInt("var" + i, val.variant); tree.SetInt("tc" + i, val.currentStage); } }
public virtual void OnPlayerInteractStop(float secondsUsed, IPlayer player, Vec3d hit) { if (secondsUsed < 0.2f) return; for (int i = 0; i < fruitPoints.Length; i++) { FruitData val = fruitPoints[i]; if (val.variant >= 0 && val.currentStage >= this.ripeStage) { Item item = Api.World.GetItem(new AssetLocation(fruitCodeBases[val.variant] + val.currentStage)); if (item == null) continue; if (item.Attributes != null && item.Attributes["onGround"].AsBool(false)) continue; // ignore fruits already on the ground if (Api.Side == EnumAppSide.Client) manager.RemoveFruit(fruitCodeBases[val.variant] + val.currentStage, positions[i]); val.variant = -1; // Flag this fruit as picked val.transitionDate = Double.MaxValue; if (val.currentStage >= this.ripeStage) // allow both ripe and overripe fruits to be picked { // Play snickety sound double posx = Blockentity.Pos.X + hit.X; double posy = Blockentity.Pos.Y + hit.Y; double posz = Blockentity.Pos.Z + hit.Z; player.Entity.World.PlaySoundAt(new AssetLocation("sounds/effect/squish1"), posx, posy, posz, player, 1.1f + (float)Api.World.Rand.NextDouble() * 0.4f, 16, 0.25f); // Update transition time of any non-started fruit, otherwise they may all start immediately double now = Api.World.Calendar.TotalDays; for (int j = 0; j < fruitPoints.Length; j++) { val = fruitPoints[j]; if (val.variant >= 0 && val.currentStage == 0 && val.transitionDate < now) { val.transitionDate = now + Api.World.Rand.NextDouble() * maxGerminationDays / 2; } } // Give the player one item ItemStack stack = new ItemStack(Api.World.GetItem(dropCode), 1); if (!player.InventoryManager.TryGiveItemstack(stack)) { Api.World.SpawnItemEntity(stack, Blockentity.Pos.ToVec3d().Add(0.5, 0.25, 0.5)); } } Blockentity.MarkDirty(); break; } } }
/// <summary> /// Add a fruit to render, with its germination date in gametime (allows for it to be grown, transitioned etc) /// </summary> public void AddFruit(Item fruit, Vec3d position, FruitData data) { if (fruit.Shape == null) { return; } if (!renderers.TryGetValue(fruit.Code, out FruitRenderer renderer)) { renderer = new FruitRenderer(capi, fruit); renderers.Add(fruit.Code, renderer); } renderer.AddFruit(position, data); }
public virtual bool OnPlayerInteract(float secondsUsed, IPlayer player, Vec3d hit) { if (player?.InventoryManager?.ActiveTool != EnumTool.Knife) return false; if (Api.Side == EnumAppSide.Server) return true; bool hasPickableFruit = false; for (int i = 0; i < fruitPoints.Length; i++) { FruitData val = fruitPoints[i]; if (val.variant >= 0 && val.currentStage >= this.ripeStage) { Item item = Api.World.GetItem(new AssetLocation(fruitCodeBases[val.variant] + val.currentStage)); if (item == null) continue; if (item.Attributes != null && item.Attributes["onGround"].AsBool(false)) continue; // ignore fruits already on the ground hasPickableFruit = true; break; } } return hasPickableFruit && secondsUsed < 0.3f; }
public override void FromTreeAttributes(ITreeAttribute tree, IWorldAccessor worldForResolving) { base.FromTreeAttributes(tree, worldForResolving); if (positionsCount == 0) positionsCount = tree.GetInt("count"); if (positionsCount == 0) positionsCount = 10; if (fruitPoints == null) fruitPoints = new FruitData[positionsCount]; for (int i = 0; i < positionsCount; i++) { double td = tree.GetDouble("td" + i); int var = tree.GetInt("var" + i); int tc = tree.GetInt("tc" + i); FruitData val = fruitPoints[i]; if (val == null) { val = new FruitData(-1, td, this, null); fruitPoints[i] = val; } if (Api is ICoreClientAPI && val.variant >= 0) //there was an existing FruitData at this position { manager.RemoveFruit(fruitCodeBases[val.variant] + val.currentStage, positions[i]); } val.variant = var; val.currentStage = tc; val.transitionDate = td; if (Api is ICoreClientAPI && val.variant >= 0 && val.currentStage > 0) { val.SetRandomRotation(Api.World, i, positions[i], this.Blockentity.Pos); manager.AddFruit(new AssetLocation(fruitCodeBases[val.variant] + val.currentStage), positions[i], val); } } }
public override void Initialize(ICoreAPI api, JsonObject properties) { base.Initialize(api, properties); dateLastChecked = Api.World.Calendar.TotalDays; // Read the properties fruitCodeBases = properties["fruitCodeBases"].AsArray<string>(new string[0]); if (fruitCodeBases.Length == 0) return; positionsCount = properties["positions"].AsInt(0); if (positionsCount <= 0) return; string maturePlant = properties["maturePlant"].AsString(null); if (maturePlant == null) return; Block mature = api.World.GetBlock(new AssetLocation(maturePlant)); if (!(mature is BlockFruiting matureCrop)) return; if (Api.Side == EnumAppSide.Client) { points = matureCrop.GetFruitingPoints(); } maxFruit = properties["maxFruit"].AsInt(5); fruitStages = properties["fruitStages"].AsInt(6); maxGerminationDays = properties["maxGerminationDays"].AsFloat(6); transitionDays = properties["transitionDays"].AsFloat(1); successfulGrowthChance = properties["successfulGrowthChance"].AsFloat(0.75f); ripeStage = properties["ripeStage"].AsInt(fruitStages - 1); dropCode = new AssetLocation(properties["dropCode"].AsString(null)); // Set up the manager - used for the instanced renderer manager = Api.ModLoader.GetModSystem<FruitingSystem>(); // If initialising client-side after reading from tree attributes, send fruits to the renderer bool addToManager = false; if (Api.Side == EnumAppSide.Client && fruitPoints != null) { LightRgba = Api.World.BlockAccessor.GetLightRGBs(Blockentity.Pos); addToManager = true; } InitializeArrays(); if (addToManager) { for (int i = 0; i < positionsCount; i++) { FruitData val = fruitPoints[i]; if (val.variant >= fruitCodeBases.Length) val.variant %= fruitCodeBases.Length; if (val.variant >= 0 && val.currentStage > 0) { val.SetRandomRotation(Api.World, i, positions[i], this.Blockentity.Pos); manager.AddFruit(new AssetLocation(fruitCodeBases[val.variant] + val.currentStage), positions[i], val); } } } // Tick the blockEntity server-side only if (Api.Side == EnumAppSide.Server) Blockentity.RegisterGameTickListener(CheckForGrowth, 2250); }
internal void AddFruit(Vec3d position, FruitData data) { positions[position] = data; }