TPT Script Server

This page is used for previewing and submitting scripts for use with the Script Manager

Available Scripts

(2) TPTMulti by LBPHacker
(3) set wifi v2 by jacob1
(4) Script Paste by jacob1
(5) Random Element by jacob1
(6) Magical Merge Master 3000 by nucular
(7) More Fuel Mod-heavy by jward212
(9) Breakpoints (BRPT) by boxmein
(10) Cockroaches! by boxmein
(11) Random tree by ssccsscc
(12) Element with random properties by ssccsscc
(15) Minimalistic Element Dehider by nucular
(16) FPS Gauge by mniip
(17) TPT Radio by jward212
(18) Print Debugger by FeynmanLogomaker
(19) Powered BHOL by jacob1
(20) Light and lamps by electronic_steve
(21) Extremely Durable TTAN by QuentinADay
(22) New Buttons by QuentinADay
(23) Pure Energy by QuentinADay
(25) RCA's HUD XV Update I by RCAProduction
(27) 123456787654 vols by mjpowder
(28) Singularity Bomb by QuentinADay
(29) ES wifi set by electronic_steve
(30) Lua Elements Pack by FeynmanLogomaker
(31) stkm gun by jward212
(32) space building materials by kjack1111
(34) Rust bomb by Damian97
(35) Simple command block by ssccsscc
(36) Everlasting Fusion by QuentinADay
(37) Screenshot Organiser by mecha-man
(38) Napalm mod by cccp3
(39) Rocket fuel mod v0.15 by cccp3
(41) Useful web links by jward212
(42) TPT Logic Gates Mod by iamdumb
(43) ESTools by electronic_steve
(44) Head Crabs-HL2 by jward212
(45) Procedural Save Generator by boxmein
(46) smooth colours for nametag by jward212
(47) ZAKPACK by zak03
(49) Performance Monitor by FeynmanLogomaker
(51) Texter by byzod
(52) Texter default fonts by byzod
(53) Schicko's Font Pack for Texter by Schicko
(54) Realistic Element Names by Atomic10
(55) TPT's Mod V.3 Update 1 by Amy
(56) Temporaryaccount-Decorator by Temporaryaccount
(57) random save loader by jward212
(58) Tmp gradient display by ssccsscc
(59) particle re-orderer by mniip
(60) Electric Glow by jacob1
(61) More Fuel Mod-lite by jward212
(63) Rythidium by janekbe04
(64) Simple FPS GUI by Sfsjunior
(65) Enhanced Element Dehider by ChargedCreeper
(66) Graph of average temp by ssccsscc
(67) Template save loader by jacob1
(68) Lua Text Generator by jBot-42
(70) Pixel's Freezer by Pixelguru26
(71) Thingy | Fusion For Ever by TheChosenEvilOne
(72) op explosions by zolly_bro
(73) Scar by DorkyBobster
(74) ScreenShotMod by lill74
(77) Useful Things by TheEvilChosenOne
(78) Alchemy Mod by _MrN_
(79) Nuke v2 by Fnaf65
(80) Compressor mod by TheChosenEvilOne
(81) Custom Render Mode Loader by jacob1
(82) Spacewars by JosephMA
(83) MOAR - Alpha 0.1 by TheChosenEvilOne
(86) Element Creator by cxi
(88) Soapworm by LBPHacker
(90) Pressure Bomb by God_Kra
(91) SMFB by wntjq69
(92) Potato by cxi
(93) Subatomic Pack (BDS) by TPT_PL
(94) Acidic Pack (BDS) by TPT_PL
(95) Starbound Building Materials by Sanpypr
(96) Factory problems by TPT_PL
(97) Gamma Ray-diation by Kostia4381
(98) Magic by livingfossil
(99) Cross-window Copy/Cut/Paste by LBPHacker
(100) Langton's Ant with variations by LBPHacker
(101) Remote particle creator/deleter by TPT_PL
(102) Force fields by electronic_steve
(103) Reinforced Concrete by 12Me21
(105) TPT_PL's Lua Mod by TPT_PL
(106) Nuke v4 by Fnaf65
(107) CHEMMOD V1 by KevAK
(108) Chemicals by Ligan
(109) VonDaniel's Template by VonDaniel
(110) The Inaccurate Radioactivity Toy Mod by TuDoR2007
(112) textmonsterPack by textmonster404
(113) Meteor by TheScienceKid
(114) Tgpm by TuDoR2007
(115) Civilizations by TPT_PL
(116) RAD-MOD 1.2.1B by Kev_AK
(117) MicroLua by RamiLego4Game
(118) Extra customizable HUD by djy0212
(119) Ingame brush editor by ssccsscc
(120) Window Maker by Paul_31415
(121) CHEM-MOD V1.2B by Kev_AK
(122) Rainbow PHOT by Mrprocom
(123) stronger stickmanv by yuval
(124) 3D Pressure Visualizer by mniip
(125) Arkadian Liquid by JanKaszanka
(126) Fuel by nukers473
(127) Immersive Radioactivity v2.1 by Potbelly
(128) ElementLaunchingTool by juh9870
(129) CHEM-MOD_v1.2.2b by KevAK
(130) Slingshot by Mrprocom
(131) Perlin Noise Generator by DoubleF
(132) Element Replace by TomW1605
(133) Flooder V2 by TheAwesomeMutant
(134) Link Sign GUI by QuanTech
(135) Element dehider by 4e616d65
(136) Subphoton ROM Builder by mad-cow
(137) Hardened Dust by Liftski
(138) Bio-Vir by TheAwesomeMutant
(140) Orbit Simulator by Mrprocom
(141) johnnyou's Font for Texter by johnnyou (49796346)
(142) auto_wifi by phisically
(143) Layering helper by ssccsscc
(144) Layering Helper Extended by LuaMaster
(145) TPT Remade by TuDoR2007
(146) All-seeing sampler by djy0212
(147) Layering helper remastered by ssccsscc
(148) Eraser by thepowdertoy12
(149) EXPLOSIONS by olix3001
(150) Simple rocket fuel mod by ArseniyPlotnikov2006
(151) Pure Fission by Fnaf65
(154) Graph by ssccsscc
(155) Little's Pack! by LittleProgramming
(156) Lead by LoftisGaming
(157) WIFI Tuner by ssccsscc
(158) Previous Brush by TomW1605
(159) HUD Auto-Hider by Tim
(161) Stack tool by thepowdertoy12
(162) Oil and plastic by ArseniyPlotnikov2006
(163) Colored Ember by DUC
(164) Timer by ssccsscc
(165) Bacteria Mod by TuDoR2007
(166) Noise filter by LBPHacker
(167) Future-proof element dehider by LBPHacker
(168) RadioactiveNuke by DreamingWarlord
(169) Only Hot Element by DreamingWarlord
(170) Philosopher's Stone by Godhydra
(171) Conic section generator by LBPHacker
(172) Interface API by ssccsscc
(173) Metals&Materials by Ferrous26
(174) tpt.all by LBPHacker
(175) The Visual Elements Pack by Goblin01, vvv331
(176) Layering Helper Reforged by PowderNotSolid
(177) SNOWified SING by LBPHacker
(178) FPS Chart by Goblin01
(179) TPT font writer by Goblin01
(180) Simple Ruler by PowderNotSolid
(181) Heat Modifier by DreamingWarlord
(182) TPT Remade II by TuDoR2007
(183) Gravity simulator by ArseniyPlotnikov2k6
(184) Unobtainium by christheboss894
(185) TPTMIDI noteblock in tpt by djy0212
(186) DreamingWarlord's Lua Tool by DreamingWarlord
(187) Elements Tooltip by Goblin01
(189) Explodium script by 0d15ea5ebe57c0debadc0ffee0ddf00d
(190) more powered force elements by 6nop6nop
(191) Yzaak1Scifire Modpack by Yzaak1Scifire
(194) Fluor and more modpack! by galaktor
(195) Hot Powder by lieve_blendi
(196) Star by TUANMINHVIETNAM
(197) Heat Powders by lieve_blendi
(200) Tangeriinium (thx 2 cxi 4 code) by LostEditor
(201) Freezer by lieve_blendi
(202) Powder Power! by TPTSortaGuy
(203) PowderPlus v1.4 by PowderPlus Team
(204) fire by ME
(205) Stacked Goo Animations by Maticzpl
(206) Stickman Control for Android Version by PhauloRiquelme
(207) Spark Removal Button by Xyz
(208) More HEAC's! by Maxhd1234
(209) Immersive Radioactivity v3.0 by Potbelly
(210) Subframe Chipmaker Script by Maticzpl
(211) Realistic Propellants by ArseniyPlotnikov2k6
(212) Mass Equals Gravity by Maticzpl
(213) PhiMod v1 by ArolaunTech
(214) PC Controls for Android by Cracker1000
(217) Single-pixel pipe configurator by LBPHacker
(218) Omega Death Laser Gun by Dogeoum
(219) Notifications by Maticzpl
(221) Powderizer by ArolaunTech
(222) ElemDehider 1.2 by Inventor70
(223) Unobtainum V2 by DoomKittyAttack
(224) Organics Mod v0.2B by PowderPlus Team
(225) Gravity distortion by Avolte55
(226) tmp Wifi by PhauloRiquelme
(227) Alchemagica Mod v1.0 by RebMiami
(228) Fan Elements Mod by RebMiami
(229) Impossibilities by ArolaunTech
(230) Realistic Explosives by ArseniyPlotnikov2k6
(231) libactivation by anamorphic
(232) Alloy Brushes by Maticzpl
(233) Gravity bender by pres
(234) Slow Tick by Pixel
(235) Paste ID by Maticzpl
(236) many things by jadenflp2
(237) Territect by Rebmiami
(238) Better Descriptions v1.0.5 by ashyboi2022
(239) LIGHTNING SPRK by GOLmaster10101
(240) Small Bombs by juh9870
(241) Save Shop by aaccbb
(242) Moving solids v1.3.0 Beta by ArolaunTech
(244) Alchemistry by rdococ
(245) ETRD (Formerly PowderIM) by aaccbb
(246) RadonX by Justadirtblock
(248) Water-X by deuterium_oxide
(250) Indestructible INSL by CheekyRand0m
(252) Console's Mod by Console/Compec
(255) Slow motion by LBPHacker
(256) Powered Repeller by Hythonia
(257) Zeta's Electric Tools. by Zetalasis
(258) Azure serum (AZSR) by ALumpOfPowderToy
(259) COLORFULSAND by xert
(260) Lightning Circle by defaultuser0
(261) Powder Future Tech by JonaHungary
(262) TPTGlowingSolids by DestinyDyson
(263) Volcano Bomb by I_am_the_NugsWorld
(264) Neon Lights by Rebmiami
(265) Radioactive Materials by xyz
(266) Eater mod by VIPERGAMEZ
(267) the biology mod by someone
(268) Atomic Physics by qe
(269) Pure Radiation by ronansb
(270) Fake Elements by That_PowderToy_Guy
(271) Tachyons and MISC by RamenNoods
(272) Exotic Particles by rdococ
(273) FPS Slider by aaccbb
(274) Enphosian's Radioactive mod pack by Enphosian
(276) ROM Builder by QnpfvTPz

+ Submit new script!

Title: Author:
Script:
local xy_properties = {}
for _, name in pairs({ "x", "y" }) do
    xy_properties[name] = true
    xy_properties[sim["FIELD_" .. name:upper()]] = true
end
local float_properties = {}
for _, name in pairs({ "x", "y", "vx", "vx", "temp" }) do
    float_properties[name] = true
    float_properties[sim["FIELD_" .. name:upper()]] = true
end
local default_epsilon = 1e-3

local function round(a)
    return math.floor(a + 0.5)
end

local element_name_cache = {}
local function rebuild_element_name_cache()
    element_name_cache = {}
    for long_name, element_id in pairs(elem) do
        if type(long_name) == "string" and long_name:find("_PT_") then
            local ok, display_name = pcall(elem.property, element_id, "Name")
            if ok then
                element_name_cache[display_name:upper()] = element_id
            end
        end
    end
end

local function prop_value_smart(property_value)
    if type(property_value) == "string" then
        do -- * It may be a temperature value. Chicken wings, anyone?
            local num, kfc = property_value:upper():match("^(.+)([KFC])$")
            if num then
                num = tonumber(num)
            end
            if num then
                if kfc == "F" then
                    num = (num - 32) / 1.8
                    kfc = "C"
                end
                if kfc == "C" then
                    num = num + 273.15
                end
                return num
            end
        end
        do -- * It may be an element name.
            local upper = property_value:upper()
            local elementID = element_name_cache[upper]
            local ok, display_name = pcall(elem.property, elementID, "Name")
            if not ok or display_name:upper() ~= upper then
                rebuild_element_name_cache()
                elementID = element_name_cache[upper]
            end
            if elementID then
                return elementID
            end
        end
        do -- * It may be a # colour code.
            local lower = property_value:lower()
            local code = lower:match("^#([a-f%d][a-f%d][a-f%d][a-f%d][a-f%d][a-f%d])$")
            if not code then
                code = lower:match("^#([a-f%d][a-f%d][a-f%d])$")
                if code then
                    code = code:sub(1, 1):rep(2) .. code:sub(2, 2):rep(2) .. code:sub(3, 3):rep(2)
                end
            end
            if code then
                return tonumber("0xFF" .. code)
            end
        end
        do -- * It may be a number in string form.
            local num = tonumber(property_value)
            if num then
                return num
            end
        end
    end
    return property_value
end

local function prop_value(property_value)
    property_value = prop_value_smart(property_value)
    if type(property_value) ~= "number" then
        error("invalid property value")
    end
    return property_value
end

local function prop_value_func(property_value)
    return type(property_value) == "function" and property_value or prop_value(property_value)
end

local particle_proxy_m = {}

function particle_proxy_m:__index(property_key)
    return sim.partProperty(self.id, property_key)
end

function particle_proxy_m:__newindex(property_key, property_value)
    return sim.partProperty(self.id, property_key, prop_value(property_value))
end

local weak_proxy_store = setmetatable({}, { __mode = "v" })
local function make_particle_proxy(id)
    local grab_proxy = weak_proxy_store[id]
    if grab_proxy then
        return grab_proxy
    end
    local new_proxy = setmetatable({ id = id }, particle_proxy_m)
    weak_proxy_store[id] = new_proxy
    return new_proxy
end

local make_particle_set

local particle_set_i = {}
local particle_set_m = {}

function particle_set_m:__index(key)
    if type(key) == "number" then
        return self:range(key, key)
    else
        return particle_set_i[key]
    end
end

function particle_set_m:__add(other)
    local result = make_particle_set()
    for id in self:iterate() do
        result:insert(id)
    end
    for id in other:iterate() do
        result:insert(id)
    end
    return result
