Jump to content

Frepo

Vintarian
  • Posts

    68
  • Joined

  • Last visited

Posts posted by Frepo

  1. I made it do the opposite (increase fall damage). It's basically a custom made behavior for the player, that extends EntityBehavior and overrides the OnFallToGround method in there. But I'm not quite sure it completely replaces the fall damage mechanic (actually I think my method adds on top of the vanilla method), I wrote that a looong time ago with little experience. But it increased the damage for sure, so I was content and never looked back at it again. Maybe you can find something useful here.

    Patch json

    Spoiler
    [
    	// HEALTH BEHAVIOR OVERHAUL
    	{ 
    		"side": "Server", 
    		"file": "game:entities/humanoid/player", 
    		"op": "add", 
    		"path": "/client/behaviors/0", 
    		"value": { "code": "BehaviorFTHealth" }
    	},
    	{ 
    		"side": "Server", 
    		"file": "game:entities/humanoid/player", 
    		"op": "add", 
    		"path": "/server/behaviors/0", 
    		"value": { "code": "BehaviorFTHealth" }
    	}
    ]

     

    My custom behavior class

    Spoiler
    namespace fregtech
    {
        internal class BehaviorFTHealth : EntityBehavior
        {
            public BehaviorFTHealth(Entity entity) : base(entity)
            {
            }
    
            public override void Initialize(EntityProperties properties, JsonObject typeAttributes)
            {
                base.Initialize(properties, typeAttributes);
            }
    
            // FALL DAMAGE OVERHAUL
            public override void OnFallToGround(Vec3d positionBeforeFalling, double withYMotion)
            {
                float fallDmgMod = 1.8f;
                IInventory inv;
    
    
                if (!this.entity.Properties.FallDamage)
                {
                    return;
                }
                double yDistance = Math.Abs(positionBeforeFalling.Y - this.entity.Pos.Y);
                if (yDistance < 3.5)
                {
                    return;
                }
                if (withYMotion > -0.19)
                {
                    return;
                }
    
                // reduce from gear
                if(this.entity is EntityPlayer) // this.entity.Api.Side == EnumAppSide.Server
                {
                    EntityPlayer plr = this.entity as EntityPlayer;
                    // GearInventory = characterInventory:
                    // 0 = head (clothes), 1 = cape (clothes), 2 = over shirt (clothes), 3 = pants (clothes), 4 = boots (clothes), 5 = gloves (misc)
                    // 6 = necklace (misc), 7 = brooch (misc), 8 = mask (misc), 9 = belt (misc), 10 = bracelet (misc), 11 = under shirt (clothes)
                    // 12 = helmet (armor), 13 = body (armor), 14 = legs (armor)
                    inv = plr.GearInventory;
                    
                    if(inv != null)
                    {
                        // each piece of clothing reduces fall damage by 5%
                        if (!inv[0].Empty) {fallDmgMod -= 0.05f;}
                        if (!inv[1].Empty) { fallDmgMod -= 0.05f; }
                        if (!inv[2].Empty) { fallDmgMod -= 0.05f; }
                        if (!inv[3].Empty) { fallDmgMod -= 0.05f; }
                        if (!inv[4].Empty) { // boots
                            if (inv[4].Itemstack.GetName() == "Catfeet boots") { fallDmgMod -= 0.25f; }
                            else { fallDmgMod -= 0.05f; }
                        }
                        if (!inv[11].Empty) { fallDmgMod -= 0.05f; }
    
                        // legs armor
                        if (!inv[14].Empty)
                        {
    
                        }
                    }
                }
    
                if (fallDmgMod <= 0.0)
                {
                    return;
                }
    
                double fallDamage = yDistance - 3.5;
                double expectedYMotion = -0.04100000113248825 * Math.Pow(fallDamage, 0.75) - 0.2199999988079071;
                double yMotionLoss = Math.Max(0.0, -expectedYMotion + withYMotion);
                fallDamage -= 20.0 * yMotionLoss;
                if (fallDamage <= 0.0)
                {
                    return;
                }
                
                //// TESTING
                //if (this.entity.World.Side == EnumAppSide.Client)
                //{
                //    (this.entity.Api as ICoreClientAPI).TriggerIngameError(this, "error-msg", Lang.Get("Extra damage factor: " + fallDmgMod, Array.Empty<object>()));
                //}
    
                this.entity.ReceiveDamage(new DamageSource
                {
                    Source = EnumDamageSource.Fall,
                    Type = EnumDamageType.Gravity
                }, (float)fallDamage*fallDmgMod);
            }
    
            public override string PropertyName()
            {
                //throw new NotImplementedException();
                return "extrafalldamage";
            }
        }
    }

     

    Maybe you can just write something like this

    public override void OnFallToGround(Vec3d positionBeforeFalling, double withYMotion)
    {
    	return;
    }

    But again, I'm very unsure that this method completely replaces the vanilla mechanic.

     

    • Like 1
  2. With my current knowledge I think I would be able to get the player to being downed instead of dead at 0 hp. And thats about it :)
    What I would primarily need take a deep dive into is:
    - Animation (player crawling)
    - Getting some sort custom UI graphics displayed on the screeen (e.g. a revive-meter)
    - Restricting player controls

    But some day I will make this happen, if noone else already has.

  3. If you take a look in the BlockLantern class, you'll find the method GetLightHsv, which retrieves the light from the lantern.

    Spoiler
    		public override byte[] GetLightHsv(IBlockAccessor blockAccessor, BlockPos pos, ItemStack stack = null)
    		{
    			if (pos != null)
    			{
    				BELantern be = blockAccessor.GetBlockEntity(pos) as BELantern;
    				if (be != null)
    				{
    					return be.GetLightHsv();
    				}
    			}
    			if (stack != null)
    			{
    				string lining = stack.Attributes.GetString("lining", null);
    				stack.Attributes.GetString("material", null);
    				int v = (int)(this.LightHsv[2] + ((lining != "plain") ? 2 : 0));
    				byte[] lightHsv = new byte[]
    				{
    					this.LightHsv[0],
    					this.LightHsv[1],
    					(byte)v
    				};
    				BELantern.setLightColor(this.LightHsv, lightHsv, stack.Attributes.GetString("glass", null));
    				return lightHsv;
    			}
    			return base.GetLightHsv(blockAccessor, pos, stack);
    		}

     

    And in this method is a call to it's entity class, BELantern:
    BELantern.setLightColor(this.LightHsv, lightHsv, stack.Attributes.GetString("glass", null));

    Where the glass attribute is passed in. And as the name implies, this is where the light is set depending on the glass.

    Spoiler
    		public static void setLightColor(byte[] origLightHsv, byte[] lightHsv, string color)
    		{
    			if (color != null)
    			{
    				switch (color.Length)
    				{
    				case 3:
    					if (color == "red")
    					{
    						lightHsv[0] = 0;
    						lightHsv[1] = 4;
    						return;
    					}
    					break;
    				case 4:
    				{
    					char c = color[0];
    					if (c != 'b')
    					{
    						if (c == 'p')
    						{
    							if (color == "pink")
    							{
    								lightHsv[0] = 54;
    								lightHsv[1] = 4;
    								return;
    							}
    						}
    					}
    					else if (color == "blue")
    					{
    						lightHsv[0] = 42;
    						lightHsv[1] = 4;
    						return;
    					}
    					break;
    				}
    				case 5:
    				{
    					char c = color[0];
    					if (c != 'b')
    					{
    						if (c == 'g')
    						{
    							if (color == "green")
    							{
    								lightHsv[0] = 20;
    								lightHsv[1] = 4;
    								return;
    							}
    						}
    					}
    					else if (color == "brown")
    					{
    						lightHsv[0] = 5;
    						lightHsv[1] = 4;
    						return;
    					}
    					break;
    				}
    				case 6:
    				{
    					char c = color[0];
    					if (c != 'v')
    					{
    						if (c == 'y')
    						{
    							if (color == "yellow")
    							{
    								lightHsv[0] = 11;
    								lightHsv[1] = 4;
    								return;
    							}
    						}
    					}
    					else if (color == "violet")
    					{
    						lightHsv[0] = 48;
    						lightHsv[1] = 4;
    						return;
    					}
    					break;
    				}
    				}
    			}
    			lightHsv[1] = origLightHsv[1];
    			lightHsv[0] = origLightHsv[0];
    		}

     

    Also in BELantern is where the player sets the color by interaction.

    Spoiler
    		internal bool Interact(IPlayer byPlayer)
    		{
    			ItemSlot slot = byPlayer.InventoryManager.ActiveHotbarSlot;
    			if (slot.Empty)
    			{
    				return false;
    			}
    			CollectibleObject obj = slot.Itemstack.Collectible;
    			if (obj.FirstCodePart(0) == "glass" && obj.Variant.ContainsKey("color"))
    			{
    				if (this.glass != "quartz" && byPlayer.WorldData.CurrentGameMode != EnumGameMode.Creative)
    				{
    					ItemStack stack = new ItemStack(this.Api.World.GetBlock(new AssetLocation("glass-" + this.glass)), 1);
    					if (!byPlayer.InventoryManager.TryGiveItemstack(stack, true))
    					{
    						this.Api.World.SpawnItemEntity(stack, this.Pos.ToVec3d().Add(0.5, 0.0, 0.5), null);
    					}
    				}
    				this.glass = obj.Variant["color"];
    				if (byPlayer.WorldData.CurrentGameMode != EnumGameMode.Creative && this.glass != "quartz")
    				{
    					slot.TakeOut(1);
    				}
    				if (this.Api.Side == EnumAppSide.Client)
    				{
    					(byPlayer as IClientPlayer).TriggerFpAnimation(EnumHandInteract.HeldItemInteract);
    				}
    				Vec3d soundpos = this.Pos.ToVec3d().Add(0.5, 0.0, 0.5);
    				this.Api.World.PlaySoundAt(this.Api.World.GetBlock(new AssetLocation("glass-" + this.glass)).Sounds.Place, soundpos.X, soundpos.Y, soundpos.Z, byPlayer, true, 32f, 1f);
    				BELantern.setLightColor(this.origlightHsv, this.lightHsv, this.glass);
    				this.Api.World.BlockAccessor.ExchangeBlock(base.Block.Id, this.Pos);
    				this.MarkDirty(true, null);
    				return true;
    			}
    			if (this.lining == null || (this.lining == "plain" && obj is ItemMetalPlate && (obj.Variant["metal"] == "gold" || obj.Variant["metal"] == "silver")))
    			{
    				this.lining = obj.Variant["metal"];
    				if (this.Api.Side == EnumAppSide.Client)
    				{
    					(byPlayer as IClientPlayer).TriggerFpAnimation(EnumHandInteract.HeldItemInteract);
    				}
    				Vec3d soundpos2 = this.Pos.ToVec3d().Add(0.5, 0.0, 0.5);
    				this.Api.World.PlaySoundAt(new AssetLocation("sounds/block/plate"), soundpos2.X, soundpos2.Y, soundpos2.Z, byPlayer, true, 32f, 1f);
    				slot.TakeOut(1);
    				this.MarkDirty(true, null);
    				return true;
    			}
    			return false;
    		}

     


     

    • Like 1
    • Thanks 1
  4. @Spear and Fang Have you made any machines that uses behaviors based on the BEBehaviorMPConsumer?

    Where the autosifter differ from the toggle is that it's behavior is BEBehaviorMPConsumer, where the toggle is just BEBehaviorMPBase, just like your passthrough axle. 
    In the Initialize method ( in BEBehaviorMPConsumer) there's a line that caught my eye;

    this.Shape = properties["mechPartShape"].AsObject<CompositeShape>(null);

    If don't know if this property is set anywhere, can't find anything when I search the word. So I was wondering if you know where this is set, or if I need to set this myself somewhere.

  5. Wouldn't it be nice if players could revive each other?
    - The player enters a state of being downed when hp reaches 0 (or a configurable chance for this happening).
    - While downed: You can't do anything except slowly crawl around (no jumping).
    - You die after 60 seconds (configurable), if no friend was able to revive you during this window.
    - Revival takes a couple of seconds to perform (hold interact button with a bandage or any healing item in hand)

    Not a new concept by any means, but I think it would be super nice to get a chance to save your friend when out on long expeditions far far away from home.
    Also, it's fun! These situations tend to get quite intense, being on a timer to save your friend.

  6. I'm doing some fixing of the auto-sifter mod, since I love the machine and dev is discontinued, and also to start learning how to mod for the MP-system.
    Currently I'm stuck at combining the static shape model with the animated/rotating part. Only the static model shows up in-game. 

    The code for the wooden toggle is used as basis for this machine, since they both only connect horizontally ("ns", "we"), and don't have inventories and such.
    I've split the model into 3 separate models

    autosifter-inventory.json is the complete model (left pic), autosifter-stand.json is the static part (middle), and autosifter-moving.json is the rotating part (right)
    image.png.49b9a42c80c0ad053ed57efd58e71998.png image.png.a02f3491a4c5994ee5b5a791d58714b8.png image.png.ab437d6c16cb67e5a9048643e52932a1.png

    As far as I understood, the shape is set to the moving one in the json for BlockAutoSifter (at shapeByType)

    Spoiler
    {
    	code: "tieredautosifter",
    	class: "BlockAutoSifter",
    	entityclass: "AutoSifterBlockEntity",
    	behaviors: [{name: "NWOrientable" }],
    	entityBehaviors: [{ name: "MPSifter" }],
    	creativeinventory: { "general": ["*-ns"],"mechanics": ["*-ns"] },
    	variantgroups: [
    		{ code:"metal", states: ["steel","meteoriciron","iron","blackbronze","bismuthbronze","tinbronze","empty"]},
    		{ code:"orientation", states: ["ns", "we"]},
    	],
    	blockmaterial: "Metal",
    	shapeInventory: { base: "block/autosifter-inventory" },
    	shapeByType: {
    		"*-ns": { base: "block/autosifter-moving", rotateY: 90 },
    		"*-we": { base: "block/autosifter-moving", rotateY: 0 },
    	},
    	textures: {
    		grate: { base: "grate_{metal}" },
    		lip: { base: "ingot/{metal}"},
    		metalbase: { base: "metalbase"},
    		metalalts: { base: "metalalts"},
    		axle: { base: "game:block/wood/planks/generic" },
    	},
    	drops: [
    		{ type: "block", code: "tieredautosifter-{metal}-ns", quantity: { avg: 1 }  }
    	],
    	sidesolid: {
    		all: false
    	},
    	sideopaque: {
    		all: false
    	},
    	attributes: {
    		maxblocksByType: {
    			"tieredautosifter-steel-*": 7,
    			"tieredautosifter-meteoriciron-*": 4,
    			"tieredautosifter-iron-*": 3,
    			"tieredautosifter-blackbronze-*": 2,
    			"tieredautosifter-bismuthbronze-*": 1,
    			"tieredautosifter-tinbronze-*": 1
    		}
    	},
    	resistance: 3.5,
    	lightAbsorption: 1,
    	maxStackSize: 1,
    	groundTransform: {
    		translation: { x: 0, y: 0, z: 0 },
    		rotation: { x: -90, y: 0, z: 0 },
    		origin: { x: 0.5, y: 0.45, z: 0.38 },
    		scale: 2.7
    	},
    	guiTransform: {
    		rotation: { x: -43, y: 45, z: 1 },
    		scale: 0.96
    	},
    	tpHandTransform: {
    		translation: { x: -1.2, y: -0.4, z: -0.6 },
    		rotation: { x: -90, y: 11, z: -103 },
    		origin: { x: 0.5, y: 0.25, z: 0.5 },
    		scale: 0.42
    	},
    	collisionSelectionBoxByType: {
    		"*-we": { x1: 0, y1: 0.34375, z1: 0, x2: 1, y2: 1, z2: 1 },
    		"*-ns": { x1: 0, y1: 0.34375, z1: 0, x2: 1, y2: 1, z2: 1, rotateY: 90 },
    	},
    	sounds: {
    		"place": "game:sounds/block/chute",
    		"walk": "game:sounds/walk/stone"
    	}
    }

     

    Here is the code for BlockAutoSifter, it's basically ripped off the vanilla BlockToggle:

    Spoiler
    namespace fregtech
    {
        public class BlockAutoSifter : BlockMPBase
        {
            public bool IsOrientedTo(BlockFacing facing)
            {
                string dirs = base.LastCodePart(0);
                return dirs[0] == facing.Code[0] || (dirs.Length > 1 && dirs[1] == facing.Code[0]);
    
            }
    
    
            public override bool HasMechPowerConnectorAt(IWorldAccessor world, BlockPos pos, BlockFacing face)
            {
                return this.IsOrientedTo(face);
            }
    
    
            public override bool TryPlaceBlock(IWorldAccessor world, IPlayer byPlayer, ItemStack itemstack, BlockSelection blockSel, ref string failureCode)
            {
    
                if (!this.CanPlaceBlock(world, byPlayer, blockSel, ref failureCode))
                {
                    return false;
                }
    
                foreach (BlockFacing face in BlockFacing.HORIZONTALS)
                {
                    BlockPos pos = blockSel.Position.AddCopy(face);
                    IMechanicalPowerBlock block = world.BlockAccessor.GetBlock(pos) as IMechanicalPowerBlock;
                    if (block != null && block.HasMechPowerConnectorAt(world, pos, face.Opposite))
                    {
                        AssetLocation loc = new AssetLocation(this.Code.Domain, base.FirstCodePart(0) + "-" + base.FirstCodePart(1) + "-" + face.Opposite.Code[0].ToString() + face.Code[0].ToString());
                        Block toPlaceBlock = world.GetBlock(loc);
                        if (toPlaceBlock == null)
                        {
                            loc = new AssetLocation(this.Code.Domain, base.FirstCodePart(0) + "-" + base.FirstCodePart(1) + "-" + face.Code[0].ToString() + face.Opposite.Code[0].ToString());
                            toPlaceBlock = world.GetBlock(loc);
                        }
    
                        if (toPlaceBlock != null && toPlaceBlock.DoPlaceBlock(world, byPlayer, blockSel, itemstack))
                        {
                            block.DidConnectAt(world, pos, face.Opposite);
                            this.WasPlaced(world, blockSel.Position, face);
                            return true;
                        }
                    }
                }
    
                if (base.TryPlaceBlock(world, byPlayer, itemstack, blockSel, ref failureCode))
                {
                    this.WasPlaced(world, blockSel.Position, null);
                    return true;
                }
                return false;
            }
    
    
            public override void OnNeighbourBlockChange(IWorldAccessor world, BlockPos pos, BlockPos neibpos)
            {
                BlockEntity blockEntity = world.BlockAccessor.GetBlockEntity(pos);
                BEBehaviorMPSifter bempsifter = (blockEntity != null) ? blockEntity.GetBehavior<BEBehaviorMPSifter>() : null;
                if (bempsifter != null && !bempsifter.IsAttachedToBlock())
                {
                    foreach (BlockFacing face in BlockFacing.HORIZONTALS)
                    {
                        BlockPos npos = pos.AddCopy(face);
                        BlockAngledGears blockagears = world.BlockAccessor.GetBlock(npos) as BlockAngledGears;
                        if (blockagears != null && blockagears.Facings.Contains(face.Opposite) && blockagears.Facings.Length == 1)
                        {
                            world.BlockAccessor.BreakBlock(npos, null, 1f);
                        }
                    }
                }
                base.OnNeighbourBlockChange(world, pos, neibpos);
            }
    
            public override void DidConnectAt(IWorldAccessor world, BlockPos pos, BlockFacing face)
            {
            }
        }
    }

     

    Here is the code for the BEBehaviorMPSifter, which is mostly ripped off the BEBehaviorMPToggle. Here in lies the problem, I think, probably in the getStandMesh and/or OnTesselation methods. Instead of adding the autosifter-stand model to the autosifter-moving model, it replaces it. And I have no idea why.
     

    Spoiler
    namespace fregtech
    {
        public class BEBehaviorMPSifter : BEBehaviorMPConsumer
        {
            public BEBehaviorMPSifter(BlockEntity blockentity) : base(blockentity)
            {
            }
    
            public override void Initialize(ICoreAPI api, JsonObject properties)
            {
                base.Initialize(api, properties);
    
                string domainAndPath = "block/autosifter-stand.json";
                AssetLocation code = base.Block.Code;
                this.sifterStandLoc = AssetLocation.Create(domainAndPath, (code != null) ? code.Domain : "fregtech");
                JsonObject attributes = base.Block.Attributes;
                if (attributes != null && attributes["sifterStandLoc"].Exists)
                {
                    JsonObject attributes2 = base.Block.Attributes;
                    this.sifterStandLoc = ((attributes2 != null) ? attributes2["sifterStandLoc"].AsObject<AssetLocation>(null) : null);
                }
                this.sifterStandLoc.WithPathPrefixOnce("shapes/").WithPathAppendixOnce(".json");
    
                if (api.Side == EnumAppSide.Client)
                {
                    this.capi = (api as ICoreClientAPI);
                }
                this.orientations = this.Block.Variant["orientation"];
                string a = this.orientations;
                if (a == "ns")
                {
                    this.AxisSign = new int[] { 0, 0, -1 };
                    this.orients[0] = BlockFacing.NORTH;
                    this.orients[1] = BlockFacing.SOUTH;
                    this.sides[0] = this.Position.AddCopy(BlockFacing.WEST);
                    this.sides[1] = this.Position.AddCopy(BlockFacing.EAST);
                    return;
                }
                if (!(a == "we"))
                {
                    return;
                }
                int[] array = new int[3];
                array[0] = -1;
                this.AxisSign = array;
                this.orients[0] = BlockFacing.WEST;
                this.orients[1] = BlockFacing.EAST;
                this.sides[0] = this.Position.AddCopy(BlockFacing.NORTH);
                this.sides[1] = this.Position.AddCopy(BlockFacing.SOUTH);
            }
    
            public override float GetResistance()
            {
                this.Api.World.BlockAccessor.GetBlockEntity(this.Position);
                float speed = (this.network == null) ? 0f : Math.Abs(this.network.Speed * base.GearedRatio);
                float speedLimiter = 5f * (float)Math.Exp((double)speed * 2.8 - 5.0);
                Block blockabove = this.Api.World.BlockAccessor.GetBlock(this.Position.AddCopy(0, 1, 0));
                if (blockabove.Attributes != null && blockabove.Attributes.IsTrue("pannable"))
                {
                    return 0.125f + speedLimiter;
                }
                return (0.125f + speedLimiter) * 0.5f;
            }
    
            public override void JoinNetwork(MechanicalNetwork network)
            {
                base.JoinNetwork(network);
                float speed = (network == null) ? 0f : (Math.Abs(network.Speed * base.GearedRatio) * 1.6f);
                if (speed > 1f)
                {
                    network.Speed /= speed;
                    network.clientSpeed /= speed;
                }
            }
    
            public bool IsAttachedToBlock()
            {
                return (this.orientations == "ns" || this.orientations == "we") && (this.Api.World.BlockAccessor.IsSideSolid(this.Position.X, this.Position.Y - 1, this.Position.Z, BlockFacing.UP) || this.Api.World.BlockAccessor.IsSideSolid(this.Position.X, this.Position.Y + 1, this.Position.Z, BlockFacing.DOWN));
            }
    
            private MeshData getStandMesh(string orient)
            {
                ICoreAPI api = this.Api;
                AssetLocation code = base.Block.Code;
                return ObjectCacheUtil.GetOrCreate<MeshData>(api, ((code != null) ? code.ToString() : null) + "-" + orient + "-stand", delegate
                {
                    Shape shape = Vintagestory.API.Common.Shape.TryGet(this.capi, this.sifterStandLoc);
                    MeshData mesh;
                    this.capi.Tesselator.TesselateShape(this.Block, shape, out mesh, null, null, null);
                    if (orient == "ns")
                    {
                        mesh.Rotate(new Vec3f(0.5f, 0.5f, 0.5f), 0f, 1.5707964f, 0f);
                    }
                    return mesh;
                });
            }
    
            public override bool OnTesselation(ITerrainMeshPool mesher, ITesselatorAPI tesselator)
            {
                MeshData mesh = this.getStandMesh(base.Block.Variant["orientation"]);
                mesher.AddMeshData(mesh, 1);
                return base.OnTesselation(mesher, tesselator);
            }
    
            protected readonly BlockFacing[] orients = new BlockFacing[2];
    
            protected readonly BlockPos[] sides = new BlockPos[2];
    
            private ICoreClientAPI capi;
    
            private string orientations;
    
            private AssetLocation sifterStandLoc;
        }
    }

     

    So this is the end result... the machine functions as intended (guess the BlockEntity code is OK), but the wooden axle is nowhere to be seen.
    image.png.64856da4b09066de91f9120f2db6b3a0.png

  7. Me and some friends play with a "hardcore" modpack I put together, I've added some stuff myself, tweaked many of the mods and vanilla mechanics.
    If you wan't, you can give me your mail and I'll send you the modpack.

    Settings i strongly recommend with this pack:
    - Hunger: 125%
    - Creature Strength: 150%
    - Global deposit spawn rate: 80%
    - Worldmap and coordinates: OFF (very important)
    - Soil sand and gravel gravity with side-ways instability: ON (very important)

    Some notes on what's tweaked and changed:
    - When you die; Each item in your inventory (and hotbar and worn) have 50% chance to be deleted
    - Food tweaks (red meat nerfed, animal drops nerfed, bread buffed)
    - Many recipes changed to "weave together" different mods, but mostly to make stuff more costly
    - Raw grass, must be dried before you get dry grass
    - Tool tier system changed (stone -> copper -> tin/bismuth bronze -> black bronze -> iron -> steel -> titanium)
    - Sleeping made harder, low tier beds can break after use, can't sleep in rain or if it's too cold (make a fire and build better beds to mitigate the cold)
    - All rocks have a small chance of coming loose and fall when mining a nearby block, deals quite a bit of damage if it lands on you
    - Increased fall damage (+70%!!!), each piece of clothing reduces fall damage by 5%
    - Can't break cobblestone with hands anymore
    - Can't place water sources with buckets anymore
    - Drifters spawn everywhere!
    - More dealy wild animals

    Notable mods in the pack:
    - Primitive Surivial
    - Medieval Expansion
    - Better Crates
    - Better Ruins
    - Compass2
    - Dungeons and Shafts
    - Feverstone Wilds
    - Kreatures and Kritters
    - Farm To Table (fixed)
    - Ranged Weapons
    - Titanium Tools
    - Lichen
    - NoWaterproofInventories (crashes the game since .NET7 update, I might have fixed it, not sure yet, just remove if crashes the game)

    + much much more

    • Amazing! 1
  8. Structure like this.

    E.g. Location of Vanilla file you want to replace:
    ..\Vintagestory\assets\survival\worldgen\treengenproperties.json

    Make a json (copy original) to this location in your mod to use that one instead:
    ..\MyMod_v1.0\assets\game\worldgen\treengenproperties.json

    It's kinda confusing, since the files we are looking to replace are placed in the folder named "survival", one would think we should also name our folder the same, but no, it's "game" for some reason. But I don't question it, it works for me :)

    • Thanks 1
  9. I can't get this recipe to work

    	{
    		ingredientPattern: "TP",
    		ingredients: {
    			"T": { type: "block", code: "fregtech:fttrunkmetal-east", attributes: { type: "reinforced" } },
    			"P": { type: "block", code: "game:woodbucket" },
    		},
    		width: 2,
    		height: 1,
    		shapeless: true,
    		attributes: {
    				liquidContainerProps: {
    					requiresContent: { type: "item", code: "game:dye-blue" },
    					requiresLitres: 1.0
    				}
    		},
    		output: { type: "block", code: "fregtech:fttrunkmetal-east", attributes: { type: "reinforcedblue" } }
    	},

    Is something written wrong or is it simply not possible to combine several items with attributes in the same recipe?

    Coz it works fine like e.g. this:

    Spoiler
    {
    	ingredientPattern: "TP",
    	ingredients: {
    		"T": { type: "block", code: "fregtech:fttrunkmetal-east", attributes: { type: "reinforced" } },
    		"P": { type: "item", code: "game:leather-blue", quantity: 1 },
    	},
    	width: 2,
    	height: 1,
    	shapeless: true,
    	output: { type: "block", code: "fregtech:fttrunkmetal-east", attributes: { type: "reinforcedblue" } }
    },

     

     

  10. Re-installed Visual Studio and made sure the .NET desktop development workload is installed as well.

    image.png.3394c871e37c122a84bad67db1ee93e8.png

    Still same error. I'm not accustomed to visual studio at all, but I noticed that VintagestoryAPI.dll is compiled against an older runtime version of .NET (v4.x).

    image.png.0fc700b8e21bf7502bded4e0ca65163a.png

    This must be because VS i targeting a 4.x-framework (4.8 in my case), right? So changing the target framework to 7.x (if I only could) should recompile the assembly against that version instead, right?

  11. I just got back to modding again after a long time away. I noticed the game is now .NET7-based. After downloading the SDK I started my old project and tried a rebuild, and got this error:

    image.thumb.png.cc7c3ec9f97f3fdccb8dc34472acbbf2.png

    My mod still works fine after I updated the game (no changes whatsoever to the mod). I'm not sure what to make of this error. I realized my project wasn't targeting the .NET7 framework. I guess that must be the cause of the error. Problem is, I can't select the .NET7 framework in my project settings. It simply isn't there! 

    image.png.75b073334c8a67705a02afd35645617e.png

    The SDK seems to be installed and working.
    image.png.96390c324c849baae8c73077f6fbfd2b.png

    Visual Studio is up to date.
    image.png.b68d6cbb62400b2bd5977aa282454ec4.png

    Someone at StackOverflow mentioned you should check the box "Use previews of the .NET SDK"-box, and so I did.
    image.png.d568b954b817e1d750d25c990f6aedb9.png

     

    Is there anything else I need to do to make VS find the new framework version?

  12. Love the idea of more hazards in the environment. In addition to you venus flytrap I would also like to see a plant that can shoot some sort of projectile at you.
    And puffball mushrooms that, when stepped on, releases a poisonous cloud.

  13. Are there any loot tables (JSONs) for the collapsed chests found underground, where we can add custom loot?

    I found the loot tables for the urns in BlockLootVessel. But the collapsed chests are just variants of the standard chest you craft, and there seems to be no loot randomizing in BlockGenericTypedContainer or BlockEntityGenericTypedContainer (nor in their respective sub-classes).

  14. Anyone adept in harmony transpiler patches? I want to insert a new tier level between bronze and iron, so the progression goes ... -> tin/bismuth bronze -> black bronze -> iron -> ....
    The relevant jsons have been patched (changing miningTiers and toolTiers) and working fine. But I need to add the tier to a list in GetPlacedBlockInfo (in Block class):
    image.png.362222272b843391475e1e7e598eb5de.png so the code becomes -> image.png.7aab77dd901368b9b36d65f352d902eb.png

    So I thought it would be nice to accomplish this with a small transpiler patch. But I'm unsure how to go about this. Studying the IL-code for the list

    Spoiler
    	/* 0x000001D5 1D           */ IL_01D5: ldc.i4.7
    	/* 0x000001D6 8D????????   */ IL_01D6: newarr    [System.Private.CoreLib]System.String
    	/* 0x000001DB 25           */ IL_01DB: dup
    	/* 0x000001DC 16           */ IL_01DC: ldc.i4.0
    	/* 0x000001DD 72????????   */ IL_01DD: ldstr     "tier_hands"
    	/* 0x000001E2 28????????   */ IL_01E2: call      !!0[] [System.Private.CoreLib]System.Array::Empty<object>()
    	/* 0x000001E7 286C080006   */ IL_01E7: call      string Vintagestory.API.Config.Lang::Get(string, object[])
    	/* 0x000001EC A2           */ IL_01EC: stelem.ref
    	/* 0x000001ED 25           */ IL_01ED: dup
    	/* 0x000001EE 17           */ IL_01EE: ldc.i4.1
    	/* 0x000001EF 72????????   */ IL_01EF: ldstr     "tier_stone"
    	/* 0x000001F4 28????????   */ IL_01F4: call      !!0[] [System.Private.CoreLib]System.Array::Empty<object>()
    	/* 0x000001F9 286C080006   */ IL_01F9: call      string Vintagestory.API.Config.Lang::Get(string, object[])
    	/* 0x000001FE A2           */ IL_01FE: stelem.ref
    	/* 0x000001FF 25           */ IL_01FF: dup
    	/* 0x00000200 18           */ IL_0200: ldc.i4.2
    	/* 0x00000201 72????????   */ IL_0201: ldstr     "tier_copper"
    	/* 0x00000206 28????????   */ IL_0206: call      !!0[] [System.Private.CoreLib]System.Array::Empty<object>()
    	/* 0x0000020B 286C080006   */ IL_020B: call      string Vintagestory.API.Config.Lang::Get(string, object[])
    	/* 0x00000210 A2           */ IL_0210: stelem.ref
    	/* 0x00000211 25           */ IL_0211: dup
    	/* 0x00000212 19           */ IL_0212: ldc.i4.3
    	/* 0x00000213 72????????   */ IL_0213: ldstr     "tier_bronze"
    	/* 0x00000218 28????????   */ IL_0218: call      !!0[] [System.Private.CoreLib]System.Array::Empty<object>()
    	/* 0x0000021D 286C080006   */ IL_021D: call      string Vintagestory.API.Config.Lang::Get(string, object[])
    	/* 0x00000222 A2           */ IL_0222: stelem.ref
    	/* 0x00000223 25           */ IL_0223: dup
    	/* 0x00000224 1A           */ IL_0224: ldc.i4.4
    	/* 0x00000225 72????????   */ IL_0225: ldstr     "tier_iron"
    	/* 0x0000022A 28????????   */ IL_022A: call      !!0[] [System.Private.CoreLib]System.Array::Empty<object>()
    	/* 0x0000022F 286C080006   */ IL_022F: call      string Vintagestory.API.Config.Lang::Get(string, object[])
    	/* 0x00000234 A2           */ IL_0234: stelem.ref
    	/* 0x00000235 25           */ IL_0235: dup
    	/* 0x00000236 1B           */ IL_0236: ldc.i4.5
    	/* 0x00000237 72????????   */ IL_0237: ldstr     "tier_steel"
    	/* 0x0000023C 28????????   */ IL_023C: call      !!0[] [System.Private.CoreLib]System.Array::Empty<object>()
    	/* 0x00000241 286C080006   */ IL_0241: call      string Vintagestory.API.Config.Lang::Get(string, object[])
    	/* 0x00000246 A2           */ IL_0246: stelem.ref
    	/* 0x00000247 25           */ IL_0247: dup
    	/* 0x00000248 1C           */ IL_0248: ldc.i4.6
    	/* 0x00000249 72????????   */ IL_0249: ldstr     "tier_titanium"
    	/* 0x0000024E 28????????   */ IL_024E: call      !!0[] [System.Private.CoreLib]System.Array::Empty<object>()
    	/* 0x00000253 286C080006   */ IL_0253: call      string Vintagestory.API.Config.Lang::Get(string, object[])
    	/* 0x00000258 A2           */ IL_0258: stelem.ref
    	/* 0x00000259 1307         */ IL_0259: stloc.s   tiers

     

    I get the feeling I can't just insert my code before IL_0225: ldstr     "tier_iron", since this would mess up the indexing, right? In that case, I would need to replace each subsequent ldc.i4.x instruction with x+1? Or must I replace all the IL-code for the entire list? If so, how do I replace this list? By replacing each instruction with a nop and then adding all the code again, including the new index?

  15. Yeah it's not supposed to be a game changer by any means, merely a "quality of life" tool.
    I would love to have one when firing up my 16 clay ovens, takes like half a minute to light them all with the torch :D

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.