User prompt
remove open tab button
User prompt
Clicking the game2 button will open this game https://upit.com/create/-jjol2nG8v/history
User prompt
Open TLeater scene when game2 button is clicked
User prompt
Save the current game on the game1 button and name it Merge TL Mania. When you click on the game1 button, the Merge TL Mania game will open.
User prompt
Code all boxes in the pop-up window as buttons
User prompt
Save the 9 boxes in the opened window to asset and code in order from game1 to 9
User prompt
delete shop2 and shop3 assets
User prompt
Delete assets from shop1 to 9
User prompt
Create 3x3 squares in this tab and create assets from game1 to 9 in the resulting squares.
User prompt
put opened tab on top of everything
User prompt
Set the resolution of the opened tab to 1820x1800
User prompt
Create a button under the restart button, when that button is pressed, a tab with a resolution of 1920x1900 will open.
User prompt
remove market
User prompt
Please fix the bug: 'Uncaught ReferenceError: shopPrices is not defined' in or related to this line: 'var price = shopPrices[shopIdx - 1];' Line Number: 107
User prompt
Delete everything in the market, leaving only the boxes
User prompt
Please fix the bug: 'LK.openScene is not a function' in or related to this line: 'LK.openScene('TLeater');' Line Number: 1063
User prompt
Please fix the bug: 'storage.saveScene is not a function' in or related to this line: 'storage.saveScene('MergeTL');' Line Number: 1063
User prompt
Please fix the bug: 'LK.saveScene is not a function' in or related to this line: 'LK.saveScene('MergeTL');' Line Number: 1063
User prompt
Save this scene as MergeTL and open a new scene, the name of that scene will be TLeater
User prompt
its not working
User prompt
When you click on shop2 after the avalible text, generalBg should be replaced with generalBg2, and gridBg should be replaced with gridBg2.
User prompt
When shop2 is purchased, generalBg should be replaced with generalBg2, and gridBg should be replaced with gridBg2. this command not work now pls control this
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Tile class var TLTile = Container.expand(function () { var self = Container.call(this); // Properties self.levelIdx = 0; // 0-5 self.gridX = 0; self.gridY = 0; self.asset = null; // Set tile level and update asset self.setLevel = function (levelIdx) { self.levelIdx = levelIdx; if (self.asset) { self.removeChild(self.asset); self.asset.destroy(); } var assetId = TL_LEVELS[levelIdx].asset; self.asset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); }; // Animate merge pop self.mergePop = function () { tween(self, { scaleX: 1.2, scaleY: 1.2 }, { duration: 80, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 80, easing: tween.easeIn }); } }); }; // Animate spawn self.spawnPop = function () { self.scaleX = self.scaleY = 0.2; tween(self, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.bounceOut }); }; // Animate move to (x, y) in px self.moveTo = function (x, y, onFinish) { tween(self, { x: x, y: y }, { duration: 120, easing: tween.cubicOut, onFinish: onFinish }); }; // Animate coin pop at this tile self.coinPop = function (amount) { var coin = LK.getAsset('coinpop', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y - 60 }); coin.alpha = 0.9; game.addChild(coin); var txt = new Text2('+' + amount, { size: 60, fill: '#fff700', font: "Alfa Slab One" }); txt.anchor.set(0.5, 0.5); txt.x = self.x; txt.y = self.y - 60; game.addChild(txt); tween(coin, { y: coin.y - 80, alpha: 0 }, { duration: 600, easing: tween.linear }); tween(txt, { y: txt.y - 80, alpha: 0 }, { duration: 600, easing: tween.linear, onFinish: function onFinish() { coin.destroy(); txt.destroy(); } }); }; // Set grid position (not pixel position) self.setGrid = function (gx, gy) { self.gridX = gx; self.gridY = gy; var pos = getTilePixelPos(gx, gy); self.x = pos.x; self.y = pos.y; }; // For drag highlight self.setHighlight = function (on) { if (on) { self.asset.alpha = 0.7; } else { self.asset.alpha = 1; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xf7f7f7 }); /**** * Game Code ****/ // --- General background image --- /* We use 6 TL tile images, one for each denomination. Asset IDs: 'tile5', 'tile10', 'tile20', 'tile50', 'tile100', 'tile200' Each tile is a square, 300x300 px. */ /* Coin pop effect */ // TL tile levels and their properties var generalBg = LK.getAsset('generalBg', { width: 2048, height: 2732, anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); generalBg.alpha = 1; game.addChild(generalBg); // --- Grid setup --- var TL_LEVELS = [{ value: 5, asset: 'tile5', donation: 5, color: 0xf7d13d // yellow }, { value: 10, asset: 'tile10', donation: 10, color: 0xf7a13d // orange }, { value: 20, asset: 'tile20', donation: 20, color: 0xf76d3d // red-orange }, { value: 50, asset: 'tile50', donation: 50, color: 0x3db8f7 // blue }, { value: 100, asset: 'tile100', donation: 100, color: 0x44de83 // green }, { value: 200, asset: 'tile200', donation: 200, color: 0xb8b031 // gold }]; // Helper: get next level index, or null if max function getNextLevelIndex(levelIdx) { if (levelIdx < TL_LEVELS.length - 1) return levelIdx + 1; return null; } var GRID_SIZE = 4; // 4x4 var TILE_SIZE = 320; // px, including margin var TILE_MARGIN = 20; // px var gridOriginX = Math.floor((2048 - (GRID_SIZE * TILE_SIZE + (GRID_SIZE - 1) * TILE_MARGIN)) / 2); var gridOriginY = 700; // 2D array of tiles (null or TLTile) // --- Grid background highlight --- // Use a dedicated grid background asset to avoid name conflict with 200 TL asset var gridAsset = LK.getAsset('gridBg', { width: GRID_SIZE * TILE_SIZE + (GRID_SIZE - 1) * TILE_MARGIN + 40, height: GRID_SIZE * TILE_SIZE + (GRID_SIZE - 1) * TILE_MARGIN + 40, anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: gridOriginY + (GRID_SIZE * TILE_SIZE + (GRID_SIZE - 1) * TILE_MARGIN) / 2 }); gridAsset.alpha = 1; game.addChild(gridAsset); var grid = []; for (var i = 0; i < GRID_SIZE; ++i) { grid[i] = []; for (var j = 0; j < GRID_SIZE; ++j) grid[i][j] = null; } // Helper: get pixel position for grid cell function getTilePixelPos(gx, gy) { return { x: gridOriginX + gx * (TILE_SIZE + TILE_MARGIN) + TILE_SIZE / 2, y: gridOriginY + gy * (TILE_SIZE + TILE_MARGIN) + TILE_SIZE / 2 }; } // --- Score and donation --- // --- Donation Frame --- var donationFrame = LK.getAsset('tile200', { width: 600, height: 120, anchorX: 0.5, anchorY: 0, x: 2048 / 2, y: 0 }); donationFrame.alpha = 0.18; LK.gui.top.addChild(donationFrame); // Use persistent storage for donation (money) var donation = storage.donation || 0; var donationTxt = new Text2('Sermaye: ' + donation + ' TL', { size: 90, fill: '#1a1a1a', font: "Alfa Slab One" }); donationTxt.anchor.set(0.5, 0); donationTxt.y = 20; LK.gui.top.addChild(donationTxt); // --- Draw a line for where we put the money --- // We'll use a very thin, wide box as a line, just below the grid var lineY = gridOriginY + GRID_SIZE * (TILE_SIZE + TILE_MARGIN) + 40; var line = LK.getAsset('box', { width: 1200, height: 12, color: 0x1a1a1a, anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: lineY }); line.alpha = 0.18; game.addChild(line); // --- Next tile preview --- var nextLevelIdx = 0; function randomLevelIdx() { // --- Level Bar UI --- // Draw a horizontal bar of all TL tile levels at the bottom of the game area var levelBarTiles = []; var levelBarLabels = []; var levelBarTileSize = 120; var levelBarSpacing = 32; var totalBarWidth = TL_LEVELS.length * levelBarTileSize + (TL_LEVELS.length - 1) * levelBarSpacing; var barStartX = Math.floor((2048 - totalBarWidth) / 2) + levelBarTileSize / 2; var levelBarY = 2732 - 320 - levelBarTileSize / 2; // Move up: 320px margin from bottom, centered on tile (2x more up) for (var i = 0; i < TL_LEVELS.length; ++i) { // Draw colored slot behind the tile var slot = LK.getAsset('coinpop', { width: levelBarTileSize + 24, height: levelBarTileSize + 24, color: TL_LEVELS[i].color, anchorX: 0.5, anchorY: 0.5, x: barStartX + i * (levelBarTileSize + levelBarSpacing), y: levelBarY }); slot.alpha = 0.32; game.addChild(slot); var tileAsset = LK.getAsset(TL_LEVELS[i].asset, { width: levelBarTileSize, height: levelBarTileSize, anchorX: 0.5, anchorY: 0.5, x: barStartX + i * (levelBarTileSize + levelBarSpacing), y: levelBarY }); tileAsset.alpha = 0.92; game.addChild(tileAsset); levelBarTiles.push(tileAsset); var label = new Text2(TL_LEVELS[i].value + " TL", { size: 38, fill: 0x1A1A1A, font: "Alfa Slab One" }); label.anchor.set(0.5, 0); label.x = tileAsset.x; label.y = tileAsset.y + levelBarTileSize / 2 + 6; game.addChild(label); levelBarLabels.push(label); } // Only 5TL, 10TL, 20TL as next tile (0,1,2) return Math.floor(Math.random() * 3); } nextLevelIdx = randomLevelIdx(); // Place next tile preview and label 2x further up (closer to Sermaye text) var nextTilePreviewY = donationTxt.y + donationTxt.height + 180; var nextTilePreview = LK.getAsset(TL_LEVELS[nextLevelIdx].asset, { anchorX: 0.5, anchorY: 0.0, x: 2048 / 2, // centered horizontally y: nextTilePreviewY // 2x further up below Sermaye text }); var nextTileTxt = new Text2('Next', { size: 60, fill: '#1a1a1a', font: "Alfa Slab One" }); nextTileTxt.anchor.set(0.5, 1); nextTileTxt.x = 2048 / 2; nextTileTxt.y = nextTilePreview.y - 10; // above the preview image, with a small gap game.addChild(nextTilePreview); game.addChild(nextTileTxt); // --- Restart Button --- // Use a custom asset for the restart button, top right, larger size (like pause button), margin 30 from top/right var restartBtn = LK.getAsset('restartBtn', { width: 360, height: 180, anchorX: 1, anchorY: 0, x: 2048 - 30, y: 30 }); restartBtn.alpha = 0.85; game.addChild(restartBtn); // Button interaction restartBtn.interactive = true; restartBtn.down = function (x, y, obj) { // Call LK reset (will restart the game) LK.showGameOver(); }; // --- Custom Tab Button --- // Place this button under the restart button, with a margin of 30px var tabBtnY = restartBtn.y + restartBtn.height + 30; var tabBtn = LK.getAsset('shop2', { width: 360, height: 120, anchorX: 1, anchorY: 0, x: 2048 - 30, y: tabBtnY }); tabBtn.alpha = 0.92; game.addChild(tabBtn); // Add a label to the button var tabBtnLabel = new Text2('Open Tab', { size: 54, fill: '#ffffff', font: "Alfa Slab One" }); tabBtnLabel.anchor.set(0.5, 0.5); tabBtnLabel.x = tabBtn.x - tabBtn.width / 2; tabBtnLabel.y = tabBtn.y + tabBtn.height / 2; game.addChild(tabBtnLabel); // Tab popup container (hidden by default) var tabPopup = new Container(); tabPopup.visible = false; tabPopup.width = 1920; tabPopup.height = 1900; // Center the popup in the game area tabPopup.x = (2048 - 1920) / 2; tabPopup.y = (2732 - 1900) / 2; // Add a background to the popup var tabBg = LK.getAsset('generalBg2', { width: 1920, height: 1900, anchorX: 0, anchorY: 0, x: 0, y: 0 }); tabBg.alpha = 0.98; tabPopup.addChild(tabBg); // Add a close button to the popup (top right of popup) var closeBtn = LK.getAsset('shop3', { width: 120, height: 120, anchorX: 1, anchorY: 0, x: 1920 - 20, y: 20 }); closeBtn.alpha = 0.95; closeBtn.interactive = true; closeBtn.down = function () { tabPopup.visible = false; isMarketOpen = false; }; tabPopup.addChild(closeBtn); // Add a label to the popup var tabPopupLabel = new Text2('Tab Content', { size: 90, fill: '#1a1a1a', font: "Alfa Slab One" }); tabPopupLabel.anchor.set(0.5, 0); tabPopupLabel.x = 1920 / 2; tabPopupLabel.y = 60; tabPopup.addChild(tabPopupLabel); // Add the popup to the game game.addChild(tabPopup); // Button interaction to open the tab tabBtn.interactive = true; tabBtn.down = function () { tabPopup.visible = true; isMarketOpen = true; }; // --- Helper: update next tile preview --- function updateNextTilePreview() { if (nextTilePreview) { nextTilePreview.destroy(); } if (nextTileTxt) { nextTileTxt.destroy(); } var nextTilePreviewY = donationTxt.y + donationTxt.height + 180; nextTilePreview = LK.getAsset(TL_LEVELS[nextLevelIdx].asset, { anchorX: 0.5, anchorY: 0.0, x: 2048 / 2, y: nextTilePreviewY }); nextTileTxt = new Text2('Next', { size: 60, fill: '#1a1a1a', font: "Alfa Slab One" }); nextTileTxt.anchor.set(0.5, 1); nextTileTxt.x = 2048 / 2; nextTileTxt.y = nextTilePreview.y - 10; game.addChild(nextTilePreview); game.addChild(nextTileTxt); } // --- Spawn a new tile at (gx,gy) with levelIdx --- function spawnTile(gx, gy, levelIdx) { var tile = new TLTile(); tile.setLevel(levelIdx); tile.setGrid(gx, gy); tile.spawnPop(); grid[gx][gy] = tile; game.addChild(tile); return tile; } // --- Find empty cells --- function getEmptyCells() { var arr = []; for (var i = 0; i < GRID_SIZE; ++i) { for (var j = 0; j < GRID_SIZE; ++j) { if (!grid[i][j]) arr.push({ gx: i, gy: j }); } } return arr; } // --- Merge logic --- // Returns true if a merge happened function tryMergeAt(gx, gy) { var tile = grid[gx][gy]; if (!tile) return false; var merged = false; // Find all adjacent (left, right, above) same-level tiles var mergeGroup = [{ gx: gx, gy: gy, tile: tile }]; // Left if (gx > 0 && grid[gx - 1][gy] && grid[gx - 1][gy].levelIdx === tile.levelIdx) { mergeGroup.push({ gx: gx - 1, gy: gy, tile: grid[gx - 1][gy] }); } // Right if (gx < GRID_SIZE - 1 && grid[gx + 1][gy] && grid[gx + 1][gy].levelIdx === tile.levelIdx) { mergeGroup.push({ gx: gx + 1, gy: gy, tile: grid[gx + 1][gy] }); } // Above if (gy > 0 && grid[gx][gy - 1] && grid[gx][gy - 1].levelIdx === tile.levelIdx) { mergeGroup.push({ gx: gx, gy: gy - 1, tile: grid[gx][gy - 1] }); } // Only merge if there are at least 2 of the same level (including self) if (mergeGroup.length > 1) { // Find the "main" tile to upgrade (choose the first in mergeGroup) var main = mergeGroup[0]; var nextIdx = getNextLevelIndex(tile.levelIdx); if (nextIdx === null) return false; // Already max, can't merge // Remove all tiles in mergeGroup from grid for (var i = 0; i < mergeGroup.length; ++i) { var g = mergeGroup[i]; grid[g.gx][g.gy] = null; } // Animate all but main tile to move to main, then destroy for (var i = 1; i < mergeGroup.length; ++i) { var g = mergeGroup[i]; g.tile.setHighlight(false); g.tile.moveTo(main.tile.x, main.tile.y, function (t) { return function () { t.destroy(); }; }(g.tile)); } main.tile.setHighlight(false); // Upgrade main tile main.tile.setLevel(nextIdx); main.tile.mergePop(); // Coin pop var donationAmt = TL_LEVELS[nextIdx].donation; main.tile.coinPop(donationAmt); donation += donationAmt; storage.donation = donation; donationTxt.setText('Sermaye: ' + donation + ' TL'); // Place upgraded tile in grid grid[main.gx][main.gy] = main.tile; // Check win: if merged to 200TL if (nextIdx === 5) { // Win if two 200TL tiles merged LK.showYouWin(); return true; } // After merge, try to merge again at main LK.setTimeout(function () { tryMergeAt(main.gx, main.gy); }, 180); merged = true; } return merged; } // --- Drag and drop logic --- var draggingTile = null; var dragOrigin = null; // {gx,gy} var dragOffset = { x: 0, y: 0 }; var dragValid = false; var dragTarget = null; // Helper: get grid cell from px function getGridCellFromPos(x, y) { for (var i = 0; i < GRID_SIZE; ++i) { for (var j = 0; j < GRID_SIZE; ++j) { var pos = getTilePixelPos(i, j); var dx = Math.abs(x - pos.x); var dy = Math.abs(y - pos.y); if (dx < TILE_SIZE / 2 && dy < TILE_SIZE / 2) { return { gx: i, gy: j }; } } } return null; } // --- Game events --- // Place next tile on tap/click // --- Track if market is open globally --- var isMarketOpen = false; game.down = function (x, y, obj) { if (isMarketOpen) return; // Don't allow placing in top 100px if (y < 100) return; // If dragging, ignore if (draggingTile) return; // Only allow placing if empty cell var cell = getGridCellFromPos(x, y); if (!cell) return; if (grid[cell.gx][cell.gy]) return; // Place tile var tile = spawnTile(cell.gx, cell.gy, nextLevelIdx); // Try merge LK.setTimeout(function () { tryMergeAt(cell.gx, cell.gy); // After merge, check if all slots are filled var allFilled = true; for (var i = 0; i < GRID_SIZE; ++i) { for (var j = 0; j < GRID_SIZE; ++j) { if (!grid[i][j]) { allFilled = false; break; } } if (!allFilled) break; } if (allFilled) { LK.showGameOver(); } }, 120); // Next tile nextLevelIdx = randomLevelIdx(); updateNextTilePreview(); }; // --- Drag to move tiles (optional, for more fun) --- game.move = function (x, y, obj) { if (isMarketOpen) return; // If not dragging, check if user is holding on a tile if (!draggingTile && obj && obj.event && obj.event.type === 'pointerdown') { var cell = getGridCellFromPos(x, y); if (!cell) return; var tile = grid[cell.gx][cell.gy]; if (!tile) return; draggingTile = tile; dragOrigin = { gx: cell.gx, gy: cell.gy }; dragOffset.x = tile.x - x; dragOffset.y = tile.y - y; tile.setHighlight(true); } // If dragging if (draggingTile) { draggingTile.x = x + dragOffset.x; draggingTile.y = y + dragOffset.y; // Highlight valid drop var cell = getGridCellFromPos(x, y); if (cell && (!grid[cell.gx][cell.gy] || cell.gx === dragOrigin.gx && cell.gy === dragOrigin.gy)) { dragValid = true; dragTarget = cell; draggingTile.setHighlight(true); } else { dragValid = false; dragTarget = null; draggingTile.setHighlight(false); } } }; game.up = function (x, y, obj) { if (isMarketOpen) return; if (draggingTile) { // Drop if (dragValid && dragTarget && (dragTarget.gx !== dragOrigin.gx || dragTarget.gy !== dragOrigin.gy)) { // Move tile grid[dragOrigin.gx][dragOrigin.gy] = null; grid[dragTarget.gx][dragTarget.gy] = draggingTile; draggingTile.setGrid(dragTarget.gx, dragTarget.gy); draggingTile.setHighlight(false); // Try merge LK.setTimeout(function () { tryMergeAt(dragTarget.gx, dragTarget.gy); }, 120); } else { // Snap back draggingTile.setGrid(dragOrigin.gx, dragOrigin.gy); draggingTile.setHighlight(false); } draggingTile = null; dragOrigin = null; dragValid = false; dragTarget = null; } }; // --- Game update (not used for logic, but required) --- game.update = function () { // No per-frame logic needed }; // --- Start: spawn 2 random tiles --- function startGame() { var empties = getEmptyCells(); for (var i = 0; i < 2; ++i) { if (empties.length === 0) break; var idx = Math.floor(Math.random() * empties.length); var cell = empties[idx]; var lvl = randomLevelIdx(); spawnTile(cell.gx, cell.gy, lvl); empties.splice(idx, 1); } } startGame(); ; // Save this scene as MergeTL and open a new scene named TLeater // Scene change to 'TLeater' would happen here if supported
===================================================================
--- original.js
+++ change.js
@@ -344,10 +344,84 @@
restartBtn.down = function (x, y, obj) {
// Call LK reset (will restart the game)
LK.showGameOver();
};
-// --- Market Button ---
-// (market removed)
+// --- Custom Tab Button ---
+// Place this button under the restart button, with a margin of 30px
+var tabBtnY = restartBtn.y + restartBtn.height + 30;
+var tabBtn = LK.getAsset('shop2', {
+ width: 360,
+ height: 120,
+ anchorX: 1,
+ anchorY: 0,
+ x: 2048 - 30,
+ y: tabBtnY
+});
+tabBtn.alpha = 0.92;
+game.addChild(tabBtn);
+// Add a label to the button
+var tabBtnLabel = new Text2('Open Tab', {
+ size: 54,
+ fill: '#ffffff',
+ font: "Alfa Slab One"
+});
+tabBtnLabel.anchor.set(0.5, 0.5);
+tabBtnLabel.x = tabBtn.x - tabBtn.width / 2;
+tabBtnLabel.y = tabBtn.y + tabBtn.height / 2;
+game.addChild(tabBtnLabel);
+// Tab popup container (hidden by default)
+var tabPopup = new Container();
+tabPopup.visible = false;
+tabPopup.width = 1920;
+tabPopup.height = 1900;
+// Center the popup in the game area
+tabPopup.x = (2048 - 1920) / 2;
+tabPopup.y = (2732 - 1900) / 2;
+// Add a background to the popup
+var tabBg = LK.getAsset('generalBg2', {
+ width: 1920,
+ height: 1900,
+ anchorX: 0,
+ anchorY: 0,
+ x: 0,
+ y: 0
+});
+tabBg.alpha = 0.98;
+tabPopup.addChild(tabBg);
+// Add a close button to the popup (top right of popup)
+var closeBtn = LK.getAsset('shop3', {
+ width: 120,
+ height: 120,
+ anchorX: 1,
+ anchorY: 0,
+ x: 1920 - 20,
+ y: 20
+});
+closeBtn.alpha = 0.95;
+closeBtn.interactive = true;
+closeBtn.down = function () {
+ tabPopup.visible = false;
+ isMarketOpen = false;
+};
+tabPopup.addChild(closeBtn);
+// Add a label to the popup
+var tabPopupLabel = new Text2('Tab Content', {
+ size: 90,
+ fill: '#1a1a1a',
+ font: "Alfa Slab One"
+});
+tabPopupLabel.anchor.set(0.5, 0);
+tabPopupLabel.x = 1920 / 2;
+tabPopupLabel.y = 60;
+tabPopup.addChild(tabPopupLabel);
+// Add the popup to the game
+game.addChild(tabPopup);
+// Button interaction to open the tab
+tabBtn.interactive = true;
+tabBtn.down = function () {
+ tabPopup.visible = true;
+ isMarketOpen = true;
+};
// --- Helper: update next tile preview ---
function updateNextTilePreview() {
if (nextTilePreview) {
nextTilePreview.destroy();
white restart button but no have text. In-Game asset. 2d. High contrast. No shadows
round money board. In-Game asset. 2d. High contrast. No shadows
black wallpaper,no text. In-Game asset. 2d. High contrast. No shadows
4x4 grid no emoji no text , dark,black,shadow. In-Game asset. 2d. High contrast. No shadows
360x120 market text black,shadow,white. In-Game asset. 2d. High contrast. No shadows