end

function particle_set_m:__sub(other)
    local result = make_particle_set()
    for id in self:iterate() do
        result:insert(id)
    end
    for id in other:iterate() do
        result:remove(id)
    end
    return result
end

function particle_set_m:__mul(other)
    local result = make_particle_set()
    for id in self:iterate() do
        result:insert(id)
    end
    for id in (tpt.all() - other):iterate() do
        result:remove(id)
    end
    return result
end

function particle_set_m:__eq(other)
    if self:count() ~= other:count() then
        return false
    end
    for id in self:iterate() do
        if not other:has(id) then
            return false
        end
    end
    return true
end

function particle_set_m:__lt(other)
    for id in self:iterate() do
        if not other:has(id) then
            return false
        end
    end
    for id in other:iterate() do
        if not self:has(id) then
            return true
        end
    end
    return false
end

function particle_set_m:__le(other)
    for id in self:iterate() do
        if not other:has(id) then
            return false
        end
    end
    for id in other:iterate() do
        if not self:has(id) then
            return true
        end
    end
    return true
end

function particle_set_m:__tostring()
    return tostring(self:count())
end

function particle_set_i:next(current_id)
    local next_id, next_proxy = next(self.particle_assoc, current_id)
    if not next_id then
        return
    end
    return next_id, next_proxy
