--Version 2.8_hotfix1 (2.x final)
--Author: djy0212
--Email: "moc.liamxof)ta(2120yjd" in reverse
--Will not add more features, only bugfixes
--[[
All-seeing Eye Sampler(or silk touch in tpt) manual
1.Introduction
All-seeing Eye Sampler is a script that provides some convenient tools to better create mixtures and special particles. It contains two pseudo-elements in the "Tools" menu. One is an eye in a triangle and the other is some falling particles. There are four modes of the tool which are "pixel mode", "random mode", "pattern mode" and "layering mode". With the tool, you can create *exact* copies of a particle, mixtures of particles either randomly or regularly, or make layered materials with ease. Nearly all operations that requires you to copy a few pixels again and again can be simplified to just a few keypresses and clicks.
All operations in this script is done via the two tools in the "tools" menu and keyboard. Let's call the tool with a triangle eye icon "eye tool" or "sampler", and call the other with particles "particles tool". When the eye tool is selected, you can press "home" key to cycle among modes.
Each mode has its hotkey, but there are some global hotkeys.
Global hotkeys(while selecting the "eye" tool):
End: Switch 'velocity sampler', useful for sampling energy particles
Home: Switch among modes
ScrLock: Select "particle" tool
Global hotkeys(while selecting the "particle" tool):
Delete: Clear selected sample
ScrLock: Select "eye" tool
Also, you can press ScrLock(Scroll Lock) any time to quickly select the "eye" tool.
2.Modes
(1).Pixel mode
+--------+
|AAAAAAAA|
|AAAAAAAA|
|AAAAAAAA|
+--------+
The default mode. In this mode, click any particle with the eye tool to sample it. It will sample the element with its all properties. Then use the particle tool to draw the sample.
(2).Random mode
+--------+
|BABABCCB|
|AABACAAA|
|BCBCABCA|
+--------+
This is the second mode so press "home" one time while holding the eye tool to reach it. When you are in this mode, you can draw particle mixtures.
Firstly, put all particles you want to mix on the screen. Then use the eye tool and click each one type of the particles one time. A message of the current particle and total amount of particles will appear. When you finished sampling, you can adjust the weight of each particle by selecting it in the particle list (Use "page up" and "page down" to navigate) and press "+" or "-" on the numpad.
The particle with a higher weight is more likely to appear in the mixture and vice versa.
You can use "/" on the numpad to insert a "empty" particle to the particle list. Try to increase the weight and it can make an effect of spray brush.
Hotkeys(while selecting the "eye" tool):
PageUp: Moves the pointer to the previous particle in the particle list
PageDown: Moves the pointer to the next particle in the particle list
Delete: Delete the target particle
+: Increase the weight of target particle
-: Decrease the weight
*: View the info of target particle
/: Replace the target particle with "the air"(or "Empty")
(3).Pattern mode
+--------+
|ABABABAB|
|ABABABAB|
|ABABABAB|
+--------+
In this mode, you can draw particle patterns. Press "+" on the numpad first to set the pattern size. Then position your cursor on the upper left corner of the pattern drawn,and press "/" on the numpad to copy the pattern. If you click it, it will only sample the one under your cursor.
Advanced usage:
There's a pointer just like that in random mode, but it's 2-dimensional. Use arrow keys to navigate. Click with the eye tool to put the particle under the cursor to the target particle.
Control keys(while selecting the "eye" tool):
+: Set the size of the pattern
Arrow keys: Move the pointer
Delete: Delete the target particle
-: Set pattern offset
*: View detailed info
/: Sample pattern
(4).Layering mode
+--------+
+-|CCCCCCCC|
+-|B|CCCCCCCC|
|A|B|CCCCCCCC|
|A|B+--------+
|A+--------+
+--------+
In this mode, you can stack particles up to 5 layers. Use it just like random mode.
Control keys(while selecting the "eye" tool):
PageUp: Moves the pointer to the previous particle
PageDown: Moves the pointer to the next particle
Delete: Delete the target particle
*: View the info of target particle
/: Sample existing stack
3.Save & Load
Press "insert" when selecting eye or particle tool to enter save&load mode. In this mode, the icon of the eye tool will turn to blue, which means "save". The icon of the particle tool will turn to yellow, which means "load". Press "insert" again when selecting particle(load) tool to return to normal mode.
(1).Save
Click the eye tool and an input box will appear. Enter the name of the file, and it will be saved in the "samples" folder in your game directory. It can be copied and shared with other people.
(2).Load
Click the particle tool and if there's any sample, a file list will be rendered on the screen. Use arrow keys, pageup and pagedown to navigate through those files. Press numpad "+" to load one. You can also press numpad "-" and manually input the file name.
When in the load interface, you can press numpad "/" to copy the sample you are holding (NOT that you selected in the file list) to the clipboard (in a text form). This can be shared and loaded into others' game by press numpad "*" in this interface.
Control keys:
+: Load file
Arrow keys, PageUp and PageDown:
Move the selection
Delete: Delete the selected file
-: Manually input the file name
/: Copy sample to clipboard
*: Load sample from clipboard
End: Refresh file list
Home: Recalculate preview color (will lose holding sample)
4.
If you have any problem, please try reading the (messy garbage) source and modify it by yourself.
You can also email me or PM me.
]]--
HUD_MODE = 0 -- 0:fixed, 1:follow cursor
HUD_POS_X = 12
HUD_POS_Y = 51
local keys
local chars
--use sdl2 key code
if (tpt.version["jacob1s_mod"] ~= nil and tpt.version["jacob1s_mod"] >= 42) or (tpt.version["major"] >= 94) then
keys = {
["insert"] = 0x40000049,
["delete"] = 127,
["home" ] = 0x4000004a,
["end" ] = 0x4000004d,
["pgup" ] = 0x4000004b,
["pgdn" ] = 0x4000004e,
["num/" ] = 0x40000054,
["num*" ] = 0x40000055,
["num-" ] = 0x40000056,
["num+" ] = 0x40000057,
["up" ] = 0x40000052,
["down" ] = 0x40000051,
["left" ] = 0x40000050,
["right" ] = 0x4000004F,
["num1" ] = 0x40000059,
["num2" ] = 0x40000060,
["scrlck"] = 0x40000047
}
else
keys = {
["insert"] = 277,
["delete"] = 127,
["home" ] = 278,
["end" ] = 279,
["pgup" ] = 280,
["pgdn" ] = 281,
["num/" ] = 267,
["num*" ] = 268,
["num-" ] = 269,
["num+" ] = 270,
["up" ] = 273,
["down" ] = 274,
["left" ] = 276,
["right" ] = 275,
["num1" ] = 257,
["num2" ] = 258,
["scrlck"] = 71
}
end
if (tpt.version["major"] < 94 or tpt.version["jacob1s_mod"] ~= nil) then -- old version ascii characters
chars = {
["eye"] = "\xa3",
["eye_elem"] = "\xa3",
["part_elem"] = "\x9a",
["part"] = "\x9a",
["save"] = "\x82",
["load"] = "\x81"
}
elseif (tpt.version["major"] == 94 and tpt.version["minor"] <= 1) then -- use unicode symbols and compat
chars = {
["eye"] = "\xee\x80\xa3",
["eye_elem"] = "/0\\",
["part_elem"] = "!!!",
["part"] = "\xee\x80\x9a",
["save"] = "\xee\x80\x82",
["load"] = "\xee\x80\x81"
}
else -- unicode element name should be fixed now
chars = {
["eye"] = "\xee\x80\xa3",
["eye_elem"] = "\xee\x80\xa3",
["part_elem"] = "\xee\x80\x9a",
["part"] = "\xee\x80\x9a",
["save"] = "\xee\x80\x82",
["load"] = "\xee\x80\x81"
}
end
local moden = 0
local mode_max_n = 3
--0:single pixel, 1:random mixed, 2:pattern mixed, 3:layered
local modes = {}
local save = {}
modes[0] = {
name = 'Pixel',
thepixel={},
tooltips = {pked = "A perfectly-sampled particle.",pick = "Sample a particle on the screen EXACTLY.", color = 0x000000}
}
modes[1] = {
name = 'Random',
list = {},
selected_number = 1,
tooltips = {pked = "Some random-mixed particles.",pick = "Mix several particles on the screen RANDOMLY.", color = 0xffffff}
}
modes[2] = {
name = 'Pattern',
list = {},
width = 0,
height = 0,
ptr_lr = 1,
ptr_ud = 1,
x_offset = 0,
y_offset = 0,
tooltips = {pked = "Some regularly-mixed particles.",pick = "Put several particles together REGULARLY", color = 0x7f7f7f}
}
modes[3] = {
name = 'Layering',
list = {},
selected_number = 1,
tooltips = {pked = "A stack of particles.",pick = "Stack them ALL!", color = 0x7f3f3f}
}
modes[233] = {
name = 'Save & Load',
file_list = {},
selected_file_number = 1,
tooltips = {pked = chars["load"].." Load",pick = chars["save"].." Save", color1 = 0x6b94ce, color2=0xf7de62},
tosave_mode = 0,
color_data = {}
}
local unpack = unpack or table.unpack
math.randomseed(tostring(os.time()):reverse():sub(1, 7))
local id_picker = 'PICKER_PT_PICK'
local id_picked = 'PICKER_PT_PKED'
local el_picker = elem.allocate("picker","pick")
elem.element(el_picker, elem.element(elem.DEFAULT_PT_DMND))
elem.property(el_picker,"Name","["..chars["eye_elem"].."]") -- an icon of an eye
elem.property(el_picker,"Description","Sample a particle on the screen EXACTLY.")
elem.property(el_picker,"Colour",0x000000)
elem.property(el_picker,"HeatConduct",0)
elem.property(el_picker,"MenuSection",elem.SC_TOOL)
elem.property(el_picker,"HighTemperature",sim.MIN_TEMP)
elem.property(el_picker,"HighTemperatureTransition",0)
elem.property(el_picker,"Properties",elem.PROP_NOCTYPEDRAW)
elem.property(el_picker,"Properties", bit.band(elem.property(el_picker, "Properties"), bit.bnot(elem.PROP_DRAWONCTYPE)))
local function picker_gfx(i, colr, colg, colb)
if i ~= 0 then
sim.partKill(i) -- remove garbage, make sure that it will be removed paused or unpaused
end
return 1, ren.PMODE_NONE, 0, 0, 0, 0
end
elem.property(el_picker, "Graphics", picker_gfx)
local el_picked = elem.allocate("picker","pked")
elem.element(el_picked, elem.element(elem.DEFAULT_PT_DMND))
elem.property(el_picked,"Name","["..chars["part_elem"].."]") --an icon of some particles
elem.property(el_picked,"Description","A perfectly-sampled particle.")
elem.property(el_picked,"Colour",0x000000)
elem.property(el_picked,"HeatConduct",0)
elem.property(el_picked,"MenuSection",elem.SC_TOOL)
--elem.property(el_picked,"HighTemperature",sim.MIN_TEMP)
--elem.property(el_picked,"HighTemperatureTransition",0)
elem.property(el_picked, "Properties", bit.band(elem.PROP_NOCTYPEDRAW, bit.bnot(elem.PROP_DRAWONCTYPE)))
--replace the placed PKED
local function picked_gfx(i, colr, colg, colb)
modes[moden].pked_handler() -- there must be at least 1 pked, replace them all
return 0, ren.PMODE_FLAT , 0, colr, colg, colb
end
elem.property(el_picked, "Graphics", picked_gfx)
--from lbphacker,modified
local PROPERTIES = {}
local prop_vel_enabled = false
local function gen_prop_list(enable_vel)
prop_vel_enabled = enable_vel
PROPERTIES = {}
for key, value in next, sim do
if key:find("^FIELD_") and key ~= "FIELD_X" and key ~= "FIELD_Y" and (key ~= "FIELD_VX" or enable_vel) and (key ~= "FIELD_VY" or enable_vel) then
PROPERTIES[key] = true
end
end
end
gen_prop_list(false)
local function pick_part(pixel,part_index)
for key in next, PROPERTIES do
pixel[key]=sim.partProperty(part_index, sim[key])
end
end
local function create_part(pixel,x,y)
local idx = sim.partCreate(-1,x,y,pixel['FIELD_TYPE'] or 0)
if idx == 0 then
return
end
for key in next, PROPERTIES do
if key ~= 'FIELD_TYPE' then
sim.partProperty(idx,sim[key],pixel[key] or 0)
end
end
end
local function replace_part(pixel,part_index)
sim.partProperty(part_index,sim.FIELD_TYPE,pixel['FIELD_TYPE'] or 0)
for key in next, PROPERTIES do
if key ~= 'FIELD_TYPE' then
sim.partProperty(part_index,sim[key],pixel[key] or 0)
end
end
end
local function reverseTable(tab)
local tmp = {}
for i = 1, #tab do
local key = #tab
tmp[i] = tab[#tab-i+1]
end
return tmp
end
local function restrict_ptr(ptr,max,offset)
if ptr+offset < 1 then
return 1
end
if ptr+offset > max then
return max
end
return ptr+offset
end
modes[0].pked_handler = function()
for index in sim.parts() do
if sim.partProperty(index,sim.FIELD_TYPE) == el_picked then
replace_part(modes[0].thepixel,index)
end
end
end
modes[0].quick_pked_color = function()
if modes[0].thepixel['FIELD_TYPE'] then
return elem.property(modes[0].thepixel['FIELD_TYPE'],"Colour")
else
return 0x000000
end
end
--from internet
modes[1].WeightedRandomSelect = function(innerTable)
local totalWeight = 0
-- Sum the weight values
for _, t in ipairs(innerTable) do
totalWeight = totalWeight + t[1]
end
-- Generate random number from 1 to the total weight
local weightSelect = math.random(1, totalWeight)
local thisThing = 0
-- Check every row of the inner table till we find one smaller than our random number, then return it
for sumdumvar, weight in ipairs(innerTable) do
thisThing = thisThing+(innerTable[sumdumvar][1])
if (weightSelect <= thisThing) then
return innerTable[sumdumvar][2]
end
end
end
modes[1].pked_handler = function()
ran_list = modes[1].list
if next(ran_list) == nil then
ran_list = {{1,{['FIELD_TYPE']=0}}} -- to prevent placing PKED when no element is sampled(random mode)
end
local total_weight = 0
for index in sim.parts() do
if sim.partProperty(index,sim.FIELD_TYPE) == el_picked then
replace_part(modes[1].WeightedRandomSelect(ran_list),index)
end
end
end
local function avg_color(list)
local totalR,totalB,totalG,n = 0,0,0,0
for _,item in ipairs(list) do --average color
local rgb_color = elem.property(item[2]['FIELD_TYPE'],"Colour")
totalR = totalR + item[1]*bit.rshift(bit.band(0xff0000,rgb_color),16)--R
totalG = totalG + item[1]*bit.rshift(bit.band(0x00ff00,rgb_color),8) --G
totalB = totalB + item[1]*bit.band(0x0000ff,rgb_color) --B
n = n + item[1]
end
totalR = math.floor(totalR/n)
totalG = math.floor(totalG/n)
totalB = math.floor(totalB/n)
return bit.bor(bit.lshift(totalR,16),bit.lshift(totalG,8),totalB)
end
modes[1].quick_pked_color = function()
return avg_color(modes[1].list)
end
modes[2].quick_pked_color = function()
local totalR,totalB,totalG = 0,0,0
for _,item in ipairs(modes[2].list) do --average color without freq.
for _,jtem in ipairs(item) do
if jtem then
local rgb_color = elem.property(jtem['FIELD_TYPE'],"Colour")
totalR = totalR + bit.rshift(bit.band(0xff0000,rgb_color),16)--R
totalG = totalG + bit.rshift(bit.band(0x00ff00,rgb_color),8) --G
totalB = totalB + bit.band(0x0000ff,rgb_color) --B
end
end
end
local n = modes[2].height*modes[2].width
totalR = math.floor(totalR/n)
totalG = math.floor(totalG/n)
totalB = math.floor(totalB/n)
local final_color = bit.bor(bit.lshift(totalR,16),bit.lshift(totalG,8),totalB)
return final_color
end
modes[3].quick_pked_color = function()
local totalR,totalB,totalG = 0,0,0
for _,item in ipairs(modes[3].list) do --average color without freq.
if item then
local rgb_color = elem.property(item['FIELD_TYPE'],"Colour")
totalR = totalR + bit.rshift(bit.band(0xff0000,rgb_color),16)--R
totalG = totalG + bit.rshift(bit.band(0x00ff00,rgb_color),8) --G
totalB = totalB + bit.band(0x0000ff,rgb_color) --B
end
end
totalR = math.floor(totalR/5)
totalG = math.floor(totalG/5)
totalB = math.floor(totalB/5)
local final_color = bit.bor(bit.lshift(totalR,16),bit.lshift(totalG,8),totalB)
return final_color
end
local function global_pked_color()
elem.property(el_picked,"Colour",modes[moden].quick_pked_color())
end
modes[2].pked_handler = function()
local x,y,sel_x,sel_y,sel_pixel
for index in sim.parts() do
if sim.partProperty(index,sim.FIELD_TYPE) == el_picked then
x,y = sim.partPosition(index)
sel_x = (x-modes[2].x_offset)%modes[2].width + 1
sel_y = (y-modes[2].y_offset)%modes[2].height + 1
--print(sel_x,sel_y)
if not (modes[2].width==0 and modes[2].height==0) then
sel_pixel = modes[2].list[sel_y][sel_x]
else
sel_pixel = nil
end
if sel_pixel then
replace_part(sel_pixel,index)
else
replace_part({['FIELD_TYPE']=0},index)
end
end
end
end
modes[3].pked_handler = function()
local li = modes[3].list
if next(li) == nil then
li = {{1,{['FIELD_TYPE']=0}}} -- to prevent placing PKED when no element is sampled
end
for index in sim.parts() do
if sim.partProperty(index,sim.FIELD_TYPE) == el_picked then
local x, y = sim.partProperty(index,sim.FIELD_X), sim.partProperty(index,sim.FIELD_Y)
sim.partKill(index)
for _,item in ipairs(li) do
a = item
local i = sim.partCreate(-3, x, y, item['FIELD_TYPE'])
replace_part(item,i)
end
end
end
end
local function change_mode(mode)
modes[0].thepixel = {}
modes[1].list = {}
modes[1].selected_number = 1
mode2_def = {
list = {},
width = 0,
height = 0,
ptr_lr = 1,
ptr_ud = 1,
x_offset = 0,
y_offset = 0
}
modes[3].list = {}
modes[3].selected_number = 1
for k,v in pairs(mode2_def) do
modes[2][k] = v
end
elem.property(el_picked,"Colour",0x000000)
if not mode then
return
end
if mode == -1 then
gen_prop_list(false)
mode = 0
end
if 0 <= mode and mode <= mode_max_n then
elem.property(el_picked,"Description",modes[mode].tooltips.pked)
elem.property(el_picker,"Description",modes[mode].tooltips.pick)
elem.property(el_picker,"Colour",modes[mode].tooltips.color)
end
moden = mode
end
local notice = ''
local notice_cd = 0
local max_notice_cd = 200
local function hud_plain(msg)
notice = msg
notice_cd = max_notice_cd
end
local function step_display()
if notice_cd > 0 and tpt.hud() then
local text = '-'..chars["eye"]..'- '..notice
local backwidth = tpt.textwidth(text)+8
local posx, posy
if HUD_MODE == 0 then -- fixed
posx = HUD_POS_X
posy = HUD_POS_Y
else -- follow cursor
local mx,my = tpt.mousex,tpt.mousey
if mx>612-backwidth then
posx = mx - backwidth
else
posx = mx
end
if my>372 then -- 384-12
posy = my - 12
else
posy = my
end
end
graphics.fillRect(posx, posy, backwidth, 12, 0, 0, 0, 128)
graphics.drawText(posx+3, posy+3, text, 255, 208, 16, math.floor(notice_cd/max_notice_cd*255))
notice_cd = notice_cd - 1
end
end
event.register(event.tick,step_display)
modes[1].hud_alert = function()
local sel = modes[1].list[modes[1].selected_number]
if sel then
if sel[2]['FIELD_TYPE'] == 0 then
hud_plain(modes[1].selected_number..' of '..#modes[1].list..', Weight: '..sel[1]..', Elem: (Empty)')
else
hud_plain(modes[1].selected_number..' of '..#modes[1].list..', Weight: '..sel[1]..', Elem: '..tpt.element(sel[2]['FIELD_TYPE']))
end
else
hud_plain(modes[1].selected_number..' of '..#modes[1].list..', (blank)')
end
end
modes[2].hud_alert = function(detail)
local sel = modes[2].list[modes[2].ptr_ud][modes[2].ptr_lr]
if sel and not detail then
hud_plain('X: '..modes[2].ptr_lr..', Y: '..modes[2].ptr_ud..', Elem: '..tpt.element(sel['FIELD_TYPE']))
elseif not sel and not detail then
hud_plain('X: '..modes[2].ptr_lr..', Y: '..modes[2].ptr_ud..', (blank)')
elseif sel and detail then
hud_plain('W: '..modes[2].width..', H: '..modes[2].height..', Xoff: '..modes[2].x_offset..', Yoff: '..modes[2].y_offset..', X: '..modes[2].ptr_lr..', Y: '..modes[2].ptr_ud..', Elem: '..tpt.element(sel['FIELD_TYPE']))
elseif not sel and detail then
hud_plain('W: '..modes[2].width..', H: '..modes[2].height..', Xoff: '..modes[2].x_offset..', Yoff: '..modes[2].y_offset..', X: '..modes[2].ptr_lr..', Y: '..modes[2].ptr_ud..', (blank)')
end
end
modes[3].hud_alert = function()
local sel = modes[3].list[modes[3].selected_number]
if sel then
if sel['FIELD_TYPE'] == 0 then
hud_plain(modes[3].selected_number..' of 5 layers, Elem: (Empty)')
else
hud_plain(modes[3].selected_number..' of 5 layers, Elem: '..tpt.element(sel['FIELD_TYPE']))
end
else
hud_plain(modes[3].selected_number..' of 5 layers, (blank)')
end
end
modes[2].pick_parts = function(x,y)
modes[2].ptr_lr = 1
modes[2].ptr_ud = 1
modes[2].x_offset = x%modes[2].width
modes[2].y_offset = y%modes[2].height
for j=1,modes[2].height do
for i=1,modes[2].width do
local temp_pixel = {}
local index = sim.partID(i+x-1,j+y-1)
if index ~= nil then
pick_part(temp_pixel,index)
else
temp_pixel = false
end
modes[2].list[j][i] = temp_pixel
end
end
global_pked_color()
end
modes[3].pick_parts = function(x,y) -- pick layered particles
local l = {}
for index in sim.parts() do
if #l == 5 then
break
end
local x1,y1 = sim.partPosition(index)
if x1 == x and y1 == y then
local temp_pixel = {}
pick_part(temp_pixel,index)
table.insert(l,temp_pixel)
end
end
modes[3].list = l
global_pked_color()
end
--from internet
function string.split(str, delimiter)
if str==nil or str=='' or delimiter==nil then
return nil
end
local result = {}
for match in (str..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match)
end
return result
end
local function serialize_pixel(pixel)
local str = ''
for key in next, PROPERTIES do
str = str..','..key:sub(7,key:len())..':'..(tostring(pixel[key]) or '0')
end
return str:sub(2,str:len())
end
local function serialdumps_pixel(str)
local prop_nh = str:split(',')
local prop = {}
for _,s in ipairs(prop_nh) do
local ob_temp = s:split(':')
prop['FIELD_'..ob_temp[1]] = tonumber(ob_temp[2])
end
return prop
end
modes[0].serialize = function()
local str = 'MODE01@'
str = str..serialize_pixel(modes[0].thepixel)
return str
end
modes[0].serialdumps = function(str)
if str:sub(1,7) ~= 'MODE01@' then return false end
str = str:sub(8,str:len())
change_mode()
modes[0].thepixel = serialdumps_pixel(str)
global_pked_color()
return true
end
modes[1].serialize = function()
local str = 'MODE02@'
for _,weighted in ipairs(modes[1].list) do
str = str..serialize_pixel(weighted[2])
str = str..'#'..tostring(weighted[1])..'*'
end
return str:sub(1,str:len()-1)
end
modes[1].serialdumps = function(str)
if str:sub(1,7) ~= 'MODE02@' then return false end
str = str:sub(8,str:len())
change_mode()
local p1 = str:split('*')
local res = {}
for _,s in ipairs(p1) do -- s is 'pixel#freq'
local temp_pair = s:split('#')
local ttemp = temp_pair[1]
temp_pair[1] = tonumber(temp_pair[2])
temp_pair[2] = serialdumps_pixel(ttemp)
table.insert(res,temp_pair)
end
modes[1].list = res
global_pked_color()
return true
end
modes[2].serialize = function()
local str = 'MODE03@'
str = str..tostring(modes[2].width)..','..tostring(modes[2].height)..','..tostring(modes[2].x_offset)..','..tostring(modes[2].y_offset)..'*'
for ir,row in ipairs(modes[2].list) do
for ic,pixel in ipairs(row) do
if pixel then
str = str..serialize_pixel(pixel)
str = str..'#'..tostring(ir)..','..tostring(ic)..'*'
end
end
end
return str:sub(1,str:len()-1)
end
modes[2].serialdumps = function(str)
if str:sub(1,7) ~= 'MODE03@' then return false end
str = str:sub(8,str:len())
change_mode()
local p1 = str:split('*')
local posinfo = table.remove(p1,1)
posinfo = posinfo:split(',')
for i,v in ipairs(posinfo) do posinfo[i] = tonumber(v) end
local w,h
w,h,modes[2].x_offset,modes[2].y_offset= posinfo[1],posinfo[2],posinfo[3],posinfo[4]
modes[2].set_raw_size(h,w)
for _,s in ipairs(p1) do -- s is 'pixel#coord'
local temp_pair = s:split('#')
local co = temp_pair[2]:split(',')
modes[2].list[tonumber(co[1])][tonumber(co[2])] = serialdumps_pixel(temp_pair[1])
end
global_pked_color()
return true
end
modes[3].serialize = function()
local str = 'MODE04@'
for _,pixel in ipairs(modes[3].list) do
str = str..serialize_pixel(pixel)..'*'
end
return str:sub(1,str:len()-1)
end
modes[3].serialdumps = function(str)
if str:sub(1,7) ~= 'MODE04@' then return false end
str = str:sub(8,str:len())
change_mode()
local p1 = str:split('*')
local res = {}
for _,s in ipairs(p1) do
table.insert(res,serialdumps_pixel(s))
end
modes[3].list = res
global_pked_color()
return true
end
local function picker_hook(x,y,button)
local x2,y2 = sim.adjustCoords(x,y)
if x > 611 or y > 383 then --prevent clicking out of scene
return
end
if (tpt.selectedl==id_picker and button==1) or (tpt.selectedr==id_picker and button==2) or (tpt.selecteda==id_picker and button==3) then --corresponding buttons
if sim.partID(x2,y2) == nil then
return false
end
if moden == 1 then
local pixel = {}
pick_part(pixel,sim.partID(x2,y2))
modes[1].list[modes[1].selected_number] = {1,pixel}
modes[1].hud_alert()
modes[1].selected_number = modes[1].selected_number + 1
elseif moden == 0 then
pick_part(modes[0].thepixel,sim.partID(x2,y2))
elseif moden == 2 then
if not (modes[2].width==0 and modes[2].height==0) then
local temp_pixel = {}
pick_part(temp_pixel,sim.partID(x2,y2))
modes[2].list[modes[2].ptr_ud][modes[2].ptr_lr] = temp_pixel
modes[2].hud_alert()
else
hud_plain('Set pattern size first! (Press "+")')
end
elseif moden == 3 then
local pixel = {}
pick_part(pixel,sim.partID(x2,y2))
modes[3].list[modes[3].selected_number] = pixel
modes[3].hud_alert()
if modes[3].selected_number < 5 then
modes[3].selected_number = modes[3].selected_number + 1
end
end
--print(x2,y2,modes[0].thepixel['FIELD_TYPE'])
global_pked_color() --change the color of 'pick'
return false
end
end
event.register(event.mousedown,picker_hook)
local function picker_hook_up(x,y,button)
local x2,y2 = sim.adjustCoords(x,y)
if x > 611 or y > 383 then --prevent clicking out of scene
return
end
if (tpt.selectedl==id_picker and button==1) or (tpt.selectedr==id_picker and button==2) or (tpt.selecteda==id_picker and button==3) then --corresponding buttons
if moden == 0 then -- mouse release after sampled in mode0
tpt.selectedl=id_picked
end
end
end
event.register(event.mouseup,picker_hook_up)
modes[0].pick_key_handler = function(key)
end
modes[1].pick_key_handler = function(key)
if key == keys["delete"] then
if modes[1].list[modes[1].selected_number] then
table.remove(modes[1].list,modes[1].selected_number)
global_pked_color()
end
elseif key == keys["pgup"] then
modes[1].selected_number = restrict_ptr(modes[1].selected_number,#modes[1].list,-1)
elseif key == keys["pgdn"] then
modes[1].selected_number = restrict_ptr(modes[1].selected_number,#modes[1].list+1,1)
elseif key == keys["num-"] and modes[1].list[modes[1].selected_number] then
if modes[1].list[modes[1].selected_number][1] > 0 then
modes[1].list[modes[1].selected_number][1] = modes[1].list[modes[1].selected_number][1] - 1
global_pked_color()
end
elseif key == keys["num+"] and modes[1].list[modes[1].selected_number] then
modes[1].list[modes[1].selected_number][1] = modes[1].list[modes[1].selected_number][1] + 1
global_pked_color()
elseif key == keys["num*"] then
--only an hud_alert
elseif key == keys["num/"] then
modes[1].list[modes[1].selected_number] = {1,{['FIELD_TYPE']=0}} --empty element
else
return
end
modes[1].hud_alert()
return false
end
modes[2].proper_input = function(text1,text2)
local whs = tpt.input(text1, text2)
local n = string.find(whs,',')
if n == nil then
tpt.throw_error('Invalid format!')
return false
end
local d1 = tonumber(string.sub(whs, 1, n - 1))
local d2 = tonumber(string.sub(whs, n + 1, #whs))
if type(d1) ~= 'number' or type(d2) ~= 'number' then
tpt.throw_error('Invaild format!')
return false
end
return d1,d2
end
modes[2].set_raw_size = function(d1,d2)
modes[2].width,modes[2].height = d1,d2
for i=1,modes[2].height do
modes[2].list[i] = {}
for j=1,modes[2].width do
modes[2].list[i][j] = false
end
end
end
modes[2].set_pattern_size = function()
local d1,d2 = modes[2].proper_input("Pattern size", "Enter pattern size:\"w,h\"")
if d1 == false then
return false
end
--print(string.sub(whs, 1, n - 1)..'\\'..string.sub(whs, n, #whs))
change_mode()
modes[2].set_raw_size(d1,d2)
hud_plain('W: '..modes[2].width..', H: '..modes[2].height)
end
modes[2].set_xy_offset = function()
local d1,d2 = modes[2].proper_input("Pattern offset", "Enter pattern offset:\"x,y\"")
if d1 == false then
return false
end
modes[2].x_offset,modes[2].y_offset = d1,d2
modes[2].hud_alert(true)
end
modes[2].pick_key_handler = function(key)
if (modes[2].width==0 and modes[2].height==0) then
if key == keys["num+"] then
modes[2].set_pattern_size()
return false
else
hud_plain('Set pattern size first! (Press "+")')
return false
end
end
if key == keys["up"] then
modes[2].ptr_ud = restrict_ptr(modes[2].ptr_ud ,modes[2].height,-1)
elseif key == keys["down"] then
modes[2].ptr_ud = restrict_ptr(modes[2].ptr_ud ,modes[2].height,1)
elseif key == keys["left"] then
modes[2].ptr_lr = restrict_ptr(modes[2].ptr_lr ,modes[2].width,-1)
elseif key == keys["right"] then
modes[2].ptr_lr = restrict_ptr(modes[2].ptr_lr ,modes[2].width,1)
elseif key == keys["num+"] then
modes[2].set_pattern_size()
return false
elseif key == keys["num-"] then
modes[2].set_xy_offset()
return false
elseif key == keys["delete"] then
modes[2].list[modes[2].ptr_ud][modes[2].ptr_lr] = false
elseif key == keys["num*"] then
modes[2].hud_alert(true)
return false
elseif key == keys["num/"] then
local x,y = sim.adjustCoords(tpt.mousex,tpt.mousey)
modes[2].pick_parts(x,y)
hud_plain('Pattern sampled!')
return false
else
return
end
modes[2].hud_alert()
return false
end
modes[3].pick_key_handler = function(key)
if key == keys["delete"] then
if modes[3].list[modes[3].selected_number] then
table.remove(modes[3].list,modes[3].selected_number)
global_pked_color()
end
elseif key == keys["pgup"] then
if modes[3].selected_number > 1 then
modes[3].selected_number = modes[3].selected_number - 1
end
elseif key == keys["pgdn"] then
if modes[3].selected_number < 5 and modes[3].selected_number <= #modes[3].list then
modes[3].selected_number = modes[3].selected_number + 1
end
elseif key == keys["num*"] then
--only an hud_alert
elseif key == keys["num/"] then
local x,y = sim.adjustCoords(tpt.mousex,tpt.mousey)
modes[3].pick_parts(x,y)
hud_plain('Stack sampled!')
return false
else
return
end
modes[3].hud_alert()
return false
end
local function pick_adv_key_hook(key, code, is_repeat, shift, ctrl, alt)
if is_repeat then
return
end
if tpt.selectedl ~= id_picker then
return
end
if key == keys["home"] then
local n = moden
if n >= mode_max_n then
n = 0
else
n = n + 1
end
change_mode(n)
hud_plain('Mode '..(moden+1)..': '..modes[moden].name)
return false
elseif key == keys["end"] then
if prop_vel_enabled then
gen_prop_list(false)
hud_plain('Velocity sampling is disabled!')
else
gen_prop_list(true)
hud_plain('Velocity sampling is enabled!')
end
return false
elseif key == keys["insert"] then -- insert(save&load)
save.initialize()
return false
end
return modes[moden].pick_key_handler(key)
end
event.register(event.keypress,pick_adv_key_hook)
local function pked_key_hook(key, code, is_repeat, shift, ctrl, alt)
if is_repeat then
return
end
if tpt.selectedl ~= id_picked then
return
end
if key == keys["delete"] then --delete
change_mode()
return false
elseif key == keys["insert"] then -- insert(save&load)
save.initialize()
return false
end
end
event.register(event.keypress,pked_key_hook)
-- something related to cray, keep it
-- press alt holding "picked" to directly draw a dot
-- press ctrl will enable stacking
local function cray_bug_special_mouse(x,y,button)
--print(cray_alt,button)
if tpt.selectedl ~= id_picked or bit.band(event.getmodifiers(),256) == 0 or button ~= 1 then
return
end
local x2,y2 = sim.adjustCoords(x,y)
if bit.band(event.getmodifiers(),64) == 0 then
sim.partCreate(-1,x2,y2,el_picked)
else
sim.partCreate(-3,x2,y2,el_picked)
end
return false
end
event.register(event.mousedown,cray_bug_special_mouse)
--local save = {} --see upward
save.cols,save.rows=5,20
function save.number_to_coord(num)
num = num -1
local cols,rows = save.cols,save.rows
local page,numinpage = math.floor(num/(cols*rows)),num%(cols*rows)
local col,row = math.floor(numinpage/rows),numinpage%rows
return page,col,row
end
function save.scan_files()
if not fs.isDirectory('./samples') then fs.makeDirectory('./samples') end
local dir = fs.list('./samples')
for _,item in ipairs(dir) do
if fs.isDirectory('./samples/'..item) then
table.remove(dir,_)
end
end
modes[233].file_list = dir
end
function save.scan_color()
if not fs.isFile('./samples/color/colors.txt') then return end
for line in io.lines('./samples/color/colors.txt') do
modes[233].color_data[line:split(':')[1]] = tonumber(line:split(':')[2],16)
end
end
function save.save_color()
if not fs.isDirectory('./samples/color') then fs.makeDirectory('./samples/color') end
local f = io.open('./samples/color/colors.txt','w')
for k,item in next,modes[233].color_data do
f:write(k..':'..bit.tohex(bit.band(0xffffff,item),6)..'\n')
end
f:close()
end
function save.purge_color()
local color_data_new = {}
for _,item in ipairs(modes[233].file_list) do
if modes[233].color_data[item] then
color_data_new[item] = modes[233].color_data[item]
end
end
modes[233].color_data = color_data_new
end
function save.refresh_color()
save.scan_files()
modes[233].color_data = {}
for _,file in ipairs(modes[233].file_list) do
if save.load_file(file) then
local color = modes[modes[233].tosave_mode].quick_pked_color()
modes[233].color_data[file] = color
end
end
save.save_color()
modes[233].tosave_mode = 0
save.ret()
change_mode(0)
end
function save.load_file(filename)
if show_hud == nil then show_hud = true end
local f = io.open('./samples/'..filename,'r')
if not f then
hud_plain('File does not exist!')
return false
end
local data = f:read('*a')
f:close()
if not(data:sub(1,23) == 'DJY_ALL_SEEING_SAMPLER@') then
hud_plain('Invalid file!')
return false
end
data = data:sub(24,data:len())
change_mode(tonumber(data:sub(5,6))-1)
modes[233].tosave_mode = tonumber(data:sub(5,6))-1
if modes[moden].serialdumps(data) then
hud_plain('Loaded '..filename..'!')
else
hud_plain('Error!')
return false
end
return true
end
function save.save_file(filename)
if not fs.isDirectory('./samples') then fs.makeDirectory('./samples') end
local f = io.open('./samples/'..filename..'.smp','w')
f:write('DJY_ALL_SEEING_SAMPLER@'..modes[modes[233].tosave_mode].serialize())
f:close()
hud_plain('Sample is saved to '..filename..'.smp !')
end
function save.limit_text(text, length)
if text:len() > length then
return text:sub(1,length-3)..'...'
end
return text
end
function save.unpack_color(longint)
if longint ~= nil then
--print(longint,type(longint))
return {bit.rshift(bit.band(0xff0000,longint),16),bit.rshift(bit.band(0x00ff00,longint),8),bit.rshift(bit.band(0x0000ff,longint),0)}
else
return {0,0,0}
end
end
function save.draw_HUD()
if not modes[233].file_list[modes[233].selected_file_number] then return end
local page_to_draw = save.number_to_coord(modes[233].selected_file_number)
local cw,ch = 100,12 --cw:cell width ch:cell height
local text_max_length = 20 --max text length in cell
for index,name in ipairs(modes[233].file_list) do
local page,bc,br = save.number_to_coord(index)
if page == page_to_draw then
local bx,by = 16+bc*cw,67+br*ch
local bgcolor = modes[233].color_data[name]
graphics.fillRect(bx,by,cw,ch,0,0,0,192)
if bgcolor then
graphics.fillRect(bx+cw-ch+1,by+1,ch-3,ch-3,unpack(save.unpack_color(bgcolor)))
end
graphics.drawText(bx,by,save.limit_text(name,text_max_length), 255, 208, 16)
if index == modes[233].selected_file_number then
graphics.drawRect(bx-1,by-1,cw+1,ch+1, 255, 255, 255, 192)
end
end
end
local bcx = 16
graphics.fillRect(bcx,320,600-bcx,16,0,0,0,192)
graphics.drawText(bcx,320,"Selected sample: ", 255, 208, 16)
bcx = bcx + tpt.textwidth("Selected sample: ")+1
graphics.fillRect(bcx,320,ch-3,ch-3,unpack(save.unpack_color(modes[233].color_data[modes[233].file_list[modes[233].selected_file_number]])))
bcx = bcx + ch-2
graphics.drawText(bcx,320,save.limit_text(modes[233].file_list[modes[233].selected_file_number],100), 255, 208, 16)
end
--some placeholder
modes[233].pked_handler = function()
for index in sim.parts() do
if sim.partProperty(index,sim.FIELD_TYPE) == el_picked then
sim.partKill(index)
end
end
end
--step functions for saving&loading
function save.pick_save_handler()
if tpt.selectedl ~= id_picker then
return
end
local whs = tpt.input('Save to file','Enter file name:')
if whs == '' then
hud_plain('File name is empty!')
tpt.selectedl = 'DEFAULT_PT_DUST'
return
end
save.save_file(whs)
modes[233].color_data[whs..'.smp'] = modes[modes[233].tosave_mode].quick_pked_color()
save.save_color()
save.ret()
end
function save.pked_load_handler()
if tpt.selectedl ~= id_picked then
return
end
save.draw_HUD()
end
--key function
function save.pked_load_key(key, code, is_repeat, shift, ctrl, alt)
if is_repeat then
return
end
if tpt.selectedl ~= id_picked then
return
end
if key == keys["up"] then --u
modes[233].selected_file_number = restrict_ptr(modes[233].selected_file_number ,#modes[233].file_list,-1)
elseif key == keys["down"] then --d
modes[233].selected_file_number = restrict_ptr(modes[233].selected_file_number ,#modes[233].file_list,1)
elseif key == keys["left"] then --l
modes[233].selected_file_number = restrict_ptr(modes[233].selected_file_number ,#modes[233].file_list,-save.rows)
elseif key == keys["right"] then --r
modes[233].selected_file_number = restrict_ptr(modes[233].selected_file_number ,#modes[233].file_list,save.rows)
elseif key == keys["pgup"] then --pgup
modes[233].selected_file_number = restrict_ptr(modes[233].selected_file_number ,#modes[233].file_list,-save.rows*save.cols)
elseif key == keys["pgdn"] then --pgdn
modes[233].selected_file_number = restrict_ptr(modes[233].selected_file_number ,#modes[233].file_list,save.rows*save.cols)
elseif key == keys["num+"] then -- +
save.load_file(modes[233].file_list[modes[233].selected_file_number])
save.ret()
elseif key == keys["num-"] then -- -
local whs = tpt.input('Load file','Enter file name:')
if whs == '' then
hud_plain('File name is empty!')
return
end
save.load_file(whs)
save.ret()
elseif key == keys["num/"] then -- /
tpt.set_clipboard('DJY_ALL_SEEING_SAMPLER@'..modes[modes[233].tosave_mode].serialize())
hud_plain('Sample is copied to clipboard!')
save.ret()
elseif key == keys["num*"] then -- *
local data = tpt.get_clipboard()
if not(data:sub(1,23) == 'DJY_ALL_SEEING_SAMPLER@') then
hud_plain('Invalid clipboard data!')
return false
end
data = data:sub(24,data:len())
modes[233].tosave_mode = tonumber(data:sub(5,6))-1
save.ret()
if modes[moden].serialdumps(data) then
hud_plain('Sample is pasted from clipboard!')
else
hud_plain('Error!')
end
elseif key == keys["insert"] then -- insert:return to normal mode
save.ret()
elseif key == keys["end"] then -- end:refresh
save.scan_files()
save.scan_color()
hud_plain('Found '..#modes[233].file_list..' file(s)!')
elseif key == keys["delete"] then -- delete
local filename = modes[233].file_list[modes[233].selected_file_number]
if tpt.confirm('Delete file','Are you sure that you want to delete '..filename..' ?','Delete') then
fs.removeFile('./samples/'..filename)
end
save.scan_files()
save.purge_color()
save.save_color()
elseif key == keys["home"] then -- home:refresh_color
if tpt.confirm('Rescan','Are you sure that you want to rescan all the sample\'s color? You\'ll lose your current unsaved sample!','Rescan') then
save.refresh_color()
hud_plain('Sample colors are rescanned!')
end
else
return
end
return false
end
function save.initialize()
tpt.selectedl = 'DEFAULT_PT_DUST'
modes[233].tosave_mode = moden
moden=233
event.unregister(event.keypress,pked_key_hook)
event.unregister(event.keypress,pick_adv_key_hook)
event.unregister(event.mousedown,cray_bug_special_mouse)
event.unregister(event.mousedown,picker_hook)
event.unregister(event.mouseup,picker_hook_up)
local tt = modes[233].tooltips
elem.property(el_picker,"Description",tt.pick)
elem.property(el_picker,"Colour",tt.color1)
elem.property(el_picked,"Description",tt.pked)
elem.property(el_picked,"Colour",tt.color2)
hud_plain(modes[233].name)
save.scan_files()
save.scan_color()
event.register(event.tick,save.pick_save_handler)
event.register(event.tick,save.pked_load_handler)
event.register(event.keypress,save.pked_load_key)
end
function save.ret()
tpt.selectedl = id_picked
moden = modes[233].tosave_mode
save_def = {
file_list = {},
selected_file_number = 1,
tosave_mode = 0,
color_data = {}
}
for k,v in pairs(save_def) do
modes[233][k] = v
end
event.unregister(event.tick,save.pick_save_handler)
event.unregister(event.tick,save.pked_load_handler)
event.unregister(event.keypress,save.pked_load_key)
local tt = modes[moden].tooltips
elem.property(el_picker,"Description",tt.pick)
elem.property(el_picker,"Colour",tt.color)
elem.property(el_picked,"Description",tt.pked)
global_pked_color()
event.register(event.keypress,pked_key_hook)
event.register(event.keypress,pick_adv_key_hook)
event.register(event.mousedown,cray_bug_special_mouse)
event.register(event.mousedown,picker_hook)
event.register(event.mousedown,picker_hook)
event.register(event.mouseup,picker_hook_up)
end
local function quick_switch_tool(key, code, is_repeat, shift, ctrl, alt)
if is_repeat or key ~= keys["scrlck"] then
return
end
if tpt.selectedl == id_picker then
tpt.selectedl = id_picked
hud_plain("Selected tool: ["..chars["part"].."]")
return false
else
tpt.selectedl = id_picker
hud_plain("Selected tool: ["..chars["eye"].."]")
return false
end
end
event.register(event.keypress,quick_switch_tool)
Description:
Changelog: