Jump to content

IceMetalPunk

Members
  • Posts

    402
  • Joined

  • Last visited

Posts posted by IceMetalPunk

  1. Version 1.1 Released

     

    Change Log

    -Fixed a major bug with ore generation that caused Weatherstone ores to only generate close to (0,0) and in mixed groups. Worlds generated in 1.1 and later will correctly generate Weatherstone ore uniformly over the entire world, and only one vein and one type of Weatherstone ore per chunk.

     

    Video!

     

     

    (Can someone please explain how to embed a YouTube video into a post on these forums?)

  2. WeatherWorks is a mod based around the idea of making nature (and of course, weather) a useful, and sometimes annoying, part of playing Minecraft. In vanilla, the weather does nothing except increase spawns. Yawn! Well now, it can do so much more!

     

    Download link: https://dl.dropboxusercontent.com/u/11662651/Minecraft/Mods/WeatherWorks-1.1.zip

     

    WeatherWorks adds quite a few things, so it's best if I explain them in list form :) .

     

    *UPDATE* Or, just watch this explanation video to learn all about it :)

     

     

    (Can someone please explain how to embed a YouTube video into a post on these forums?)

     

    Weatherstone

     

    Ores

    Weatherstone ores generate below Y=16. You'll know a weatherstone ore when you see it: the embedded minerals have a glow. Mining a Weatherstone ore of any type will drop 1-3 Weatherstone shards, regardless of any fortune on your pick. The elements don't care for your greed :P If you Silk Touch a Weatherstone ore, you can smelt it, but you'll only get 1 shard in return.

     

    Shards

    Crafting 9 Weatherstone shards together will give you the corresponding Weatherstone block. That's...about it for these ingredient items.

     

    Sunstone

    A Sunstone block will light up when supplied with redstone power. But unlike a redstone lamp, Sunstone's light level depends on the strength of the signal you give it. So a signal strength of 3 produces a light level of 3, while a full 15 signal gives a full 15 light level. Now you have perfect mood lighting for any base! You could, if you want, connect it to a daylight sensor for an indoor "skylight", or even invert the daylight sensor's signal with a comparator to have your lights fade on as it gets dark :)

     

    Cloudstone

    Any entities which fall onto Cloudstone will take absolutely no fall damage, regardless of how far they fell or any armor they had. Kind of like water, but without the liquid-flowing-drowning-redstone-breaking-sinking-swimming mess. Also, Cloudstone is *very* soft, so you can instant-mine it with your bare hands.

     

    Thunderstone

    When Thunderstone has no redstone power, it's just a normal block. But power it, and it becomes electrified! Any entities touching an electrified Thunderstone block will take 1.5 hearts of damage per cycle, with 2 cycles per second--that's 3 hearts of damage per second, for those who hate math! It's like super-cactus with the ability to turn it off at any time :) Note that it harms *all* entities that touch it while it's active...that includes item entities. So be careful not to drop items onto it while it's active or it'll destroy them!

     

    Rainstone

    A Rainstone block will speed up the random tick rate in a small volume around it. That means crops in range will grow faster. It will also hydrate your farmland without needing water, and put out fires in range, both on blocks and on entities. (Zombies and Skeletons in the sun will immediately catch fire again, though, faster than this can put them out.) Water is quite fragile, though, being a liquid and all, so if you don't silk touch Rainstone, you'll lose it!

     

    Earthstone

    Ashes to ashes, dust to dust...the earth decays all things. Earthstone amplifies the decaying power of the earth itself. It will, at random intervals, decay certain mobs within a 3 block radius, according to the following rules:

     

    Witches in the radius decay into zombies.

    Villagers decay into zombie villagers.

    Zombies of all kinds decay into skeletons.

    Horses decay into zombie horses.

    Zombie horses decay into skeleton horses.

    Large and medium slimes and magma cubes decay into smaller slimes and magma cubes.

    Small slimes and magma cubes are unchanged.

     

    Once an Earthstone decays a mob, it won't decay the same mob again, unless another Earthstone decays it. In other words, if it was the last Earthstone to decay a mob, it won't do it again. This is based on the position of the block, not the block itself, so breaking it and replacing it in the same location won't change that. You'll have to move it or lead the mobs near a different Earthstone :) Also note that the decayed mobs are *different* mobs, so they'll lose any special properties they had, like names or custom armor. Be aware of that.

     

    Additionally, since this decays on random block ticks, you can place an Earthstone in the range of a Rainstone to speed up the decay intervals as well :)

     

    Moonstone

    Moonstone ore looks an awful lot like coal ore, but look closely and you'll see it has a slight glow around the embedded Moonstone shards. A Moonstone block radiates with a dark energy that hostile mobs find irresistible. Any hostile mobs within a 10 block radius of Moonstone will pathfind their way towards it, pulled in by its allure. This works for all vanilla hostile mobs except boss mobs, and it should work with most other mods' hostile mobs, as long as they use the most recent AI system as of 1.7.10. Just imagine making a mob farm where you place a Moonstone beyond some trap doors, and just watch all the mobs drop single-file to their deaths! :)

     

    Weather--it matters!

     

    Wind

    Wind is a new weather event that occurs often, at random intervals, during rain. Any entities that are outside in the rain (that means with no blocks above their head) will be pushed in a random direction with a random force for about a second per gust. You'll know it's windy because (1) you'll hear the wind gusts, and (2) you and everything else outside will be pushed. This excludes minecarts that are on rails and any entities attached to solid blocks (like paintings, item frames, and lead knots). Don't like it? Then stay inside, you wuss :P Or, you know, build your base in a desert where it doesn't rain :)

     

    Nature's Inertia

    Okay, okay, so the idea of being pushed around in the rain is annoying. But there's a way to counter it! The Nature's Inertia enchantment can be applied to any piece of armor. It has only one level, but wearing multiple pieces of armor with it will stack the effect. Each piece of armor you wear with Nature's Inertia will decrease the effect of wind on you to 60%. A full set, therefore, will make it only about 12% its original strength, which makes it unnoticeable for small gusts, and more than tolerable for strong ones. You can get this enchantment for about 5 levels, so it's fairly cheap.

     

    Nature's Song

    You can put the Nature's Song enchantment on any item with durability. If you have a Nature's Song item in your inventory and you're outside while it's raining, that item will repair itself over time, at least until you come inside or it stops raining. There are 3 levels of Nature's Song, with each level increasing the rate of repair. You'll also get an extra boost to the repair rate if you're outside in a thunderstorm compared to regular rain.

     

    Nature's Soul

    You can apply Nature's Soul to any armor. If you're injured, but are outside in the rain while wearing Nature's Soul armor, you will regenerate health at the cost of armor durability. There are 3 levels of Nature's Soul, with each increasing the rate of healing. And yes, this does stack with multiple armor pieces to provide faster recovery, but it will be at the cost of damage to all your armor pieces with this enchantment. Thunderstorms provide an extra speed boost to your recovery compared to regular rain as well. Your enchanted armor will lose 2 durability per half-heart it heals you, regardless of level.

     

    Nature's Fury

    When applied to any sword, Nature's Fury channels the strength of Tlaloc himself (that's the Aztec rain god, if you were wondering). When held as your active item while outside in the rain, you'll get a constant strength effect with a level equal to the level of the enchantment (there are 3 levels of Nature's Fury). For example, if you're in the rain holding a Nature's Fury II sword, you'll get Strength II. Thunderstorms add 1 extra level to the strength effect. This means the maximum boost you can get is holding a Nature's Fury III sword in a thunderstorm, giving you constant Strength IV.

     

    Note: While wind won't occur in biomes with no rainfall, all the enchantments will still work there as long as the world is raining or thunder-storming.

     

    Redstone Meteorology

     

    Now that weather is important, it'd be nice to have some control over it, right? Well, there are two devices specifically made for this.

     

    Weather Detector

    Crafted with 3 iron, 1 redstone dust, a bucket, and a comparator, the Weather Detector will output a redstone signal proportional to the strength of the weather. Clear skies give 0 output, rain gives 7, and thunderstorms give a full 15 strength output. The detector needs a clear view of the sky, though, but you *can* put transparent blocks over it. If there are any opaque blocks above the detector, it'll just output 0 regardless of the weather. So you don't have any Nature's Inertia armor yet--who cares? Hook one of these up to a light and you'll know when it's too windy to leave the house :)

     

    Note: The Weather Detector will work just fine in biomes with no rainfall. Let's just say it measures the darkness of the sky rather than actual rainfall, mmkay? :P

     

    Weather Manipulator

    What's that? You want control over the weather itself? Don't like the rain, do you? Or do you really want your full money's worth out of your Nature's enchantments? Then the Weather Manipulator is perfect for you! Crafted with 1 Cloudstone, 1 Sunstone, 1 Thunderstone, 1 Rainstone, 2 redstone dust, 2 iron ingots, and 1 Nether star, a Weather Manipulator block lets you decide what the weather is like! Yes, it's expensive, but you're controlling the planet's atmosphere--you didn't think that would be cheap, did you? Place this down anywhere, and you can right-click it to choose a weather type. When supplied with a redstone signal, this will change the world's weather to the selected type. It only triggers when the signal is first turned on, though; it's not constant. So if the weather changes again, you'll have to re-trigger the Manipulator to change it back. Of course, it might be useful to connect one of these up to a Weather Detector...for automation...just sayin'.

     

    Also, contrary to what it looks like, the Weather Manipulator does not need a clear view of the sky. Its beam will permeate all blocks to reach the heavens above.

     

    Download link: https://dl.dropboxusercontent.com/u/11662651/Minecraft/Mods/WeatherWorks-1.1.zip

     

    ---------

     

    Change Log

    Version 1.1

    -Fixed a major bug with ore generation that caused Weatherstone ores to only generate close to (0,0) and in mixed groups. Worlds generated in 1.1 and later will correctly generate Weatherstone ore uniformly over the entire world, and only one vein and one type of Weatherstone ore per chunk.

     

    Version 1.0

    -Initial release

  3. Ohhhh, okay. Seeing as this is my first look into custom AI, I'll give it a shot and see how it goes...I'll post here if I run into any other problems with it. Thanks.

     

    *EDIT* Awesome, I got it all to work! I just had to basically copy over the pathfinding code to the LivingUpdateEvent handler for those certain mobs that don't use the new tasks system, like you mentioned, and everything works perfectly now :) Thank you!

  4. Simulate a click for whom? You can't just 'click' a mouse without a player to perform the action - what's your specific scenario, and why can't the player click the mouse themselves?

     

    I'm wanting to create a mod that fully automates the killing of mobs. To do this the mod would need to continuously swing the sword/item (which is why I figured simply simulating the mouse click would be a good idea).

    That seems very manual for something you're trying to automate. Why not just set the mobs' health to 0?

  5. I have a new AI task I'm trying to add to all hostile mobs (except bosses). It simply makes them head to a Moonstone block (a custom block in my mod) if they're within 10 blocks of one. However, it seems it's only applying to certain mobs and not others. I've tested with all the hostile mob spawn eggs, and this is the result:

     

    It works with Witches, Skeletons, Silverfish, Creepers, and Zombies, who all properly move towards the Moonstone.

     

    It does not work with Spiders, Slimes, Zombie Pigmen, Endermen, Blazes, Magma Cubes, or Ghasts, who completely ignore the Moonstone.

     

    I'm not sure why it's being so selective. Here's my event listener code to add the task to mobs:

     

    	@SubscribeEvent
    public void onJoinWorld(EntityJoinWorldEvent event) {
    
                    /* If this is a mob, handle specific cases that don't extend EntityMob, then the general case. Don't be too general, as dragons implement IMob as well, and we don't want them using this AI */
    	if (event.entity instanceof IMob) {
    		if (event.entity instanceof EntityGhast) {
    			((EntityGhast)event.entity).tasks.addTask(4, new EntityAIMoonstoneAttract((EntityLiving) event.entity, 1.0d));
    		}
    		else if (event.entity instanceof EntityMagmaCube) {
    			((EntityMagmaCube)event.entity).tasks.addTask(4, new EntityAIMoonstoneAttract((EntityLiving) event.entity, 1.0d));
    		}
    		else if (event.entity instanceof EntitySlime) {
    			((EntitySlime)event.entity).tasks.addTask(4, new EntityAIMoonstoneAttract((EntityLiving) event.entity, 1.0d));
    		}
                            else if (event.entity instanceof EntityWither) { return; }
    		else if (event.entity instanceof EntityMob) {
    			((EntityMob)event.entity).tasks.addTask(4, new EntityAIMoonstoneAttract((EntityLiving) event.entity, 1.0d));
    		}
    	}
    }

     

    And here's the task code itself (the EntityAIMoonstoneAttract class):

     

    package com.IceMetalPunk.weatherworks;
    
    import net.minecraft.block.Block;
    import net.minecraft.entity.EntityLiving;
    import net.minecraft.entity.ai.EntityAIBase;
    
    public class EntityAIMoonstoneAttract extends EntityAIBase {
    private EntityLiving entity=null; 
        private double xPosition;
        private double yPosition;
        private double zPosition;
        private double speed;
        private static final String __OBFID = "CL_00001608";
    
        public EntityAIMoonstoneAttract(EntityLiving p_i1648_1_, double p_i1648_2_)
        {
            this.entity = p_i1648_1_;
            this.speed = p_i1648_2_;
            this.setMutexBits(1);
        }
    
        /**
         * Returns whether the EntityAIBase should begin execution.
         */
        public boolean shouldExecute()
        {
            // Get entity block position
        	int bX=(int)Math.floor(this.entity.posX);
        	int bY=(int)Math.floor(this.entity.posY);
        	int bZ=(int)Math.floor(this.entity.posZ);
        	boolean foundMoonstone=false;
        	int xx=0, yy=0, zz=0;
        	
            // Check a 10x10x10 cube around the entity, stopping if we find a Moonstone
        	outerLoop:
        	for (xx=-10; xx<=10; ++xx) {
        		for (yy=-10; yy<=10; ++yy) {
        			for (zz=-10; zz<=10; ++zz) {
        				Block block=this.entity.worldObj.getBlock(bX+xx, bY+yy, bZ+zz);
        				if (block instanceof BlockMoonstone) {
        					foundMoonstone=true;
        					break outerLoop;
        				}
        			}
        		}
        	}
    
            // Update with the position of the found Moonstone (if there's one) and allow AI execution
        	if (!foundMoonstone) {
        		return false;
        	}
            else
            {
                this.xPosition = bX+xx;
                this.yPosition = bY+yy;
                this.zPosition = bZ+zz;
                return true;
            }
        }
    
        /**
         * Returns whether an in-progress EntityAIBase should continue executing
         */
        public boolean continueExecuting()
        {
            return !this.entity.getNavigator().noPath();
        }
    
        /**
         * Execute a one shot task or start executing a continuous task
         */
        public void startExecuting()
        {
            this.entity.getNavigator().tryMoveToXYZ(this.xPosition, this.yPosition, this.zPosition, this.speed);
        }
    }
    

     

    It's especially strange to me since Endermen, at least, are definitely children of the EntityMob class, and I'm specifically handling EntitySlimes in the hook, yet neither mob seems to care about the Moonstone.

  6. I've made a custom enchantment for armor that heals you if you wear it in the rain. It works fine, but whenever it heals the player (using PlayerEntity#heal()), the view shakes. Since it heals them once per tick until they're fully healed, that gets annoying. The heal() method simply calls the setHealth() method, so doing that directly does nothing. And setHealth() updates a dataWatcher that I don't have access to.

     

    So basically, how can I heal the player without the shaking animation? (Note: I'm healing in tiny amounts per tick, like 0.05 to 0.2 per tick. If that matters.)

     

    *EDIT* More info: whenever I heal the player with these amounts, he also flashes red. So it's as though healing the player with such small amounts is animating as though it's damaging him, despite still correctly adding hearts. How can I get around this?

     

    *EDIT 2* I've made it work by healing in integer amounts, but doing it less often. Unfortunately, that requires a constantly-running cooldown timer, which I was hoping to avoid. If there's a way to make it work with tiny amounts, let me know, please; meanwhile, the cooldown will have to stay.

  7. Oh boy if there wasn't enough help needed before, there will be soon

    Field fireField=Entity.class.getField("fire");
    

    Fire is a private field and therefore is only accessible with

    Class.getDeclaredField(String)
    

    Also note that when the code is recompiled it'll stop working since the field names are reverted to their obfuscated variants.

     

    Secondly you need an object to grab local variables from. You need an entity and then you need to invoke the

    set(Object targetInstance, Object newValue)

    method with your now accessible field. And that's about it.

     

    I'm not following why you need to change the burntime on entities struck by lightning though

     

    Ah, sorry, as I said, I have absolutely zero experience with reflection in any language >_< I need to change the burntime because lightning does that already. I want everything to be exactly like the vanilla lightning, except with the DamageSource coming from my custom electricity type instead of fire damage. So I've had to cancel the entire event and replace it with my own, as there doesn't seem to be a way to simply change that source (if there is, please let me know; it'd be much easier).

     

    So I've managed to make everything error-free, but I can't seem to test it to see if it works. I have this test code that should summon a lightning bolt at the player's position when a block is right-clicked:

     

    	@Override
    public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float px, float py, float pz) {
    	world.addWeatherEffect(new EntityLightningBolt(world, px, py, pz));
    	return true;
    }

     

    When I right-click it, I get the sky flashing and sound effects, but the lightning bolt itself doesn't appear.

  8. Sadly, the setFire() method doesn't work as you'd expect; it doesn't allow you to increase the value of the member, only decrease it. And in the case of lightning, the value needs to be increased, so I can't use the setter that comes with the Entity class.

     

    I'm looking into reflection, but I'm not quite sure I understand how it works. Looking online at some examples, I tried this code to change the fire field from private to public, but I ended up getting a NoSuchField exception for "fire", even though Entity#fire is definitely a member:

     

        	/* Reflection to change private members to public for access */
        	try {
    		// Get Entity#fire field and make it accessible
    		Field fireField=Entity.class.getField("fire");
    		fireField.setAccessible(true);
    
    		// Make modifiers accessible
    		Field modifiersField = Field.class.getDeclaredField("modifiers");
    	    	modifiersField.setAccessible(true);
    
    		// Change the modifiers on the fire field to make it public and not private.
    	    try {
    			modifiersField.setInt(fireField, (fireField.getModifiers() & ~Modifier.PRIVATE) | Modifier.PUBLIC);
    		} catch (IllegalArgumentException e) {
    			e.printStackTrace();
    		} catch (IllegalAccessException e) {
    			e.printStackTrace();
    		}
    	} catch (NoSuchFieldException e) {
    		e.printStackTrace();
    	} catch (SecurityException e) {
    		e.printStackTrace();
    	}

     

    Am I not understanding something about how reflection works?

  9. @thebest108: I tried that first, and it didn't work, either.

     

    @jeffryfisher: I figured I'd deal with the special cases of "static" entities once I got the main system working. 4 blocks per second per second is certainly not trivial xD But they are, in fact, supposed to accelerate. Keep in mind it only happens for a short period of time each "gust" of wind, it's not constantly accelerating them.

     

    @coolAlias: Aha! When it didn't work for the player, I kept trying to fix it without thinking to test other entities. It actually does work for other entities (I had to lower the amounts and timings to get something decent, but that's trivial). I didn't realize player motion was entirely client-side. I guess I'll have to implement a packet handler for this, then. I'll try that and see where it goes from there. Thanks!

     

    *EDIT* Well...that made things worse. Now, when the wind starts, the game just locks up completely. The debug output tells me that, for some odd reason, even though the packet message instance is created with the proper x, y, and z velocity values, by the time the client receives it, it's reading ridiculously large (as in, on the order of 1E9) x and y values instead. I can't figure out why.

     

    Here's the updated tick handler source:

    package com.IceMetalPunk.weatherworks;
    
    import java.util.List;
    
    import cpw.mods.fml.common.eventhandler.EventBus;
    import cpw.mods.fml.common.eventhandler.SubscribeEvent;
    import cpw.mods.fml.common.gameevent.TickEvent.Phase;
    import cpw.mods.fml.common.gameevent.TickEvent.ServerTickEvent;
    import net.minecraft.client.Minecraft;
    import net.minecraft.entity.Entity;
    import net.minecraft.entity.player.EntityPlayerMP;
    import net.minecraft.server.MinecraftServer;
    import net.minecraft.world.World;
    
    public class WeatherTickHandler {
    private byte dir=0;
    private float amt=1.0f;
    private int cooldown=0;
    private final double threshold=20.0d;
    
    public WeatherTickHandler(EventBus bus) {
    	bus.register(this);;
    }
    
    @SubscribeEvent
    public void onTick(ServerTickEvent event) {
    	if (event.phase==Phase.START) {
    
    		// Decrement the cooldown
    		if (this.cooldown>0) { --this.cooldown; }
    
    		// Only blow wind when cooldown<=40
    		if (this.cooldown>this.threshold) { return; }
    
    		World world=MinecraftServer.getServer().getEntityWorld();
    
    		// Only blow wind when it's raining
    		if (world.getWorldInfo().isRaining() || world.getWorldInfo().isThundering()) {
    
    			// Calculate wind speed on x and z axes
    			float velX=(this.dir==0)?this.amt:((this.dir==1)?-this.amt:0);
    			float velZ=(this.dir==2)?this.amt:((this.dir==3)?-this.amt:0);
    
    			// Iterate through the list of entities
    			List entities=world.getLoadedEntityList();
    			for (int p=0; p<entities.size(); ++p) {
    				Entity ent=((Entity)entities.get(p));
    
    				/* Debug message */
    				if (ent instanceof EntityPlayerMP && this.cooldown==this.threshold) {
    					System.out.println("Sending "+((double)(velX*this.threshold))+", "+(0.0d)+", "+((double)(velZ*this.threshold)));
    					WeatherworksPacketHandler.INSTANCE.sendToAll(new WeatherManipMessage((byte)2, (double)(velX*this.threshold), 0.0d, (double)(velZ*this.threshold), (byte)0));
    				}
    				/* End debug message */
    
    				// Add the velocity to each entity
    				ent.addVelocity((double)velX, 0.0d, (double)velZ);
    			}
    		}
    
    		// When cooldown=0, reset cooldown and choose a random direction and amount for next time
    		if (this.cooldown==0) {
    			this.cooldown=100;
    			this.dir=(byte)world.rand.nextInt(4);
    			this.amt=world.rand.nextFloat()/4.0f;
    		}
    	}
    }
    }
    

     

    Here's the message class ("val" is there for other message types; it's irrelevant to this issue):

    public class WeatherManipMessage implements IMessage {
    
    public byte val=0;
    public double x, y, z;
    public byte messageType=0;
    
    public WeatherManipMessage() {};
    
    public WeatherManipMessage(byte type, double x, double y, double z, byte state) {
    	this.val=state;
    	this.x=x;
    	this.y=y;
    	this.z=z;
    	this.messageType=type;
    	System.out.println("Created message with "+this.x+", "+this.y+", "+this.z);
    }
    
    @Override
    public void fromBytes(ByteBuf buf) {
    	this.messageType=buf.readByte();
    	this.x=buf.readInt();
    	this.y=buf.readInt();
    	this.z=buf.readInt();
    	this.val=buf.readByte();
    }
    
    @Override
    public void toBytes(ByteBuf buf) {
    	buf.writeByte(this.messageType);
    	buf.writeDouble(this.x);
    	buf.writeDouble(this.y);
    	buf.writeDouble(this.z);
    	buf.writeByte(this.val);
    }
    }

     

    Here's the message handler:

    public static class WeatherWorksWindHandler implements IMessageHandler<WeatherManipMessage,IMessage> {
    
    	@Override
    	public IMessage onMessage(WeatherManipMessage message, MessageContext ctx) {
    		if (message.messageType==2) {
    			EntityPlayer player=Minecraft.getMinecraft().thePlayer;
    			System.out.println("Moved "+player+" by "+(message.x)+", "+(message.y)+", "+(message.z));
    			player.addVelocity(message.x, message.y, message.z);
    		}
    		return null;
    	}
    
    }

     

    And here's the registration of the message handler, in the preinit event handler:

    int messageID=0;
    WeatherworksPacketHandler.INSTANCE.registerMessage(WeatherWorksWindHandler.class, WeatherManipMessage.class, messageID++, Side.CLIENT);

     

    Given that, this is the strange output I'm getting in the console:

    [15:47:14] [server thread/INFO] [sTDOUT]: [com.IceMetalPunk.weatherworks.WeatherTickHandler:onTick:51]: Sending 0.611920952796936, 0.0, 0.0
    [15:47:14] [server thread/INFO] [sTDOUT]: [com.IceMetalPunk.weatherworks.WeatherManipMessage:<init>:26]: Created message with 0.611920952796936, 0.0, 0.0
    [15:47:14] [Client thread/INFO] [sTDOUT]: [com.IceMetalPunk.weatherworks.WeatherManipMessage$WeatherWorksWindHandler:onMessage:84]: Moved EntityClientPlayerMP['Player703'/7, l='MpServer', x=582.96, y=57.62, z=-124.47] by 1.071879387E9, 1.073741824E9, 0.0

     

    As you can see, the values it's sending are fine, the constructor is setting those values fine as well...but when the client receives the message, it's reading massive and incorrect values instead. I can't figure out why?

  10. I'm trying to simulate wind during rain in my mod. So I have a tick handler that's listening for the ServerTickEvent, and after a cooldown, it checks the world for rain, then iterates through all loaded entities and adds to their velocity. Sounds simple, right?

     

    Except it's not doing anything. I put a debug message into the console logger to see what's going on, but the message looks fine: it tells me it's moving me with a random x or z amount between 1 and 4, just as it should be. But I don't actually move...nothing moves. Am I using the Entity#addVelocity() function incorrectly or something?

     

    This is the code for the tick handler:

     

    package com.IceMetalPunk.weatherworks;
    
    import java.util.List;
    
    import cpw.mods.fml.common.eventhandler.EventBus;
    import cpw.mods.fml.common.eventhandler.SubscribeEvent;
    import cpw.mods.fml.common.gameevent.TickEvent.Phase;
    import cpw.mods.fml.common.gameevent.TickEvent.ServerTickEvent;
    import net.minecraft.client.Minecraft;
    import net.minecraft.entity.Entity;
    import net.minecraft.entity.player.EntityPlayerMP;
    import net.minecraft.server.MinecraftServer;
    import net.minecraft.world.World;
    
    public class WeatherTickHandler {
    private byte dir=0;
    private float amt=1.0f;
    private int cooldown=0;
    
    public WeatherTickHandler(EventBus bus) {
    	bus.register(this);;
    }
    
    @SubscribeEvent
    public void onTick(ServerTickEvent event) {
    	if (event.phase==Phase.START) {
    
    		// Decrement the cooldown
    		if (this.cooldown>0) { --this.cooldown; }
    
    		// Only blow wind when cooldown<=40
    		if (this.cooldown>40) { return; }
    
    		World world=MinecraftServer.getServer().getEntityWorld();
    
    		// Only blow wind when it's raining
    		if (world.getWorldInfo().isRaining() || world.getWorldInfo().isThundering()) {
    
    			// Calculate wind speed on x and z axes
    			float velX=(this.dir==0)?this.amt:((this.dir==1)?-this.amt:0);
    			float velZ=(this.dir==2)?this.amt:((this.dir==3)?-this.amt:0);
    
    			// Iterate through the list of entities
    			List entities=world.getLoadedEntityList();
    			for (int p=0; p<entities.size(); ++p) {
    				Entity ent=((Entity)entities.get(p));
    
    				/* Debug message */
    				if (ent instanceof EntityPlayerMP) {
    					System.out.println("Moving "+ent+" by "+velX+", "+velZ);
    				}
    				/* End debug message */
    
    				// Add the velocity to each entity
    				ent.addVelocity(velX, 0.0d, velZ);
    			}
    		}
    
    		// When cooldown=0, reset cooldown and choose a random direction and amount for next time
    		if (this.cooldown==0) {
    			this.cooldown=100;
    			this.dir=(byte)world.rand.nextInt(4);
    			this.amt=world.rand.nextFloat()*3.0f+1.0f;
    		}
    	}
    }
    }

  11. I'm pretty sure there's no lightningBolt damage source; it just uses the inFire damage source. But I've been looking into event listeners (I've never used them; I'm very new to modding), and I think that is the right way to go. I've found there's an EntityStruckByLightning event; I'm going to try and use that to make this work. The only thing I'm unsure about is mod compatibility; canceling getting struck by lightening could cause issues with other mods, I'd think. Hopefully, if I copy the usual code and just change the damage source, it won't be too much of a problem...

     

    *EDIT* And already I've run into another problem. The default lightning-strike code increments the private member "fire". I don't have access to that variable inside the event listener, and there doesn't seem to be any method to get the current value of "fire", so it's not even possible to use setFire() to do the incrementation (even if I could, setFire() takes an int and multiplies it by 20, so I couldn't add 1 even then). How would I replicate the onStruckByLightning code in an event listener if I have this problem?

  12. I've implemented a custom damage type ("electricity") by simply creating a new public static DamageSource in my main mod class. It works fine with my custom electricity-damaging blocks, but I'd also like the direct damage from a lightning strike to be considered electricity damage. (This way, if the 5 instant damage from a bolt kills a player, it says they were electrocuted instead of burned.)

     

    I don't have any experience with modifying base classes; how would I go about overriding the onStruckByLightning() method for the base entity class so that all entities that don't override it themselves will have this proper electricity behavior?

  13. I'm trying to make a block that, depending on its metadata, can have any light level from 0 to 15. Since each of these blocks can have a different light level, I figure I need a tile entity. But how can I make the tile entity set the light level properly? I've tried using World#setLightValue(), but that doesn't seem to have any effect, even if I add a call to World#updateLightByType() after it. How can I make this work properly? (Also, do I even need a tile entity for this, or is there a more efficient way of handling it?)

     

    Here's the tile entity's code:

     

    package com.IceMetalPunk.weatherworks;
    
    import net.minecraft.tileentity.TileEntity;
    import net.minecraft.world.EnumSkyBlock;
    
    public class TileEntitySunstone extends TileEntity {
    private int metaData=0;
    @Override
    public void updateEntity() {
    	if (this.worldObj.isRemote) { return; }
    	int thisMeta=this.worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord);
    	if (thisMeta!=this.metaData) {
    		this.worldObj.setLightValue(EnumSkyBlock.Block, this.xCoord, this.yCoord, this.zCoord, thisMeta);
    		this.worldObj.markBlockRangeForRenderUpdate(this.xCoord-thisMeta, this.yCoord-thisMeta, this.zCoord-thisMeta, this.xCoord+thisMeta, this.yCoord+thisMeta, this.zCoord+thisMeta);
    		this.worldObj.updateLightByType(EnumSkyBlock.Block, this.xCoord, this.yCoord, this.zCoord);
    		this.metaData=thisMeta;
    	}
    }
    }

  14. I'm trying to use a TESR to basically make a custom "beacon" beam effect. It'll eventually use a custom texture, but for now it's using the regular beacon beam texture. I've basically copied the beacon's TESR code and modified it for my purposes (mainly a tiny bit of cleanup, checking the proper "active" variable in the tile entity, and calculating the proper height of the beam for the up/down animation).

     

    The problem I'm having is that although the beam renders the proper shape and size and everything, but if I look up so that the block itself is out of view, the beam disappears as well. The entity's getMaxRenderDistanceSquared() is set to 250000, so it should definitely be in range....

     

    Here's the entire TESR code. The getActiveLevel() method just returns the tile entity's activeLevel value, which goes from beamTime to 0 decrementing every tick. And beamTime is a public final byte that lets me adjust how long the beam stays on. The fancy math involving f1 and maxLevel just calculates the height of the beam.

     

    package com.IceMetalPunk.weatherworks;
    
    import cpw.mods.fml.relauncher.Side;
    import cpw.mods.fml.relauncher.SideOnly;
    import net.minecraft.client.renderer.OpenGlHelper;
    import net.minecraft.client.renderer.Tessellator;
    import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
    import net.minecraft.tileentity.TileEntity;
    import net.minecraft.util.AxisAlignedBB;
    import net.minecraft.util.MathHelper;
    import net.minecraft.util.ResourceLocation;
    import org.lwjgl.opengl.GL11;
    import net.minecraft.item.ItemDye;
    
    @SideOnly(Side.CLIENT)
    public class WeatherManipulatorRenderer extends TileEntitySpecialRenderer
    {
        private static final ResourceLocation field_147523_b = new ResourceLocation("textures/entity/beacon_beam.png");
    
        public void renderTileEntityAt(TileEntityWeatherManipulator tileEnt, double rX, double rY, double rZ, float timeParam)
        {
            byte f1 = tileEnt.getActiveLevel();
            byte maxLevel= tileEnt.beamTime;
            GL11.glAlphaFunc(GL11.GL_GREATER, 0.1F);
        	
            if (f1 > 0)
            {
                Tessellator tessellator = Tessellator.instance;
                this.bindTexture(field_147523_b);
                GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, 10497.0F);
                GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, 10497.0F);
                GL11.glDisable(GL11.GL_LIGHTING);
                GL11.glDisable(GL11.GL_CULL_FACE);
                GL11.glDisable(GL11.GL_BLEND);
                GL11.glDepthMask(true);
                OpenGlHelper.glBlendFunc(770, 1, 1, 0);
                float f2 = (float)tileEnt.getWorldObj().getTotalWorldTime() + timeParam;
                float f3 = -f2 * 0.2F - (float)MathHelper.floor_float(-f2 * 0.1F);
                byte b0 = 1;
                double d3 = (double)f2 * 0.025D * (1.0D - (double)(b0 & 1) * 2.5D);
                tessellator.startDrawingQuads();
                tessellator.setBrightness(255);
                
                tessellator.setColorRGBA(255, 255, 255, 32);
                
                double d5 = (double)b0 * 0.2D;
                double d7 = 0.5D + Math.cos(d3 + 2.356194490192345D) * d5;
                double d9 = 0.5D + Math.sin(d3 + 2.356194490192345D) * d5;
                double d11 = 0.5D + Math.cos(d3 + (Math.PI / 4D)) * d5;
                double d13 = 0.5D + Math.sin(d3 + (Math.PI / 4D)) * d5;
                double d15 = 0.5D + Math.cos(d3 + 3.9269908169872414D) * d5;
                double d17 = 0.5D + Math.sin(d3 + 3.9269908169872414D) * d5;
                double d19 = 0.5D + Math.cos(d3 + 5.497787143782138D) * d5;
                double d21 = 0.5D + Math.sin(d3 + 5.497787143782138D) * d5;
                double b=1020-4*rY;
        		double x=(double)f1/(double)maxLevel;
        		double d23=b*x-b*x*x+rY;
                System.out.println(f1+" / "+maxLevel+" * (256 - "+rY+") = "+d23);
                double d25 = 0.0D;
                double d27 = 1.0D;
                double d28 = (double)(-1.0F + f3);
                double d29=1;
                
                tessellator.addVertexWithUV(rX + d7, rY + d23, rZ + d9, d27, d29);
                tessellator.addVertexWithUV(rX + d7, rY, rZ + d9, d27, d28);
                tessellator.addVertexWithUV(rX + d11, rY, rZ + d13, d25, d28);
                tessellator.addVertexWithUV(rX + d11, rY + d23, rZ + d13, d25, d29);
                tessellator.addVertexWithUV(rX + d19, rY + d23, rZ + d21, d27, d29);
                tessellator.addVertexWithUV(rX + d19, rY, rZ + d21, d27, d28);
                tessellator.addVertexWithUV(rX + d15, rY, rZ + d17, d25, d28);
                tessellator.addVertexWithUV(rX + d15, rY + d23, rZ + d17, d25, d29);
                tessellator.addVertexWithUV(rX + d11, rY + d23, rZ + d13, d27, d29);
                tessellator.addVertexWithUV(rX + d11, rY, rZ + d13, d27, d28);
                tessellator.addVertexWithUV(rX + d19, rY, rZ + d21, d25, d28);
                tessellator.addVertexWithUV(rX + d19, rY + d23, rZ + d21, d25, d29);
                tessellator.addVertexWithUV(rX + d15, rY + d23, rZ + d17, d27, d29);
                tessellator.addVertexWithUV(rX + d15, rY, rZ + d17, d27, d28);
                tessellator.addVertexWithUV(rX + d7, rY, rZ + d9, d25, d28);
                tessellator.addVertexWithUV(rX + d7, rY + d23, rZ + d9, d25, d29);
                tessellator.draw();
                GL11.glEnable(GL11.GL_BLEND);
                OpenGlHelper.glBlendFunc(770, 771, 1, 0);
                GL11.glDepthMask(false);
                tessellator.startDrawingQuads();
                tessellator.setBrightness(255);
                tessellator.setColorRGBA(255, 255, 255, 32);
                
                double d30 = 0.2D;
                double d4 = 0.2D;
                double d6 = 0.8D;
                double d8 = 0.2D;
                double d10 = 0.2D;
                double d12 = 0.8D;
                double d14 = 0.8D;
                double d16 = 0.8D;
                double d18 = d23;
                double d20 = 0.0D;
                double d22 = 1.0D;
                double d24 = (double)(-1.0F + f3);
                double d26 = 1;
                tessellator.addVertexWithUV(rX + d30, rY + d18, rZ + d4, d22, d26);
                tessellator.addVertexWithUV(rX + d30, rY, rZ + d4, d22, d24);
                tessellator.addVertexWithUV(rX + d6, rY, rZ + d8, d20, d24);
                tessellator.addVertexWithUV(rX + d6, rY + d18, rZ + d8, d20, d26);
                tessellator.addVertexWithUV(rX + d14, rY + d18, rZ + d16, d22, d26);
                tessellator.addVertexWithUV(rX + d14, rY, rZ + d16, d22, d24);
                tessellator.addVertexWithUV(rX + d10, rY, rZ + d12, d20, d24);
                tessellator.addVertexWithUV(rX + d10, rY + d18, rZ + d12, d20, d26);
                tessellator.addVertexWithUV(rX + d6, rY + d18, rZ + d8, d22, d26);
                tessellator.addVertexWithUV(rX + d6, rY, rZ + d8, d22, d24);
                tessellator.addVertexWithUV(rX + d14, rY, rZ + d16, d20, d24);
                tessellator.addVertexWithUV(rX + d14, rY + d18, rZ + d16, d20, d26);
                tessellator.addVertexWithUV(rX + d10, rY + d18, rZ + d12, d22, d26);
                tessellator.addVertexWithUV(rX + d10, rY, rZ + d12, d22, d24);
                tessellator.addVertexWithUV(rX + d30, rY, rZ + d4, d20, d24);
                tessellator.addVertexWithUV(rX + d30, rY + d18, rZ + d4, d20, d26);
                tessellator.draw();
                GL11.glEnable(GL11.GL_LIGHTING);
                GL11.glEnable(GL11.GL_TEXTURE_2D);
                GL11.glDepthMask(true);
            }
        }
    
        public void renderTileEntityAt(TileEntity tileEnt, double rX, double rY, double rZ, float timeParam)
        {
            this.renderTileEntityAt((TileEntityWeatherManipulator)tileEnt, rX, rY, rZ, timeParam);
        }
    }

  15. I have a GUI class that extends GuiScreen. No inventory is needed, it just draws two buttons: one for the setting and a Done button. Clicking the setting button changes the setting, which means it changes the button's text and also calls a method in the associated tile entity to set a byte value appropriately (according to the current button text). The problem I'm having is that I need it to call the method from the tile entity on the server, but as a client-side GUI class, it only runs on the client, meaning it gets the client-side tile entity instead.

     

    How do I make a button toggle on the client-side GUI element call a method on the server-side tile entity?

  16. Why do you need to know which inventory contains the stack?

    As he said, because the item is being used as an RF container, and it's supposed to do different things depending on whether it's in the player's inventory or in a tile entity's inventory (I assume if it's in a tile entity, it charges that entity up, but if it's in a player's inventory, it instead charges the items in his inventory; that's the usual battery design).

     

    ItemStacks have NBT data stored in their stackTagCompound member. One thing you could do is override the useItemRightClick() method; whenever a player right-clicks the item, it "activates" it, which in reality just stores that player's UUID in the NBT tags for later use.

     

    As for clearing that tag when the item is put into a TileEntity's inventory...I'm not sure about that. If they were all your own tile entities, you could just make a custom slot class that extends slot but overrides the putStack() method to clear the tag. But since they're likely other people's containers...I'm not sure where to go from there.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.