node-haxball
Version:
The most powerful and lightweight API that allows you to develop your original Haxball(www.haxball.com) host, client, and standalone applications both on node.js and browser environments and also includes every possible hack and functionality that you can
393 lines (370 loc) • 12.6 kB
JavaScript
module.exports = function(API){
const { Library, VariableType, Utils } = API;
Object.setPrototypeOf(this, Library.prototype);
Library.call(this, "bricks", {
version: 0.1,
author: "abc & JerryOldson",
description: "A library to manage brick structures that can be used in games like tetris, bricks classic, etc.",
});
this.defineVariable({
name: "gridSize",
description: "The interval value in milliseconds",
type: VariableType.Integer,
value: 4,
range: {
min: 3,
max: 5,
step: 1
}
});
this.defineVariable({
name: "bricks",
description: "Click to edit bricks",
type: VariableType.Void,
value: ()=>{
const gui = that.room.librariesMap.gui;
if (!gui)
return;
const headHtml = `
<title>Bricker Editor</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/nano.min.css"/>
<style>
.container {
width: 20em;
display: flex;
flex-direction: column;
}
.brickContainer {
display: grid;
grid-template: auto / ${Array(that.gridSize).fill("1fr").join(" ")};
margin: 10px 0;
}
.brickPiece {
aspect-ratio: 1 / 1;
height: auto;
border: 1px solid
border-radius: 3px;
background-color: white;
cursor: pointer;
}
.brickControls {
margin-top: 10px;
display: flex;
gap: 8px;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
width: 20em;
}
.brickControls > div > button {
line-height: 20px;
}
.brickControls > div > span {
font-size: 1.5em;
vertical-align: middle;
}
.tooltip {
position: relative;
display: inline-block;
}
.tooltip .tooltiptext {
visibility: hidden;
background-color: black;
color:
text-align: center;
border-radius: 6px;
padding: 5px 6px;
position: absolute;
z-index: 1;
top: 120%;
left: 50%;
transform: translateX(-50%);
white-space: nowrap;
}
.tooltip:hover .tooltiptext {
visibility: visible;
}
.b1 {
width: 2.4em;
}
.b2 {
width: 3.2em;
}
</style>
`;
const bodyHtml = `
<div class="container">
<div class="brickControls">
<div style="display: flex">
<div class="tooltip" style="display: flex">
<input type="checkbox" id="active">
<span class="tooltiptext">Whether the brick is active</span>
</div>
<div class="pickrEl"></div>
</div>
<div>
<button class="tooltip b1" id="prevBrick">◀
<span class="tooltiptext">Previous brick</span>
</button>
<span id="brickInfo"></span>
<button class="tooltip b1" id="nextBrick">▶
<span class="tooltiptext">Next brick</span>
</button>
</div>
<div>
<button class="tooltip b2" id="addBrick">➕
<span class="tooltiptext">Add brick</span>
</button>
<button class="tooltip b2" id="removeBrick">🗑
<span class="tooltiptext">Remove brick</span>
</button>
</div>
</div>
<div id="brickContainer" class="brickContainer"></div>
</div>
`;
const script = `
let brickIndex = 0;
const pickr = new Pickr({
el: '.pickrEl',
theme: 'nano',
components: {
preview: true,
hue: true,
interaction: {
hex: true,
rgba: false,
input: true,
clear: false,
save: true
}
}
});
(function addTooltipToColorPicker(){
const pickerEl = document.querySelector('.pickr');
pickerEl.classList.add("tooltip");
const spanEl = document.createElement('span');
spanEl.className = 'tooltiptext';
spanEl.innerText = 'Color of the brick';
pickerEl.appendChild(spanEl);
})();
let rendererChangingPickr = false;
pickr.on('change', (color, evt, instnace) => {
if (rendererChangingPickr) return;
const rgbaArray = color.toRGBA();
const rgbaString = 'rgb('+rgbaArray[0].toFixed(0)+','+rgbaArray[1].toFixed(0)+','+rgbaArray[2].toFixed(0)+')';
bricks[brickIndex].color = colorToNumber(rgbaString);
pickr.setHSVA(color.h, color.s, color.v, color.a);
renderBrick();
});
pickr.on('init', () => {
renderBrick();
});
const active = document.getElementById('active');
active.onchange = ()=>{
const brick = bricks[brickIndex];
brick.active = active.checked;
};
function renderBrick() {
const brick = bricks[brickIndex];
const brickContainer = document.querySelector('.brickContainer');
active.checked = brick.active;
brickContainer.innerHTML = '';
const color = numberToColor(brick.color);
rendererChangingPickr = true;
pickr.setColor(brick.color.toString(16));
rendererChangingPickr = false;
document.querySelector('#brickInfo').innerText = brickIndex+1;
brick.grid.forEach((pieces, row) => {
for (let col = 0; col < pieces.length; col++) {
const piece = pieces[col]
const pieceEl = document.createElement('div');
pieceEl.className = 'brickPiece';
if (piece == 1) {
pieceEl.style.backgroundColor = color;
} else {
pieceEl.style.backgroundColor = 'white';
}
pieceEl.addEventListener('click', () => {
if (pieceEl.style.backgroundColor === 'white') {
pieceEl.style.backgroundColor = color;
brick.grid[row][col] = 1;
} else {
pieceEl.style.backgroundColor = 'white';
brick.grid[row][col] = 0;
}
brick.init();
})
brickContainer.appendChild(pieceEl);
}
});
};
document.querySelector('#prevBrick').addEventListener('click', () => {
if (brickIndex == 0) {
brickIndex = bricks.length-1;
renderBrick();
return;
}
brickIndex--;
renderBrick();
});
document.querySelector('#nextBrick').addEventListener('click', () => {
if (brickIndex == bricks.length-1) {
brickIndex = 0;
renderBrick();
return;
}
brickIndex++;
renderBrick();
});
document.querySelector('#addBrick').addEventListener('click', () => {
const newBrick = getBlankBrick();
bricks.push(newBrick);
brickIndex = bricks.length-1;
renderBrick();
});
document.querySelector('#removeBrick').addEventListener('click', () => {
bricks.splice(brickIndex, 1);
if (bricks.length === 0) {
brickContainer.innerHTML = "<p>No bricks left</p>";
return;
}
brickIndex = Math.max(0, brickIndex - 1);
renderBrick();
});
`;
var win = gui.newWindowFromContent(headHtml, bodyHtml, 200, 200);
win.bricks = array;
win.numberToColor = Utils.numberToColor;
win.colorToNumber = Utils.colorToNumber;
win.getBlankBrick = ()=>new Brick([[0, 0, 0, 0],
[],
[],
[]], Utils.colorToNumber("rgb(230,230,0)"), false);
gui.addScriptFromURLToWindow(win, "https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/pickr.min.js", () => {
gui.addScriptFromContentToWindow(win, script)
});
}
});
var that = this;
function calculateSize(grid){
var maxx = -1, maxy = -1;
for (var i=0;i<that.gridSize;i++)
for (var j=0;j<that.gridSize;j++)
if (grid[i][j]){
if (i>maxy)
maxy=i;
if (j>maxx)
maxx=j;
}
grid.width = maxx+1;
grid.height = maxy+1;
}
function Brick(grid, color, active=true){
this.active = active;
this.grid = grid;
this.color = color;
this.rotations = null;
this.init();
}
Brick.prototype = {
init: function(){
this.rotations = [this.grid];
calculateSize(this.grid);
var oldGrid = this.grid;
for (var r=0;r<3;r++){
var newGrid = [], minx = that.gridSize, miny = that.gridSize;
for (var i=0;i<that.gridSize;i++){
var a = [];
for (var j=0;j<that.gridSize;j++){
a[j] = oldGrid[j][that.gridSize-i-1];
if (a[j]){
if (j<minx)
minx=j;
if (i<miny)
miny=i;
}
}
newGrid.push(a);
}
if (minx>0 || miny>0)
for (var i=0;i<that.gridSize;i++)
for (var j=0;j<that.gridSize;j++)
newGrid[i][j] = ((i+miny)>=that.gridSize||(j+minx)>=that.gridSize)?0:newGrid[i+miny][j+minx];
if (this.rotations.find((rotation)=>{
for (var i=0;i<that.gridSize;i++)
for (var j=0;j<that.gridSize;j++)
if (rotation[i][j]!=newGrid[i][j])
return false;
return true;
}))
break;
calculateSize(newGrid);
this.rotations.push(newGrid);
oldGrid = newGrid;
}
}
};
var array = [
new Brick([[1, 0, 0, 0],
[],
[],
[]], Utils.colorToNumber("rgb(230,230,0)"), true),
new Brick([[1, 1, 0, 0],
[],
[],
[]], Utils.colorToNumber("rgb(58,122,196)"), true),
new Brick([[1, 1, 0, 0],
[],
[],
[]], Utils.colorToNumber("rgb(255,50,50)"), true),
new Brick([[0, 1, 1, 0],
[],
[],
[]], Utils.colorToNumber("rgb(255,50,50)"), true),
new Brick([[1, 0, 0, 0],
[],
[],
[]], Utils.colorToNumber("rgb(50,170,50)"), true),
new Brick([[0, 1, 0, 0],
[],
[],
[]], Utils.colorToNumber("rgb(50,170,50)"), true),
new Brick([[1, 0, 0, 0],
[],
[],
[]], Utils.colorToNumber("rgb(255,50,50)"), true),
new Brick([[1, 0, 0, 0],
[],
[],
[]], Utils.colorToNumber("rgb(255,125,0)"), true),
new Brick([[1, 0, 0, 0],
[],
[],
[]], Utils.colorToNumber("rgb(200,50,200)"), true),
new Brick([[1, 1, 0, 0],
[],
[],
[]], Utils.colorToNumber("rgb(100,100,255)"), true),
new Brick([[1, 0, 0, 0],
[],
[],
[]], Utils.colorToNumber("rgb(255,50,50)"), true),
new Brick([[1, 1, 1, 0],
[],
[],
[]], Utils.colorToNumber("rgb(50,170,50)"), true),
new Brick([[1, 0, 1, 0],
[],
[],
[]], Utils.colorToNumber("rgb(50,170,50)"), true),
new Brick([[1, 1, 1, 0],
[],
[],
[]], Utils.colorToNumber("rgb(255,50,50)"), true),
];
Object.assign(this, {
Brick,
array
});
};