end

function particle_set_i:iterate()
    return self.next, self
end

function particle_set_i:bbox(x1, y1, x2, y2, eyeball_xy)
    return self:gte("x", x1, eyeball_xy):gte("y", y1, eyeball_xy):lt("x", x2, eyeball_xy):lt("y", y2, eyeball_xy)
end

function particle_set_i:cursor(width, height, eyeball_xy)
    local x, y = sim.adjustCoords(tpt.mousex, tpt.mousey)
    return self:bbox(x, y, x + width, y + height, eyeball_xy)
end

function particle_set_i:filter(func, ...)
    local result = make_particle_set()
    for id, proxy in self:iterate() do
        if func(proxy, ...) then
            result:insert(id)
        end
    end
    return result
end

function particle_set_i:clone()
    return self:filter(function()
        return true
    end)
end

function particle_set_i:eq(property_key, property_value, eyeball_xy, epsilon)
    property_value = prop_value(property_value)
    if float_properties[property_key] then
        epsilon = epsilon or default_epsilon
        if eyeball_xy and xy_properties[property_key] then
            return self:filter(function(proxy)
                return round(proxy[property_key]) == property_value
            end)
        else
            return self:filter(function(proxy)
                return math.abs(proxy[property_key] - property_value) < epsilon
            end)
        end
    else
        return self:filter(function(proxy)
            return proxy[property_key] == property_value
        end)
    end
end

function particle_set_i:neq(property_key, property_value, eyeball_xy, epsilon)
    property_value = prop_value(property_value)
    if float_properties[property_key] then
        epsilon = epsilon or default_epsilon
        if eyeball_xy and xy_properties[property_key] then
            return self:filter(function(proxy)
                return round(proxy[property_key]) ~= property_value
            end)
        else
            return self:filter(function(proxy)
                return math.abs(proxy[property_key] - property_value) >= epsilon
            end)
        end
    else
        return self:filter(function(proxy)
            return proxy[property_key] ~= property_value
        end)
    end
end

function particle_set_i:gt(property_key, property_value, eyeball_xy)
    property_value = prop_value(property_value)
    if eyeball_xy and xy_properties[property_key] then
        return self:filter(function(proxy)
            return round(proxy[property_key]) > property_value
        end)
    else
        return self:filter(function(proxy)
            return proxy[property_key] > property_value
        end)
    end
end

function particle_set_i:gte(property_key, property_value, eyeball_xy)
    property_value = prop_value(property_value)
    if eyeball_xy and xy_properties[property_key] then
        return self:filter(function(proxy)
            return round(proxy[property_key]) >= property_value
        end)
    else
        return self:filter(function(proxy)
            return proxy[property_key] >= property_value
        end)
    end
end

function particle_set_i:lt(property_key, property_value, eyeball_xy)
    property_value = prop_value(property_value)
    if eyeball_xy and xy_properties[property_key] then
        return self:filter(function(proxy)
            return round(proxy[property_key]) < property_value
        end)
    else
        return self:filter(function(proxy)
            return proxy[property_key] < property_value
        end)
    end
end

function particle_set_i:lte(property_key, property_value, eyeball_xy)
    property_value = prop_value(property_value)
    if eyeball_xy and xy_properties[property_key] then
        return self:filter(function(proxy)
            return round(proxy[property_key]) <= property_value
        end)
    else
        return self:filter(function(proxy)
            return proxy[property_key] <= property_value
        end)
    end
end

function particle_set_i:each(func, ...)
    for id, proxy in self:iterate() do
        func(proxy, ...)
    end
    return self
end

