User prompt
add card deck icon
User prompt
add to card pile: Card Title: Darkness Between Dimensions Subtitle: A Red Reality Type: Land Description: Spawns a machine that, after a random amount of time, will transport anything nearby into the Darkness Between Dimensions. Players in the Darkness Between Dimensions can be saved by divine grace, or with the item “Scranton’s Grappling Hook”. After a random amount of time, players will be returned to the board. They will come back... squishier.
Code edit (1 edits merged)
Please save this source code
User prompt
SCP Containment: Foundation Arena
Initial prompt
The following is information pertaining to the proper gameplay of SCP-3301. In order to maintain containment of the artifact, monthly testing of SCP-3301 is required. Personnel assigned to SCP-3301 are required to maintain a thorough knowledge of the rules and regulations of the game. Introduction - Close Document The following is an introduction copied directly out of “THE FOUNDATION” gameplay handbook. The handbook is a leather-bound pamphlet with embossed silver print displaying the name of the game, the production studio, and the words “50TH ANNIVERSARY EDITION”. THE FOUNDATION A GAME BY CRYOGEN STUDIOS A DIVISION OF DR. WONDERTAINMENT Welcome, weary researcher, to THE FOUNDATION! A board game only for the strong willed and mighty of heart. But beware! For danger lurks around every corner, and foul things are creeping in the night. Do you have what it takes to stand betwixt humanity and the darkness? Or will you too be lost to the chaos eternal? Only time will tell! Become Mr. Collector, or maybe Mr. Containment! The choice is yours! Setup - Close Document SCP-3301 is played between 2 to 8 teams of two players. Each primary player chooses a secondary player, to act as their representative on the game board. The primary players are all selected when each set of players places their game pieces1 on the board. The board contained within SCP-3301 varies depending on which version of the “game” is being played, and is random. Gameplay is similar between each board, though the setting is different (i.e. The Land of the Unclean, Echoes of the Mariana, Bigfoot’s Jungle, The Cosmic Starfish, etc.), and different hostile entities appear as opponents on different boards. Testing has revealed at least twenty-three different game boards, though there are possibly more. Once the game board is unfolded, and the silver card box placed in the middle of the board in the appropriate space, the surrounding area (a space roughly 300m in diameter) will undergo dramatic anomalous spatial changes. Observers will see the space appear to fall away, as if the viewpoint of observers outside of the area of effect has become a bird’s eye view of a space much larger than the affected area. This observed space will mimic the current game board, only on a much larger scale and typically built into an arena setting. Within the affected space, the primary players will find themselves seated around a flat, crystalline surface suspended above the aforementioned game board arena, while their game piece representatives will be at the starting positions on the game board below. The start of the game is typically accompanied by music and fireworks, and a voice announcing the beginning of a new game. Once the game has begun, nothing can be passed through the spatial distortion surrounding the game arena. Secondary players who are killed or die in-game will appear outside of the distortion shortly after their death, unharmed. Primary players will remain within the anomaly for the duration of the game. Gameplay - Close Document After the introduction has finished, the game begins. Each player starts the game by drawing seven cards from the silver box in the middle of the board, and end each turn by drawing one. If a player has more than ten cards in their hand at the end of a turn, they must discard one by returning it to the silver box. There are several types of cards that can appear in the box, and each affects the game in different ways: Green Cards - are cards that influence the environment of the board. These can range from obstacles that can be used against other players, to introducing other environments and the hostile entities within them to the board, to traps and mazes, etc. Blue Cards - are companion cards. Companion cards “equip” a companion to the game piece player, who will follow the player until the card’s time limit expires, the companion is killed, or the player is killed. Companions can perform different tasks and have different abilities to assist their player, or hinder other players. Red Cards - are weapon, equipment, or ability cards. These cards allow the player on the board to perform different abilities, or have access to different weapons and equipment. Each card lists the ability or weapon’s stats, strengths, weaknesses, limitations, etc. These cards last as long as the player is alive, or until some requirement of the card or another card are met (for example, running a Red Card through the card titled “Upgrade" will result in the Red Card disappearing, and another card being issued to the player). Additionally, if an equipment card is utilized (such as a trap, or reality anchor, etc.) the card is expended after the equipment is utilized. Orange Cards - are hostile or otherwise uncontained anomalies.2 They appear randomly across the board, and players often do not know they are nearby until they are within range of them, upon which the board will produce a card on that spot. Entities that appear as Orange Cards can be destroyed or contained, depending on the player’s equipment and chosen endgame. Yellow Cards - are cards that influence the way that the earnings of the game are distributed. At the beginning of each game, an incorporeal numerical counter appears above the game board, and another above each player. The counter starts at different values depending on the difficulty setting (for example, $10,000USD at normal difficulty), and yellow cards distribute this money across the players as the game progresses. Players who die have their “earnings” moved back into the central counter to be redistributed. Yellow cards, and the money value shown in the counters, have no impact on the actual win conditions of the game, but at the end of each game the players who won will have their sum, in the form of a gold bar of equal value engraved with the Wondertainment logo, distributed to them through the silver box in the center of the board. White Cards - are fortune cards. White Cards, and their counterpart Black Cards, are randomly distributed as an additional card to players at the beginning of their turn. White and Black Cards must be played as soon as they are drawn, and while typically only affecting the player who drew them, can potentially affect other nearby players or the entire board, if applicable.3 White Cards provide a random instance of “good fortune”, as described by the card. Black Cards - are misfortune cards. Black Cards are more rare than White Cards, but their effect on the game is typically more severe. Black Cards, like their White Card counterparts, must be used as soon as they are drawn, and are expended after their use. Purple Cards - are Wondertainment cards. These are very rare,4 and have random effects on the outcome of the game. Typically, Purple cards insert a random Wondertainment product into the game, typically with exaggerated properties or abilities.5 For examples of game cards used in testing, see Addendum 3301.3. On the outside of the game board is a slider pointing to different difficulty settings. This slider can be moved before the game has begun, but retracts into the board afterwards. The difficulty settings are as follows: Neutralized Mode: All entities and artifacts are replaced with plush versions of themselves. No entity does any damage to anything else. Lullaby music plays across the board. The game mode lacks a win condition, and just ends after all of the players have fallen asleep. Thaumiel Mode: Entities have reduced damage and players are not “killed”, only “knocked out”. Certain win conditions are removed. Blood and gore are no longer visible, and nudity is no longer possible, as all players have an unremovable skintight bodysuit beneath their lowest layer of clothes. Safe Mode: The baseline game setting. Euclid Mode: Entities do more damage, are more resistant to attack, and have more health. Players become unable to view the movements and plays of other players. Keter Mode: Same as Euclid Mode, only entities are faster as well. Maksur Mode: Same as Keter Mode, only players start with a gunshot wound to a random part of their body. Apollyon Mode: Two random, hostile supreme divine beings appear on the map after the fourth turn. Players may start the game blind. The game is played in a team vs. team format, where each two person team competes against every other two person team. Every player begins at their “containment facility”, a starting point and base of operations on their side of the board. Within the facility, each player has several spaces for “equipment”, which they can swap out at the beginning of their turn with cards from their hand, in order to equip different items to their “piece”. Typically, there are two “weapon” spaces, three “equipment” spaces, two “ability” spaces, and one “companion” space. Playing any card into that space “equips” their piece with the item, ability, or companion. If an item, ability, or companion is lost, the physical card will disappear as if made of smoke. Additionally, some cards will increase the overall number of equipment slots (notably the card “Big Bag o’ Taters” will increase the number of weapon and equipment slots from 2 / 3 to 4 / 5). At the beginning of each turn, the primary player will select one card, and make equipment additions or subtractions if necessary.6 Afterwards, the player will roll two six-sided dice. The number that is rolled corresponds with a radius within which the secondary player may move, or take an action. If the secondary player has a ranged ability or weapon, they may move to the edge of their movement radius and use it, so long as the target is within range. From this point on, the game proceeds in any number of variations. Based on the cards drawn by the primary players, and the actions taken by the secondary players, any number of outcomes are possible, depending on which victory condition the players choose to seek. Victory Conditions - Close Document There are several victory conditions that players may choose to seek during the course of the game. The following describes conditions needed to reach each end game: MR. FINDER: Buried on each game board is a hidden anomalous item, denoted by a golden W mark on it. This item is typically protected by anomalous entities, a hazardous location, or a dangerous meme or powerful antimeme. Individuals who search for clues, discover the location of the item, survive its protections and safely return it to their containment facility will become MR. FINDER, and win the game. MR. COLLECTOR: This victory condition only becomes available if a Purple Card is drawn. If any player is able to contain the Wondertainment product and return it to their containment facility, they will become MR. COLLECTOR, and win the game. MR. GENOCIDE: Any player that directly kills every other player in the game will become MR. GENOCIDE, and win the game. This victory condition becomes unobtainable if any players are killed by means other than by the hand of anyone seeking the MR. GENOCIDE7 victory condition. MR. ESCHATOLOGY: Any player who is able to summon three “supreme divine beings” will become MR. ESCHATOLOGY, and will win the game. Divine beings (i.e. Mr. God, A Very Angry Star, The Boy in the Pit, The Gate Guardian) do not fulfill the requirements of the MR. ESCHATOLOGY win condition; they must be “supreme” (i.e. DEER, The Starfish, A Clockwork God, The Serpent, The Eel, Mary Nakayama, etc.). MR. COALITION: If a player is somehow able to destroy every anomalous artifact or entity, they become MR. COALITION and win the game. To become MR. COALITION, the winner does not actually have to destroy every single anomalous artifact or entity, they must only have destroyed the majority of the artifacts or entities, and all artifacts and entities must be destroyed.8 MR. CONTAINMENT: Any player who contains more than half of the anomalies on the board (as denoted by their Orange Cards) and returns them to their containment facility becomes MR. CONTAINMENT, and wins the game. MR. FOUNDATION: Any player who contains every entity and artifact, as well as every other player, becomes MR. FOUNDATION, and wins the game. Individuals winning under the condition of MR. FOUNDATION also receive $50,000USD upon completion of the game. Can only be completed on Safe Mode difficulty or higher. MR. SURVIVOR: If a player is the last player standing, and has not met any other win conditions, that player becomes MR. SURVIVOR, and wins the game. Additionally, the game manual claims that there are many other secret victory conditions to go alongside those listed above. The existence of these secret conditions means that some games may end suddenly and without warning, when a player meets an unexpected condition.9 Rules - Close Document The structure of THE FOUNDATION allows for a variety of gameplay styles and approaches to the different goals of the game. In order to seemingly aid this effort, there are very few enumerated rules to the game. The following are the rules of the game, exactly as described in the manual: No hits below the belt. Targeting will result in a 15 yard penalty and a loss of downs. Drink your Ovaltine. Suspend your disbelief a little bit. This isn’t a game for children, so kids who play it are really cool. Leave your loyalties at the door. Buy Wondertainment products. cardback.png The back of SCP-3301 cards. Addendum 3301.3: Game Cards The following are examples of cards used within SCP-3301. While not all cards appear to carry relevant information, many appear to be references to anomalies within Foundation containment, or Foundation personnel. As previously mentioned in this document, the full scale of this breach of information security is part of an ongoing investigation. Several statistics are utilized within the card system, each corresponding to a certain attribute that players have. The statistics are listed below: ATK: Attack power. A value that determines the amount of damage a player can do to another player or an entity. Since entities cannot be contained until their health has reached 0, this is arguably the most important statistic. All players start with 6 ATK. DEF: Defense. A value that influences the amount of damage a player takes after being attacked. All players start with 6 DEF. HP: Hit Points. A value that determines how much damage a player can sustain before dying. Can be replaced with cards that increase health, such as medicines or vampiric cards. All players start with 10 HP. SPD: Speed. A value that determines which entity in an encounter can attack or defend first, and how often they can do so. All players start with 8 SPD. ACC: Accuracy. A value that determines how likely a given attack is to striking a target. All players start with 8 ACC. The game manual mentions other non-enumerated statistics that players can add to or subtract from, such as Smooth Talking, Memetic Resistance, Ethics, etc. While there are cards that may affect these stats, the only known way to gauge their level is by asking an entity with limited or full omniscience, such as A Librarian. Typically pieces for players take the shape of symbols representing various Groups of Interest or notable Persons of Interest, while pieces for the secondary players are simply small tokens of the same color as the primary player’s piece. 2. Notably, many of these anomalies appear to be direct references to anomalies within Foundation containment, or anomalies that the Foundation is otherwise aware of. The depth of this breach of information is still being investigated. 3. During one test, Researcher H. Korver drew a White Card called “Divine Assistance”, which summoned a “divine being” for his partner (Dr. Andrew White), as well as for every other player. For the next two hours, the divine beings engaged in combat with each other while the game pieces attempted to collect as many yellow cards as they could. 4. Only six Wondertainment cards have been drawn in more than fifty iterations of the game tested thus far 5. Such as one game of “Echoes of the Mariana”, which saw the inclusion of Mr. Fish as an enormous, predatory shark. 6. Once equipment has been added, the equipment cannot be changed until the beginning of the next turn, unless otherwise affected by another player. 7. Notably, the typical voice that announces the players’ victory at the end of the game is replaced with a different, more aggressive voice, which has not yet been identified. 8. This win condition does not match its description in the manual, and whether this is accidental or not is unknown. 9. This occurrence has only happened once in testing, when Agent Staski and Agent Polliver won the game after becoming “MR. LAMENT”, with victory conditions of “Any player who is capable of killing another player only by saying hurtful things to them becomes MR. LAMENT, and wins the game.” In this case, Agent Staski made a rude comment about Agent Parker’s mother, which resulted in Agent Parker slipping as he charged Agent Staski and falling into a ravine. 10. While an image of this card is visible within the game manual, the card has not yet been seen during gameplay testing. 11. During a testing session, Dr. Richard McMillan used his turn after another player had played “Consolidation” to ask his companion, The Librarian, what the effect of the card was, as it was not readily apparent. In response, The Librarian said, “MR. CONTAINMENT and MR. FOUNDATION are dead, it seems. This echoes of a situation from somewhere similar, but very far away. I sense that MR. THRESHER may have taken their place.” When questioned further, The Librarian was unsure how to respond and appeared noticeably uncomfortable. 12. While the ████ card does summon an in-game version of ••/•••••/••/•, the effect does not seem to permeate the barrier of the spatial anomaly, as the real-world ••/•••••/••/• does not appear, despite the necessary triggering factors being present.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, playerLevel: 1, containedAnomalies: 0 }); /**** * Classes ****/ var Button = Container.expand(function () { var self = Container.call(this); self.background = self.attachAsset('button', { anchorX: 0.5, anchorY: 0.5 }); self.text = new Text2('Button', { size: 24, fill: 0xFFFFFF }); self.text.anchor.set(0.5, 0.5); self.addChild(self.text); self.setText = function (text) { self.text.setText(text); }; self.setCallback = function (callback) { self.callback = callback; }; self.down = function (x, y, obj) { tween(self, { scaleX: 0.95, scaleY: 0.95 }, { duration: 100, easing: tween.easeOut }); }; self.up = function (x, y, obj) { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.easeOut }); if (self.callback) { self.callback(); } }; return self; }); var Card = Container.expand(function () { var self = Container.call(this); // Card properties self.cardType = 'none'; self.cardName = ''; self.cardDescription = ''; self.power = 0; self.cost = 0; self.revealed = false; // Card visuals self.back = self.attachAsset('cardBack', { anchorX: 0.5, anchorY: 0.5 }); self.front = self.attachAsset('cardFront', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); self.nameText = new Text2('', { size: 20, fill: 0x000000 }); self.nameText.anchor.set(0.5, 0); self.nameText.y = -70; self.addChild(self.nameText); self.descriptionText = new Text2('', { size: 16, fill: 0x000000 }); self.descriptionText.anchor.set(0.5, 0); self.descriptionText.y = 0; self.descriptionText.alpha = 0; self.addChild(self.descriptionText); self.powerText = new Text2('', { size: 24, fill: 0xFF0000 }); self.powerText.anchor.set(0.5, 0.5); self.powerText.x = 50; self.powerText.y = 70; self.powerText.alpha = 0; self.addChild(self.powerText); self.costText = new Text2('', { size: 24, fill: 0x0000FF }); self.costText.anchor.set(0.5, 0.5); self.costText.x = -50; self.costText.y = 70; self.costText.alpha = 0; self.addChild(self.costText); // Initialize a card with data self.init = function (data) { self.cardType = data.type; self.cardName = data.name; self.cardDescription = data.description; self.power = data.power || 0; self.cost = data.cost || 0; self.nameText.setText(self.cardName); self.descriptionText.setText(self.cardDescription); self.powerText.setText(self.power.toString()); self.costText.setText(self.cost.toString()); }; // Flip the card to reveal front self.reveal = function () { if (!self.revealed) { tween(self.back, { alpha: 0 }, { duration: 300, easing: tween.easeInOut }); tween(self.front, { alpha: 1 }, { duration: 300, easing: tween.easeInOut }); tween(self.descriptionText, { alpha: 1 }, { duration: 300, easing: tween.easeInOut }); tween(self.powerText, { alpha: 1 }, { duration: 300, easing: tween.easeInOut }); tween(self.costText, { alpha: 1 }, { duration: 300, easing: tween.easeInOut }); self.revealed = true; } }; // Flip card back self.hide = function () { if (self.revealed) { tween(self.back, { alpha: 1 }, { duration: 300, easing: tween.easeInOut }); tween(self.front, { alpha: 0 }, { duration: 300, easing: tween.easeInOut }); tween(self.descriptionText, { alpha: 0 }, { duration: 300, easing: tween.easeInOut }); tween(self.powerText, { alpha: 0 }, { duration: 300, easing: tween.easeInOut }); tween(self.costText, { alpha: 0 }, { duration: 300, easing: tween.easeInOut }); self.revealed = false; } }; // Card interaction events self.down = function (x, y, obj) { tween(self, { scaleX: 1.1, scaleY: 1.1 }, { duration: 100, easing: tween.easeOut }); }; self.up = function (x, y, obj) { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.easeOut }); if (!self.revealed) { self.reveal(); LK.getSound('cardDraw').play(); } // Trigger card selection in the game if (typeof cardSelected === 'function') { cardSelected(self); } }; return self; }); var Entity = Container.expand(function () { var self = Container.call(this); // Entity properties self.entityType = 'none'; self.health = 100; self.maxHealth = 100; self.energy = 100; self.maxEnergy = 100; self.power = 10; self.moveRange = 3; self.boardX = 0; self.boardY = 0; self.isMoving = false; self.isPlayer = false; // Entity visuals self.graphic = null; self.healthBarBg = self.attachAsset('barBackground', { anchorX: 0.5, anchorY: 0.5, y: -45 }); self.healthBar = self.attachAsset('healthBar', { anchorX: 0, anchorY: 0.5, x: -30, y: -45 }); self.energyBarBg = self.attachAsset('barBackground', { anchorX: 0.5, anchorY: 0.5, y: -35 }); self.energyBar = self.attachAsset('energyBar', { anchorX: 0, anchorY: 0.5, x: -30, y: -35 }); self.nameText = new Text2('', { size: 16, fill: 0xFFFFFF }); self.nameText.anchor.set(0.5, 1); self.nameText.y = -50; self.addChild(self.nameText); // Initialize entity with data self.init = function (data, isPlayerEntity) { self.entityType = data.type; self.health = data.health || 100; self.maxHealth = data.maxHealth || 100; self.energy = data.energy || 100; self.maxEnergy = data.maxEnergy || 100; self.power = data.power || 10; self.moveRange = data.moveRange || 3; self.isPlayer = isPlayerEntity || false; // Set up appropriate graphic based on type if (self.isPlayer) { self.graphic = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); } else if (self.entityType === 'opponent') { self.graphic = self.attachAsset('opponent', { anchorX: 0.5, anchorY: 0.5 }); } else if (self.entityType === 'anomaly') { self.graphic = self.attachAsset('anomaly', { anchorX: 0.5, anchorY: 0.5 }); } self.nameText.setText(data.name || self.entityType); self.updateBars(); }; // Update health and energy bars self.updateBars = function () { var healthPercent = self.health / self.maxHealth; var energyPercent = self.energy / self.maxEnergy; self.healthBar.width = 60 * healthPercent; self.energyBar.width = 60 * energyPercent; }; // Take damage self.takeDamage = function (amount) { self.health -= amount; if (self.health <= 0) { self.health = 0; self.die(); } self.updateBars(); LK.effects.flashObject(self, 0xff0000, 300); }; // Use energy self.useEnergy = function (amount) { if (self.energy >= amount) { self.energy -= amount; self.updateBars(); return true; } return false; }; // Restore health self.heal = function (amount) { self.health += amount; if (self.health > self.maxHealth) { self.health = self.maxHealth; } self.updateBars(); LK.effects.flashObject(self, 0x00ff00, 300); }; // Restore energy self.restoreEnergy = function (amount) { self.energy += amount; if (self.energy > self.maxEnergy) { self.energy = self.maxEnergy; } self.updateBars(); LK.effects.flashObject(self, 0x0088ff, 300); }; // Move to a board position self.moveToPosition = function (boardX, boardY) { if (self.isMoving) { return; } self.isMoving = true; self.boardX = boardX; self.boardY = boardY; var targetX = BOARD_OFFSET_X + boardX * TILE_SIZE + TILE_SIZE / 2; var targetY = BOARD_OFFSET_Y + boardY * TILE_SIZE + TILE_SIZE / 2; tween(self, { x: targetX, y: targetY }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { self.isMoving = false; LK.getSound('playerMove').play(); // Check for anomaly at this position checkForAnomalyAtPosition(boardX, boardY); } }); }; // Attack another entity self.attack = function (target) { if (!target) { return; } if (self.useEnergy(20)) { LK.getSound('playerAttack').play(); // Animation var originalX = self.x; var originalY = self.y; var targetX = target.x; var targetY = target.y; // Move towards target tween(self, { x: originalX + (targetX - originalX) * 0.7, y: originalY + (targetY - originalY) * 0.7 }, { duration: 200, easing: tween.easeIn, onFinish: function onFinish() { // Apply damage target.takeDamage(self.power); // Move back tween(self, { x: originalX, y: originalY }, { duration: 300, easing: tween.easeOut }); } }); } }; // Die function self.die = function () { tween(self, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 500, easing: tween.easeIn, onFinish: function onFinish() { self.destroy(); if (self.entityType === 'anomaly') { containAnomaly(self); } // Check if game is over checkGameState(); } }); }; // Entity interaction events self.down = function (x, y, obj) { if (gameState !== GAME_STATE.PLAYER_TURN) { return; } if (self.isPlayer) { // Select the player selectEntity(self); } else if (selectedEntity && selectedEntity.isPlayer) { // If player is selected and this is another entity, try to attack var dx = Math.abs(selectedEntity.boardX - self.boardX); var dy = Math.abs(selectedEntity.boardY - self.boardY); if (dx <= 1 && dy <= 1) { selectedEntity.attack(self); endPlayerTurn(); } } }; return self; }); var Highlight = Container.expand(function () { var self = Container.call(this); self.graphic = self.attachAsset('highlight', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3 }); self.boardX = 0; self.boardY = 0; self.setPosition = function (bx, by) { self.boardX = bx; self.boardY = by; self.x = BOARD_OFFSET_X + bx * TILE_SIZE + TILE_SIZE / 2; self.y = BOARD_OFFSET_Y + by * TILE_SIZE + TILE_SIZE / 2; }; self.down = function (x, y, obj) { if (gameState !== GAME_STATE.PLAYER_TURN || !selectedEntity || !selectedEntity.isPlayer) { return; } // Calculate the distance between selected entity and this highlight var dx = Math.abs(selectedEntity.boardX - self.boardX); var dy = Math.abs(selectedEntity.boardY - self.boardY); var distance = Math.max(dx, dy); // Check if we can move there if (distance <= selectedEntity.moveRange) { // Check if the destination is empty if (isTileEmpty(self.boardX, self.boardY)) { selectedEntity.moveToPosition(self.boardX, self.boardY); clearHighlights(); endPlayerTurn(); } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Game constants var BOARD_SIZE = 8; var TILE_SIZE = 120; var BOARD_OFFSET_X = (2048 - BOARD_SIZE * TILE_SIZE) / 2; var BOARD_OFFSET_Y = (2732 - BOARD_SIZE * TILE_SIZE) / 2 - 100; var GAME_STATE = { SETUP: 0, PLAYER_TURN: 1, OPPONENT_TURN: 2, GAME_OVER: 3 }; // Game variables var gameBoard = []; var gameState = GAME_STATE.SETUP; var playerEntities = []; var opponentEntities = []; var anomalies = []; var selectedEntity = null; var highlights = []; var cards = []; var score = 0; var turnCounter = 0; var playerHand = []; var cardsInDeck = 20; // Game board and UI var boardGraphic = game.addChild(LK.getAsset('board', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: BOARD_OFFSET_Y + BOARD_SIZE * TILE_SIZE / 2 })); boardGraphic.width = BOARD_SIZE * TILE_SIZE; boardGraphic.height = BOARD_SIZE * TILE_SIZE; // Score text var scoreTxt = new Text2('Score: 0', { size: 50, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.y = 20; // Turn indicator var turnTxt = new Text2('Player Turn', { size: 40, fill: 0xFFFFFF }); turnTxt.anchor.set(0.5, 0); LK.gui.top.addChild(turnTxt); turnTxt.y = 80; // Draw card button var drawCardBtn = new Button(); drawCardBtn.setText('Draw Card (' + cardsInDeck + ')'); drawCardBtn.x = 2048 - 150; drawCardBtn.y = 2732 - 100; game.addChild(drawCardBtn); drawCardBtn.setCallback(function () { if (gameState === GAME_STATE.PLAYER_TURN && playerHand.length < 5 && cardsInDeck > 0) { drawCard(); } }); // End turn button var endTurnBtn = new Button(); endTurnBtn.setText('End Turn'); endTurnBtn.x = 150; endTurnBtn.y = 2732 - 100; game.addChild(endTurnBtn); endTurnBtn.setCallback(function () { if (gameState === GAME_STATE.PLAYER_TURN) { endPlayerTurn(); } }); // Card deck initialization var cardTypes = [{ type: 'ability', name: 'Psychic Shield', description: 'Increase defense for 2 turns', power: 15, cost: 25 }, { type: 'weapon', name: 'Anomaly Neutralizer', description: 'Deal extra damage to anomalies', power: 30, cost: 40 }, { type: 'equipment', name: 'Energy Booster', description: 'Restore energy', power: 50, cost: 15 }, { type: 'ability', name: 'Containment Field', description: 'Trap anomalies for easy capture', power: 25, cost: 35 }, { type: 'weapon', name: 'Tactical Rifle', description: 'Medium range attack', power: 20, cost: 30 }, { // New card type type: 'land', name: 'Darkness Between Dimensions', description: 'Spawns a machine that, after a random amount of time, will transport anything nearby into the Darkness Between Dimensions. Players in the Darkness Between Dimensions can be saved by divine grace, or with the item “Scranton’s Grappling Hook”. After a random amount of time, players will be returned to the board. They will come back… squishier.', power: 0, cost: 0 }]; // Initialize game board function initBoard() { gameBoard = []; for (var y = 0; y < BOARD_SIZE; y++) { gameBoard[y] = []; for (var x = 0; x < BOARD_SIZE; x++) { gameBoard[y][x] = { entities: [], type: 'empty' }; } } } // Check if a tile is empty function isTileEmpty(x, y) { if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE) { return false; } // Check for any entities on this tile for (var i = 0; i < playerEntities.length; i++) { if (playerEntities[i].boardX === x && playerEntities[i].boardY === y) { return false; } } for (var i = 0; i < opponentEntities.length; i++) { if (opponentEntities[i].boardX === x && opponentEntities[i].boardY === y) { return false; } } for (var i = 0; i < anomalies.length; i++) { if (anomalies[i].boardX === x && anomalies[i].boardY === y) { return false; } } return true; } // Initialize player entity function initPlayerEntity() { var player = new Entity(); player.init({ type: 'player', name: 'SCP Agent', health: 150, maxHealth: 150, energy: 100, maxEnergy: 100, power: 25, moveRange: 3 }, true); player.boardX = 0; player.boardY = 0; player.x = BOARD_OFFSET_X + player.boardX * TILE_SIZE + TILE_SIZE / 2; player.y = BOARD_OFFSET_Y + player.boardY * TILE_SIZE + TILE_SIZE / 2; game.addChild(player); playerEntities.push(player); } // Initialize opponent entities function initOpponents() { // Create 2 opponents for (var i = 0; i < 2; i++) { var opponent = new Entity(); opponent.init({ type: 'opponent', name: 'Chaos Agent ' + (i + 1), health: 120, maxHealth: 120, energy: 100, maxEnergy: 100, power: 20, moveRange: 2 }); // Place opponents on the opposite side opponent.boardX = BOARD_SIZE - 1 - i; opponent.boardY = BOARD_SIZE - 1; opponent.x = BOARD_OFFSET_X + opponent.boardX * TILE_SIZE + TILE_SIZE / 2; opponent.y = BOARD_OFFSET_Y + opponent.boardY * TILE_SIZE + TILE_SIZE / 2; game.addChild(opponent); opponentEntities.push(opponent); } } // Initialize anomalies function initAnomalies() { // Create 4 anomalies for (var i = 0; i < 4; i++) { var anomaly = new Entity(); anomaly.init({ type: 'anomaly', name: 'SCP-' + (Math.floor(Math.random() * 899) + 100), health: 80, maxHealth: 80, energy: 50, maxEnergy: 50, power: 15, moveRange: 1 }); // Find a random empty position for the anomaly var randomX, randomY; do { randomX = Math.floor(Math.random() * BOARD_SIZE); randomY = Math.floor(Math.random() * BOARD_SIZE); } while (!isTileEmpty(randomX, randomY)); anomaly.boardX = randomX; anomaly.boardY = randomY; anomaly.x = BOARD_OFFSET_X + anomaly.boardX * TILE_SIZE + TILE_SIZE / 2; anomaly.y = BOARD_OFFSET_Y + anomaly.boardY * TILE_SIZE + TILE_SIZE / 2; game.addChild(anomaly); anomalies.push(anomaly); } } // Show movement highlights function showMovementHighlights() { if (!selectedEntity) { return; } clearHighlights(); var range = selectedEntity.moveRange; for (var y = 0; y < BOARD_SIZE; y++) { for (var x = 0; x < BOARD_SIZE; x++) { var dx = Math.abs(selectedEntity.boardX - x); var dy = Math.abs(selectedEntity.boardY - y); var dist = Math.max(dx, dy); if (dist <= range) { var highlight = new Highlight(); highlight.setPosition(x, y); game.addChild(highlight); highlights.push(highlight); } } } } // Clear highlights function clearHighlights() { for (var i = 0; i < highlights.length; i++) { highlights[i].destroy(); } highlights = []; } // Select an entity function selectEntity(entity) { // Deselect previous entity if (selectedEntity) { tween(selectedEntity, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.easeOut }); } // Select new entity selectedEntity = entity; if (selectedEntity) { tween(selectedEntity, { scaleX: 1.1, scaleY: 1.1 }, { duration: 100, easing: tween.easeOut }); showMovementHighlights(); } } // Draw a card function drawCard() { if (playerHand.length >= 5 || cardsInDeck <= 0) { return; } cardsInDeck--; drawCardBtn.setText('Draw Card (' + cardsInDeck + ')'); var cardType = cardTypes[Math.floor(Math.random() * cardTypes.length)]; var card = new Card(); card.init(cardType); card.x = 2048 / 2; card.y = 2732 - 150; // Position cards in hand var cardSpacing = 170; var handWidth = (playerHand.length + 1) * cardSpacing; var startX = (2048 - handWidth) / 2 + cardSpacing / 2; card.x = startX + playerHand.length * cardSpacing; game.addChild(card); playerHand.push(card); LK.getSound('cardDraw').play(); } // Use a card function cardSelected(card) { if (gameState !== GAME_STATE.PLAYER_TURN || !selectedEntity || !selectedEntity.isPlayer) { return; } // Apply card effect if (card.cardType === 'ability') { selectedEntity.restoreEnergy(card.power); } else if (card.cardType === 'weapon') { selectedEntity.power += card.power; // Temporary power boost LK.setTimeout(function () { selectedEntity.power -= card.power; }, 5000); } else if (card.cardType === 'equipment') { selectedEntity.heal(card.power); } // Remove card from hand var index = playerHand.indexOf(card); if (index !== -1) { playerHand.splice(index, 1); card.destroy(); // Reorganize hand var cardSpacing = 170; var handWidth = playerHand.length * cardSpacing; var startX = (2048 - handWidth) / 2 + cardSpacing / 2; for (var i = 0; i < playerHand.length; i++) { tween(playerHand[i], { x: startX + i * cardSpacing }, { duration: 300, easing: tween.easeInOut }); } } } // Check for anomaly at position function checkForAnomalyAtPosition(x, y) { for (var i = 0; i < anomalies.length; i++) { if (anomalies[i].boardX === x && anomalies[i].boardY === y) { // Initiate containment var anomaly = anomalies[i]; // Lower health to make containment easier for demo anomaly.takeDamage(40); if (anomaly.health <= 0) { containAnomaly(anomaly); } } } } // Contain an anomaly function containAnomaly(anomaly) { LK.getSound('anomalyContain').play(); var index = anomalies.indexOf(anomaly); if (index !== -1) { anomalies.splice(index, 1); // Increase score score += 100; scoreTxt.setText('Score: ' + score); // Update stored anomaly count storage.containedAnomalies = (storage.containedAnomalies || 0) + 1; // Check if all anomalies are contained if (anomalies.length === 0) { // Win condition LK.setScore(score); if (score > storage.highScore) { storage.highScore = score; } LK.showYouWin(); } } } // End player turn function endPlayerTurn() { if (gameState !== GAME_STATE.PLAYER_TURN) { return; } gameState = GAME_STATE.OPPONENT_TURN; turnTxt.setText('Opponent Turn'); clearHighlights(); selectEntity(null); // Opponent turn timer LK.setTimeout(executeOpponentTurn, 1000); } // Execute opponent turn function executeOpponentTurn() { // Process each opponent for (var i = 0; i < opponentEntities.length; i++) { var opponent = opponentEntities[i]; // Find nearest target (player or anomaly) var target = null; var minDist = 999; // Check player entities for (var j = 0; j < playerEntities.length; j++) { var dx = Math.abs(opponent.boardX - playerEntities[j].boardX); var dy = Math.abs(opponent.boardY - playerEntities[j].boardY); var dist = dx + dy; if (dist < minDist) { minDist = dist; target = playerEntities[j]; } } // Take action based on distance if (target) { if (minDist <= 1) { // Attack if adjacent LK.setTimeout(function (attacker, defender) { return function () { attacker.attack(defender); }; }(opponent, target), 500 * i); } else { // Move toward target var moveX = opponent.boardX; var moveY = opponent.boardY; if (target.boardX > opponent.boardX) { moveX++; } else if (target.boardX < opponent.boardX) { moveX--; } if (target.boardY > opponent.boardY) { moveY++; } else if (target.boardY < opponent.boardY) { moveY--; } // Check if the tile is empty if (isTileEmpty(moveX, moveY)) { LK.setTimeout(function (entity, x, y) { return function () { entity.moveToPosition(x, y); }; }(opponent, moveX, moveY), 500 * i); } } } } // Process each anomaly (simple random movement) for (var i = 0; i < anomalies.length; i++) { var anomaly = anomalies[i]; // Random movement var directions = [{ x: 1, y: 0 }, { x: -1, y: 0 }, { x: 0, y: 1 }, { x: 0, y: -1 }]; var validMoves = []; for (var j = 0; j < directions.length; j++) { var moveX = anomaly.boardX + directions[j].x; var moveY = anomaly.boardY + directions[j].y; if (moveX >= 0 && moveX < BOARD_SIZE && moveY >= 0 && moveY < BOARD_SIZE && isTileEmpty(moveX, moveY)) { validMoves.push({ x: moveX, y: moveY }); } } if (validMoves.length > 0) { var randomMove = validMoves[Math.floor(Math.random() * validMoves.length)]; LK.setTimeout(function (entity, x, y) { return function () { entity.moveToPosition(x, y); }; }(anomaly, randomMove.x, randomMove.y), 1000 + 300 * i); } } // End opponent turn after all moves LK.setTimeout(startPlayerTurn, 2000); } // Start player turn function startPlayerTurn() { gameState = GAME_STATE.PLAYER_TURN; turnTxt.setText('Player Turn'); turnCounter++; // Restore energy for player entities for (var i = 0; i < playerEntities.length; i++) { playerEntities[i].restoreEnergy(20); } // Check game state checkGameState(); } // Check game state function checkGameState() { // Check if player is defeated if (playerEntities.length === 0) { LK.setScore(score); LK.showGameOver(); return; } // Check if all opponents are defeated if (opponentEntities.length === 0) { score += 500; scoreTxt.setText('Score: ' + score); LK.setScore(score); if (score > storage.highScore) { storage.highScore = score; } LK.showYouWin(); return; } // Check if maximum turns reached if (turnCounter >= 30) { LK.setScore(score); if (score > storage.highScore) { storage.highScore = score; } LK.showGameOver(); return; } } // Initialize game function initGame() { // Clear everything clearHighlights(); for (var i = 0; i < playerEntities.length; i++) { playerEntities[i].destroy(); } playerEntities = []; for (var i = 0; i < opponentEntities.length; i++) { opponentEntities[i].destroy(); } opponentEntities = []; for (var i = 0; i < anomalies.length; i++) { anomalies[i].destroy(); } anomalies = []; for (var i = 0; i < playerHand.length; i++) { playerHand[i].destroy(); } playerHand = []; // Setup variables score = 0; scoreTxt.setText('Score: ' + score); turnCounter = 0; gameState = GAME_STATE.SETUP; selectedEntity = null; cardsInDeck = 20; drawCardBtn.setText('Draw Card (' + cardsInDeck + ')'); // Initialize game elements initBoard(); initPlayerEntity(); initOpponents(); initAnomalies(); // Start game gameState = GAME_STATE.PLAYER_TURN; turnTxt.setText('Player Turn'); // Draw initial cards for (var i = 0; i < 3; i++) { drawCard(); } // Play background music LK.playMusic('bgmusic', { loop: true }); } // Game navigation game.down = function (x, y, obj) { // Empty handler for board clicks not handled by entities }; game.move = function (x, y, obj) { // Empty handler for mouse movement }; // Main game loop game.update = function () { // Initialize game if we're in setup if (gameState === GAME_STATE.SETUP) { initGame(); } }; // Start the game initGame();
===================================================================
--- original.js
+++ change.js
@@ -580,8 +580,15 @@
name: 'Tactical Rifle',
description: 'Medium range attack',
power: 20,
cost: 30
+}, {
+ // New card type
+ type: 'land',
+ name: 'Darkness Between Dimensions',
+ description: 'Spawns a machine that, after a random amount of time, will transport anything nearby into the Darkness Between Dimensions. Players in the Darkness Between Dimensions can be saved by divine grace, or with the item “Scranton’s Grappling Hook”. After a random amount of time, players will be returned to the board. They will come back… squishier.',
+ power: 0,
+ cost: 0
}];
// Initialize game board
function initBoard() {
gameBoard = [];
Healthbar. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Player. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Opponent. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
EnergyBar. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Board. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Anomaly. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Button. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
animal. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
SCP. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
SCP card. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
anomalyneutralizer card. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
containmentfield card. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
cardFront. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
barBackground. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
darknessbetweendimensions. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
manual. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
moneyCounterDisplay. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Wondertainment product. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
EndTurnButton. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
randomscp. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Attack. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Containment. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
White Card called “Panacea” which was used to heal. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
modified Colt AR-15. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
giant flaming angel's sword. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
hidden golden anomaly. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Orangutan. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows