const Phaser = require('phaser'); const Item = require('./elements/item'); const { TEXT, POSITIONS: { ITEM_LIST }, } = require('./constants'); const X = ITEM_LIST.x(); const Y = ITEM_LIST.y(); const WIDTH = ITEM_LIST.width(); const HEIGHT = ITEM_LIST.height(); const ITEM_WIDTH = ITEM_LIST.itemWidth(); const ITEM_HEIGHT = ITEM_LIST.itemHeight(); const BOX_X = X + ITEM_WIDTH * 0.5; const BOX_Y = Y + ITEM_HEIGHT + ITEM_HEIGHT * 2; const BOX_ROWS = 6; const BOX_COLUMNS = 3; const INV_X = X + ITEM_WIDTH * 0.5; const INV_Y = Y + ITEM_HEIGHT * 12; const INV_ROWS = 3; const INV_COLUMNS = 3; const COMB_X = X + ITEM_WIDTH * 0.5; const COMB_Y = Y + ITEM_HEIGHT * 19; const COMB_ROWS = 1; const COMB_COLUMNS = 3; const drawVbox = (graphics) => { const boxDrawX = BOX_X - ITEM_WIDTH * 0.05; const boxDrawY = BOX_Y - ITEM_HEIGHT * 0.05; graphics.strokeRect(boxDrawX, boxDrawY, ITEM_WIDTH * 1.1 * BOX_COLUMNS, ITEM_HEIGHT * 1.1 * BOX_ROWS); for (let i = 0; i < (BOX_COLUMNS - 1); i += 1) { const x = boxDrawX + (i + 1) * ITEM_WIDTH * 1.1; graphics.lineBetween(x, boxDrawY, x, boxDrawY + ITEM_HEIGHT * 1.1 * BOX_ROWS); } for (let i = 0; i < (BOX_ROWS - 1); i += 1) { const y = boxDrawY + (i + 1) * ITEM_HEIGHT * 1.1; graphics.lineBetween(boxDrawX, y, boxDrawX + ITEM_WIDTH * 1.1 * BOX_COLUMNS, y); } }; const drawInventory = (graphics) => { const invDrawX = INV_X - ITEM_WIDTH * 0.05; const invDrawY = INV_Y - ITEM_HEIGHT * 0.05; graphics.strokeRect(invDrawX, invDrawY, ITEM_WIDTH * 1.1 * INV_COLUMNS, ITEM_HEIGHT * 1.1 * INV_ROWS); for (let i = 0; i < (INV_COLUMNS - 1); i += 1) { const x = invDrawX + (i + 1) * ITEM_WIDTH * 1.1; graphics.lineBetween(x, invDrawY, x, invDrawY + ITEM_HEIGHT * 1.1 * INV_ROWS); } for (let i = 0; i < (INV_ROWS - 1); i += 1) { const y = invDrawY + (i + 1) * ITEM_HEIGHT * 1.1; graphics.lineBetween(invDrawX, y, invDrawX + ITEM_WIDTH * 1.1 * INV_COLUMNS, y); } }; const drawCombiner = (graphics) => { const combDrawX = COMB_X - ITEM_WIDTH * 0.05; const combDrawY = COMB_Y - ITEM_HEIGHT * 0.05; graphics.strokeRect(combDrawX, combDrawY, ITEM_WIDTH * 1.1 * COMB_COLUMNS, ITEM_HEIGHT * 1.1 * COMB_ROWS); for (let i = 0; i < (COMB_COLUMNS - 1); i += 1) { const x = combDrawX + (i + 1) * ITEM_WIDTH * 1.1; graphics.lineBetween(x, combDrawY, x, combDrawY + ITEM_HEIGHT * 1.1 * COMB_ROWS); } for (let i = 0; i <= (COMB_ROWS - 1); i += 1) { const y = combDrawY + (i + 1) * ITEM_HEIGHT * 1.1; graphics.lineBetween(combDrawX, y, combDrawX + ITEM_WIDTH * 1.1 * COMB_COLUMNS, y); } }; class CombinerHitBox extends Phaser.GameObjects.Rectangle { constructor(scene, x, y, i) { super(scene, x, y, ITEM_WIDTH, ITEM_HEIGHT, 0x000000); this.setOrigin(0); this.x = x; this.y = y; this.slot = i; this.item = false; this.itemSelect = () => this.setFillStyle(0x222222); this.itemDeselect = () => this.setFillStyle(0x000000); } allocate(item) { if (this.item) this.deallocate(); item.setPosition(this.x + item.width / 2, this.y + item.height / 2); this.item = item; } deallocate() { if (this.item) this.item.setPosition(this.item.origX, this.item.origY); this.item = false; } } class DeleteHitBox extends Phaser.GameObjects.Rectangle { constructor(scene, x, y) { super(scene, x, y, ITEM_WIDTH * 3.4, ITEM_HEIGHT * 1.25, 0x444444); this.setOrigin(0); this.itemSelect = () => this.setFillStyle(0xff0000); this.itemDeselect = () => this.setFillStyle(0x444444); } } const itemCheckHitbox = (scene, pointer) => { const { list } = scene.scene.get('MenuCrypList').children; const hitboxes = list.filter(c => c.cryp) .concat(scene.children.list.filter(c => c instanceof CombinerHitBox || c instanceof DeleteHitBox)); let found; for (let i = 0; i < hitboxes.length; i += 1) { if (Phaser.Geom.Rectangle.ContainsPoint(hitboxes[i].getBounds(), pointer.position)) { found = hitboxes[i]; } else { hitboxes[i].itemDeselect(); } } return found; }; class ItemList extends Phaser.Scene { constructor() { super({ key: 'ItemList', active: true }); } updateData(parent, key, data) { if (key === 'player' || key === 'scores') { this.registry.events.off('changedata', this.updateData, this); this.registry.events.off('setdata', this.updateData, this); this.scene.restart(); } } create() { const player = this.registry.get('player'); const scores = this.registry.get('scores') || []; if (!player) return false; const { vbox } = player; this.registry.events.on('changedata', this.updateData, this); this.registry.events.on('setdata', this.updateData, this); if (!vbox.bound) return false; if (!this.combinerItems || vbox.bound.length < this.registry.get('boundLength')) { this.combinerItems = [-1, -1, -1]; } this.registry.set('boundLength', vbox.bound.length); const ws = this.registry.get('ws'); // Static Elements const graphics = this.add.graphics(); graphics.lineStyle(5, 0x808080, 1.0); drawCombiner(graphics); drawInventory(graphics); drawVbox(graphics); this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 0.5, `vBox - ${vbox.bits}b`, TEXT.HEADER); this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 11, 'inventory', TEXT.HEADER); this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 18, 'iCombinator', TEXT.HEADER); this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 23, `Wins: ${player.score.wins}`, TEXT.HEADER); this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 24, `Losses: ${player.score.losses}`, TEXT.HEADER); const discard = this.add .rectangle(X + ITEM_WIDTH * 0.4, Y + ITEM_HEIGHT * 1.5, ITEM_WIDTH * 3.4, ITEM_HEIGHT * 1.25, 0x444444) .setInteractive() .setOrigin(0) .on('pointerdown', () => this.registry.get('ws').sendVboxDiscard(vbox.instance)); this.add.text(discard.getCenter().x, discard.getCenter().y, 'discard - 5b', TEXT.HEADER) .setOrigin(0.5, 0.5); const combine = this.add .rectangle(X + ITEM_WIDTH * 0.4, Y + ITEM_HEIGHT * 20.25, ITEM_WIDTH * 3.4, ITEM_HEIGHT * 1.25, 0x444444) .setInteractive() .setOrigin(0) .on('pointerdown', () => { ws.sendVboxCombine(vbox.instance, this.combinerItems); this.combinerItems = [-1, -1, -1]; this.children.list.filter(obj => obj instanceof CombinerHitBox).forEach(cBox => cBox.deallocate()); }); this.add.text(combine.getCenter().x, combine.getCenter().y, 'combine', TEXT.HEADER) .setOrigin(0.5, 0.5); for (let i = 0; i < 3; i += 1) { const ITEM_X = ITEM_WIDTH * 1.1 * (i % COMB_COLUMNS) + COMB_X; const ITEM_Y = ITEM_HEIGHT * 1.1 * Math.floor(i / COMB_COLUMNS) + COMB_Y; this.add.existing(new CombinerHitBox(this, ITEM_X, ITEM_Y, i)); } const del = this.add.existing(new DeleteHitBox(this, X + ITEM_WIDTH * 0.4, Y + ITEM_HEIGHT * 15.5)); this.add.text(del.getCenter().x, del.getCenter().y, 'drop', TEXT.HEADER) .setOrigin(0.5, 0.5); // Generate Items vbox.bound.forEach((item, i) => { const ITEM_X = ITEM_WIDTH * 1.1 * (i % INV_COLUMNS) + INV_X + ITEM_WIDTH * 0.5; const ITEM_Y = ITEM_HEIGHT * 1.1 * Math.floor(i / INV_COLUMNS) + INV_Y + ITEM_HEIGHT * 0.5; const clickFn = () => this.registry.set('itemInfo', { item }); const itemBox = new Item(this, item, i, ITEM_X, ITEM_Y, ITEM_WIDTH, ITEM_HEIGHT); itemBox.on('pointerdown', clickFn); this.input.setDraggable(itemBox); this.add.existing(itemBox); }); vbox.free.forEach((type, i) => { type.forEach((item, j) => { const ITEM_X = ITEM_WIDTH * 1.1 * i + BOX_X + ITEM_WIDTH * 0.5; const ITEM_Y = ITEM_HEIGHT * 1.1 * j + BOX_Y + ITEM_HEIGHT * 0.5; const clickFn = () => { this.registry.set('itemInfo', { item }); ws.sendVboxAccept(vbox.instance, i, j); }; const itemBox = new Item(this, item, i, ITEM_X, ITEM_Y, ITEM_WIDTH, ITEM_HEIGHT); itemBox.on('pointerdown', clickFn); this.add.existing(itemBox); }); }); // Restore previous combiner item slots this.combinerItems.forEach((index, i) => { if (index === -1) return false; const item = this.children.list.filter(obj => obj instanceof Item).find(it => it.index === index); const hitBox = this.children.list.filter(obj => obj instanceof CombinerHitBox).find(hb => hb.slot === i); if (item) hitBox.allocate(item); return true; }); // allocation functions const allocate = (item, hitBox) => { hitBox.allocate(item); this.combinerItems[hitBox.slot] = item.index; this.registry.set('combinerItems', this.combinerItems); }; const deallocate = (item) => { const clearIndex = this.combinerItems.indexOf(item.index); if (clearIndex !== -1) { this.children.list.filter(obj => obj instanceof CombinerHitBox) .forEach((cBox) => { if (cBox.item === item) cBox.deallocate(); }); this.combinerItems[clearIndex] = -1; this.registry.set('combinerItems', this.combinerItems); } item.setPosition(item.origX, item.origY); }; const findUnallocated = () => { for (let i = 0; i <= 2; i += 1) { if (this.combinerItems[i] === -1) { return this.children.list.filter(obj => obj instanceof CombinerHitBox).find(hb => hb.slot === i); } } return false; }; // this.add.text(ITEM_WIDTH * 11, ITEM_HEIGHT * 1.1, 'Scoreboard', TEXT.HEADER); // scores.forEach(([name, score], i) => { // const SCORE_X = ITEM_WIDTH * 11; // const SCORE_Y = ITEM_HEIGHT * 1.1 * (i + 2); // this.add.text(SCORE_X, SCORE_Y, `${score.wins} - ${score.losses} | ${name}`, TEXT.NORMAL); // }); // Add Handlers this.input.on('dragstart', (pointer, item) => { if (!(item instanceof Item)) return false; return true; }); this.input.on('drag', (pointer, item, dragX, dragY) => { if (!(item instanceof Item)) return false; item.setPosition(dragX, dragY); const hitBox = itemCheckHitbox(this, pointer); if (hitBox) hitBox.itemSelect(); return true; }); this.input.on('dragend', (pointer, item) => { if (!(item instanceof Item)) return false; // Check first for hitbox interaction const hitBox = itemCheckHitbox(this, pointer); if (hitBox) { // hitbox can only be the combinerhitbox, deletehitbox or cryp avatar hitBox.itemDeselect(); if (hitBox instanceof CombinerHitBox) { if (hitBox.item === item) deallocate(item); else allocate(item, hitBox); } else if (hitBox instanceof DeleteHitBox) { ws.sendVboxReclaim(vbox.instance, item.index); } else { ws.sendVboxApply(vbox.instance, hitBox.cryp.id, item.index); deallocate(item); } return true; } // If not interacting with hitbox and didn't move much try to allocate the item if (Math.hypot(item.x - item.origX, item.y - item.origY) < Math.hypot(item.width, item.height)) { // Check theres a free combiner slot const cBox = findUnallocated(); if (cBox) { allocate(item, cBox); return true; } } // If the item hasn't been allocated above reset to natural location // Check if item needs to be deallocated // Scene will restart if there is vbox change deallocate(item); return true; }); return this; } cleanUp() { this.registry.events.off('changedata', this.updateData, this); this.registry.events.off('setdata', this.updateData, this); this.scene.remove(); } } module.exports = ItemList;