PageSourceSearch

https://powder-toy.vercel.app/scripts/tptw.js

js powder-toy.vercel.app collected 2026-10-03 07:31:38 UTC 14,806 bytes, 423 lines download raw bytes

1import { Settings } from "./settings.js";
2import { Elements } from "./elements.js";
3import { NameButton, UsernameButton, Buttons, SetCurrentOpenedSave } from "./save.js";
4
5export const ParticleContainer = document.getElementById("ParticleContainer");
6export const PlaceholderContainer = document.getElementById("PlaceholderContainer");
7
8export function Random(Max, Min) {
9    return Math.floor(Math.random() * (Max - Min + 1)) + Min;
10}
11
12export function IsMobile() {
13    const userAgent = navigator.userAgent.toLowerCase();
14    return /android|webos|iphone|ipad|ipod|blackberry|windows phone|opera mini/i.test(userAgent);
15}
16
17export function Clear() {
18    SetCurrentOpenedSave(undefined);
19
20    ParticleContainer.innerHTML = "";
21    NameButton.value = "";
22    UsernameButton.value = "";
23
24    Array.from(Buttons.getElementsByTagName("div")).forEach(Button => {
25        Button.setAttribute("disabled", false);
26    });
27    Array.from(Buttons.getElementsByTagName("input")).forEach(Button => {
28        Button.disabled = false;
29    });
30}
31
32export function Notify(Title, Message, MarkIcon) {
33    const Container = document.createElement("div");
34    Container.style.position = "absolute";
35    Container.style.left = "50%";
36    Container.style.top = "50%";
37    Container.style.transform = "translate(-50%, -50%)";
38    Container.style.width = "75%";
39    Container.style.height = "35%";
40    Container.style.backgroundColor = "white";
41    Container.style.color = "black";
42    Container.style.transition = "all 0.125s ease";
43    Container.style.overflow = "hidden";
44    document.body.appendChild(Container);
45
46    const TitleElement = document.createElement("span");
47    TitleElement.style.position = "absolute";
48    TitleElement.style.display = "flex";
49    TitleElement.style.paddingLeft = "2.5%";
50    TitleElement.style.width = "100%";
51    TitleElement.style.height = "10%";
52    TitleElement.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
53    TitleElement.style.color = "white";
54    TitleElement.style.alignItems = "center";
55    TitleElement.style.alignContent = "center";
56    TitleElement.innerHTML = Title;
57    Container.appendChild(TitleElement);
58
59    const CloseButton = document.createElement("div");
60    CloseButton.style.position = "absolute";
61    CloseButton.innerHTML = "X";
62    CloseButton.style.right = "4.5%";
63    CloseButton.style.cursor = "pointer";
64    TitleElement.appendChild(CloseButton);
65
66    const Image = document.createElement("img");
67    Image.src = MarkIcon;
68    Image.draggable = false;
69    Image.style.position = "absolute";
70    Image.style.transform = "translate(-50%, -50%)";
71    Image.style.width = "10%";
72    Image.style.top = "52.5%";
73    Image.style.left = "10%";
74    Container.appendChild(Image);
75
76    const MessageContent = document.createElement("div");
77    MessageContent.innerHTML = Message;
78    MessageContent.style.position = "absolute";
79    MessageContent.style.transform = "translate(0%, -50%)";
80    MessageContent.style.top = "52.5%";
81    MessageContent.style.left = "20%";
82    MessageContent.style.fontSize = "1.5rem";
83    Container.appendChild(MessageContent);
84
85    CloseButton.addEventListener("click", () => {
86        Container.style.opacity = 0;
87        setTimeout(() => {
88            Container.remove();
89        }, 125);
90    });
91
92    CloseButton.addEventListener("mouseenter", () => {
93        CloseButton.style.color = "red";
94    });
95
96    CloseButton.addEventListener("mouseleave", () => {
97        CloseButton.style.color = "white";
98    });
99}
100
101export function Print(x) {
102    const LogContainer = document.getElementById("LogContainer");
103
104    if (LogContainer) {
105        const Span = document.createElement("span");
106        Span.innerHTML = x;
107        LogContainer.appendChild(Span);
108
109        setTimeout(() => {
110            Span.style.opacity = "0";
111            setTimeout(() => {
112                Span.remove();
113            }, 2000);
114        }, 3000);
115    }
116}
117
118export function PowderEffect(R, G, B, EffectStrength) {
119    return {
120        R: Random(R + EffectStrength, R),
121        G: Random(G + EffectStrength, G),
122        B: Random(B + EffectStrength, B)
123    };
124}
125
126export function RgbString(RgbString) {
127    const RgbArray = RgbString.match(/\d+/g).map(Number);
128    return {
129        R: RgbArray[0],
130        G: RgbArray[1],
131        B: RgbArray[2]
132    };
133}
134
135export function CombustElement(CausticPart, FlammablePart) {
136    GetAllParticles().forEach(Particle => {
137        if (Particle !== FlammablePart && Particle.dataset.flammable === "true") {
138            if (CheckCollision(FlammablePart, Particle)) {
139                CreateParticle(Elements.find(element => element.Name === "SMKE"), Particle.offsetLeft, Particle.offsetTop);
140                Particle.remove();
141            }
142        }
143    });
144
145    CreateParticle(Elements.find(element => element.Name === "SMKE"), FlammablePart.offsetLeft, FlammablePart.offsetTop);
146    FlammablePart.remove();
147    CausticPart.remove();
148}
149
150export function ExplodeParticle(Particle, Lifetime) {
151    CreateExplosion(Random(255, 200), Random(25, 0), Random(25, 0), Particle.offsetLeft, Particle.offsetTop, Lifetime);
152    Particle.remove();
153}
154
155export function CheckCollision(E0, E1) {
156    const r1 = E0.getBoundingClientRect();
157    const r2 = E1.getBoundingClientRect();
158  
159    return !(r1.right < r2.left || 
160             r1.left > r2.right || 
161             r1.bottom < r2.top || 
162             r1.top > r2.bottom);
163}
164
165export function CreateParticle(
166    {
167        Name,
168        Color,
169        Flammable,
170        Caustic,
171        RadioactiveSettings,
172        Light,
173        Temp,
174        MeltingPoint,
175        BoilingPoint,
176        Type,
177        ColdType,
178        HotType
179    }, PositionX, PositionY) {
180    const ParticleContainer = document.getElementById("ParticleContainer");
181    const PowderStrength = Type !== "Solid" ? Settings.PowderEffectStrength : Settings.PowderEffectStrength / 4;
182    const GridSize = parseFloat(document.body.getAttribute("grid-size"));
183
184    function StringToRgb(RgbString) {
185        const [R, G, B] = RgbString.match(/\d+/g).map(Number);
186        return { R, G, B };
187    }
188
189    if (Name === "NONE") {
190        return;
191    }
192
193    if (ParticleContainer) {
194        const BaseColor = StringToRgb(Color);
195        const TargetColor = `rgb(${BaseColor.R + Random(PowderStrength, -PowderStrength) / 4}, ${BaseColor.G + Random(PowderStrength, -PowderStrength) / 4}, ${BaseColor.B + Random(PowderStrength, -PowderStrength) / 4})`;
196
197        const Particle = document.createElement("div");
198        Particle.style.position = "absolute";
199        Particle.style.left = `${PositionX}px`;
200        Particle.style.top = `${PositionY}px`;
201        Particle.style.width = `${GridSize}px`;
202        Particle.style.height = `${GridSize}px`;
203        Particle.style.backgroundColor = TargetColor;
204
205        Particle.style.pointerEvents = Type === "None" ? "none" : "";
206
207        Particle.className = Name.toUpperCase();
208        Particle.dataset.name = Name;
209        Particle.dataset.particle = "true";
210        Particle.dataset.type = Type;
211        Particle.dataset.coldType = ColdType;
212        Particle.dataset.hotType = HotType;
213        Particle.dataset.fixedType = Type;
214        Particle.dataset.color = TargetColor;
215        Particle.dataset.fixedColor = TargetColor;
216        Particle.dataset.flammable = Flammable.toString();
217        Particle.dataset.caustic = Caustic.toString();
218
219        Particle.dataset.radioactive = RadioactiveSettings ? RadioactiveSettings.Radioactive.toString() : "false";
220        Particle.dataset.fixedRadioactive = RadioactiveSettings ? RadioactiveSettings.Radioactive.toString() : "false";
221        Particle.dataset.radioactivity = RadioactiveSettings ?  RadioactiveSettings.Radioactivity.toString() : "";
222        Particle.dataset.decayParticle = RadioactiveSettings ? RadioactiveSettings.DecayParticle : "";
223        
224        Particle.dataset.light = Light.toString();
225        Particle.dataset.temp = Temp.toString();
226        Particle.dataset.meltingPoint = MeltingPoint !== undefined ? MeltingPoint.toString() : "";
227        Particle.dataset.boilingPoint = BoilingPoint !== undefined ? BoilingPoint.toString() : "";
228        Particle.id = Name;
229        Particle.classList.add("PART");
230        ParticleContainer.appendChild(Particle);
231
232        return Particle;
233    }
234}
235
236export function CreateRawParticle(X, Y) {
237    const GridSize = parseFloat(document.body.getAttribute("grid-size"));
238
239    const Particle = document.createElement("div");
240    Particle.style.position = "absolute";
241    Particle.style.left = `${X}px`;
242    Particle.style.top = `${Y}px`;
243    Particle.style.width = `${GridSize}px`;
244    Particle.style.height = `${GridSize}px`;
245    return Particle;
246}
247
248export function CreateExplosion(R, G, B, X, Y, Lifetime) {
249    const AllParticles = GetAllParticles();
250
251    for (let Index = 0; Index < Random(9, 0); Index++) {
252        const XDirection = Random(5, -5);
253        const YDirection = Random(5, -5);
254
255        const Particle = CreateRawParticle(X, Y);
256        Particle.style.backgroundColor = `rgb(${PowderEffect(R, G, B, Settings.PowderEffectStrength).R}, ${PowderEffect(R, G, B, Settings.PowderEffectStrength).G}, ${PowderEffect(R, G, B, Settings.PowderEffectStrength).B})`;
257        document.body.appendChild(Particle);
258
259        var Time = 0;
260
261        function Update() {
262            Time += 0.15;
263
264            Particle.style.left = `${Particle.offsetLeft + XDirection}px`;
265            Particle.style.top = `${Particle.offsetTop + YDirection}px`;
266
267            Particle.style.opacity = 1 - Time / Lifetime;
268
269            if (false) {
270                AllParticles.forEach(OtherParticle => {
271                    if (OtherParticle !== Particle && OtherParticle.dataset.hotType === "Explosive") {
272                        if (CheckCollision(Particle, OtherParticle)) {
273                            ExplodeParticle(OtherParticle, 15);
274                        }
275                    }
276                });
277            }
278
279            const ParticleRect = Particle.getBoundingClientRect();
280            const TouchingPart = document.elementFromPoint(ParticleRect.left, ParticleRect.top);
281            if (TouchingPart.dataset.hotType === "Explosive") {
282                CreateExplosion(RgbString(TouchingPart.dataset.color).R, RgbString(TouchingPart.dataset.color).G, RgbString(TouchingPart.dataset.color).B, TouchingPart.offsetLeft, TouchingPart.offsetTop, 15);
283                TouchingPart.remove();
284            }
285
286            if (Time <= Lifetime) {
287                requestAnimationFrame(Update);
288            } else {
289                Particle.remove();
290            }
291        }
292
293        Update();
294    }
295}
296
297export function DrawLineParticle(Element, StartPositionX, StartPositionY, EndPositionX, EndPositionY) {
298    const GridSize = parseFloat(document.body.getAttribute("grid-size"));
299
300    const Distance = Math.sqrt(Math.pow(EndPositionX - StartPositionX, 2) + Math.pow(EndPositionY - StartPositionY, 2));
301    const NumberOfElements = Math.floor(Distance / GridSize);
302    const DeltaX = (EndPositionX - StartPositionX) / NumberOfElements;
303    const DeltaY = (EndPositionY - StartPositionY) / NumberOfElements;
304
305    for (let Index = 0; Index < NumberOfElements; Index++) {
306        let CurrentX = StartPositionX + DeltaX * Index;
307        let CurrentY = StartPositionY + DeltaY * Index;
308
309        CurrentX = Math.round(CurrentX / GridSize) * GridSize;
310        CurrentY = Math.round(CurrentY / GridSize) * GridSize;
311
312        CreateParticle(Element, CurrentX, CurrentY);
313    }
314}
315
316export function DrawLinePlaceholder(StartPositionX, StartPositionY, EndPositionX, EndPositionY) {
317    const GridSize = parseFloat(document.body.getAttribute("grid-size"));
318
319    const Distance = Math.sqrt(Math.pow(EndPositionX - StartPositionX, 2) + Math.pow(EndPositionY - StartPositionY, 2));
320    const NumberOfElements = Math.floor(Distance / GridSize);
321    const DeltaX = (EndPositionX - StartPositionX) / NumberOfElements;
322    const DeltaY = (EndPositionY - StartPositionY) / NumberOfElements;
323
324    PlaceholderContainer.innerHTML = "";
325
326    for (let Index = 0; Index < NumberOfElements; Index++) {
327        let CurrentX = StartPositionX + DeltaX * Index;
328        let CurrentY = StartPositionY + DeltaY * Index;
329
330        CurrentX = Math.round(CurrentX / GridSize) * GridSize;
331        CurrentY = Math.round(CurrentY / GridSize) * GridSize;
332
333        const PlaceholderItem = document.createElement("div");
334        PlaceholderItem.style.position = "absolute";
335        PlaceholderItem.style.backgroundColor = "rgb(60, 60, 60)";
336        PlaceholderItem.style.width = `${GridSize}px`;
337        PlaceholderItem.style.aspectRatio = "1 / 1";
338        PlaceholderItem.style.left = `${CurrentX}px`;
339        PlaceholderItem.style.top = `${CurrentY}px`;
340
341        PlaceholderContainer.appendChild(PlaceholderItem);
342    }
343}
344
345export function ClearLinePlaceholder() {
346    PlaceholderContainer.innerHTML = "";
347}
348
349export function GetAllParticles() {
350    return Array.from(ParticleContainer.getElementsByTagName("div"));
351}
352
353export function GetParticleFromPosition(X, Y) {
354    let Element = document.elementFromPoint(X, Y);
355    if (Element && Element.classList.contains("PART")) {
356        return Element;
357    }
358    return null;
359}
360
361export function GetParticleFromName(Name) {
362    const AllParticles = GetAllParticles();
363    if (AllParticles.length > 0) {
364        return AllParticles.find(Particle => Particle.dataset.name.toUpperCase() === Name.toUpperCase());
365    }
366}
367
368export function GetElement(Name) {
369    return Elements.find(element => element.Name === Name);
370}
371
372export function IsPlaceOccupied(X, Y, GridSize) {
373    const Particles = Array.from(ParticleContainer.children);
374    return Particles.some(Particle => {
375        const ParticleRect = Particle.getBoundingClientRect();
376        const ParticleX = Math.floor(ParticleRect.left / GridSize) * GridSize;
377        const ParticleY = Math.floor(ParticleRect.top / GridSize) * GridSize;
378        return ParticleX === X && ParticleY === Y;
379    });
380}
381
382export class math {
383    static Clamp(value, min, max) {
384        return Math.min(Math.max(value, min), max);
385    }
386
387    static Lerp(a, b, t) {
388        return a + (b - a) * t;
389    }
390
391    static Map(value, inMin, inMax, outMin, outMax) {
392        return (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
393    }
394
395    static Sign(value) {
396        return value < 0? -1 : value > 0? 1 : 0;
397    }
398
399    static Rad(degrees) {
400        return degrees * Math.PI / 180;
401    }
402
403    static Random(Max, Min) {
404        return Math.floor(Math.random() * (Max - Min + 1)) + Min;
405    }
406
407    static Gcd(A, B) {
408        while (B !== 0) {
409            var C = B;
410            B = A % B;
411            A = C;
412        }
413        return A;
414    }
415
416    static Lcm(A, B) {
417        return (A * B) / math.Gcd(A, B);
418    }
419
420    static RoundTo(X, Y) {
421        return Math.round(X / Y) * Y;
422    }
423}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.