Custom Pink Rings Demo

Back


The Goal

Recall how "pink rings" work in the xar package of Fractal Block World. When you use pink rings you will grow until you reach a chunk containing a pink sphere. That is, we consider the chunk containing the pink rings, and we find its finest ancestor chunk which contains pink spheres.

The point of this guide is to explain how to make your own pink rings, without relying on any special hardcoded functionality of the engine. We will make "striped" pink rings. With this package, you can use the striped pink rings by looking at them and pressing "F" (assuming that is how your keybindings are set up).

We will make a package that you install in the Data/Packages folder of the game. So, you will be able to start a new game and fly around this new world.

We will just go over the interesting parts of the package we are making.


Pink Sphere Basic Entity (Destination)

First, in the BasicEnts folder of our package, we create a file called
bent_ring_pink_striped_dest.lua
which should read as follows:
function p.__get_mesh()     return "sphere_pink_striped" end
function p.__get_pulsates() return false end
function p.__get_scale()    return 0.7 end
This is a very simple entity. It uses the mesh named sphere_pink_striped (which we must add to the Meshes/mesh_names.txt file). You can set up that mesh the same way the base package sets up the "sphere_pink" mesh.

Note that we could have just as easily used a "block" instead of a "basic entity" for the pink destination entity. This is what the basic entity looks like:



Pink Rings Basic Entity (Source)

Now we must define the pink rings entity. When the player uses this entity, they are teleported to a striped pink sphere. What we do is create a file called
bent_ring_pink_striped_source.lua
in the BasicEnts folder and have it read as follows:
function p.__get_mesh() return "" end

function p.payload(level, bp)
    --Will play a sound of worked.
    local chunk_id = ga_bp_to_parent_chunk_id(level, bp)
    game_tele_pink_striped.tele(chunk_id) --In this package.
end

function p.__on_touch(level, bp)
    local dist = ga_block_dist_to_viewer(level, bp)
    if (dist > 0.4) then return end

    p.payload(level, bp)
end

function p.__get_can_use(level, bp)
    return true
end

function p.__get_use_msg(level, bp)
    return "Grow until find pink striped sphere"
end

function p.__on_use(level, bp)
    p.payload(level, bp)
end

function p.__render(level, bp)
    local cur_time = ga_get_game_time()
    local speed_mod = 1.0
    --
    local angle1 = cur_time * 50.0 * speed_mod
    local axis1 = std.vec(1.0, 0.0, 0.0)
    ga_render_matrix_rotated(angle1, axis1)
    ga_render_mesh("ring_pink_striped_large")
    --
    local angle2 = cur_time * 70.0 * speed_mod
    local axis2 = std.vec(0.0, 1.0, 0.0)
    ga_render_matrix_rotated(angle2, axis2)
    ga_render_mesh("ring_pink_striped_med")
    --
    local angle3 = cur_time * 90.0 * speed_mod
    local axis3 = std.vec(1.0, 0.0, 0.0)
    ga_render_matrix_rotated(angle3, axis3)
    ga_render_mesh("ring_pink_striped_small")
end
Let us break this down. The __get_mesh function returns the empty string, because the entity uses a custom render function. The functions __on_touch and __on_use both call the payload function, which makes the teleportation request. The request works by calling a function of a script called game_tele_pink_striped, which we will go over in the next section.

Teleportation Script

In the Game folder of the package, create the file
game_tele_pink_striped.lua
and have it read as follows:
-------------------------------------------------------------------------------
--                          Pink teleportation API
-------------------------------------------------------------------------------
--This is the only part of this script which can be called from the outside.

--Starts a "pink" teleportation for the specified block pos.
--That is, we start at the given chunk_id, then we move up the
--chunk tree (towards the root) until we reach a chunk with
--a "pink sphere" bent there.
--Will play a sound if worked.
function p.tele(source_chunk_id)
    ga_debug_push("game_tele_pink_striped.tele")

    --The hard work.
    local should_tele = true
    local target_chunk_id = p.tele_pink_helper(source_chunk_id, should_tele)
    local worked = (target_chunk_id >= 0)

    --Playing a sound.
    if( worked ) then
        ga_play_sound("pink_ring") --Sound in the base package.
    else
        ga_play_sound("error")     --Sound in the base package.
    end

    ga_debug_pop("game_tele_pink_striped.tele")
end

--This can be called from the outside.
--Returns -1 if the dest chunk does not exist.
function p.get_pink_dest_chunk_id(source_chunk_id)
    local should_tele = false
    return p.tele_pink_helper(source_chunk_id, should_tele)
end

--This can be called from the outside.
--Returns -1 if the dest chunk does not exist.
--Does not actually teleport.
function p.get_pink_dest_level(source_chunk_id)
    local target_chunk_id = p.get_pink_dest_chunk_id(source_chunk_id)
    if( target_chunk_id < 0 ) then return -1 end
    local target_level = ga_chunk_id_to_level(target_chunk_id)
    return target_level
end

-------------------------------------------------------------------------------
--                          Helper function
-------------------------------------------------------------------------------
--Do NOT call this from outside this script.

--Helper function.
--Returns the chunk id of the dest chunk (if it worked),
--and returns -1 otherwise.
function p.tele_pink_helper(source_chunk_id, should_tele)
    local bent_type = "bent_ring_pink_striped_dest"
    local cur_chunk_id = source_chunk_id
    while( cur_chunk_id >= 0 ) do
        local data = ga_search_for_bent_in_chunk(cur_chunk_id, bent_type)
        if( data.is_valid ) then
            --Found the bent.
            local bent_lbp = data.value
            local target_offset = std.block_center(bent_lbp) --Use a different function name?
            local target_chunk_path = ga_chunk_id_to_path(cur_chunk_id)
            if( should_tele ) then
                ga_tele(target_chunk_path, target_offset);
            end
            return cur_chunk_id
        end
        local parent_chunk_id = ga_chunk_id_to_parent_chunk_id(cur_chunk_id)
        cur_chunk_id = parent_chunk_id
    end
    return -1
end
We do not need the functions get_pink_dest_chunk_id and get_pink_dest_level, but you might find them useful. The function tele_pink_helper is the fundamental algorithm. We start at a given chunk_id. Then, we keep on passing to the parent of the "chunk we are in" until we reach a chunk which contains a basic entity of type bent_ring_pink_striped_dest. The way we go from a chunk to its parent (in this function) is with ga_chunk_id_to_parent_chunk_id.

The way we search for whether a chunk has a given basic entity is with the ga_search_for_bent_in_chunk function. Note that there is also the function ga_search_for_bt_in_chunk to search for a block type inside of a chunk.

That is it!

A Comment

This all is relatively simple because we are able to teleport right away in one function call. That is, we crawl up the chunk tree to find what we need and then teleport if appropriate.

On the other hand, blue rings are much more complicated because we need to have the engine expand the chunk tree (explore) while we query the newly created chunks to see what to do next and determine if we are done. An important ingredient for doing this is the "explore while loop" which we will explain in the guide about custom blue rings.

Download

Here is the package we created.