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

#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() { }
    }
}

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:
#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
 
#3
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 :)
 

Von

Administrator
Staff member
#5
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
 
#8
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
Usama Khan said:
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
The laser block isn't updated to Besiege v0.25 unfortunately, so it can't be used with the most recent version right now.
 
#11
#13
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?
 

Lench

Active Member
#14
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.
 
#15
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.
 
#16
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((
 
Top