function particle_set_i:top()
    local new_ids = {}
    for id in self:iterate() do
        table.insert(new_ids, sim.partID(sim.partPosition(id)))
    end
    return tpt.all(new_ids)
end

function particle_set_i:range(first, last)
    local count = self:count()
    first = first or 1
    last = last or count
    if first < 0 then
        first = count + first + 1
    end
    if last < 0 then
        last = count + last + 1
    end
    local ordered = {}
    for id in self:iterate() do
        table.insert(ordered, id)
    end
    table.sort(ordered)
    local new_ids = {}
    for ix = first, last do
        table.insert(new_ids, ordered[ix])
    end
    return tpt.all(new_ids)
end

function particle_set_i:set(property_key, property_value)
    property_value = prop_value_func(property_value)
    for id, proxy in self:iterate() do
        if type(property_value) == "function" then
            proxy[property_key] = property_value(proxy)
        else
            proxy[property_key] = property_value
        end
    end
    return self
end

function particle_set_i:add(property_key, property_value)
    property_value = prop_value_func(property_value)
    for id, proxy in self:iterate() do
        if type(property_value) == "function" then
            proxy[property_key] = proxy[property_key] + property_value(proxy)
        else
            proxy[property_key] = proxy[property_key] + property_value
        end
    end
    return self
end

function particle_set_i:randomise(property_key, property_values)
    local pv_size = #property_values
    for ix = 1, pv_size do
        property_values[ix] = prop_value_func(property_values[ix])
    end
    for id, proxy in self:iterate() do
        local property_value = property_values[math.random(1, pv_size)]
        if type(property_value) == "function" then
            proxy[property_key] = property_value(proxy)
        else
            proxy[property_key] = property_value
        end
    end
    return self
end

function particle_set_i:rotate(angle, xcenter, ycenter)
    angle = (angle + 180) % 360 - 180
    local x1, y1, x2, y2 = self:get_bbox(true)
    if not xcenter then
        xcenter = round((x1 + x2) / 2)
    end
    if not ycenter then
        ycenter = round((y1 + y2) / 2)
    end
    local do180 = false
    if angle < -90 then
        angle = angle + 180
        do180 = true
    elseif angle > 90 then
        angle = angle - 180
        do180 = true
    end
    local pos = {}
    for id, proxy in self:iterate() do
        pos[id] = {
            x = round(proxy.x),
            y = round(proxy.y),
        }
    end
    local function shear(xf, yf)
        for id, item in pairs(pos) do
            local x = round(item.x)
            local y = round(item.y)
            local nx = x + round(xf * (y - ycenter))
            local ny = y + round(yf * (x - xcenter))
            if do180 then
                nx = 2 * xcenter - nx
                ny = 2 * ycenter - ny
            end
            item.x = nx
            item.y = ny
        end
    end
    local rangle = math.rad(angle)
    local xf = -math.tan(rangle / 2)
    local yf = math.sin(rangle)
    shear(xf,  0, do180)
    shear( 0, yf, false)
    shear(xf,  0, false)
    for id, proxy in self:iterate() do
        proxy.x = pos[id].x
        if sim.partExists(id) then
            proxy.y = pos[id].y
        end
    end
end

local field_type = sim.FIELD_TYPE
local field_x = sim.FIELD_X
local field_y = sim.FIELD_Y
local fields = {}
for key, value in pairs(sim) do
    if key:find("^FIELD_") then
        fields[value] = true
    end
end
function particle_set_i:reorder()
    local props = {}
    local ids = {}
    for _, proxy in self:iterate() do
        local part = {
            proxy = proxy,
            x = round(proxy.x),
            y = round(proxy.y),
        }
        for field in pairs(fields) do
            part[field] = proxy[field]
        end
        table.insert(props, part)
        table.insert(ids, proxy)
    end
    table.sort(ids, function(a, b)
        return a.id < b.id
    end)
    table.sort(props, function(a, b)
        if a.y < b.y then return  true end
        if a.y > b.y then return false end
        if a.x < b.x then return  true end
        if a.x > b.x then return false end
        return a.proxy.id < b.proxy.id
    end)
    for ix, part in pairs(props) do
        local proxy = ids[ix]
        proxy[field_type] = part[field_type]
        for field in pairs(fields) do
            if field ~= field_type then
                proxy[field] = part[field]
            end
        end
    end
    return self
