https://pvzm.net/ to play [Read-only GitHub mirror] pvzm.net
modded vs pvz plants-vs-zombies plantsvszombies javascript online zombie noads jspvz pvzm game plants plant
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

izombie level verification

Clay 2ce89c4c 016cb3c2

+939 -134
+1 -1
game/UI.css
··· 636 636 background: url(images/interface/Button.png) no-repeat; 637 637 } 638 638 639 - #imgSF { 639 + #imgSF, #imgSFNT { 640 640 position: absolute; 641 641 cursor: url(images/interface/Pointer.cur), pointer; 642 642 z-index: 256;
+8 -6
game/js/Cheatcodes.js
··· 73 73 }, 5000); 74 74 75 75 // Check for cheat codes 76 - for (i in cheatCodes) { 77 - if (keySequence.includes(i)) { 78 - const cheat = cheatCodes[i]; 79 - getCheatAction(cheat)(); 80 - if (shouldClearSequence(cheat)) { 81 - keySequence = ""; 76 + if (!["izombieverifynormal", "izombieverifywater"].includes(oS.Lvl)) { 77 + for (i in cheatCodes) { 78 + if (keySequence.includes(i)) { 79 + const cheat = cheatCodes[i]; 80 + getCheatAction(cheat)(); 81 + if (shouldClearSequence(cheat)) { 82 + keySequence = ""; 83 + } 82 84 } 83 85 } 84 86 }
+12 -127
game/level/izombieleveleditor.js
··· 526 526 downloadButton.style.display = "none"; 527 527 levelDataElement.style.display = "none"; 528 528 copyButtonElement.style.display = "none"; 529 - titleElement.innerText = "Saving..."; 529 + titleElement.innerText = "Loading..."; 530 + // download lvl as a backup 531 + downloadBytesAsFile(encodeIZL3(cloneFromPlants(`Backup of "${l}" @ ${new Date().toISOString()}`, f)), `BACKUP_${l.replaceAll(" ", "_")}_${new Date().toISOString()}.izl3`); 530 532 alert( 531 533 "RULES:\n\n" + 532 534 "1. Playability Required - Your level must be completable. Test it before submitting to ensure it's actually possible to beat.\n" + 533 535 "2. Be Yourself - Don't pretend to be other community members or moderators when submitting levels.\n" + 534 536 "3. Keep It Family-Friendly - Level names, author names, and the level itself must all be appropriate for all ages. No inappropriate content or language." 535 537 ); 536 - const author = prompt("Author name:"); 537 - const newLevelData = encodeIZL3(cloneFromPlants(l, f, true)); 538 - titleElement.innerText = "Configuring..."; 539 - let serverConfig; 540 - fetch(`${$User.Server.URL}/api/config`, { 541 - headers: { 542 - Accept: "application/msgpack", 543 - }, 544 - }) 545 - .then((response) => { 546 - if (!response.ok) { 547 - throw new Error("Failed to get server configuration"); 548 - } 549 - return response.arrayBuffer(); 550 - }) 551 - .then((config) => { 552 - serverConfig = msgpack.deserialize(config); 553 - let turnstileToken; 554 - let container; 555 - if (serverConfig.turnstileEnabled) { 556 - container = document.createElement("div"); 557 - container.style.position = "absolute"; 558 - container.style.left = "50%"; 559 - container.style.top = "50%"; 560 - container.style.transform = "translate(-50%, -50%)"; 561 - container.style.zIndex = "1000"; 562 - container.id = "turnstile-container"; 563 - 564 - window.turnstile.render(container, { 565 - sitekey: serverConfig.turnstileSiteKey, 566 - callback(token) { 567 - turnstileToken = token; 568 - }, 569 - }); 570 - } 571 - titleElement.innerText = "Please complete the verification"; 572 - $("dAll").appendChild(container); 573 - 574 - // function to wait for turnstile token if enabled 575 - function waitForTurnstileToken() { 576 - return new Promise((resolve) => { 577 - if (!serverConfig.turnstileEnabled) { 578 - resolve(null); 579 - return; 580 - } 581 - 582 - const checkToken = () => { 583 - if (turnstileToken) { 584 - resolve(turnstileToken); 585 - } else { 586 - setTimeout(checkToken, 500); 587 - } 588 - }; 589 - checkToken(); 590 - }); 591 - } 592 - 593 - // wait for turnstile token or proceed immediately if disabled 594 - waitForTurnstileToken().then((token) => { 595 - titleElement.innerText = "Uploading..."; 596 - 597 - // prepare query parameters 598 - const queryParams = new URLSearchParams(); 599 - queryParams.append("author", author || "Anonymous"); 600 - if (token) { 601 - queryParams.append("turnstileResponse", token); 602 - } 603 - 604 - // upload level data as octet-stream 605 - fetch(`${$User.Server.URL}/api/levels?${queryParams.toString()}`, { 606 - method: "POST", 607 - headers: { 608 - "Content-Type": "application/octet-stream", 609 - }, 610 - body: newLevelData, 611 - }) 612 - .then((response) => { 613 - if (!response.ok) { 614 - return response.json().then((data) => { 615 - throw new Error( 616 - data.error + (data.message ? ` (${data.message})` : "") || "Failed to upload level" 617 - ); 618 - }); 619 - } 620 - return response.json(); 621 - }) 622 - .then((data) => { 623 - console.log("Level uploaded successfully:", data); 624 - titleElement.innerText = `Level uploaded successfully! ID: ${data.id}`; 625 - 626 - // show close button again 627 - closeButton.style.display = ""; 628 - closeButton.style.top = "75%"; 629 - closeButton.style.top = "50%"; 630 - closeButton.style.left = "50%"; 631 - closeButton.style.transform = "translate(-50%, -50%)"; 632 - }) 633 - .catch((error) => { 634 - console.error("Error uploading level:", error); 635 - titleElement.innerText = `Upload failed: ${error.message}`; 636 - 637 - // show close button again 638 - closeButton.style.display = ""; 639 - }) 640 - .finally(() => { 641 - // remove turnstile container if it exists 642 - const turnstileContainer = document.getElementById("turnstile-container"); 643 - if (turnstileContainer) { 644 - turnstileContainer.remove(); 645 - } 646 - }); 647 - }); 648 - }) 649 - .catch((error) => { 650 - console.error("Error fetching server configuration:", error); 651 - titleElement.innerText = "Failed to load server configuration"; 652 - 653 - setTimeout(() => { 654 - closeButton.style.display = ""; 655 - uploadButton.style.display = ""; 656 - downloadButton.style.display = ""; 657 - levelDataElement.style.display = ""; 658 - copyButtonElement.style.display = ""; 659 - titleElement.innerText = "Here's your level data - keep this somewhere safe!"; 660 - }, 3000); 661 - }); 538 + alert("Now sending you to verification. Beat your level to prove it's playable!"); 539 + // put level data into levelDataToLoad for the verification level to read 540 + levelDataToLoad = cloneFromPlants(l, f, true); 541 + // check if water or normal 542 + if (oS.NowLevel === "NPool") { 543 + SelectModal("izombieverifywater"); 544 + } else { 545 + SelectModal("izombieverifynormal"); 546 + } 662 547 }; 663 548 uploadButton.style.zIndex = "1000"; 664 549 // uploadButton.style.display = "none"; // hide for now
+430
game/level/izombieverifynormal.js
··· 1 + pNameValue = []; 2 + zNameValue = []; 3 + // make sure everything in levelDataToLoad is defined 4 + if (typeof levelDataToLoad === "undefined") { 5 + alert("Invalid level data!"); 6 + SelectModal(0); 7 + } else { 8 + // make sure its a table the one with {} 9 + if (typeof levelDataToLoad !== "object") { 10 + alert("Invalid level data!"); 11 + SelectModal(0); 12 + } 13 + // make sure it has the right keys 14 + if ( 15 + !Object.hasOwn(levelDataToLoad, "plants") || 16 + !Object.hasOwn(levelDataToLoad, "music") || 17 + !Object.hasOwn(levelDataToLoad, "sun") || 18 + !Object.hasOwn(levelDataToLoad, "lfValue") || 19 + !Object.hasOwn(levelDataToLoad, "stripeCol") 20 + ) { 21 + /* alert("Invalid level data!"); 22 + SelectModal(0); */ 23 + levelDataToLoad = { 24 + lfValue: [0, 1, 1, 1, 1, 1], 25 + music: "Cerebrawl", 26 + name: "Error", 27 + plants: [ 28 + { 29 + plantCol: 1, 30 + plantName: "oWallNut", 31 + plantRow: 2, 32 + zIndex: 6, 33 + }, 34 + { 35 + plantCol: 1, 36 + plantName: "oWallNut", 37 + plantRow: 3, 38 + zIndex: 9, 39 + }, 40 + { 41 + plantCol: 1, 42 + plantName: "oWallNut", 43 + plantRow: 4, 44 + zIndex: 12, 45 + }, 46 + { 47 + plantCol: 1, 48 + plantName: "oWallNut", 49 + plantRow: 1, 50 + zIndex: 3, 51 + }, 52 + { 53 + plantCol: 2, 54 + plantName: "oWallNut", 55 + plantRow: 1, 56 + zIndex: 3, 57 + }, 58 + { 59 + plantCol: 2, 60 + plantName: "oWallNut", 61 + plantRow: 3, 62 + zIndex: 9, 63 + }, 64 + { 65 + plantCol: 1, 66 + plantName: "oWallNut", 67 + plantRow: 5, 68 + zIndex: 15, 69 + }, 70 + { 71 + plantCol: 2, 72 + plantName: "oWallNut", 73 + plantRow: 5, 74 + zIndex: 15, 75 + }, 76 + { 77 + plantCol: 4, 78 + plantName: "oWallNut", 79 + plantRow: 5, 80 + zIndex: 15, 81 + }, 82 + { 83 + plantCol: 4, 84 + plantName: "oWallNut", 85 + plantRow: 4, 86 + zIndex: 12, 87 + }, 88 + { 89 + plantCol: 5, 90 + plantName: "oWallNut", 91 + plantRow: 3, 92 + zIndex: 9, 93 + }, 94 + { 95 + plantCol: 4, 96 + plantName: "oWallNut", 97 + plantRow: 3, 98 + zIndex: 9, 99 + }, 100 + { 101 + plantCol: 7, 102 + plantName: "oWallNut", 103 + plantRow: 5, 104 + zIndex: 15, 105 + }, 106 + { 107 + plantCol: 7, 108 + plantName: "oWallNut", 109 + plantRow: 4, 110 + zIndex: 12, 111 + }, 112 + { 113 + plantCol: 7, 114 + plantName: "oWallNut", 115 + plantRow: 3, 116 + zIndex: 9, 117 + }, 118 + { 119 + plantCol: 8, 120 + plantName: "oWallNut", 121 + plantRow: 3, 122 + zIndex: 9, 123 + }, 124 + ], 125 + stripeCol: 9, 126 + sun: 0, 127 + }; 128 + } 129 + // make sure the keys are the right types 130 + if ( 131 + !Array.isArray(levelDataToLoad.plants) || 132 + typeof levelDataToLoad.music !== "string" || 133 + typeof levelDataToLoad.sun !== "number" || 134 + typeof levelDataToLoad.name !== "string" || 135 + !Array.isArray(levelDataToLoad.lfValue) || 136 + typeof levelDataToLoad.stripeCol !== "number" 137 + ) { 138 + alert("Invalid level data!"); 139 + SelectModal(0); 140 + } 141 + } 142 + for (let i = 0; i < levelDataToLoad.plants.length; i++) { 143 + let plant = levelDataToLoad.plants[i]; 144 + if (!pNameValue.includes(window[plant.plantName])) { 145 + pNameValue.push(window[plant.plantName]); 146 + } 147 + } 148 + if (levelDataToLoad.selectedZombies) { 149 + for (let i = 0; i < levelDataToLoad.selectedZombies.length; i++) { 150 + let zombie = levelDataToLoad.selectedZombies[i]; 151 + if (!zNameValue.includes(window[zombie])) { 152 + zNameValue.push(window[zombie]); 153 + } 154 + } 155 + } 156 + // if lfValue is [0, 1, 1, 2, 2, 1, 1], then we use background4, otherwise background2 157 + backgroundImage = levelDataToLoad.lfValue[3] === 2 ? "images/interface/background4.jpg" : "images/interface/background2.jpg"; 158 + CSpeed(1, 10, 1); 159 + oS.Init( 160 + { 161 + PName: pNameValue, 162 + ZName: 163 + zNameValue.length === 0 164 + ? [oIImp, oIConeheadZombie, oIPoleVaultingZombie, oIBucketheadZombie, oIFootballZombie, oIJackinTheBoxZombie, oIScreenDoorZombie] 165 + : zNameValue, 166 + PicArr: [backgroundImage, "images/interface/trophy.png", "images/interface/Stripe.png"], 167 + LF: levelDataToLoad.lfValue, 168 + backgroundImage, 169 + ShowScroll: false, 170 + SunNum: levelDataToLoad.sun, 171 + BrainsNum: 5, 172 + DKind: 0, 173 + ProduceSun: false, 174 + CardKind: 1, 175 + LevelName: "VERIFICATION: " + levelDataToLoad.name, 176 + LevelEName: "izombieverifynormal", 177 + StartGameMusic: levelDataToLoad.music, 178 + InitLawnMower() { 179 + var a = 6; 180 + while (--a) { 181 + CustomSpecial(oBrains, a, -1); 182 + } 183 + }, 184 + ArP: { 185 + ArC: [1, levelDataToLoad.stripeCol - 1], 186 + ArR: [1, 5], 187 + Auto: 1, 188 + P: [], 189 + }, 190 + LvlClearFunc() { 191 + $("dMenu0").style.display = ""; 192 + SetVisible(document.querySelector("#dOptionsMenu > div[onclick='ShowSpeed()']")); 193 + SetVisible(document.querySelector("img[src='images/interface/icon_speed.png']")); 194 + document.querySelector("#dOptionsMenu > div[onclick='ShowSpeed()']").style.cssText = ""; 195 + }, 196 + RiddleAutoGrow() { 197 + var k = oS.ArP; 198 + var f = k.ArC; 199 + var j = k.ArR; 200 + var e = k.P; 201 + var d = oS.PName; 202 + var c; 203 + var g = f[0]; 204 + var b = f[1]; 205 + var i = j[0]; 206 + var h = j[1]; 207 + var a; 208 + if (k.Auto) { 209 + while (i <= h) { 210 + CustomSpecial(oBrains, i, 0); 211 + /*for (a = g; a <= b; a++) { 212 + CustomSpecial( 213 + d[e[(c = Math.floor(Math.random() * e.length))]], 214 + i, 215 + a 216 + ); 217 + e.splice(c, 1); 218 + }*/ 219 + ++i; 220 + } 221 + } 222 + NewImg("iStripe", "images/interface/Stripe.png", "left:" + (GetX1X2(levelDataToLoad.stripeCol)[0] - 11) + "px;top:65px", EDAll); 223 + }, 224 + StartGame() { 225 + restoreToPlants(levelDataToLoad); // load the plants 226 + // clear all query parameters from the url without reloadng 227 + window.history.pushState({}, document.title, window.location.pathname); 228 + SetVisible($("dSunNum")); 229 + $("dMenu0").style.display = "none"; 230 + SetHidden(document.querySelector("#dOptionsMenu > div[onclick='ShowSpeed()']")); 231 + SetHidden(document.querySelector("img[src='images/interface/icon_speed.png']")); 232 + document.querySelector("#dOptionsMenu > div[onclick='ShowSpeed()']").style.cursor = "url(images/interface/Cursor.cur) 0 0, auto"; 233 + SetBlock($("dTop")); 234 + oP.Monitor({ 235 + ar: [0], 236 + f(d) { 237 + var b = oS.Chose; 238 + var a = arguments.callee; 239 + switch (d) { 240 + case 0: 241 + BeginCool(); 242 + d.onclick = null; 243 + (function () { 244 + SetVisible($("dFlagMeter"), $("dFlagMeterContent")); 245 + ClearChild($("oEmbed")); 246 + StopMusic(); 247 + PlayMusic((oS.LoadMusic = levelDataToLoad.music)); 248 + BeginCool(); 249 + oP.Monitor(); 250 + })(); 251 + } 252 + }, 253 + }); 254 + SetVisible($("dFlagMeter")); 255 + oS.RiddleAutoGrow(); 256 + }, 257 + }, 258 + { 259 + FlagToEnd() { 260 + NewImg("imgSFNT", "images/interface/trophy.png", "left:417px;top:233px;z-index:255", EDAll, { 261 + onclick() { 262 + PlaySound2("winmusic"); 263 + SetHidden(document.querySelector(".trophy")); 264 + let closeButton = document.createElement("input"); 265 + closeButton.setAttribute("type", "button"); 266 + closeButton.setAttribute("value", "EXIT"); 267 + closeButton.id = "btnNextLevel"; // not actually a next level button, but it's the same style 268 + closeButton.style.top = "60%"; 269 + closeButton.style.left = "calc(33.333% - 56.5px)"; // "calc(50% - 120px)"; 270 + closeButton.onclick = function () { 271 + $("dAll").style.zIndex = ""; 272 + let oldLv = oS.Lvl; 273 + SelectModal(0); 274 + SetBlock($("dSurface"), $("iSurfaceBackground")); 275 + oS.Lvl = oldLv; 276 + }; 277 + closeButton.style.zIndex = "1000"; 278 + closeButton.style.display = "none"; // hide the button until after upload is done 279 + $("dAll").appendChild(closeButton); 280 + let coverElement = document.createElement("div"); 281 + coverElement.style.position = "absolute"; 282 + coverElement.style.left = "0"; 283 + coverElement.style.top = "0"; 284 + coverElement.style.width = "100%"; 285 + coverElement.style.height = "100%"; 286 + coverElement.style.backgroundColor = "rgba(0, 0, 0, 0.75)"; 287 + coverElement.style.zIndex = "999"; 288 + $("dAll").appendChild(coverElement); 289 + let titleElement = document.createElement("div"); 290 + titleElement.style.position = "absolute"; 291 + titleElement.style.left = "50%"; 292 + titleElement.style.top = "30%"; 293 + titleElement.style.transform = "translate(-50%, -50%)"; 294 + titleElement.style.width = "40%"; 295 + titleElement.style.height = "40px"; 296 + titleElement.innerText = "Configuring..."; 297 + titleElement.style.fontSize = "xx-large"; 298 + titleElement.style.textAlign = "center"; 299 + titleElement.style.color = "white"; 300 + titleElement.style.zIndex = "1000"; 301 + $("dAll").appendChild(titleElement); 302 + $("dAll").style.zIndex = "10000"; 303 + const author = prompt("Author name:"); 304 + const newLevelData = encodeIZL3(levelDataToLoad); 305 + let serverConfig; 306 + fetch(`${$User.Server.URL}/api/config`, { 307 + headers: { 308 + Accept: "application/msgpack", 309 + }, 310 + }) 311 + .then((response) => { 312 + if (!response.ok) { 313 + throw new Error("Failed to get server configuration"); 314 + } 315 + return response.arrayBuffer(); 316 + }) 317 + .then((config) => { 318 + serverConfig = msgpack.deserialize(config); 319 + let turnstileToken; 320 + let container; 321 + if (serverConfig.turnstileEnabled) { 322 + container = document.createElement("div"); 323 + container.style.position = "absolute"; 324 + container.style.left = "50%"; 325 + container.style.top = "50%"; 326 + container.style.transform = "translate(-50%, -50%)"; 327 + container.style.zIndex = "1000"; 328 + container.id = "turnstile-container"; 329 + 330 + window.turnstile.render(container, { 331 + sitekey: serverConfig.turnstileSiteKey, 332 + callback(token) { 333 + turnstileToken = token; 334 + }, 335 + }); 336 + } 337 + titleElement.innerText = "Please complete the verification"; 338 + $("dAll").appendChild(container); 339 + 340 + // function to wait for turnstile token if enabled 341 + function waitForTurnstileToken() { 342 + return new Promise((resolve) => { 343 + if (!serverConfig.turnstileEnabled) { 344 + resolve(null); 345 + return; 346 + } 347 + 348 + const checkToken = () => { 349 + if (turnstileToken) { 350 + resolve(turnstileToken); 351 + } else { 352 + setTimeout(checkToken, 500); 353 + } 354 + }; 355 + checkToken(); 356 + }); 357 + } 358 + 359 + // wait for turnstile token or proceed immediately if disabled 360 + waitForTurnstileToken().then((token) => { 361 + titleElement.innerText = "Uploading..."; 362 + 363 + // prepare query parameters 364 + const queryParams = new URLSearchParams(); 365 + queryParams.append("author", author || "Anonymous"); 366 + if (token) { 367 + queryParams.append("turnstileResponse", token); 368 + } 369 + 370 + // upload level data as octet-stream 371 + fetch(`${$User.Server.URL}/api/levels?${queryParams.toString()}`, { 372 + method: "POST", 373 + headers: { 374 + "Content-Type": "application/octet-stream", 375 + }, 376 + body: newLevelData, 377 + }) 378 + .then((response) => { 379 + if (!response.ok) { 380 + return response.json().then((data) => { 381 + throw new Error(data.error + (data.message ? ` (${data.message})` : "") || "Failed to upload level"); 382 + }); 383 + } 384 + return response.json(); 385 + }) 386 + .then((data) => { 387 + console.log("Level uploaded successfully:", data); 388 + titleElement.innerText = `Level uploaded successfully! ID: ${data.id}`; 389 + 390 + // show close button again 391 + closeButton.style.display = ""; 392 + closeButton.style.top = "75%"; 393 + closeButton.style.top = "50%"; 394 + closeButton.style.left = "50%"; 395 + closeButton.style.transform = "translate(-50%, -50%)"; 396 + }) 397 + .catch((error) => { 398 + console.error("Error uploading level:", error); 399 + titleElement.innerText = `Upload failed: ${error.message}`; 400 + 401 + // show close button again 402 + closeButton.style.display = ""; 403 + }) 404 + .finally(() => { 405 + // remove turnstile container if it exists 406 + const turnstileContainer = document.getElementById("turnstile-container"); 407 + if (turnstileContainer) { 408 + turnstileContainer.remove(); 409 + } 410 + }); 411 + }); 412 + }) 413 + .catch((error) => { 414 + console.error("Error fetching server configuration:", error); 415 + titleElement.innerText = "Failed to load server configuration"; 416 + 417 + setTimeout(() => { 418 + closeButton.style.display = ""; 419 + uploadButton.style.display = ""; 420 + downloadButton.style.display = ""; 421 + levelDataElement.style.display = ""; 422 + copyButtonElement.style.display = ""; 423 + titleElement.innerText = "Here's your level data - keep this somewhere safe!"; 424 + }, 3000); 425 + }); 426 + }, 427 + }); 428 + }, 429 + } 430 + );
+488
game/level/izombieverifywater.js
··· 1 + pNameValue = []; 2 + zNameValue = []; 3 + // make sure everything in levelDataToLoad is defined 4 + if (typeof levelDataToLoad === "undefined") { 5 + alert("Invalid level data!"); 6 + SelectModal(0); 7 + } else { 8 + // make sure its a table the one with {} 9 + if (typeof levelDataToLoad !== "object") { 10 + alert("Invalid level data!"); 11 + SelectModal(0); 12 + } 13 + // make sure it has the right keys 14 + if ( 15 + !Object.hasOwn(levelDataToLoad, "plants") || 16 + !Object.hasOwn(levelDataToLoad, "music") || 17 + !Object.hasOwn(levelDataToLoad, "sun") || 18 + !Object.hasOwn(levelDataToLoad, "lfValue") || 19 + !Object.hasOwn(levelDataToLoad, "stripeCol") 20 + ) { 21 + /* alert("Invalid level data!"); 22 + SelectModal(0); */ 23 + levelDataToLoad = { 24 + lfValue: [0, 1, 1, 2, 2, 1, 1], 25 + music: "Cerebrawl", 26 + name: "Error", 27 + plants: [ 28 + { 29 + plantCol: 2, 30 + plantName: "oWallNut", 31 + plantRow: 1, 32 + zIndex: 3, 33 + }, 34 + { 35 + plantCol: 1, 36 + plantName: "oWallNut", 37 + plantRow: 1, 38 + zIndex: 3, 39 + }, 40 + { 41 + plantCol: 1, 42 + plantName: "oLilyPad", 43 + plantRow: 3, 44 + zIndex: 9, 45 + }, 46 + { 47 + plantCol: 1, 48 + plantName: "oLilyPad", 49 + plantRow: 4, 50 + zIndex: 12, 51 + }, 52 + { 53 + plantCol: 1, 54 + plantName: "oWallNut", 55 + plantRow: 2, 56 + zIndex: 6, 57 + }, 58 + { 59 + plantCol: 1, 60 + plantName: "oWallNut", 61 + plantRow: 3, 62 + zIndex: 9, 63 + }, 64 + { 65 + plantCol: 2, 66 + plantName: "oLilyPad", 67 + plantRow: 3, 68 + zIndex: 9, 69 + }, 70 + { 71 + plantCol: 2, 72 + plantName: "oWallNut", 73 + plantRow: 3, 74 + zIndex: 9, 75 + }, 76 + { 77 + plantCol: 1, 78 + plantName: "oWallNut", 79 + plantRow: 4, 80 + zIndex: 12, 81 + }, 82 + { 83 + plantCol: 1, 84 + plantName: "oWallNut", 85 + plantRow: 5, 86 + zIndex: 15, 87 + }, 88 + { 89 + plantCol: 2, 90 + plantName: "oWallNut", 91 + plantRow: 5, 92 + zIndex: 15, 93 + }, 94 + { 95 + plantCol: 4, 96 + plantName: "oWallNut", 97 + plantRow: 5, 98 + zIndex: 15, 99 + }, 100 + { 101 + plantCol: 4, 102 + plantName: "oLilyPad", 103 + plantRow: 4, 104 + zIndex: 12, 105 + }, 106 + { 107 + plantCol: 4, 108 + plantName: "oLilyPad", 109 + plantRow: 3, 110 + zIndex: 9, 111 + }, 112 + { 113 + plantCol: 4, 114 + plantName: "oWallNut", 115 + plantRow: 4, 116 + zIndex: 12, 117 + }, 118 + { 119 + plantCol: 4, 120 + plantName: "oWallNut", 121 + plantRow: 3, 122 + zIndex: 9, 123 + }, 124 + { 125 + plantCol: 5, 126 + plantName: "oLilyPad", 127 + plantRow: 3, 128 + zIndex: 9, 129 + }, 130 + { 131 + plantCol: 5, 132 + plantName: "oWallNut", 133 + plantRow: 3, 134 + zIndex: 9, 135 + }, 136 + { 137 + plantCol: 7, 138 + plantName: "oWallNut", 139 + plantRow: 5, 140 + zIndex: 15, 141 + }, 142 + { 143 + plantCol: 7, 144 + plantName: "oLilyPad", 145 + plantRow: 4, 146 + zIndex: 12, 147 + }, 148 + { 149 + plantCol: 7, 150 + plantName: "oLilyPad", 151 + plantRow: 3, 152 + zIndex: 9, 153 + }, 154 + { 155 + plantCol: 8, 156 + plantName: "oLilyPad", 157 + plantRow: 3, 158 + zIndex: 9, 159 + }, 160 + { 161 + plantCol: 7, 162 + plantName: "oWallNut", 163 + plantRow: 4, 164 + zIndex: 12, 165 + }, 166 + { 167 + plantCol: 7, 168 + plantName: "oWallNut", 169 + plantRow: 3, 170 + zIndex: 9, 171 + }, 172 + { 173 + plantCol: 8, 174 + plantName: "oWallNut", 175 + plantRow: 3, 176 + zIndex: 9, 177 + }, 178 + ], 179 + stripeCol: 9, 180 + sun: 0, 181 + }; 182 + } 183 + // make sure the keys are the right types 184 + if ( 185 + !Array.isArray(levelDataToLoad.plants) || 186 + typeof levelDataToLoad.music !== "string" || 187 + typeof levelDataToLoad.sun !== "number" || 188 + typeof levelDataToLoad.name !== "string" || 189 + !Array.isArray(levelDataToLoad.lfValue) || 190 + typeof levelDataToLoad.stripeCol !== "number" 191 + ) { 192 + alert("Invalid level data!"); 193 + SelectModal(0); 194 + } 195 + } 196 + for (let i = 0; i < levelDataToLoad.plants.length; i++) { 197 + let plant = levelDataToLoad.plants[i]; 198 + if (!pNameValue.includes(window[plant.plantName])) { 199 + pNameValue.push(window[plant.plantName]); 200 + } 201 + } 202 + if (levelDataToLoad.selectedZombies) { 203 + for (let i = 0; i < levelDataToLoad.selectedZombies.length; i++) { 204 + let zombie = levelDataToLoad.selectedZombies[i]; 205 + if (!zNameValue.includes(window[zombie])) { 206 + zNameValue.push(window[zombie]); 207 + } 208 + } 209 + } 210 + // if lfValue is [0, 1, 1, 2, 2, 1, 1], then we use background4, otherwise background2 211 + backgroundImage = levelDataToLoad.lfValue[3] === 2 ? "images/interface/background4.jpg" : "images/interface/background2.jpg"; 212 + // if its [0, 1, 1, 2, 2, 1, 1], then we use 6 brains, otherwise 5 213 + brainsNum = levelDataToLoad.lfValue[3] === 2 ? 6 : 5; 214 + CSpeed(1, 10, 1); 215 + oS.Init( 216 + { 217 + PName: pNameValue, 218 + ZName: 219 + zNameValue.length === 0 220 + ? [ 221 + oIZombie, 222 + oIConeheadZombie, 223 + oIBucketheadZombie, 224 + oIDuckyTubeZombie1, 225 + oIDuckyTubeZombie2, 226 + oIDuckyTubeZombie3, 227 + oIScreenDoorZombie, 228 + oIPoleVaultingZombie, 229 + oIBalloonZombie, 230 + ] 231 + : zNameValue, 232 + PicArr: [backgroundImage, "images/interface/trophy.png", "images/interface/Stripe.png"], 233 + backgroundImage, 234 + Coord: 2, 235 + DKind: 0, 236 + LF: levelDataToLoad.lfValue, 237 + ShowScroll: false, 238 + ProduceSun: false, 239 + SunNum: levelDataToLoad.sun, 240 + BrainsNum: 6, 241 + CardKind: 1, 242 + LevelName: "VERIFICATION: " + levelDataToLoad.name, 243 + LvlEName: "izombieverifywater", 244 + LoadMusic: levelDataToLoad.music, 245 + StartGameMusic: levelDataToLoad.music, 246 + ArP: { 247 + ArC: [1, levelDataToLoad.stripeCol - 1], 248 + ArR: [1, 6], 249 + Auto: 1, 250 + P: { 251 + Arr: [], 252 + Arr1: [], 253 + Arr2: [], 254 + }, 255 + }, 256 + RandomGrow(Point, Arr) { 257 + /*Point.sort(function () { 258 + return Math.random() - 0.5; 259 + }); 260 + Arr.sort(function () { 261 + return Math.random() - 0.5; 262 + }); 263 + while (Point.length && Arr.length) 264 + CustomSpecial( 265 + oS.PName[Arr[Arr.length - 1]], 266 + Point[Point.length - 1][1], 267 + Point[Point.length - 1][0], 268 + 1 269 + ), 270 + Point.length--, 271 + Arr.length--;*/ 272 + }, 273 + LvlClearFunc() { 274 + $("dMenu0").style.display = ""; 275 + SetVisible(document.querySelector("#dOptionsMenu > div[onclick='ShowSpeed()']")); 276 + SetVisible(document.querySelector("img[src='images/interface/icon_speed.png']")); 277 + document.querySelector("#dOptionsMenu > div[onclick='ShowSpeed()']").style.cssText = ""; 278 + }, 279 + RiddleAutoGrow() { 280 + var k = oS.ArP; 281 + var f = k.ArC; 282 + var j = k.ArR; 283 + var e = k.P; 284 + var d = oS.PName; 285 + var Arr = []; 286 + var SummonRange = function (Arr, l, r) { 287 + for (; l <= r; ++l) { 288 + for (var j = f[0]; j <= f[1]; ++j) { 289 + Arr.push([j, l]); 290 + } 291 + } 292 + }; 293 + /*for (var i = f[0]; i <= f[1]; ++i) 294 + CustomSpecial(oILilyPad, 3, i), CustomSpecial(oLilyPad, 4, i); // 荷叶*/ 295 + (SummonRange(Arr, 3, 4), oS.RandomGrow(Arr, e.Arr)); // 处理泳池的植物 296 + (SummonRange(Arr, 1, 2), SummonRange(Arr, 5, 6), oS.RandomGrow(Arr, e.Arr1), oS.RandomGrow(Arr, e.Arr)); // 处理剩余的植物 297 + (SummonRange(Arr, 1, 6), oS.RandomGrow(Arr, e.Arr2)); // 处理南瓜头 298 + for (var i = j[0]; i <= j[1]; ++i) { 299 + CustomSpecial(oBrains, i, 0); 300 + } // 脑子 301 + NewImg("iStripe", "images/interface/Stripe.png", "left:" + (GetX1X2(levelDataToLoad.stripeCol)[0] - 11) + "px;top:65px", EDAll); 302 + }, 303 + StartGame() { 304 + restoreToPlants(levelDataToLoad); // load the plants 305 + // clear all query parameters from the url without reloadng 306 + window.history.pushState({}, document.title, window.location.pathname); 307 + $("dMenu0").style.display = "none"; 308 + SetHidden(document.querySelector("#dOptionsMenu > div[onclick='ShowSpeed()']")); 309 + SetHidden(document.querySelector("img[src='images/interface/icon_speed.png']")); 310 + document.querySelector("#dOptionsMenu > div[onclick='ShowSpeed()']").style.cursor = "url(images/interface/Cursor.cur) 0 0, auto"; 311 + (oP.Monitor(), BeginCool()); 312 + SetVisible($("dFlagMeter"), $("dFlagMeterContent"), $("dTop")); 313 + oS.RiddleAutoGrow(); 314 + }, 315 + }, 316 + { 317 + FlagToEnd() { 318 + NewImg("imgSFNT", "images/interface/trophy.png", "left:417px;top:233px;z-index:255", EDAll, { 319 + onclick() { 320 + PlaySound2("winmusic"); 321 + SetHidden(document.querySelector(".trophy")); 322 + let closeButton = document.createElement("input"); 323 + closeButton.setAttribute("type", "button"); 324 + closeButton.setAttribute("value", "EXIT"); 325 + closeButton.id = "btnNextLevel"; // not actually a next level button, but it's the same style 326 + closeButton.style.top = "60%"; 327 + closeButton.style.left = "calc(33.333% - 56.5px)"; // "calc(50% - 120px)"; 328 + closeButton.onclick = function () { 329 + $("dAll").style.zIndex = ""; 330 + let oldLv = oS.Lvl; 331 + SelectModal(0); 332 + SetBlock($("dSurface"), $("iSurfaceBackground")); 333 + oS.Lvl = oldLv; 334 + }; 335 + closeButton.style.zIndex = "1000"; 336 + closeButton.style.display = "none"; // hide the button until after upload is done 337 + $("dAll").appendChild(closeButton); 338 + let coverElement = document.createElement("div"); 339 + coverElement.style.position = "absolute"; 340 + coverElement.style.left = "0"; 341 + coverElement.style.top = "0"; 342 + coverElement.style.width = "100%"; 343 + coverElement.style.height = "100%"; 344 + coverElement.style.backgroundColor = "rgba(0, 0, 0, 0.75)"; 345 + coverElement.style.zIndex = "999"; 346 + $("dAll").appendChild(coverElement); 347 + let titleElement = document.createElement("div"); 348 + titleElement.style.position = "absolute"; 349 + titleElement.style.left = "50%"; 350 + titleElement.style.top = "30%"; 351 + titleElement.style.transform = "translate(-50%, -50%)"; 352 + titleElement.style.width = "40%"; 353 + titleElement.style.height = "40px"; 354 + titleElement.innerText = "Configuring..."; 355 + titleElement.style.fontSize = "xx-large"; 356 + titleElement.style.textAlign = "center"; 357 + titleElement.style.color = "white"; 358 + titleElement.style.zIndex = "1000"; 359 + $("dAll").appendChild(titleElement); 360 + $("dAll").style.zIndex = "10000"; 361 + const author = prompt("Author name:"); 362 + const newLevelData = encodeIZL3(levelDataToLoad); 363 + let serverConfig; 364 + fetch(`${$User.Server.URL}/api/config`, { 365 + headers: { 366 + Accept: "application/msgpack", 367 + }, 368 + }) 369 + .then((response) => { 370 + if (!response.ok) { 371 + throw new Error("Failed to get server configuration"); 372 + } 373 + return response.arrayBuffer(); 374 + }) 375 + .then((config) => { 376 + serverConfig = msgpack.deserialize(config); 377 + let turnstileToken; 378 + let container; 379 + if (serverConfig.turnstileEnabled) { 380 + container = document.createElement("div"); 381 + container.style.position = "absolute"; 382 + container.style.left = "50%"; 383 + container.style.top = "50%"; 384 + container.style.transform = "translate(-50%, -50%)"; 385 + container.style.zIndex = "1000"; 386 + container.id = "turnstile-container"; 387 + 388 + window.turnstile.render(container, { 389 + sitekey: serverConfig.turnstileSiteKey, 390 + callback(token) { 391 + turnstileToken = token; 392 + }, 393 + }); 394 + } 395 + titleElement.innerText = "Please complete the verification"; 396 + $("dAll").appendChild(container); 397 + 398 + // function to wait for turnstile token if enabled 399 + function waitForTurnstileToken() { 400 + return new Promise((resolve) => { 401 + if (!serverConfig.turnstileEnabled) { 402 + resolve(null); 403 + return; 404 + } 405 + 406 + const checkToken = () => { 407 + if (turnstileToken) { 408 + resolve(turnstileToken); 409 + } else { 410 + setTimeout(checkToken, 500); 411 + } 412 + }; 413 + checkToken(); 414 + }); 415 + } 416 + 417 + // wait for turnstile token or proceed immediately if disabled 418 + waitForTurnstileToken().then((token) => { 419 + titleElement.innerText = "Uploading..."; 420 + 421 + // prepare query parameters 422 + const queryParams = new URLSearchParams(); 423 + queryParams.append("author", author || "Anonymous"); 424 + if (token) { 425 + queryParams.append("turnstileResponse", token); 426 + } 427 + 428 + // upload level data as octet-stream 429 + fetch(`${$User.Server.URL}/api/levels?${queryParams.toString()}`, { 430 + method: "POST", 431 + headers: { 432 + "Content-Type": "application/octet-stream", 433 + }, 434 + body: newLevelData, 435 + }) 436 + .then((response) => { 437 + if (!response.ok) { 438 + return response.json().then((data) => { 439 + throw new Error(data.error + (data.message ? ` (${data.message})` : "") || "Failed to upload level"); 440 + }); 441 + } 442 + return response.json(); 443 + }) 444 + .then((data) => { 445 + console.log("Level uploaded successfully:", data); 446 + titleElement.innerText = `Level uploaded successfully! ID: ${data.id}`; 447 + 448 + // show close button again 449 + closeButton.style.display = ""; 450 + closeButton.style.top = "75%"; 451 + closeButton.style.top = "50%"; 452 + closeButton.style.left = "50%"; 453 + closeButton.style.transform = "translate(-50%, -50%)"; 454 + }) 455 + .catch((error) => { 456 + console.error("Error uploading level:", error); 457 + titleElement.innerText = `Upload failed: ${error.message}`; 458 + 459 + // show close button again 460 + closeButton.style.display = ""; 461 + }) 462 + .finally(() => { 463 + // remove turnstile container if it exists 464 + const turnstileContainer = document.getElementById("turnstile-container"); 465 + if (turnstileContainer) { 466 + turnstileContainer.remove(); 467 + } 468 + }); 469 + }); 470 + }) 471 + .catch((error) => { 472 + console.error("Error fetching server configuration:", error); 473 + titleElement.innerText = "Failed to load server configuration"; 474 + 475 + setTimeout(() => { 476 + closeButton.style.display = ""; 477 + uploadButton.style.display = ""; 478 + downloadButton.style.display = ""; 479 + levelDataElement.style.display = ""; 480 + copyButtonElement.style.display = ""; 481 + titleElement.innerText = "Here's your level data - keep this somewhere safe!"; 482 + }, 3000); 483 + }); 484 + }, 485 + }); 486 + }, 487 + } 488 + );