From 6363cf4b1aac4fb0df6f7aba3d87c2c6ed865a20 Mon Sep 17 00:00:00 2001 From: Bob Date: Sat, 15 Oct 2022 20:00:25 +0200 Subject: [PATCH] actually working rocket artillery --- .../projectile/EntityArtilleryRocket.java | 65 +++++++-- .../entity/projectile/EntityThrowableNT.java | 4 +- .../IRocketSteeringBehavior.java | 2 +- .../RocketSteeringBallisticArc.java | 80 ++++++++++- .../RocketTargetingPredictive.java | 16 ++- .../hbm/inventory/gui/GUITurretHIMARS.java | 34 +++++ .../items/tool/ItemDesignatorArtyRange.java | 21 ++- .../com/hbm/items/weapon/ItemAmmoHIMARS.java | 15 +- .../projectile/RenderArtilleryRocket.java | 20 ++- .../hbm/render/item/ItemRenderLibrary.java | 4 +- .../render/tileentity/RenderTurretHIMARS.java | 35 ++++- .../machine/TileEntityCrucible.java | 6 + .../turret/TileEntityTurretHIMARS.java | 128 +++++++++++++++++- .../models/projectiles/himars_single.png | Bin 924 -> 1570 bytes .../models/projectiles/himars_standard.png | Bin 543 -> 909 bytes .../hbm/textures/models/turrets/himars.png | Bin 4074 -> 4115 bytes 16 files changed, 380 insertions(+), 50 deletions(-) diff --git a/src/main/java/com/hbm/entity/projectile/EntityArtilleryRocket.java b/src/main/java/com/hbm/entity/projectile/EntityArtilleryRocket.java index 7b69f6562..41f75310e 100644 --- a/src/main/java/com/hbm/entity/projectile/EntityArtilleryRocket.java +++ b/src/main/java/com/hbm/entity/projectile/EntityArtilleryRocket.java @@ -6,6 +6,8 @@ import java.util.List; import com.hbm.entity.logic.IChunkLoader; import com.hbm.entity.projectile.rocketbehavior.IRocketSteeringBehavior; import com.hbm.entity.projectile.rocketbehavior.IRocketTargetingBehavior; +import com.hbm.entity.projectile.rocketbehavior.RocketSteeringBallisticArc; +import com.hbm.entity.projectile.rocketbehavior.RocketTargetingPredictive; import com.hbm.items.weapon.ItemAmmoHIMARS; import com.hbm.items.weapon.ItemAmmoHIMARS.HIMARSRocket; import com.hbm.main.MainRegistry; @@ -14,6 +16,7 @@ import api.hbm.entity.IRadarDetectable; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.entity.Entity; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.Vec3; import net.minecraft.world.ChunkCoordIntPair; @@ -27,15 +30,18 @@ public class EntityArtilleryRocket extends EntityThrowableInterp implements IChu private Ticket loaderTicket; //TODO: find satisfying solution for when an entity is unloaded and reloaded, possibly a custom entity lookup using persistent UUIDs - private Entity targetEntity = null; - private Vec3 lastTargetPos; + public Entity targetEntity = null; + public Vec3 lastTargetPos; - private IRocketTargetingBehavior targeting; - private IRocketSteeringBehavior steering; + public IRocketTargetingBehavior targeting; + public IRocketSteeringBehavior steering; public EntityArtilleryRocket(World world) { super(world); this.ignoreFrustumCheck = true; + + this.targeting = new RocketTargetingPredictive(); + this.steering = new RocketSteeringBallisticArc(); } @Override @@ -84,8 +90,16 @@ public class EntityArtilleryRocket extends EntityThrowableInterp implements IChu if(!worldObj.isRemote) { - if(this.targetEntity != null) this.targeting.recalculateTargetPosition(this, this.targetEntity); - this.steering.adjustCourse(this); + if(this.targetEntity == null) { + Vec3 delta = Vec3.createVectorHelper(this.lastTargetPos.xCoord - this.posX, this.lastTargetPos.yCoord - this.posY, this.lastTargetPos.zCoord - this.posZ); + if(delta.lengthVector() <= 15D) { + this.targeting = null; + this.steering = null; + } + } + + if(this.targeting != null && this.targetEntity != null) this.targeting.recalculateTargetPosition(this, this.targetEntity); + if(this.steering != null) this.steering.adjustCourse(this, 25D, 15D); loadNeighboringChunks((int)Math.floor(posX / 16D), (int)Math.floor(posZ / 16D)); this.getType().onUpdate(this); @@ -120,8 +134,17 @@ public class EntityArtilleryRocket extends EntityThrowableInterp implements IChu clearChunkLoader(); loadedChunks.clear(); - loadedChunks.add(new ChunkCoordIntPair(newChunkX, newChunkZ)); - loadedChunks.add(new ChunkCoordIntPair(newChunkX + (int) Math.ceil((this.posX + this.motionX) / 16D), newChunkZ + (int) Math.ceil((this.posZ + this.motionZ) / 16D))); + + int minX = Math.min(newChunkX, newChunkX + (int) Math.ceil((this.posX + this.motionX) / 16D)); + int maxX = Math.max(newChunkX, newChunkX + (int) Math.ceil((this.posX + this.motionX) / 16D)); + int minZ = Math.min(newChunkX, newChunkX + (int) Math.ceil((this.posX + this.motionX) / 16D)); + int maxZ = Math.max(newChunkZ, newChunkZ + (int) Math.ceil((this.posZ + this.motionZ) / 16D)); + + for(int x = minX; x <= maxX; x++) { + for(int z = minZ; z <= maxZ; z++) { + loadedChunks.add(new ChunkCoordIntPair(x, z)); + } + } for(ChunkCoordIntPair chunk : loadedChunks) { ForgeChunkManager.forceChunk(loaderTicket, chunk); @@ -142,6 +165,32 @@ public class EntityArtilleryRocket extends EntityThrowableInterp implements IChu } } + @Override + public void writeEntityToNBT(NBTTagCompound nbt) { + super.writeEntityToNBT(nbt); + + nbt.setDouble("targetX", this.lastTargetPos.xCoord); + nbt.setDouble("targetY", this.lastTargetPos.yCoord); + nbt.setDouble("targetZ", this.lastTargetPos.zCoord); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbt) { + super.readEntityFromNBT(nbt); + + this.lastTargetPos = Vec3.createVectorHelper(nbt.getDouble("targetX"), nbt.getDouble("targetY"), nbt.getDouble("targetZ")); + } + + @Override + protected float getAirDrag() { + return 1.0F; + } + + @Override + public double getGravityVelocity() { + return this.steering != null ? 0D : 0.01D; + } + @Override public RadarTargetType getTargetType() { return RadarTargetType.ARTILLERY; diff --git a/src/main/java/com/hbm/entity/projectile/EntityThrowableNT.java b/src/main/java/com/hbm/entity/projectile/EntityThrowableNT.java index 608f999d5..86e57240b 100644 --- a/src/main/java/com/hbm/entity/projectile/EntityThrowableNT.java +++ b/src/main/java/com/hbm/entity/projectile/EntityThrowableNT.java @@ -226,8 +226,8 @@ public abstract class EntityThrowableNT extends Entity implements IProjectile { this.prevRotationYaw += 360.0F; } - this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F; - this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F; + //this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F; + //this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F; float drag = this.getAirDrag(); double gravity = this.getGravityVelocity(); diff --git a/src/main/java/com/hbm/entity/projectile/rocketbehavior/IRocketSteeringBehavior.java b/src/main/java/com/hbm/entity/projectile/rocketbehavior/IRocketSteeringBehavior.java index eb9ef1141..659bc5fa0 100644 --- a/src/main/java/com/hbm/entity/projectile/rocketbehavior/IRocketSteeringBehavior.java +++ b/src/main/java/com/hbm/entity/projectile/rocketbehavior/IRocketSteeringBehavior.java @@ -5,5 +5,5 @@ import com.hbm.entity.projectile.EntityArtilleryRocket; public interface IRocketSteeringBehavior { /** Modifies the motion to steer towards the set target. */ - public void adjustCourse(EntityArtilleryRocket rocket); + public void adjustCourse(EntityArtilleryRocket rocket, double speed, double turnSpeed); } diff --git a/src/main/java/com/hbm/entity/projectile/rocketbehavior/RocketSteeringBallisticArc.java b/src/main/java/com/hbm/entity/projectile/rocketbehavior/RocketSteeringBallisticArc.java index c6b494fb2..6a045d021 100644 --- a/src/main/java/com/hbm/entity/projectile/rocketbehavior/RocketSteeringBallisticArc.java +++ b/src/main/java/com/hbm/entity/projectile/rocketbehavior/RocketSteeringBallisticArc.java @@ -2,10 +2,86 @@ package com.hbm.entity.projectile.rocketbehavior; import com.hbm.entity.projectile.EntityArtilleryRocket; +import net.minecraft.util.Vec3; + public class RocketSteeringBallisticArc implements IRocketSteeringBehavior { @Override - public void adjustCourse(EntityArtilleryRocket rocket) { - //TBI + public void adjustCourse(EntityArtilleryRocket rocket, double speed, double maxTurn) { + + double turnSpeed = 45; + + Vec3 direction = Vec3.createVectorHelper(rocket.motionX, rocket.motionY, rocket.motionZ).normalize(); + double horizontalMomentum = Math.sqrt(rocket.motionX * rocket.motionX + rocket.motionZ * rocket.motionZ); + Vec3 targetPos = rocket.getLastTarget(); + double deltaX = targetPos.xCoord - rocket.posX; + double deltaZ = targetPos.zCoord - rocket.posZ; + double horizontalDelta = Math.sqrt(deltaX * deltaX + deltaZ * deltaZ); + double stepsRequired = horizontalDelta / horizontalMomentum; + Vec3 target = Vec3.createVectorHelper(targetPos.xCoord - rocket.posX, targetPos.yCoord - rocket.posY, targetPos.zCoord - rocket.posZ).normalize(); + + /* the entity's angles lack precision and i lack the nerve to figure out how they're oriented */ + double rocketYaw = yaw(direction); + double rocketPitch = pitch(direction); + double targetYaw = yaw(target); + double targetPitch = pitch(target); + + boolean debug = false; + + if(debug) { + System.out.println("=== INITIAL ==="); + System.out.println("Rocket Yaw: " + rocketYaw); + System.out.println("Rocket Pitch: " + rocketPitch); + System.out.println("Target Yaw: " + targetYaw); + System.out.println("Target Pitch: " + targetPitch); + } + + turnSpeed = Math.min(maxTurn, turnSpeed / stepsRequired); + + /* ...and then we just cheat */ + if(stepsRequired <= 1) { + turnSpeed = 180D; + } + + /*if(stepsRequired > 1) { + targetPitch = rocketPitch + ((targetPitch - rocketPitch) / stepsRequired); + }*/ + + if(debug) { + System.out.println("=== ADJUSTED ==="); + System.out.println("Target Pitch: " + targetPitch); + } + + /* shortest delta of α < 180° */ + double deltaYaw = ((targetYaw - rocketYaw) + 180D) % 360D - 180D; + double deltaPitch = ((targetPitch - rocketPitch) + 180D) % 360D - 180D; + + double turnYaw = Math.min(Math.abs(deltaYaw), turnSpeed) * Math.signum(deltaYaw); + double turnPitch = Math.min(Math.abs(deltaPitch), turnSpeed) * Math.signum(deltaPitch); + + if(debug) { + System.out.println("=== RESULTS ==="); + System.out.println("Delta Yaw: " + deltaYaw); + System.out.println("Delta Pitch: " + deltaPitch); + System.out.println("Turn Yaw: " + turnYaw); + System.out.println("Turn Pitch: " + turnPitch); + } + + Vec3 velocity = Vec3.createVectorHelper(speed, 0, 0); + velocity.rotateAroundZ((float) -Math.toRadians(rocketPitch + turnPitch)); + velocity.rotateAroundY((float) Math.toRadians(rocketYaw + turnYaw + 90)); + + rocket.motionX = velocity.xCoord; + rocket.motionY = velocity.yCoord; + rocket.motionZ = velocity.zCoord; + } + + private static double yaw(Vec3 vec) { + boolean pos = vec.zCoord >= 0; + return Math.toDegrees(Math.atan(vec.xCoord / vec.zCoord)) + (pos ? 180 : 0); + } + + private static double pitch(Vec3 vec) { + return Math.toDegrees(Math.atan(vec.yCoord / Math.sqrt(vec.xCoord * vec.xCoord + vec.zCoord * vec.zCoord))); } } diff --git a/src/main/java/com/hbm/entity/projectile/rocketbehavior/RocketTargetingPredictive.java b/src/main/java/com/hbm/entity/projectile/rocketbehavior/RocketTargetingPredictive.java index 76a6131b1..a5ca5e90f 100644 --- a/src/main/java/com/hbm/entity/projectile/rocketbehavior/RocketTargetingPredictive.java +++ b/src/main/java/com/hbm/entity/projectile/rocketbehavior/RocketTargetingPredictive.java @@ -3,6 +3,7 @@ package com.hbm.entity.projectile.rocketbehavior; import com.hbm.entity.projectile.EntityArtilleryRocket; import net.minecraft.entity.Entity; +import net.minecraft.util.Vec3; /** * Basic implementation of predictive targeting. @@ -23,6 +24,10 @@ public class RocketTargetingPredictive implements IRocketTargetingBehavior { @Override public void recalculateTargetPosition(EntityArtilleryRocket rocket, Entity target) { + Vec3 speed = Vec3.createVectorHelper(rocket.motionX, rocket.motionY, rocket.motionZ); + Vec3 delta = Vec3.createVectorHelper(target.posX - rocket.posX, target.posY - rocket.posY, target.posZ - rocket.posZ); + double eta = delta.lengthVector() - speed.lengthVector(); + /* initialize with the values we already know */ double motionX = target.motionX; double motionY = target.motionY; @@ -40,11 +45,16 @@ public class RocketTargetingPredictive implements IRocketTargetingBehavior { targetMotion[19][0] = target.motionX; targetMotion[19][1] = target.motionY; targetMotion[19][2] = target.motionZ; + + if(eta <= 1) { + rocket.setTarget(target.posX, target.posY - target.yOffset + target.height * 0.5D, target.posZ); + return; + } /* generate averages and predict a new position */ - double predX = target.posX + (motionX / 20D); - double predY = target.posY - target.yOffset + target.height * 0.5D + (motionY / 20D); - double predZ = target.posZ + (motionZ / 20D); + double predX = target.posX + (motionX / 20D) * eta; + double predY = target.posY - target.yOffset + target.height * 0.5D + (motionY / 20D) * eta; + double predZ = target.posZ + (motionZ / 20D) * eta; rocket.setTarget(predX, predY, predZ); } diff --git a/src/main/java/com/hbm/inventory/gui/GUITurretHIMARS.java b/src/main/java/com/hbm/inventory/gui/GUITurretHIMARS.java index eef8d19bb..1458112b9 100644 --- a/src/main/java/com/hbm/inventory/gui/GUITurretHIMARS.java +++ b/src/main/java/com/hbm/inventory/gui/GUITurretHIMARS.java @@ -1,8 +1,13 @@ package com.hbm.inventory.gui; import com.hbm.lib.RefStrings; +import com.hbm.packet.AuxButtonPacket; +import com.hbm.packet.PacketDispatcher; import com.hbm.tileentity.turret.TileEntityTurretBaseNT; +import com.hbm.tileentity.turret.TileEntityTurretHIMARS; +import com.hbm.util.I18nUtil; +import net.minecraft.client.audio.PositionedSoundRecord; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.util.ResourceLocation; @@ -14,6 +19,35 @@ public class GUITurretHIMARS extends GUITurretBase { super(invPlayer, tedf); } + @Override + public void drawScreen(int mouseX, int mouseY, float f) { + super.drawScreen(mouseX, mouseY, f); + + TileEntityTurretHIMARS arty = (TileEntityTurretHIMARS) turret; + String mode = arty.mode == arty.MODE_AUTO ? "artillery" : "manual"; + this.drawCustomInfoStat(mouseX, mouseY, guiLeft + 151, guiTop + 16, 18, 18, mouseX, mouseY, I18nUtil.resolveKeyArray("turret.arty." + mode)); + } + + @Override + protected void mouseClicked(int x, int y, int i) { + super.mouseClicked(x, y, i); + + if(guiLeft + 151 <= x && guiLeft + 151 + 18 > x && guiTop + 16 < y && guiTop + 16 + 18 >= y) { + + mc.getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F)); + PacketDispatcher.wrapper.sendToServer(new AuxButtonPacket(turret.xCoord, turret.yCoord, turret.zCoord, 0, 5)); + return; + } + } + + @Override + protected void drawGuiContainerBackgroundLayer(float p_146976_1_, int mX, int mY) { + super.drawGuiContainerBackgroundLayer(p_146976_1_, mX, mY); + + short mode = ((TileEntityTurretHIMARS)turret).mode; + if(mode == TileEntityTurretHIMARS.MODE_MANUAL) drawTexturedModalRect(guiLeft + 151, guiTop + 16, 210, 0, 18, 18); + } + @Override protected ResourceLocation getTexture() { return texture; diff --git a/src/main/java/com/hbm/items/tool/ItemDesignatorArtyRange.java b/src/main/java/com/hbm/items/tool/ItemDesignatorArtyRange.java index b69f70150..fed462479 100644 --- a/src/main/java/com/hbm/items/tool/ItemDesignatorArtyRange.java +++ b/src/main/java/com/hbm/items/tool/ItemDesignatorArtyRange.java @@ -2,10 +2,9 @@ package com.hbm.items.tool; import java.util.List; -import com.hbm.blocks.ModBlocks; -import com.hbm.blocks.turret.TurretArty; +import com.hbm.blocks.BlockDummyable; import com.hbm.lib.Library; -import com.hbm.tileentity.turret.TileEntityTurretArty; +import com.hbm.tileentity.turret.TileEntityTurretBaseArtillery; import net.minecraft.block.Block; import net.minecraft.entity.player.EntityPlayer; @@ -38,15 +37,15 @@ public class ItemDesignatorArtyRange extends Item { Block b = world.getBlock(x, y, z); - if(b == ModBlocks.turret_arty) { - int pos[] = ((TurretArty) b).findCore(world, x, y, z); + if(b instanceof BlockDummyable) { + int pos[] = ((BlockDummyable) b).findCore(world, x, y, z); if(pos == null) return false; TileEntity te = world.getTileEntity(pos[0], pos[1], pos[2]); - if(te instanceof TileEntityTurretArty) { + if(te instanceof TileEntityTurretBaseArtillery) { if(world.isRemote) return true; @@ -79,12 +78,10 @@ public class ItemDesignatorArtyRange extends Item { if(!world.isRemote) { TileEntity te = world.getTileEntity(stack.stackTagCompound.getInteger("x"), stack.stackTagCompound.getInteger("y"), stack.stackTagCompound.getInteger("z")); - if(te instanceof TileEntityTurretArty) { - TileEntityTurretArty arty = (TileEntityTurretArty) te; - if(arty.mode == arty.MODE_MANUAL) { - arty.enqueueTarget(x + 0.5, y + 0.5, z + 0.5); - world.playSoundAtEntity(player, "hbm:item.techBoop", 1.0F, 1.0F); - } + if(te instanceof TileEntityTurretBaseArtillery) { + TileEntityTurretBaseArtillery arty = (TileEntityTurretBaseArtillery) te; + arty.enqueueTarget(x + 0.5, y + 0.5, z + 0.5); + world.playSoundAtEntity(player, "hbm:item.techBoop", 1.0F, 1.0F); } } diff --git a/src/main/java/com/hbm/items/weapon/ItemAmmoHIMARS.java b/src/main/java/com/hbm/items/weapon/ItemAmmoHIMARS.java index 4ef729c71..4809a7c80 100644 --- a/src/main/java/com/hbm/items/weapon/ItemAmmoHIMARS.java +++ b/src/main/java/com/hbm/items/weapon/ItemAmmoHIMARS.java @@ -42,14 +42,21 @@ public class ItemAmmoHIMARS extends Item { list.add(new ItemStack(item, 1, LARGE)); } + @Override + public String getUnlocalizedName(ItemStack stack) { + return "item.ammo_himars_" + itemTypes[Math.abs(stack.getItemDamage()) % itemTypes.length].name; + } + public abstract class HIMARSRocket { + public final String name; public final ResourceLocation texture; public final int amount; public final int modelType; /* 0 = sixfold/standard ; 1 = single */ - public HIMARSRocket(String name, int type, int amount) { - this.texture = new ResourceLocation(RefStrings.MODID + ":textures/models/projectiles/" + name + ".png"); + public HIMARSRocket(String name, String texture, int type, int amount) { + this.name = name; + this.texture = new ResourceLocation(RefStrings.MODID + ":textures/models/projectiles/" + texture + ".png"); this.amount = amount; this.modelType = type; } @@ -75,7 +82,7 @@ public class ItemAmmoHIMARS extends Item { private void init() { /* STANDARD ROCKETS */ - this.itemTypes[SMALL] = new HIMARSRocket("himars_standard", 0, 6) { public void onImpact(EntityArtilleryRocket rocket, MovingObjectPosition mop) { standardExplosion(rocket, mop, 25F, 3F, true); }}; - this.itemTypes[LARGE] = new HIMARSRocket("himars_single", 1, 1) { public void onImpact(EntityArtilleryRocket rocket, MovingObjectPosition mop) { standardExplosion(rocket, mop, 50F, 5F, true); }}; + this.itemTypes[SMALL] = new HIMARSRocket("standard", "himars_standard", 0, 6) { public void onImpact(EntityArtilleryRocket rocket, MovingObjectPosition mop) { standardExplosion(rocket, mop, 25F, 3F, true); }}; + this.itemTypes[LARGE] = new HIMARSRocket("single", "himars_single", 1, 1) { public void onImpact(EntityArtilleryRocket rocket, MovingObjectPosition mop) { standardExplosion(rocket, mop, 50F, 5F, true); }}; } } diff --git a/src/main/java/com/hbm/render/entity/projectile/RenderArtilleryRocket.java b/src/main/java/com/hbm/render/entity/projectile/RenderArtilleryRocket.java index 229a71e46..0b4b3d2d4 100644 --- a/src/main/java/com/hbm/render/entity/projectile/RenderArtilleryRocket.java +++ b/src/main/java/com/hbm/render/entity/projectile/RenderArtilleryRocket.java @@ -2,7 +2,9 @@ package com.hbm.render.entity.projectile; import org.lwjgl.opengl.GL11; +import com.hbm.entity.projectile.EntityArtilleryRocket; import com.hbm.items.weapon.ItemAmmoHIMARS; +import com.hbm.items.weapon.ItemAmmoHIMARS.HIMARSRocket; import com.hbm.main.ResourceManager; import net.minecraft.client.renderer.entity.Render; @@ -12,20 +14,25 @@ import net.minecraft.util.ResourceLocation; public class RenderArtilleryRocket extends Render { @Override - public void doRender(Entity shell, double x, double y, double z, float f0, float f1) { + public void doRender(Entity entity, double x, double y, double z, float f0, float f1) { GL11.glPushMatrix(); GL11.glTranslated(x, y, z); - GL11.glRotatef(shell.prevRotationYaw + (shell.rotationYaw - shell.prevRotationYaw) * f1 - 90.0F, 0.0F, 1.0F, 0.0F); - GL11.glRotatef(shell.prevRotationPitch + (shell.rotationPitch - shell.prevRotationPitch) * f1 - 90, 0.0F, 0.0F, 1.0F); + GL11.glRotatef(entity.prevRotationYaw + (entity.rotationYaw - entity.prevRotationYaw) * f1 - 90.0F, 0.0F, 1.0F, 0.0F); + GL11.glRotatef(entity.prevRotationPitch + (entity.rotationPitch - entity.prevRotationPitch) * f1 - 90, 0.0F, 0.0F, 1.0F); + GL11.glRotated(90, 0, 1, 0); + GL11.glRotated(90, 1, 0, 0); - this.bindEntityTexture(shell); + this.bindEntityTexture(entity); boolean fog = GL11.glIsEnabled(GL11.GL_FOG); if(fog) GL11.glDisable(GL11.GL_FOG); GL11.glShadeModel(GL11.GL_SMOOTH); - ResourceManager.turret_himars.renderPart("RocketStandard"); + EntityArtilleryRocket rocket = (EntityArtilleryRocket) entity; + HIMARSRocket type = rocket.getType(); + if(type.modelType == 0) ResourceManager.turret_himars.renderPart("RocketStandard"); + if(type.modelType == 1) ResourceManager.turret_himars.renderPart("RocketSingle"); GL11.glShadeModel(GL11.GL_FLAT); if(fog) GL11.glEnable(GL11.GL_FOG); @@ -34,6 +41,7 @@ public class RenderArtilleryRocket extends Render { @Override protected ResourceLocation getEntityTexture(Entity entity) { - return ItemAmmoHIMARS.itemTypes[0].texture; + EntityArtilleryRocket rocket = (EntityArtilleryRocket) entity; + return ItemAmmoHIMARS.itemTypes[rocket.getDataWatcher().getWatchableObjectInt(10)].texture; } } diff --git a/src/main/java/com/hbm/render/item/ItemRenderLibrary.java b/src/main/java/com/hbm/render/item/ItemRenderLibrary.java index 15378e952..56032d009 100644 --- a/src/main/java/com/hbm/render/item/ItemRenderLibrary.java +++ b/src/main/java/com/hbm/render/item/ItemRenderLibrary.java @@ -1360,10 +1360,12 @@ public class ItemRenderLibrary { renderers.put(ModItems.ammo_himars, new ItemRenderBase( ) { public void renderInventory() { - GL11.glTranslated(0, 2.5, 0); + GL11.glTranslated(0, -2.5, 0); GL11.glScaled(4, 4, 4); + GL11.glRotated(System.currentTimeMillis() % 3600 / 10D, 0, 1, 0); } public void renderCommonWithStack(ItemStack item) { + GL11.glTranslated(0, 1.5, 0); GL11.glRotated(-45, 0, 1, 0); GL11.glRotated(90, 1, 0, 0); HIMARSRocket type = ItemAmmoHIMARS.itemTypes[item.getItemDamage()]; diff --git a/src/main/java/com/hbm/render/tileentity/RenderTurretHIMARS.java b/src/main/java/com/hbm/render/tileentity/RenderTurretHIMARS.java index e5215764c..8acb27dac 100644 --- a/src/main/java/com/hbm/render/tileentity/RenderTurretHIMARS.java +++ b/src/main/java/com/hbm/render/tileentity/RenderTurretHIMARS.java @@ -3,6 +3,8 @@ package com.hbm.render.tileentity; import org.lwjgl.opengl.GL11; import com.hbm.blocks.ModBlocks; +import com.hbm.items.weapon.ItemAmmoHIMARS; +import com.hbm.items.weapon.ItemAmmoHIMARS.HIMARSRocket; import com.hbm.main.ResourceManager; import com.hbm.render.item.ItemRenderBase; import com.hbm.tileentity.turret.TileEntityTurretHIMARS; @@ -41,12 +43,33 @@ public class RenderTurretHIMARS extends TileEntitySpecialRenderer implements IIt GL11.glRotated(pitch, 1, 0, 0); GL11.glTranslated(0, -2.25, -2); ResourceManager.turret_himars.renderPart("Launcher"); + + double barrel = turret.lastCrane + (turret.crane - turret.lastCrane) * interp; + double length = -5D; + GL11.glTranslated(0, 0, barrel * length); ResourceManager.turret_himars.renderPart("Crane"); - bindTexture(ResourceManager.himars_standard_tex); - ResourceManager.turret_himars.renderPart("TubeStandard"); - /*ResourceManager.turret_himars.renderPart("CapStandard1"); - ResourceManager.turret_himars.renderPart("CapStandard2"); - ResourceManager.turret_himars.renderPart("CapStandard4");*/ + + if(turret.typeLoaded >= 0) { + HIMARSRocket type = ItemAmmoHIMARS.itemTypes[turret.typeLoaded]; + + if(type.modelType == 0) { + bindTexture(ResourceManager.himars_standard_tex); + ResourceManager.turret_himars.renderPart("TubeStandard"); + + for(int i = 0; i < turret.ammo; i++) { + ResourceManager.turret_himars.renderPart("CapStandard" + (5 - i + 1)); + } + } + + if(type.modelType == 1) { + bindTexture(ResourceManager.himars_single_tex); + ResourceManager.turret_himars.renderPart("TubeSingle"); + + if(turret.hasAmmo()) { + ResourceManager.turret_himars.renderPart("CapSingle"); + } + } + } GL11.glShadeModel(GL11.GL_FLAT); GL11.glPopMatrix(); @@ -67,6 +90,7 @@ public class RenderTurretHIMARS extends TileEntitySpecialRenderer implements IIt public void renderCommonWithStack(ItemStack item) { GL11.glRotatef(-90, 0F, 1F, 0F); GL11.glScaled(0.5, 0.5, 0.5); + GL11.glShadeModel(GL11.GL_SMOOTH); bindTexture(ResourceManager.turret_arty_tex); ResourceManager.turret_arty.renderPart("Base"); bindTexture(ResourceManager.turret_himars_tex); @@ -75,6 +99,7 @@ public class RenderTurretHIMARS extends TileEntitySpecialRenderer implements IIt ResourceManager.turret_himars.renderPart("Crane"); bindTexture(ResourceManager.himars_standard_tex); ResourceManager.turret_himars.renderPart("TubeStandard"); + GL11.glShadeModel(GL11.GL_FLAT); }}; } } diff --git a/src/main/java/com/hbm/tileentity/machine/TileEntityCrucible.java b/src/main/java/com/hbm/tileentity/machine/TileEntityCrucible.java index 071b5bc5b..2f6a15989 100644 --- a/src/main/java/com/hbm/tileentity/machine/TileEntityCrucible.java +++ b/src/main/java/com/hbm/tileentity/machine/TileEntityCrucible.java @@ -356,6 +356,12 @@ public class TileEntityCrucible extends TileEntityMachineBase implements IGUIPro //if no recipe is loaded, everything will land in the waste stack int recipeInputRequired = recipe != null ? getQuantaFromType(recipe.input, mat.material) : 0; + //this allows pouring the ouput material back into the crucible + if(recipe != null && getQuantaFromType(recipe.output, mat.material) > 0) { + recipeAmount += mat.amount; + continue; + } + if(recipeInputRequired == 0) { //if this type isn't required by the recipe, add it to the waste stack wasteAmount += mat.amount; diff --git a/src/main/java/com/hbm/tileentity/turret/TileEntityTurretHIMARS.java b/src/main/java/com/hbm/tileentity/turret/TileEntityTurretHIMARS.java index 1bb2d49c9..58c0afe31 100644 --- a/src/main/java/com/hbm/tileentity/turret/TileEntityTurretHIMARS.java +++ b/src/main/java/com/hbm/tileentity/turret/TileEntityTurretHIMARS.java @@ -3,9 +3,12 @@ package com.hbm.tileentity.turret; import java.util.ArrayList; import java.util.List; +import com.hbm.entity.projectile.EntityArtilleryRocket; import com.hbm.inventory.container.ContainerTurretBase; import com.hbm.inventory.gui.GUITurretHIMARS; import com.hbm.items.ModItems; +import com.hbm.items.weapon.ItemAmmoHIMARS; +import com.hbm.items.weapon.ItemAmmoHIMARS.HIMARSRocket; import com.hbm.lib.Library; import com.hbm.main.MainRegistry; import com.hbm.tileentity.IGUIProvider; @@ -17,15 +20,21 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.Container; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.MathHelper; import net.minecraft.util.Vec3; import net.minecraft.world.World; public class TileEntityTurretHIMARS extends TileEntityTurretBaseArtillery implements IGUIProvider { public short mode = 0; - public static final short MODE_AUOT = 0; + public static final short MODE_AUTO = 0; public static final short MODE_MANUAL = 1; + public int typeLoaded = -1; + public int ammo = 0; + public float crane; + public float lastCrane; + @Override @SideOnly(Side.CLIENT) public List getAmmoTypesForDisplay() { @@ -36,7 +45,7 @@ public class TileEntityTurretHIMARS extends TileEntityTurretBaseArtillery implem ammoStacks = new ArrayList(); List list = new ArrayList(); - ModItems.ammo_arty.getSubItems(ModItems.ammo_arty, MainRegistry.weaponTab, list); + ModItems.ammo_himars.getSubItems(ModItems.ammo_himars, MainRegistry.weaponTab, list); this.ammoStacks.addAll(list); return ammoStacks; @@ -109,9 +118,24 @@ public class TileEntityTurretHIMARS extends TileEntityTurretBaseArtillery implem this.turnTowardsAngle(targetPitch, targetYaw); } + public int getSpareRocket() { + + for(int i = 1; i < 10; i++) { + if(slots[i] != null) { + if(slots[i].getItem() == ModItems.ammo_himars) { + return slots[i].getItemDamage(); + } + } + } + + return -1; + } + @Override public void updateEntity() { + this.lastCrane = this.crane; + if(this.mode == this.MODE_MANUAL) { if(!this.targetQueue.isEmpty()) { this.tPos = this.targetQueue.get(0); @@ -156,8 +180,39 @@ public class TileEntityTurretHIMARS extends TileEntityTurretBaseArtillery implem if(isOn() && hasPower()) { - if(tPos != null) - this.alignTurret(); + if(!this.hasAmmo() || this.crane > 0) { + + this.turnTowardsAngle(0, this.rotationYaw); + + if(this.aligned) { + + if(this.hasAmmo()) { + this.crane -= 0.0125F; + } else { + this.crane += 0.0125F; + + if(this.crane >= 1F && !worldObj.isRemote) { + int available = this.getSpareRocket(); + + if(available != -1) { + HIMARSRocket type = ItemAmmoHIMARS.itemTypes[available]; + this.typeLoaded = available; + this.ammo = type.amount; + this.conusmeAmmo(ModItems.ammo_himars); + } + } + } + } + + this.crane = MathHelper.clamp_float(this.crane, 0F, 1F); + + } else { + + if(tPos != null) { + this.alignTurret(); + } + } + } else { this.target = null; @@ -187,7 +242,7 @@ public class TileEntityTurretHIMARS extends TileEntityTurretBaseArtillery implem searchTimer = 0; } - if(this.aligned) { + if(this.aligned && crane <= 0) { this.updateFiringTick(); } @@ -213,9 +268,70 @@ public class TileEntityTurretHIMARS extends TileEntityTurretBaseArtillery implem } } + @Override + protected NBTTagCompound writePacket() { + NBTTagCompound data = super.writePacket(); + data.setShort("mode", this.mode); + data.setInteger("type", this.typeLoaded); + data.setInteger("ammo", this.ammo); + return data; + } + + @Override + public void networkUnpack(NBTTagCompound nbt) { + super.networkUnpack(nbt); + this.mode = nbt.getShort("mode"); + this.typeLoaded = nbt.getShort("type"); + this.ammo = nbt.getInteger("ammo"); + } + + public boolean hasAmmo() { + return this.typeLoaded >= 0 && this.ammo > 0; + } + + int timer; + @Override public void updateFiringTick() { - // *chirp* *chirp* *chirp* + + timer++; + + int delay = 40; + + if(timer % delay == 0) { + + if(this.hasAmmo() && this.tPos != null) { + this.spawnShell(this.typeLoaded); + this.ammo--; + this.worldObj.playSoundEffect(xCoord, yCoord, zCoord, "hbm:turret.jeremy_fire", 25.0F, 1.0F); + } + + if(this.mode == this.MODE_MANUAL && !this.targetQueue.isEmpty()) { + this.targetQueue.remove(0); + this.tPos = null; + } + } + } + + public void spawnShell(int type) { + + Vec3 pos = this.getTurretPos(); + Vec3 vec = Vec3.createVectorHelper(this.getBarrelLength(), 0, 0); + vec.rotateAroundZ((float) -this.rotationPitch); + vec.rotateAroundY((float) -(this.rotationYaw + Math.PI * 0.5)); + + EntityArtilleryRocket proj = new EntityArtilleryRocket(worldObj); + proj.setPositionAndRotation(pos.xCoord + vec.xCoord, pos.yCoord + vec.yCoord, pos.zCoord + vec.zCoord, 0.0F, 0.0F); + proj.setThrowableHeading(vec.xCoord, vec.yCoord, vec.zCoord, 25F, 0.0F); + + if(this.target != null) + proj.setTarget(this.target); + else + proj.setTarget(tPos.xCoord, tPos.yCoord, tPos.zCoord); + + proj.setType(type); + + worldObj.spawnEntityInWorld(proj); } @Override diff --git a/src/main/resources/assets/hbm/textures/models/projectiles/himars_single.png b/src/main/resources/assets/hbm/textures/models/projectiles/himars_single.png index 2f97a307d8c8c4d821f438e5f75ee7400f5e7ee9..0e5dee8d7354df6ecf42b81b283e2453e0e53e0c 100644 GIT binary patch literal 1570 zcmdUv|2q>19LI;X+uD}0yC&;F`S!RI)!BwvVahTrt<>`MBDyi*l!n#F*D^KNeA^Ne z#e9ikxnbg5X<1TZ51|~|&0_Vl!l!Lhu6|Y^$LJ`e;`KSENK7lsxa#}>c?!*nR2F@ZY`6NpRj2Si zYQ2(fu&dX^N1-V@aLJT1HuXti3?vGz6CDLgJc zObKlDc0pds$mm*bOW7T-?idThF?2afr7~?9Ai2_BI>?pTEWJXLAlzGZ2`FXISSQfD zwb0<#L*;tE3cd991@b_ydTniOkSGAMA2~9KaLIA-#G;W@DxEKK8gXMfJgM1G9jy`I zE>t}7AsZ1WCE{xIl0MSPKjO+OYmKjT*@)Fy>KJFyqJYs8{+H7K$NC~iJ7##d;la=a zhPijo)ReNHI&9`5Tu0T}K>(Jl&Dq9>^3g5z)*PNNT7MH*jv{NtsS$LNC(bzkCq?ME z%<_B>bR4YbyYP(TOdl^AUDuVw44XX*S_hu=fVRECF@5YQM&UgSf1CEdA%X*qes_P4 z=!+#9uJ8&^7@-cU5JGNks3HiiUk;2oM%G(Ollx|4dnqM{-imXEJAw5e%ng|h3EdK@ z&s@)h^HU&X{dA+>bQN`au$SZH;!shI+$-ZfhKQc<$Vn}K6uI3NecE>nZtc3db!csV zYBBrHhE|RMeEGp(j`TE%MzhmYiJJRECSR(ebX{8}*X4TvtMYiUDPV8g8L{M(=yI?Q zh?q}+tsdi#L7TDW4*Qc0iY!u=wDMw}3HG@LH{muvEy}%QZXW5ogBnrDe0}fX8X8y^Vf8hZckBCBLD*sl62za&Lk1Fco0Vz(8~9rP0ue z!irFB`C1oS|6=aGA@=$BZhW#6*HUcBQpNcMy_|O`5Qi=&Vf2n0Pp1?sLuyo1c!8F zM#zK1hUoc>?jd-eoE8d2W%V`Vw~sbOw>68nv$#YxkhyPLwTE=#GdfhwK(?=C{{IQuZ_*%~38!#=oc`$p2tEP$Ca;Lhe*r#(yk7tS literal 924 zcmeAS@N?(olHy`uVBq!ia0vp^0YJQmgAGW&pRO4Kq*#ibJVQ8upoSx*gMooL*we)^ zq+-t7yM{%#93;eM zQqo@iU9VoZFMCzBXc?c5PbqH+h@8HmI3aP>ZuVp6%Ko08zF2Hq{I$T_+t2TlyYb)B z;{W;8FBn#5C%kg1;BQ%#o$y9RVh;1?RqGmq&y*SNVRyP}YEaet{`SM>A4fMVpU=GV zm6Swu$Bpo}itif(N_m&`c3o|qQf;mL{U1oin*EZvELg(F_Lv1o^99Qd2I!e<1Cwepn`L_wSW^pHL080mGjcUamh}!T(0`<+qgw zA8&1(u-b6lu_vjBaJcZ5AzuE~^&Pfz3VCa$3FKRx|2Tbv;yhEk1KW@PV@wVbRP5va zF!NCMtQDX4$}M>KcY5-fh0>>z5C#DKzfb%5?xM<{>*l;a^%QKh-b?T%f9jzz R1(@9!JYD@<);T3K0RYu^ltBOh diff --git a/src/main/resources/assets/hbm/textures/models/projectiles/himars_standard.png b/src/main/resources/assets/hbm/textures/models/projectiles/himars_standard.png index 79604841b1ddc34f0073ade29ba6080ce6d110d1..54a00f7df2898216a50cc251985fba61fe97c54d 100644 GIT binary patch literal 909 zcmeAS@N?(olHy`uVBq!ia0vp^0YDtW!3HEn8#e3)QY^(zo*^7SP{WbZ!N9=m;_2cT zQZeW49K-C}4kE|@UphHW)jX49$py>q{SJ1nZR;e1xaKY}v&x(H> z%;($l=EO6w_r5H=Tl&CCG~&>K>FZGmAfe-=2FvchAq1JJ+X8{rQS@ z^0eiXE^j`4^54aW|4u!hsg;*@*}`_W%ks-Jb24XsU%2>P+UTY!g#{$eN^mFIDN@V$$mV@aGnl_wUnFr-r0Tw(=+5txDK^b;{eY z;C0pef*RR-S8n)Hcg-xssPEtI=N3+0U9dBXr3(rqJh&%;)EO#4I<@ia#al zSjDTba&x%rZ@Gw&3z6lDuGNjZ7VmYqoBc4ldF~|PwTCu~zxV81ou|X2w6|`>lcd}I ztGsUM3cqdN@J{DVBmc(bD-H$OKDRB=Nw9jZYO8EtFLT(>u!$-9?C=x4ao0)Y=;UNYGoJ;kZk5KxIVpX7zMk(*7V9*D?Ta?_ zNx15#-<`_*jP>h&y~vIG{@*xOVBsRbVtjj9QBBR7HO@D-*>1j?Wvcgd#Rdl@oe7Xo zA(?sNQ2Nt%)zia%{SEzme&5RRHdPRuB~PXkisfY;=hrgZY#FmdKI;Vst063ePKmY&$ delta 502 zcmeBWpU*PExSsKvr;B4q#hkadZT${A2(&#ky~Vs_@mWEozyi*9Pd@q9(&bOCO z2`c`-UEwd!pD&^*E0_I$9Ee9h36T6mB+JCRkE?ec|=(5R;hJ>EE?a zVRu@bx%p;Jp4HUy-Lh7NbET~Xj2uodEVTJ^CVFky)~rYsh=3w<3lC5x<5RWL+rvNg z3wB@s_!MaW*>}r=T;}jtD5Z(7%TGMP096Jw;G}=@qmQ>APgg#_|MEKFgjK4jk~@s@!}hx{ff&CfKFrMuxwQX z+poz3R^gX;f)D79A};4Lg_MR1`7hMI1jm2l|MvXZb3?E1{=L_%@8pBD_0~y$7JJiB z^8ZT4UXaQO!8bHxrZYPEJxuuh7Hs^6;2nC*EprYX{B|2;wO``$f>@As&2P6e1D$2Z YP|;Jf?W|=(3zq^<1twe>72m=5hYGZBwCjjta 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 b&Rsr@#JtStOkv!wA7>j2dvmhs^?&{cB{c{h literal 4074 zcmXw+c|276|Hlv6vdhlcrb2eIZ&^lCwz-HfNXkxwu}e~jaHX_d9=_^Ld=}UO(scd4A4$ZfkAM!79QE004)@4ai>rK<7gn2e2^F z&gSQ2BWM?fP!kJ#7TOWZ;*|mbT%{HeWBbSgQejUTXhe8$DUV1j%7cB>H2@WqC$5|V zty!QVF%|{*^FW3RHxiU5n1-H-5`-!~GToAdr;)sLgr_~K-9Rh@tei?+?_D1_Jq@qA zRxP&GQ=d;LA~tT+ZfvhHCkL5N!8DNd-bGPI^&32NDjrztj7jOCB{Vg%!cPGh)~;d( z9{Vn0*!;yJ?kzd!UjQ_P--^?!eH{C(r0-h-FB3|w#UK%HVAy#LFI*nDLe#ZttVZ?i z2&QO5An?C-@)6R}4VM>IR!+ludcgsrHZ$MH@(M%oo40-+0v;_X_c~MM#5!_qUUNMV zL}hY?iB8Q3HaM0%NoPL_>6Am@7Cjd~d89VQ#g@$1U@#ARo>Y~jo&x}d%4`P^*N*;p z+zD}pqLIk}0EF^vdMn8OXY;q7$Gh!^WeZxEqYe4GI_|BtbRr#Kn{Ry`WYdf3l^T|a zr-HVH0i2%hW(lISi*xQk7X~~w)Rn^Z9?$WDql;BTnH16M$q|cP>+WaAnGnc+<{3z1 z!?D2|S-MT_+QHz}k}!Ma#kt~izz^b6BVO2jEEl7(_{n~RXnZl|RE>8#*bu#>o&9Fh z$!aF9+*;uA_X*(y>FD@-wtP$bzjN}OMzuKKen7md+QdL**xsx7D&VBA;%QF^RLFI9 z0Qc+yGb~~!0lYfqn4!3un|S7i%ziWR`w<<`Uv-ry+36Njc$*cHLT(S6K6ei0e4S|b4nOSSgwHe84yDFY-P3h;lram2U?Y#*< zPE5DcmJtAwj4m=S+!kis6$N@1rO zdNf2aL8)sz&}Drwf_9r`$Loay0FNu8oFekmntibVVEOXA`a$$81DOs`NjH7UAE1`Y z1w7_(wF8UASZxvPI!UKi2lc5^yAj&Yjg+ILLAV081&g$akl)ewG%=;6&NlbX8H z8h!V9YI5un|h`@N6C&qk9-K%fH+V8JlGW4U8nW4UeSC( zA*j;K+8D6_hgK<|dwqY^958$j2~Gg)R-~ zFp};gaGAwqk`S+tsH+Ez;!QwaK*XE|oXg;!P37mzztPARuzY%YFn)G{XQHo< z4(95L>+9=7YDP5*B6~VIYVIO2{@oO(>yQYuRmD)=-1@1#>c>MpeSJ&VOeL92 zE-bc1h=t!t1?_!)tA$r#CuZ9i`i2TU(~mi@B+48vE-ns^=j7z16aMMI(4d7Jx?Owp zs+Unz(1%wEr%9P%7;}$;3`uvP!CLM0Cz=r*>E7c->Eim4B`2oVNR_MePf2Klj&q;5s|+oY1W;M7ogvDNpR7wuEp*cn9FGkSUqSoaqB6jPF^sTP=hVq9X}ID5Is z67QETJtY=bmxzKZXP~fC;qk*IbimE#K;;Y!T>?{&u{ZMi+(8I`GEvvZPB@a^{y`_& zzqVPB8PJnOL#u0@%L8I`HX_MCt$t9{#WQ`?MJBw|N=Lj*GFEW3!9>m16O6jm6^je0 zE4eQ(m0q%twX$<=oOwO8*T*aoaQsep*%lU6N)^rwi8qr-G1Cf^!j(ki)=tk(W0Y2Q za|c&cbuvux#8;ppsTsrw0+w$nO-pmD(7VUY?&T%EB&lb2Z@@uzd}e$@s`WFQ&L*EF zmt~W7^nSI=9InBPN{p3Nq4lLV3(U)*(qj#qFV~`0qr^KK-kwLmn8nO^Xx_%$w%iPP z9*r~pH|;j@nSFn{Hj6+evBDqBY@wAtKe(DdzNY^6^DqJy0!kiSflFf@81l^sWPMso zA5<59{HL??fa zfe)uFclY)dKW|;pu4idYS+ANKNQ-;Jt%Yfe{~de6s7Ns;7|T19F4W(XHf^3xXaTIV}tAqgu^F`)}Svh&3V3&WIb2K?eM&R!mC%0AFnu9NNcBi zFq#sBkX*)b(ICpytaQf#d3`EQze(`lZW}lAr!J1G*+|g>A=2nzbkiR+>+WHn2`NznoI5%IL{aa*n1ab=UYY#9)=| z#@X?4EbYsWSY3Ruq4O8Y{EuxDpzVB>RyD0h$o}b-Zf0iYFgQGmYCGILlSbIX$J^N0 zkQ#ehQd+aiuV?H}FAV9eb&<&Dn%MqC4qlpyz-k?gF0A`cnlCjQpp8D#0YVD@mC^Kf zi_+{H!;i_$Rla^FekO->Ek?@RJ}N%>55^q6GdJ(`J9SComUHh1WGZpP0gu16|7mQ* zuHFRc+1aQ3x=D^EM)3cKmQ=xL6==2KS3#Rnf#`~T=aY8UMc1;~E4kede=voD>e z!2rs4-zAc;QKmykDNFgF^7Y6o7$;8Cxvx@A-KU`~#h-+umliKC@#>G0eRK<*yOi5x zvB%$>gy+lIARyrJWQFfd8jZLa=1lJe#MV-?lPwDf?sNbOWXpP}}%B zy6}A44t1`)F@W&CctD5UbT952?|OH2bYggdQKGe-#w}E7@$YD>yF!zG$WLI-MF<^a}lNpBUwgz|~G4eCCH`pTw0M zFom7KObKN9U7*e{a+a!NvUWjaL;R&xN?6Qa(;1i zPHk*BdwP?i#C0<8e&@w0m($38NV&_{k~m2mKdw2re{KYtYVb6facx2&S0SHN0-;e{#|H7EXwZsH-Bv1L6U!vdt-NHd*XJ^L(mLz2g^RKnQBc=sTpq^ zw|6}ScBMV>m=3OnP&cKVIgY+s?7!19^f>Bpc>)<<8t6U9O$-U6ZG*6f(x2%?hE&a< zuXKEsM(&xzZoi8f#a*&!xncG$^dRn9mz0ynbkl-xzcB^0Fb^e6MmVk$+(2dzry$%6 zqPOY|#xKqww4S0Hwhm`pO7WC zDcjNf7r9hrimanJe|$1V5M+g45( z9;>XQU8x+!g&rOEAA0~)-YafsCp%0t|GHNcu5X)UP$DOIXoskJTR|Ki1s@SSeRKu8caN%{146V B___c9