end

function particle_set_i:get(property_key)
    local result
    local multiple_values = false
    for id, proxy in self:iterate() do
        local value = proxy[property_key]
        if result then
            if result ~= value then
                multiple_values = true
                break
            end
        else
            result = value
        end
    end
    return not multiple_values and result
end

function particle_set_i:get_bbox(eyeball_xy)
    local x1 = math.huge
    local y1 = math.huge
    local x2 = -math.huge
    local y2 = -math.huge
    for id, proxy in self:iterate() do
        local x, y = proxy.x, proxy.y
        if eyeball_xy then
            x = round(x)
            y = round(y)
        end
        x1 = math.min(x1, x)
        y1 = math.min(y1, y)
        x2 = math.max(x2, x)
        y2 = math.max(y2, y)
    end
    return x1, y1, x2 + 1, y2 + 1
end

function particle_set_i:average(property_key)
    local result = 0
    for id, proxy in self:iterate() do
        result = result + proxy[property_key]
    end
    return result / self.particle_count
end

function particle_set_i:kill()
    return self:each(function(proxy)
        sim.partKill(proxy.id)
    end)
end

function particle_set_i:insert(id)
    if not self.particle_assoc[id] then
        self.particle_assoc[id] = make_particle_proxy(id)
        self.particle_count = self.particle_count + 1
    end
    return self
end

function particle_set_i:remove(id)
    if self.particle_assoc[id] then
        self.particle_assoc[id] = nil
        self.particle_count = self.particle_count - 1
    end
    return self
end

function particle_set_i:has(id)
    return self.particle_assoc[id] and true
end

function particle_set_i:count()
    return self.particle_count
end

function particle_set_i:array()
    local exported = {}
    for id in self:iterate() do
        table.insert(exported, id)
    end
    return exported
end

function particle_set_i:assoc()
    local exported = {}
    for id in self:iterate() do
        exported[id] = true
    end
    return exported
end

local easy_operators = {
    [ "[]" ] = { takes_name = false, takes_value = 4, func = function(set, _, params)
        return set:bbox(params[1], params[2], params[3], params[4])
    end },
    [ "@" ] = { takes_name = false, takes_value = 2, func = function(set, _, params)
        return set:cursor(params[1], params[2])
    end },
    [ "==" ] = { takes_name = true, takes_value = 1, func = function(set, prop, params)
        return set:eq(prop, params[1])
    end },
    [ "!=" ] = { takes_name = true, takes_value = 1, func = function(set, prop, params)
        return set:neq(prop, params[1])
    end },
    [ "<" ] = { takes_name = true, takes_value = 1, func = function(set, prop, params)
        return set:lt(prop, params[1])
    end },
    [ "<=" ] = { takes_name = true, takes_value = 1, func = function(set, prop, params)
        return set:lte(prop, params[1])
    end },
    [ ">" ] = { takes_name = true, takes_value = 1, func = function(set, prop, params)
        return set:gt(prop, params[1])
    end },
    [ ">=" ] = { takes_name = true, takes_value = 1, func = function(set, prop, params)
        return set:gte(prop, params[1])
    end },
    [ "^" ] = { takes_name = false, takes_value = 0, func = function(set, _, _)
        return set:top()
    end },
    [ "=" ] = { takes_name = true, takes_value = nil, func = function(set, prop, params)
        if #params == 1 then
            return set:set(prop, params[1])
        else
            return set:randomise(prop, params)
        end
    end },
    [ "+=" ] = { takes_name = true, takes_value = 1, func = function(set, prop, params)
        return set:add(prop, params[1])
    end },
    [ "?" ] = { takes_name = true, takes_value = 0, func = function(set, prop, _)
        return set:get(prop)
    end },
    [ "?/" ] = { takes_name = true, takes_value = 0, func = function(set, prop, _)
        return set:average(prop)
    end },
    [ "!" ] = { takes_name = false, takes_value = 0, func = function(set, _, _)
        return set:kill()
    end },
    [ "$" ] = { takes_name = false, takes_value = 0, func = function(set, _, _)
        return set:count()
    end },
    [ "*" ] = { takes_name = false, takes_value = 0, func = function(set, _, _)
        return set:reorder()
    end },
}
function particle_set_i:easy(str)
    local counter = 0
    local result = self:clone()
    for word in str:gmatch("%S+") do
        counter = counter + 1
        local prop, op_str, params_str = word:match("^([%a%d]*)([!=<>?/$*+^@]+)([%a%d#.%-,]*)$")
        if not op_str then
            error("#" .. counter .. ": missing operator")
        end
        local op = easy_operators[op_str]
        if not op then
            error("#" .. counter .. ": unknown operator")
        end
        local params = {}
        for param in params_str:gmatch("[^,]+") do
            table.insert(params, prop_value(param))
        end
        if op.takes_value and op.takes_value ~= #params then
            error("#" .. counter .. ": takes " .. op.takes_value .. " values")
        end
        if #prop > 0 and not op.takes_name then
            error("#" .. counter .. ": takes no property")
        end
        if #prop == 0 and op.takes_name then
            error("#" .. counter .. ": needs a property")
        end
        result = op.func(result, prop, params)
    end
    return result
