Block Mod Tutorial (for v0.4) [spaar`s modloader]

Discussion in 'Besiege: Modding' started by TheGuysYouDespise, Feb 20, 2016.

  1. Block Mod Info

    Installation of Block Mods:

    0. Delete any previous dll file of the block.
    1. Make sure you have the newest spaar's modloader.
    2. Put block zip/rar archive in your Mods directory under your Besiege installation.
    3. Right click the zip archive and click "Extract Here". (This options comes with WinRAR, which you also need for .rar archives in general)
    Another way is to copy all the contents of the zip archive and put it in the Besiege\Besiege_Data\Mods folder for PC.
    4. If you should get a prompt to merge folders, accept it, overwrite any duplicate files.


    The War Drum [ID: 100]:

    Press B to strike the drum, or use any mechanism to hit it.
    It has a slider determining the power, from 0.5 to 2.0. Power defines both range and force.
    The refraction shockwave can be toggled as well.

    The War Horn [ID: 300]:

    Press H to sound the Horn, or use steam to play them.
    It has a slider determining the pitch, from 0.5 to 2.0. Pitch is by default locked to musical semitones with a toggle.
    There is a slider for volume as well.




    Making Blocks:
    For all you modders, and future modders here's how to make a block mod:

    If you want to figure out the API first, and try to do some programming before making visuals, I've attached some testing resources you can use.
    Then use those and skip the first list, and return to that later.

    First make your block visuals:
    1. Get working on a block in your favorite 3d modelling software.
    2. Try going for 1 unit to be equal to the length of a standard block in besiege, i.e. if you want to make a block that is 3 times as big as the ballast make it be 3 units in each of the appropriate dimensions.
    3. UV map the 3d model
    4. Make a texture for the block matching the UV map.
    5. Export the 3d as an .obj file.
    6. Put the .obj in the Mods/Blocks/Obj folder.
    7. Put the texture image in the Mods/Blocks/Textures folder.

    Get to the mod script:
    1. Get your spaar's modloader now with the blockloader in it.
    2. Make a new mod as you would for spaar's modloader.
    2.a. Set the target framework to be .NET 3.5, or you might have problems with some stuff.
    3. Make sure you have references to "Assembly-CSharp", "Assembly-CSharp-firstpass", "Assembly-UnityScript", "Assembly-UnityScript-firstpass", and "spaarModLoader".
    4. Use this example as a template:

    BlockMod Example
    Code:
    using System;
    using System.Collections.Generic;
    using spaar.ModLoader;
    using TheGuysYouDespise;
    using UnityEngine;
    
    namespace Blocks
    {
        public class LoadExampleBlock : BlockMod
        {
            /// ModLoader stuff
            /// Set name, author and so on for the blockloader to know what you made.
            public override string Name { get { return "hornBlock"; } }
            public override string DisplayName { get { return "War Horn Block"; } }
            public override string Author { get { return "TheGuysYouDespise"; } }
            public override Version Version { get { return new Version("0.1"); } }
    
            /// <Block-loading-info>
            /// Place .obj file in Mods/Blocks/Obj
            /// Place texture in Mods/Blocks/Textures
            /// Place any additional resources in Mods/Blocks/Resources
            /// </Block-loading-info>
    
            protected Block example = new Block()
                ///ID of the Block
                .ID(300)
    
                ///Name of the Block
                .BlockName("War Horn")
    
                ///Load the 3d model information
                .Obj(new List<Obj> { new Obj("warHorn.obj", //Mesh name with extension (only works for .obj files)
                                             "warHorn.png", //Texture name with extension
                                             new VisualOffset(new Vector3(1f, 1f, 1f), //Scale
                                                              new Vector3(0f, 0f, 0f), //Position
                                                              new Vector3(0f, 0f, 0f)))//Rotation
                })
    
                ///For the button that we will create setup the visual offset needed
                .IconOffset(new Icon(new Vector3( 1.30f,   1.30f,  1.30f),  //Scale
                                     new Vector3(-0.11f,  -0.13f,  0.00f),  //Position
                                     new Vector3(  85f,      90f,   270f))) //Rotation
    
                ///Script, Components, etc. you want to be on your block.
                .Components(new Type[] {
                                        typeof(HornBlock),
                })
    
                ///Properties such as keywords for searching and setting up how how this block behaves to other elements.
                .Properties(new BlockProperties().SearchKeywords(new string[] {
                                                                 "Music",
                                                                 "Horn",
                                                                 "War",
                                                                 "War Horn",
                                                                 "Trumpet",
                                                                 "Sound",
                                                 })
                                                 //.Burnable(3f)
                                                 //.CanBeDamaged(3)
                )
    
                ///Mass of the block 0.5 being equal to a double wooden block
                .Mass(0.3f)
    
                ///Display the collider while working on the block if you wish, then replace "true" with "false" when done looking at the colliders.
                .ShowCollider(false)
    
                ///Setup the collier of the block, which can consist of several different colliders.
                ///Therefore we have this CompoundCollider,
                .CompoundCollider(new List<ColliderComposite> {
                                    ColliderComposite.Sphere(0.49f,                                //radius
                                                             new Vector3(-0.10f, -0.05f, 0.27f),   //Position
                                                             new Vector3(0f, 0f, 0f))              //Rotation
                                                             .IgnoreForGhost(),                    //Do not use this collider on the ghost
    
                                    ColliderComposite.Capsule(0.33f,                               //radius
                                                              1.33f,                               //length
                                                              Direction.Y,                         //direction
                                                              new Vector3(-0.52f, 0.38f, 0.30f),   //position
                                                              new Vector3(5f, 0f, -5f)),           //rotation
                            
                                    ColliderComposite.Capsule(0.20f,                               //radius
                                                              0.90f,                               //length
                                                              Direction.X,                         //direction
                                                              new Vector3(-0.1f, -0.36f, 0.40f),   //position
                                                              new Vector3(0f, 0f, 0f)),            //rotation
                            
                                    ColliderComposite.Box(new Vector3(0.65f, 0.65f, 0.25f),        //scale
                                                          new Vector3(0f, 0f, 0.25f),              //position
                                                          new Vector3(0f, 0f, 0f)),                //rotation
    
                                    ColliderComposite.Box(new Vector3(0.5f, 0.5f, 0.10f),          //scale
                                                          new Vector3(-0.38f, 1.05f, 0.34f),       //position
                                                          new Vector3(312f, 25f, 337.5f)),         //rotation
                            
                                    ColliderComposite.Box(new Vector3(0.5f, 0.5f, 0.10f),          //scale
                                                          new Vector3(-0.38f, 1.05f, 0.34f),       //position
                                                          new Vector3(312f, 35f, 10f)),            //rotation
                            
                                    ColliderComposite.Sphere(0.5f,                                  //radius
                                                             new Vector3(-0.10f, -0.05f, 0.35f),    //Position
                                                             new Vector3(0f, 0f, 0f))               //Rotation
                                                             .Trigger().Layer(2)
                                                             .IgnoreForGhost(),                     //Do not use this collider on the ghost
                                  //ColliderComposite.Box(new Vector3(0.35f, 0.35f, 0.15f), new Vector3(0f, 0f, 0f), new Vector3(0f, 0f, 0f)).Trigger().Layer(2).IgnoreForGhost(),   <---Example: Box Trigger on specific Layer
                })
    
                ///Make sure a block being placed on another block can intersect with it.
                .IgnoreIntersectionForBase()
    
                ///Load resources that will be needed for the block.
                .NeededResources(new List<NeededResource> {
                                    new NeededResource(ResourceType.Audio, //Type of resource - types available are Audio, Texture, Mesh, and AssetBundle
                                                       "warHorn.ogg")
                })
    
                ///Setup where on your block you can add other blocks.
                .AddingPoints(new List<AddingPoint> {
                                   (AddingPoint)new BasePoint(false, true)         //The base point is unique compared to the other adding points, the two booleans represent whether you can add to the base, and whether it sticks automatically.
                                                    .Motionable(false,false,false) //Set each of these booleans to "true" to Let the block rotate around X, Y, Z accordingly
                                                    .SetStickyRadius(0.5f),        //Set the radius of which the base point will connect to others
                                  //new AddingPoint(new Vector3(0f, 0f, 0.5f), new Vector3(-90f, 0f, 0f),true).SetStickyRadius(0.3f), <---Example: Top sticky adding point
                });
    
            public override void OnLoad()
            {
                LoadBlock(example);
            }
    
            public override void OnUnload() { }
        }
    }
    [​IMG]
    Here's how it can look when we display the colliders, and the adding points.







    5. Test your block mod, and start getting the compound collider, visual offset, icon offset and AddingPoints right.
    5.1. All points can be sticky, which makes them grab on to adjacent blocks, or not which means an adjecent block needs to grab yours for them to connect.
    5.2. The base adding point can be be made lose in its rotation by adding .Motionable(true, false, true) to the (AddingPoint)new BasePoint(true, true).
    5.3. Note this articulation is only if the block is sticky (i.e. the second bool in BasePoint, needs to be true).
    6. Then you might want to look into adding a script to your block. For tat create a new item, a C# script, and use this example as a template:

    BlockScript Example
    Code:
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using TheGuysYouDespise;
    
    public class HornBlock : BlockScript
    {
        //BlockSettings
        protected MKey activateKey;
        protected MSlider pitchSlider;
        protected MSlider volumeSlider;
        protected MToggle semitoneToggle;
    
        //other stuff
        protected AudioSource audioSource;
        protected bool hasSound = false;
        protected float HornsWithMyKeyCoefficient = 0f;
        protected bool flipped = false;
        protected Vector3 orgScale = Vector3.one;
        protected bool steamPowered = false;
        protected float steamPoweredTimer = 10f;
        protected float lastSoundTimer = 10f;
    
        //for the semitone toggle we'll be using we want to know a list of intervals we can use for that
        protected float[] musicIntervals = new float[] {1/1f, 16/15f, 9/8f, 6/5f, 5/4f, 4/3f, 64/45f, 3/2f, 8/5f, 5/3f, 16/9f, 15/8f, 2/1f};
        protected bool waitAFrame = true;
    
        //BlockLoader Specific method: is called right after the script is made - usually,
        //this is done for all blocks of this type and is safe as it waits for stuff like
        //colliders, visuals, resources and so on
        public override void SafeAwake()
        {
            //Set the blocks original scale
            orgScale = transform.localScale;
    
            activateKey = AddKey("Sound The Horn", //Display text
                                 "play",           //save name
                                 KeyCode.H);       //Keyboard button
    
            pitchSlider = AddSlider("Pitch",       //Display Text
                                    "pitch",       //save name
                                    1f,            //default value
                                    0.5f,          //minimum value
                                    2f);           //maximum value
    
            volumeSlider = AddSlider("Volume",       //Display Text
                                     "volume",       //save name
                                     1f,             //default value
                                     0.1f,           //minimum value
                                     1f);            //maximum value
    
            semitoneToggle = AddToggle("Semitone Snap",   //Display Text
                                       "semitones",       //save name
                                       true);             //default value
    
            //Call HandleSemiToneSnap() when the slider value changes
            pitchSlider.ValueChanged += HandleSemitoneSnap;
        }
    
        //BlockLoader Specific method: Is the safe 1 time called method for the prefab - that's the master gameobject, or template so to speek
        //if you need to make alterations to the block you couldn't do with the standard framework, do it here.
        public override void OnPrefabCreation()
        {
        }
    
        //BlockLoader Specific method: When the player presses spacebar or the simulate/play button in the upper left corner
        protected override void OnSimulateStart()
        {
            //if the sound file we loaded in the LoadExampleBlock
            if (resources.ContainsKey("warHorn.ogg"))
            {
                hasSound = true;
                //set the audio source we'll be using to the respective component, but if we don't find one, add one.
                audioSource = gameObject.GetComponent<AudioSource>() ?? gameObject.AddComponent<AudioSource>();
                //set the clip the audio source will be playing to the one we loaded.
                audioSource.clip = resources["warHorn.ogg"].audioClip;
            }
            //This is mostly flair:
            //For all blocks in the machine we have during simulation
            foreach (Transform t in Machine.Active().SimulationMachine)
            {
                //get the ones that are a HornBlock
                if (t.GetComponent<HornBlock>())
                {
                    //and if they use the save keyboard button to play their sound
                    if (t.GetComponent<HornBlock>().activateKey.KeyCode[0] == activateKey.KeyCode[0])
                    {
                        //Add their volume to a coefficient we need for later
                        HornsWithMyKeyCoefficient += t.GetComponent<HornBlock>().volumeSlider.Value;
                    }
                }
            }
        }
    
        //BlockLoader Specific method: When the player is simulating instead of building
        protected override void OnSimulateUpdate()
        {
            //as long as our block isn't destroyed
            if (!HasBurnedOut() && !Destroyed())
            {
                //when we press the key
                if (activateKey.IsPressed)
                {
                    //Play the horn sound
                    SoundTheHorn();
                }
                //Or when steam powers the horn
                else if(steamPowered)
                {
                    //We check all this stuff to stop spamming of powering the horn
                    if (!audioSource.isPlaying || steamPoweredTimer > 0.1f || lastSoundTimer > 1f)
                    {
                        //Play the horn sound
                        lastSoundTimer = 0f;
                        SoundTheHorn();
                    }
                }
                lastSoundTimer += Time.deltaTime;
                steamPoweredTimer += Time.deltaTime;
                steamPowered = false;
            }
        }
    
        //Checking Steam powering
        protected virtual void OnParticleCollision(GameObject other)
        {
            //ignore if we are already steam powered
            if (!steamPowered)
            {
                //if collison is with steam
                if (other.name == "SteamParticle")
                    //if it's entering from the top-ish
                    if (Vector3.Angle(-other.transform.forward, transform.forward) < 55f)
                    {
                        steamPoweredTimer = 0f;
                        steamPowered = true;
                    }
            }
        }
    
        //HornBlock defined method: We have made this method to play the sound we want
        public void SoundTheHorn()
        {
            StopCoroutine(ShakeScale(transform.FindChild("Vis").gameObject, 0.025f, 0.25f / pitchSlider.Value));
            StartCoroutine(ShakeScale(transform.FindChild("Vis").gameObject, 0.025f, 0.25f / pitchSlider.Value));
    
            if (hasSound)
            {
                //Set a couple of frames of random delay (we don't use Time.deltaTime, because if we have low fps we don't want to wait longer for the sounds to play)
                audioSource.PlayDelayed(Random.Range(0f, 0.0165f * 3f));
                //Set the pitch - the brightness - of the sound to be dependent on the timescale and the slider we have for it
                audioSource.pitch = Time.timeScale * pitchSlider.Value;
                //This is the flair coefficient, this basically helps the sounds to be dimmed when a lot of blocks play at the same time
                float Coefficient = 0.6f / (HornsWithMyKeyCoefficient < 4.5f ? 1f : HornsWithMyKeyCoefficient / 3f);
                //Set the volume - the loudness - of the sound to be dependent on the slider and coefficient
                audioSource.volume = volumeSlider.Value * Coefficient;
            }
        }
    
        //BlockLoader Specific method: When we are done simulating, usually you don't need to do anything here,
        //as the block in simulation mode is deleted, but if you have static variables or similar you might want to update it here.
        protected override void OnSimulateExit()
        {
            //reset this flair thing for good meassure
            HornsWithMyKeyCoefficient = 0f;
        }
    
        //Flip the block on F, this method needs to be called "Flip"
        public void Flip()
        {
            //play the flipping sound - BlockScript predefined
            PlayFlipSound();
            //Do the flipping we need:
            FlipNoSound();
        }
    
        //Setting the flip needed for copying, loading and such.
        public void SetFlipNoSound(bool flip)
        {
            //we don't want to do the flip if the flipping matches
            if (flipped == flip)
                return;
            //Do the flipping we need:
            FlipNoSound();
        }
    
        //HornBlock defined method: We flip the block litterally
        public void FlipNoSound()
        {
            //reverse the flipping bool
            flipped = !flipped;
            //mirror the visuals of the block to be... well... mirrored
            foreach(var vis in Visuals)
                MirrorVisual(vis); //BlockScript defined method
            //mirror the colliders in a different way where we make sure to rotate the colliders to achieve the correct stuff
            foreach (var col in Colliders)
                MirrorCollider(col); //BlockScript defined method
        }
    
        //HornBlock defined method:
        //Animates a shake in the scale of a gameobject
        protected IEnumerator ShakeScale(GameObject go, float magnitude, float duration)
        {
            float elapsed = Time.deltaTime;
            duration += Time.deltaTime;
    
            Vector3 orgScale = go.transform.localScale;
            Vector3 fromScale = orgScale;
            Vector3 targetScale = orgScale;
            float previousCoef = 10f;
    
            while (elapsed < duration)
            {
                elapsed += Time.deltaTime;
        
                //this if statement deals with time scale,
                //for >100% time scale it's run every frame
                //but as the time scale is lower we don't want the shaking to appear much faster than fitting for the time
                //in that scenario we will sample the shaking every now and then
                //we then smoothly lerp between the samples
                if (Time.timeScale >= 0.9f || (elapsed % 0.01666f) / 0.01666f < previousCoef)
                {
                    go.transform.localScale = targetScale;
                    //dampen based on the tails of the animation
                    float percentComplete = elapsed / duration;
                    float damper = 1.0f - Mathf.Clamp(5.0f * percentComplete - 4.0f, 0.0f, 1.0f);
    
                    // map value to [-1, 1]
                    float x = Random.value * 2.0f - 1.0f;
                    float y = Random.value * 2.0f - 1.0f;
                    // dampen
                    x *= magnitude * damper;
                    y *= magnitude * damper;
    
                    //Set the two points to lerp between
                    fromScale = go.transform.localScale;
                    targetScale = new Vector3(orgScale.x + x, orgScale.y + y, orgScale.z);
                }
                previousCoef = (elapsed % 0.01666f) / 0.01666f;
                go.transform.localScale = Vector3.Lerp(fromScale, targetScale, (elapsed % 0.01666f) / 0.01666f);
    
                yield return null;
            }
            go.transform.localScale = orgScale;
            yield break;
        }
    
        //HornBlock defined method: make sure that the pitch only can fit a musical interval when toggled
        protected virtual void HandleSemitoneSnap(float value)
        {
            //we have to wait a frame not to create a feedback loop where when we change the value to snap to a semi tone it will then call this function again
            if (waitAFrame)
            {
                waitAFrame = false;
                float lower, upper;
                if (semitoneToggle.IsActive)
                {
                    //we go through all of the semitones
                    for (int i = 0; i < musicIntervals.Length - 1; i++)
                    {
                        lower = musicIntervals[i];
                        upper = musicIntervals[i + 1];
                        //for 2 octaves, below and above the
                        for (int octave = 1; octave <= 2; octave++)
                        {
                            //we are inversing the intervals to get the intervals below 1.0 and above
                            lower = 1f / lower;
                            upper = 1f / upper;
    
                            //see whether the value is between the lower and upper (lower and upper can be inverse, due to we inversing the fraction)
                            if ((value >= lower && value <= upper)
                             || (value >= upper && value <= lower))
                            {
                                //find the closer of the values
                                if (Mathf.Abs(lower - value)
                                  < Mathf.Abs(upper - value))
                                {
                                    pitchSlider.Value = lower;
                                    goto ReturnAndUpdate;
                                }
                                else
                                {
                                    pitchSlider.Value = upper;
                                    goto ReturnAndUpdate;
                                }
                            }
                        }
                    }
                    //we were outside the range of the intervals
                    //clamp the value to the absolute lower and upper
                    lower = 1f / musicIntervals[musicIntervals.Length - 1];
                    upper = musicIntervals[musicIntervals.Length - 1];
                    pitchSlider.Value = Mathf.Clamp(value, lower, upper);
    
                    ReturnAndUpdate:
                    StopAllCoroutines();
                    StartCoroutine(UpdateMapper());
                }
            }
            else waitAFrame = true;
        }
    
        //HornBlock defined method
        protected virtual IEnumerator UpdateMapper()
        {
            if (BlockMapper.CurrentInstance == null)
                yield break;
            while (Input.GetMouseButton(0))
                yield return null;
            BlockMapper.CurrentInstance.Copy();
            BlockMapper.CurrentInstance.Paste();
            yield break;
        }
    
        //The following functions are for saving, loading, copying and pasting the values
    
        //Besiege Specific method
        public override void OnSave(BlockXDataHolder data)
        {
            SaveMapperValues(data);
    
            data.Write("flipped", flipped);
        }
    
        //Besiege Specific method
        public override void OnLoad(BlockXDataHolder data)
        {
            LoadMapperValues(data);
    
            // If the simulation just started we do not want to load
            // our flip state from the data holder.
            // The flip variable is automatically copied
            // over by Unity when the simulation starts.
            if (data.WasSimulationStarted) return;
    
            if (data.HasKey("flipped"))
            {
                SetFlipNoSound(data.ReadBool("flipped"));
            }
        }
    }

    7. Make your block does what you want, consider what several of the same block might do, and consider keeping it simple so they don't lag.
    7.a. To use any of your needed resources just use "resources["NameOfResource.ext"].audio, .texture, .assetbundle or .mesh.
    7.b. If you need to use the Monobehaviour Start(), Update() and FixedUpdate Methods, use the protected override MonoStart(), MonoUpdate() and MonoFixedUpdate() instead.
    7.c. There are some Methods that can help you in BlockScripts: IsBurning(), HasBurnedOut(), with more to come.
    8. Find a Unique ID, your ID can be from 100 to 999, and needs to be different from other established blocks.
    When releasing a block mod I therefore encourage you to provide the ID in the title.

    Cheers, Daniel.

    MUSIC BLOCK DOWNLOADS:

    War Drum

    War Horn


    Block Mods / Used IDs:
    100: War Drum by TheGuysYouDespise
    101: Metal Block by MaxTCC
    104: Self-Destruct Block by MaxTCC
    105: Rocket Block by TesseractCat
    107: Laser Block by TesseractCat
    109: Magnet Block by TesseractCat
    115: Gravity Block by TesseractCat
    117: Chair Block by TesseractCat
    199: Bar Magnet Block by ITR
    200: Poultryizer by TheGuysYouDespise
    211: FrostThrower by ITR/Phantom329
    223: WIP by ITR
    227: WIP by ITR
    229: WIP by ITR
    233: WIP by ITR
    239: WIP by ITR
    241: WIP by ITR
    251: WIP by ITR
    257: WIP by ITR

    269: Unstable Explosives by ITR
    270: Large Cartwheel by Fenymak
    271: Huge Cartwheel by Fenymak
    272-273: WIP by Fenymak
    274: Tank Tracks by Fenymak
    275: Motorized Tank Wheel by Fenymak
    276: Tank Wheel by Fenymak
    277: Transmission Cog by Fenymak
    278: WIP by Fenymak
    279: Rail by Fenymak
    280: Curved Rail 11.25° by Fenymak
    281: Curved Rail 22.5° by Fenymak
    282: Small Train Wheel by Fenymak
    283-299: WIP by Fenymak
    300: War Horn by TheGuysYouDespise
    301-310: WIP by TheGuysYouDespise
    329: Anchor Block by Phantom329
    350: Half Block by Phantom329
    351: Slant Block by Phantom329
    352: Long Slant Block by Phantom329
    353: Corner Block by Phantom329
    354: Long Corner Block by Phantom329
    355-371: WIP by Phantom329
    372: Large Wing by Phantom329
    373-399: WIP by Phantom329
    410: Automatron by spaar
    501: Large Modern Wheel by Wang_w571
    502: WIP by Wang_w571
    503: Target Seeking Missile by Wang_w571
    504: Target Seeking Missile [Invicible] by Wang_w571
    506: Target Seeking Missile [Motion Halt] by Wang_w571

    505: Spatial & Motion Info Tracking Block by Wang_w571
    507-508: WIP by Wang_w571
    509: Aimed Missile by Wang_w571
    510: Laser Aiming module by Wang_w571
    511-518: WIP by Wang_w571
    519: Block Spawner Block by Wang_w571
    520: WIP by Wang_w571
    602: Small Cockpit by Venhip
    603: Large Cockpit by Venhip
    700: WIP by StuChris
    701: WIP by StuChris

    702: Train Track by StuChris/MaxTCC
    Deprecated BlockLoader Info:
    BlockLoader isn't required separately anymore as it is present in spaar's ModLoader.

    Preview:


    Installation of BlockLoader:
    Use Von's installation giude, WhackyCast's video tutorial, or follow my poorly written steps:

    1. Install spaar's modloader.
    2. Put BlockLoader.zip in your Mods directory under your Besiege installation.
    3. Right click the zip archive and click "Extract Here". (This options comes with WinRAR)
    Another way is to copy all the contents of the zip archive and put it in the Besiege\Besiege_Data\Mods folder for PC.
    4. If you should get a prompt to merge folders, accept it, overwrite any duplicate files.

    I've made the first Block Mod myself, and it's a War Drum, seen in the video above.
    Here's how to install that or any other block mod (if other modders follow my approach and provide block mods in archives)

    Old BlockLoader Change Log:
    v0.11-beta:
    Launch version.
    v0.11-beta03:
    Fixed resolution UI displacement issues.
    v0.11-beta04:
    Saving issues fixed (but will break your current save files that has a modded block in it),
    fixed a rotation issue for the icons,
    streamlined compound colliders to be under the child "Collider" of a block,
    increased the range of IDs: IDs can now be from 100 to 999;
    v0.11-beta05:
    Added a new primary template, changing Blocks to be BlockMods instead of Mods, and making them a bit more understandable.
    Added Destroyed() to BlockScript, so now you can test for that.
    v0.11-beta10:
    Now we have AddingPoints for blocks, that means you can define positions on your block where you can place further on your one.
    v0.11-beta13:
    AddingPoints bugs are fixed, they are more precise and now you can make them all sticky, and the base point can be articulated.
    v0.11-beta20:
    Added OnPrefabCreation() method for BlockScript that is run once when the prefab has been created.
    Added OnSimulateFixedUpdate() and MonoFixedUpdate() to account for FixedUpdate functionality during simulation.
    Added multiple mesh loading for blocks, which require you to use the .Obj() method rather than ObjFile() and VisualOffset(), see the new template.
    All meshs but the first one is children to the Vis child.
    v0.11-beta21:
    Fixed an issue where using a method for making hold key1 didn't do anything, now you have "KeyBinding(string name, string key, KeyAction action)".
    Also added support for loading assetbundles (.unity3d) as neededresources.
    v0.2-beta3:
    Updated for version 0.2, the Unity 5 release. The framework is undergoing refinement, thus some previous frameworks have broken.
    v0.2-beta31:
    Updated to include a check for outdated blocks. The BlockLoader should give an error message in the console if an outdated block is trying to be loaded.
    v0
    v0.25:
    http://www.mediafire.com/download/ad...ader-v0.25.zip
    Updated for v0.25: major overhaul
    v0.27:
    http://www.mediafire.com/download/u0...ader-v0.27.zip
    Updated for v0.27: just small fixes to make it work for v0.27
    v0.03
    http://www.mediafire.com/download/9zwgnr2n8ys5z1y/BlockLoader-v0.3.zip
    Updated for v0.3: had to make quite a few changes to get it working again.
     
    Last edited: Feb 15, 2017
    Kopin and Hexekonig like this.
  2. IMPORTANT, this bug I currently have no solution for, but am aware of:

    Changing levels do not work for modded blocks, I.e. they disappear when you change levels.

    So when you want to use your machine with modded blocks across several levels, save it, then you can load it again when entering a new level.


    EDIT: FIXED IN 0.27
     
    Hexekonig likes this.
  3. krowanek

    krowanek Member

    Good to have it back :) Horns works and sounds great :) I had one weird bug after installation (Modloader got gliched - didn't want to load any mods I have) - caused by outdated blocks - had to delete their .dll files from my Mods folder. After that everything got back to normal :)
     
  4. Yeah, you should never have outdated blocks be laying around in your folders as it can cock up everything.
     
  5. Von

    Von Administrator Staff Member

    Original post was un-recoverable and as a result of my ever more extreme tinkering the original post is not completely lost to us. RIP
     
  6. stuChris

    stuChris Member

    do you think you could make a guitar block? or a guitar cord block?
     
  7. Franglish9265

    Franglish9265 New Member

    Yes, I have also noticed this. thank you
     
  8. Usama Khan

    Usama Khan New Member

    Hello guys i am very new to this game and i am intersted in this mods thing and i have Spaars mod , exploding cannons , i wanted laser block mod but i cant get it into the game . the problem is i tried everything but in CUSTOM blocks button there is nothing like laser block is not ther so i need some help and btw i have latest v0.25 game and its non-steam
     
  9. spaar

    spaar Member

    The laser block isn't updated to Besiege v0.25 unfortunately, so it can't be used with the most recent version right now.
     
  10. Loshirai14

    Loshirai14 New Member

    Hello! I was trying the horn block when i found out that i can't get flat and sharp notes. can you please add flat and sharp notes for it?
     
  11. seansmith

    seansmith New Member

  12. dimmon3991

    dimmon3991 New Member

    Gus help me pls. I can create model and texture of block, but i have no idea what i must do for create block mod. I understand C# example code but how i can create my own? Which program i must use for create .dll file of my mod? Exist the easy way to create .dll file?
     
  13. Lench

    Lench Active Member

    Up in the post you will see a making blocks section. In there you can read a very detailed guide with example scripts and everything. In short, download Visual Studio, get spaar's mod loader template and see TGYD's examples.
     
  14. dimmon3991

    dimmon3991 New Member

    For first try i just copy code of War Horn in Visual Studio (I use template) and i get 32 Errors! I except that code will work perfectlt and i can use it to make my own. But then i press Start button in VS i have bug report.
     
  15. dimmon3991

    dimmon3991 New Member

    BlockMod Example does'not work with error:

    Error CS0103 The name 'LoadBlock' does not exist in the current context

    In part of code:

    public override void OnLoad()
    {
    LoadBlock(example);

    }

    So that does'not work too:

    .Components(new Type[] {
    typeof(HornBlock),
    })


    So i can't create my own blocks coz example does'not work((
     
  16. yes it does, as long as you remember to inherit from BlockMod and have references to the correct files.
     
  17. Cyanogenfaw

    Cyanogenfaw New Member

    Please add scale changer, use the arrow like a rotate and move
     
  18. Cyanogenfaw

    Cyanogenfaw New Member

    Please add scale changer
     
  19. please remove your comments on this topic, your request has nothing to do with the BlockLoader. Also spamming me to do stuff doesn't excite me to do it, rather the contrary.
     

Share This Page