
Jimmeh
Members-
Posts
131 -
Joined
-
Last visited
Everything posted by Jimmeh
-
Sure thing: public class ClientInfo { private int energy, maxEnergy; public ClientInfo(){ energy = 5; maxEnergy = 10; } public int getEnergy() { return energy; } public int getMaxEnergy() { return maxEnergy; } public int getEnergyPercentage(){ return (energy / maxEnergy) * 100; } } It's just getting the percentage of how much energy the player current has compared to what they'd have at "full charge".
-
Okay, I think this is the last problem. I created this new class, hold an instance of it in the ClientProxy, and just manually call ClientProxy.energyGui.draw() in the event you said above. The outline draws fine now and allows for movement, but the "inner" area (drawRect(...)) doesn't display at all. Would you happen to have any hunches off the top of your head? public class GuiEnergyBarTwo extends Gui { private int xSize = 116, ySize = 17; //size of desired window. Max is 256x256 public void draw(){ TextureManager textureManager = ClientProxy.MINECRAFT.getTextureManager(); textureManager.bindTexture(new ResourceLocation(Test.MODID + ":textures/gui/energybar.png")); GlStateManager.pushMatrix(); //---- inner ---- drawRect(0, 0, getRightCoord(), ySize - 1, 1); //---- outline ---- GlStateManager.enableAlpha(); drawTexturedModalRect(0, 0, 0, 0, xSize, ySize); GlStateManager.popMatrix(); } private int getRightCoord(){ int energyPercent = ClientProxy.clientInfo.getEnergyPercentage(); return xSize * (energyPercent/100); //this returns the needed percentage of the length of the rectangle } }
-
Okay, hopefully last question. What's the Tessellator for? If there's GUI helper methods, GlStateManager, and Tessellator, surely they're all for (at least, slightly) different purposes, right? I'm just trying to figure everything out!
-
How would you recommend doing that? All through GlStateManager (if that's possible... I didn't see any kind of "drawRectangle" method in that class)?
-
Thanks! That all makes sense. So then I'd do something along the lines of: @SubscribeEvent public void onRender(RenderGameOverlayEvent.Post event){ if(event.getType().equals(RenderGameOverlayEvent.ElementType.ALL)) //do stuff //ClientProxy.MINECRAFT.displayGuiScreen(new GuiEnergyBar()); } ? And what should I use instead of GuiScreen? Gui, as the other person said up there ^?
-
Hm, I suppose that would make sense. So it'd be safe to assume that GuiScreen is more like a menu type of thing, which would make sense why I couldn't move or do anything.
-
Hmmm, okay. That was going to be my next question: what's the difference between GuiScreen and Gui? I can't seem to find a tutorial that actually explains this stuff, and there's no paragraph comments in those classes explaining their uses, unfortunately. Also, I'm looking in the RenderGameOverlayEvent class and I see Pre, Post, BossInfo, Text and Chat. I'm assuming I should use Pre or Post. Which would be better practice there? Thanks for all the insight!
-
So I'm using the exact same "... extends GuiScreen" class, but here's the event: @SideOnly(Side.CLIENT) public class ClientEvents { private boolean showWelcomePage = true; @SubscribeEvent public void onUpdate(TickEvent.ClientTickEvent event){ } @SubscribeEvent public void onGuiOpened(GuiOpenEvent event){ if(showWelcomePage && event.getGui() instanceof GuiMainMenu){ //we do this because when the GUI button is clicked in GUIWElcome, it opens GUiMainMenu event.setGui(new GuiWelcome()); showWelcomePage = false; } } @SubscribeEvent public void onRender(RenderGameOverlayEvent event){ ClientProxy.MINECRAFT.displayGuiScreen(new GuiEnergyBar()); } @SubscribeEvent public void onRenderPlayerPre(RenderPlayerEvent.Pre event){ float rotation = 90; GlStateManager.pushMatrix(); /*float baseRotation = (event.getEntityPlayer().renderYawOffset - event.getEntityPlayer().prevRenderYawOffset) * event.getPartialRenderTick() + event.getEntityPlayer().prevRenderYawOffset; GlStateManager.rotate(-baseRotation, 0, 1, 0);*/ if(Keyboard.isKeyDown(Keyboard.KEY_X)){ rotation++; GlStateManager.rotate(rotation, 0, 1, 0); event.getEntityPlayer().addVelocity(0.015 ,0.05, 0.015); } } @SubscribeEvent public void onRenderPlayerPost(RenderPlayerEvent.Post event){ GlStateManager.popMatrix(); } } More specifically: @SubscribeEvent public void onRender(RenderGameOverlayEvent event){ ClientProxy.MINECRAFT.displayGuiScreen(new GuiEnergyBar()); }
-
Okay, so that solved one issue of when to actually display it. However, I still can't move, I can't bring up chat, and I can't even bring up the singleplayer menu
-
So I've made a very simple "energy bar" gui thing It currently displays in the top left of the screen. Whenever it's displayed, it pauses the whole game even though I'm not telling it to. Here's the Gui class itself: public class GuiEnergyBar extends GuiScreen { private int xSize = 116, ySize = 17; //size of desired window. Max is 256x256 @Override public void initGui(){ super.initGui(); } @Override public void drawScreen(int mouseX, int mouseY, float partialTicks){ super.drawScreen(mouseX, mouseY, partialTicks); TextureManager textureManager = this.mc.getTextureManager(); textureManager.bindTexture(new ResourceLocation(Test.MODID + ":textures/gui/energybar.png")); GlStateManager.pushMatrix(); GlStateManager.enableAlpha(); drawTexturedModalRect(0, 0, 0, 0, xSize, ySize); GlStateManager.popMatrix(); } @Override public boolean doesGuiPauseGame(){ return false; } @Override public void drawDefaultBackground(){ //darkens screen outside of GUI window super.drawDefaultBackground(); } } Here's what it looks like: https://gyazo.com/02147c190bc037ce923c03ef4a93bd68 It just pauses the whole game when displayed. Also, if you have any suggestions on how I could "fill it up", that'd be great! Thanks!
-
Oh okay, I appreciate the insight. I'll give this a shot!
-
Oh okay. So the tutorial I was following was creating a chest-like thing, so that's why I think he used BlockContainer. So even if the thing I'm creating is going to have an inventory, stick with Block?
-
Hey there! So I've actually made custom blocks before and I get them to work. I'm 99% sure I'm doing the exact same thing, but this time the texture isn't appearing once the block is placed (it's probably something really simple and stupid... looking forward to that "a ha!" moment .-.). The texture is appropriately applied when you're holding the ItemBlock in your hand, but it's invisible once placed. Here's my BlockStorageBlock class: public class BlockStorageBlock extends BlockContainerBase { public BlockStorageBlock() { super(Material.WOOD, BlockReferences.BlockStorageBlock); } @Override public boolean isOpaqueCube(IBlockState state){ //opaque = can't see through, I believe return false; } //if true, you can see through the bottom @Override public boolean isFullCube(IBlockState state){ return true; } //is a normal, full sized cube? @Override public boolean isFullBlock(IBlockState state){//should fix a shadow glitch if the block is transparent or has transparent parts return false; } @Override public boolean isPassable(IBlockAccess worldIn, BlockPos pos){ return false; } @Override public BlockRenderLayer getBlockLayer(){ //BlockRenderLayer.SOLID = don't want block to be transparent, but want to remove x-ray effect. Doesn't work if want transparent textures return BlockRenderLayer.SOLID; } @Override public void breakBlock(World world, BlockPos pos, IBlockState state){ TileEntityStorageBlock te = (TileEntityStorageBlock)world.getTileEntity(pos); InventoryHelper.dropInventoryItems(world, pos, te); super.breakBlock(world, pos, state); } @Override public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, EntityLivingBase player, ItemStack stack){ if(stack.hasDisplayName()){ ((TileEntityStorageBlock)world.getTileEntity(pos)).setCustomName(stack.getDisplayName()); } } @Override public TileEntity createNewTileEntity(World world, int meta) { return new TileEntityStorageBlock(); } } Here's the BlockContainerBase class that it extends: public class BlockContainerBase extends BlockContainer { private BlockReferences reference; public BlockContainerBase(Material blockMaterialIn, BlockReferences reference) { super(blockMaterialIn); this.reference = reference; setUnlocalizedName(reference.getUnlocalizedName()); setRegistryName(reference.getRegistryName()); setCreativeTab(Test.CREATIVE_TAB); } @Override public TileEntity createNewTileEntity(World worldIn, int meta) { return null; } } The blockstorageblock.json blockstates file: { "forge_marker": 1, "defaults": { "textures": { "all": "test:blocks/blockstorageblock" } }, "variants": { "normal": { "model": "test:blockstorageblock" }, "inventory": { "model": "test:blockstorageblock" } } } The blockstorageblock.json models/block file: { "textures": { "texture": "test:blocks/blockstorageblock" }, "elements": [ { "__comment": "Voxels-5E2F00", "from": [ -0.25, 15.25, -0.5 ], "to": [ 16.5, 16.5, 0.75 ], "faces": { "down": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" } } }, { "__comment": "Voxels-5E2F00", "from": [ -0.25, 15.25, 0.75 ], "to": [ 16.5, 16, 16.25 ], "faces": { "down": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" } } }, { "__comment": "Voxels-5E2F00", "from": [ -0.25, 16, 0.75 ], "to": [ 1.25, 16.5, 16.25 ], "faces": { "down": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" } } }, { "__comment": "Voxels-9B4E00", "from": [ 0, 0, -0.25 ], "to": [ 16.25, 15.25, 0 ], "faces": { "down": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" } } }, { "__comment": "Voxels-9B4E00", "from": [ 0, 0, 0 ], "to": [ 16.25, 0.25, 16 ], "faces": { "down": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" } } }, { "__comment": "Voxels-9B4E00", "from": [ 0, 0.25, 0 ], "to": [ 0.25, 15.25, 16 ], "faces": { "down": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" } } }, { "__comment": "Voxels-5E2F00", "from": [ 0.25, 0.25, 0 ], "to": [ 16, 15.25, 15.75 ], "faces": { "down": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" } } }, { "__comment": "Voxels-9B4E00", "from": [ 0.25, 0.25, 15.75 ], "to": [ 16.25, 15.25, 16 ], "faces": { "down": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" } } }, { "__comment": "Voxels-9B4E00", "from": [ 1.25, 16, 0.75 ], "to": [ 15, 16.25, 14.75 ], "faces": { "down": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" } } }, { "__comment": "Voxels-5E2F00", "from": [ 1.25, 16, 14.75 ], "to": [ 16.5, 16.5, 16.25 ], "faces": { "down": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" } } }, { "__comment": "Voxels-5E2F00", "from": [ 15, 16, 0.75 ], "to": [ 16.5, 16.25, 1 ], "faces": { "down": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" } } }, { "__comment": "Voxels-9B4E00", "from": [ 15, 16, 1 ], "to": [ 15.25, 16.25, 14.75 ], "faces": { "down": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" } } }, { "__comment": "Voxels-5E2F00", "from": [ 15.25, 16, 1 ], "to": [ 16.5, 16.5, 14.75 ], "faces": { "down": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" } } }, { "__comment": "Voxels-5E2F00", "from": [ 15.25, 16.25, 0.75 ], "to": [ 16.5, 16.5, 1 ], "faces": { "down": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 8.96, 0.96, 11.04, 3.04 ], "texture": "#texture" } } }, { "__comment": "Voxels-9B4E00", "from": [ 16, 0.25, 0 ], "to": [ 16.25, 15.25, 15.75 ], "faces": { "down": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "up": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "north": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "south": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "west": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" }, "east": { "uv": [ 12.96, 0.96, 15.04, 3.04 ], "texture": "#texture" } } } ], "display": { "gui": { "rotation": [ 30, 225, 0 ], "translation": [ -2.921, 1.6695, 0 ], "scale": [ 0.7, 0.7, 0.7 ] } } } Any help is greatly appreciated! I've stared at this for probably an hour straight and I can't seem to figure out what I did wrong here compared to other custom blocks I've made. Thanks!
-
Okay, I skimmed through that and it seemed like it might help with this. I'll give this a shot by tomorrow afternoon (currently 4 a.m. .-.). Thank you!
-
And then to make the staff appear like it's sitting in the weapon rack, I would combine those two models? Someone said up there ^ about joining/combining two things together. Also, thank you!
-
I guess honestly the best question right now would be: what is the most efficient way to do this https://gyazo.com/35419d1418cb792f1e48bf69810cabe4? Would it be via an IBakedModel or another way? I understand now that if I'm wanting animation, that would be through a TESR. But something simple like this (a weapon rack)...?
-
Oh okay. Is that also via an IBakedModel?
-
Hmmm, alright. I'll go look through vanilla to see if I can figure this out. I'll more than likely end up back on this thread, but we'll see how it goes
-
Thank you very much. That's the first really in-depth explanation I've seen. Here's why I ask. I'm using a TESR to do this: https://gyazo.com/35419d1418cb792f1e48bf69810cabe4 I've been told that this can be done more efficiently with an IBakedModel, but that's all I knew. I wasn't sure if the block would be an IBM, or the staff that's set "into" it. I couldn't find any tutorials on how to actually implement it correctly, etc. Would you happen to know of any resources that would offer a good explanation further into this, or perhaps would be willing to walk me through it or give me a general direction?
-
Hey there, So I'm trying to understand what IBakedModels are and why/where they should be used. I've searched these forums for threads relating to this, and the best explanation I can find so far of what they are, are that they replaced ISmartItemModel/ISmartBlockModel, but I don't even know what those are either. haha. Let's say I have a CustomBlock, that extends Block. Would an IBakedModel replace CustomBlock, or would it replace the TileEntity custom class I would make to give to CustomBlock? Or perhaps it replaces the TESR that I would create for those? I looked through the Forge Github docs and there was nothing there, and I've asked Google a bit and can't find too much there either. Can anyone offer a detailed explanation of what these really are and in what cases/why you would use these? What do they do? I've heard that it can replace TESR's if I'm needing something as simple as that "display"/"render" needing to only be rendered on block changes rather than every frame/tick. But I don't understand how to take that concept and run with it. Any guidance would be greatly appreciated!
-
Okay, awesome. This will definitely help. I appreciate it!
-
Perfect. You actually helped me solve a JSON problem with my block the other day Yeah, I bought Cubik Studio last week and I really like it. All this Forge stuff is starting to make more sense now. I came from a few years on Spigot, so all of this is really new. What do you mean by non dynamic animation? Wouldn't all animation inherently be dynamic in nature? .-. And the ModelBakery creates render data using a JSON or OBJ (thinking about it now, this is probably how something with super nice models like Pixelmon works, huh?), so what do you do with that render data? Is there some other render-outputting class that will then display it? Again, sorry for all the confusion. We didn't have to worry about anything like this with Spigot, but that's the trade off for creative freedom I suppose
-
Oooooh okay. So clientside animation needs to be done via TESR's? That's actually good, because I would've been back here in the future with that exact question. Alrighty, so I'll try and find a decent tutorial about the ModelBakery and see if I can piece together the code transition. Thanks for the help
-
Okay, that was a pretty good explanation. haha. Can you give an example of what you mean by "dynamically rendering a block"? Does that mean something along the lines of post-chunk loading? And off the top of your head, how do you do what she was saying? I couldn't find anything that seemed like "rendering" in the Block class. Or perhaps the ModelBakery that you spoke of is what she meant...? Hmmm
-
Hey there! So I know a forge developer who is quite talented and very knowledgeable, unfortunately, she's not online too often. I got to speak with her today and she mentioned that in the 1.10+ versions of Forge, block models can do nearly the the same thing that special renderers can, and more efficiently at that. Unfortunately, she wasn't online long today so I wasn't able to get much more info than that. Now I just went through the whole Block class and I didn't see anything that jumped out at me. I see there's a class called BlockModelRenderer, but not a class called BlockModel. Would anyone happen to know what she's talking about? Here's the current way I'm using a special renderer: https://gyazo.com/35419d1418cb792f1e48bf69810cabe4 Just a simple weapon rack (I know the weapon rotation and translation is off). Is there a more efficient way to do that with block models? Any help is appreciated!