end

function make_particle_set()
    return setmetatable({
        particle_count = 0,
        particle_assoc = {},
    }, particle_set_m)
end

tpt.all = setmetatable({}, { __call = function(self, param, param2)
    if type(param2) == "number" and type(param) == "number" then
        return tpt.all.at(param, param2)
    end
    if param == false then
        return tpt.all.zero()
    elseif type(param) == "number" then
        return tpt.all.one(param)
    elseif type(param) == "string" then
        return tpt.all.easy(param)
    elseif type(param) == "table" then
        return tpt.all.array(param)
    elseif type(param) == "function" then
        return tpt.all.buffer(param)
    end
    local all_particles = make_particle_set()
    for id in sim.parts() do
        all_particles:insert(id)
    end
    return all_particles
end })

function tpt.all.at(x, y)
    local id = sim.partID(x, y)
    return id and tpt.all.one(id) or nil
end

function tpt.all.zero()
    return make_particle_set()
end

function tpt.all.one(id)
    local one_particle = make_particle_set()
    one_particle:insert(id)
    return one_particle
end

function tpt.all.easy(param)
    return tpt.all():easy(param)
end

function tpt.all.proxy(id)
    return make_particle_proxy(id)
end

function tpt.all.array(id_array)
    local all_particles = make_particle_set()
    for ix = 1, #id_array do
        all_particles:insert(id_array[ix])
    end
    return all_particles
end

function tpt.all.buffer(func, ...)
    local all_particles
    local sim_partCreate_original = sim.partCreate
    local ok, err = pcall(function(...)
        local id_buffer = {}
        sim.partCreate = function(...)
            local id = sim_partCreate_original(...)
            if id then
                id_buffer[id] = true
            end
            return id
        end
        func(...)
        all_particles = tpt.all.assoc(id_buffer)
    end, ...)
    sim.partCreate = sim_partCreate_original
    if not ok then
        error(err)
    end
    return all_particles
end

function tpt.all.assoc(id_assoc)
    local all_particles = make_particle_set()
    for id in pairs(id_assoc) do
        all_particles:insert(id)
    end
    return all_particles
end

function tpt.all.fill(particle_type)
    if particle_type then
        particle_type = prop_value(particle_type)
    else
        particle_type = elem.DEFAULT_PT_DMND
    end
    for ix = 0, sim.XRES - 1 do
        for iy = 0, sim.YRES - 1 do
            sim.partCreate(-2, ix, iy, particle_type)
        end
    end
end

Description:

Changelog: