diff --git a/client/src/events.js b/client/src/events.js index a7a921bf..a04ed554 100644 --- a/client/src/events.js +++ b/client/src/events.js @@ -28,8 +28,8 @@ function registerEvents(registry, events, tutorial) { registry.set('activeSkill', skill); } - function setItems(items) { - registry.set('itemList', items); + function setVbox(items) { + registry.set('vbox', items); } function setZone(zone) { @@ -177,7 +177,7 @@ function registerEvents(registry, events, tutorial) { setActiveSkill, setCryps, setGame, - setItems, + setVbox, setWs, setGameList, setZone, diff --git a/client/src/scenes/combat.cryps.js b/client/src/scenes/combat.cryps.js index efd75247..550d0bcf 100644 --- a/client/src/scenes/combat.cryps.js +++ b/client/src/scenes/combat.cryps.js @@ -85,9 +85,9 @@ class CrypImage extends Phaser.GameObjects.Image { scene.add.text(nameX, nameY, cryp.name, TEXT.NORMAL).setOrigin(team, 0); // Add cryp stat bars this.health = scene.add.existing(new StatBar(scene, this, 'HP')); - this.armour = scene.add.existing(new StatBar(scene, this, 'Armour')); + this.red_shield = scene.add.existing(new StatBar(scene, this, 'Red Shield')); + this.blue_shield = scene.add.existing(new StatBar(scene, this, 'Blue Shield')); this.evasion = scene.add.existing(new StatBar(scene, this, 'Evasion')); - this.spellShield = scene.add.existing(new StatBar(scene, this, 'Spell Shield')); this.effects = scene.add.existing(new Effects(scene, team, iter)); this.statusText = scene.add.text(statusX, statusY, '', TEXT.NORMAL); @@ -116,9 +116,9 @@ class CrypImage extends Phaser.GameObjects.Image { reduceDefense(amount, type) { if (type === 'PhysDmg') { - this.armour.takeDamage(amount); - } else if (type === 'SpellDmg') { - this.spellShield.takeDamage(amount); + this.red_shield.takeDamage(amount); + } else if (type === 'BlueDmg') { + this.blue_shield.takeDamage(amount); } } diff --git a/client/src/scenes/constants.js b/client/src/scenes/constants.js index 9311de52..5d11ba70 100644 --- a/client/src/scenes/constants.js +++ b/client/src/scenes/constants.js @@ -17,8 +17,8 @@ const itemListWidth = () => Math.floor(CANVAS_WIDTH * 0.5); const itemListHeight = () => Math.floor(CANVAS_HEIGHT * 0.5); const itemListX = () => 0; const itemListY = () => Math.floor(CANVAS_HEIGHT * 0.5); -const itemBlockWidth = () => Math.floor(itemListWidth() * 0.1); -const itemBlockHeight = () => Math.floor(itemListHeight() * 0.16); +const itemBlockWidth = () => Math.floor(itemListWidth() * 0.075); +const itemBlockHeight = () => Math.floor(itemListHeight() * 0.12); const menuNavigationWidth = () => Math.floor(CANVAS_WIDTH * 0.5); const menuNavigationHeight = () => Math.floor(CANVAS_HEIGHT * 0.3); @@ -162,31 +162,31 @@ module.exports = { [ { name: 'Amplify', description: 'increase the magic damage dealt by a cryp' }, { name: 'Attack', - description: 'a fast physical attack with phys dmg' }, + description: 'a fast attack with red damage' }, { name: 'Banish', description: 'target cryp is prevented from casting any skills and taking any damage' }, { name: 'Blast', description: 'blast the target with magic damage' }, { name: 'Block', - description: 'decreases incoming physical damage for 1T' }, + description: 'decreases incoming red damage for 1T' }, { name: 'Curse', description: 'target cryp takes increased magic damage' }, { name: 'Decay', description: - 'afflict a cryp with a spell damage based damage over time debuff' }, - { name: 'Drain', - description: 'drain hp from target cryp with a spell damage based debuff' }, + 'afflict a cryp with a blue damage based damage over time debuff' }, + { name: 'Siphon', + description: 'siphon hp from target cryp with a blue damage based debuff' }, { name: 'Empower', - description: 'increase the physical damage dealt by a cryp' }, + description: 'increase the red damage dealt by a cryp' }, { name: 'Haste', description: 'magical skill that increases speed of target cryp' }, - { name: 'Heal', description: 'heal a cryp with spell dmg' }, + { name: 'Heal', description: 'heal a cryp with blue damage' }, { name: 'Hex', description: 'magical based skill that prevents target cryp from using any skills' }, { name: 'Parry', - description: 'prevents all physical damage for 1T' }, + description: 'prevents all red damage for 1T' }, { name: 'Purge', description: 'remove magical buffs from target cryp' }, { name: 'Purify', @@ -198,14 +198,14 @@ module.exports = { { name: 'Slow', description: 'magical skill that reduces speed of target cryp' }, { name: 'Snare', - description: 'prevents physical skills from being used for 2T' }, + description: 'prevents red skills from being used for 2T' }, { name: 'Stun', description: - 'physical skill that prevents target cryp from using any skills' }, + 'red skill that prevents target cryp from using any skills' }, { name: 'Throw', - description: 'stuns and makes the target take increased physical damage' }, + description: 'stuns and makes the target take increased red damage' }, { name: 'Triage', - description: 'grants a spell dmg based healing over time buff' } + description: 'grants a blue damage based healing over time buff' } ] }, }; diff --git a/client/src/scenes/cryps.js b/client/src/scenes/cryps.js index 35596c5e..b95f6bbb 100644 --- a/client/src/scenes/cryps.js +++ b/client/src/scenes/cryps.js @@ -29,7 +29,7 @@ function renderCryps() { }, }, scene: [ - Background, + // Background, Header, ], }; diff --git a/client/src/scenes/elements/combat.statbar.js b/client/src/scenes/elements/combat.statbar.js index 8b6e234f..9ada0330 100644 --- a/client/src/scenes/elements/combat.statbar.js +++ b/client/src/scenes/elements/combat.statbar.js @@ -24,22 +24,23 @@ class StatBar extends Phaser.GameObjects.Graphics { super(scene); this.crypObj = cryp; this.type = type; + console.log(type); if (type === 'HP') { this.val = this.crypObj.cryp.hp.base; - this.max = this.crypObj.cryp.stamina.base; + this.max = this.crypObj.cryp.hp.base; this.margin = 0; - } else if (type === 'Armour') { - this.val = this.crypObj.cryp.armour.base; - this.max = this.crypObj.cryp.armour.base; + } else if (type === 'Red Shield') { + this.val = this.crypObj.cryp.red_shield.base; + this.max = this.crypObj.cryp.red_shield.base; this.margin = 1; + } else if (type === 'Blue Shield') { + this.val = this.crypObj.cryp.blue_shield.base; + this.max = this.crypObj.cryp.blue_shield.base; + this.margin = 2; } else if (type === 'Evasion') { this.val = this.crypObj.cryp.evasion.base; this.max = this.crypObj.cryp.evasion.base; - this.margin = 2; - } else if (type === 'Spell Shield') { - this.val = this.crypObj.cryp.spell_shield.base; - this.max = this.crypObj.cryp.spell_shield.base; this.margin = 3; } const { statTextX, statTextY } = statTextCoord(cryp.team, cryp.iter, this.margin); diff --git a/client/src/scenes/elements/item.js b/client/src/scenes/elements/item.js index 9febb941..a8245571 100644 --- a/client/src/scenes/elements/item.js +++ b/client/src/scenes/elements/item.js @@ -5,23 +5,36 @@ const { COLOURS, } = require('../constants'); +function FindColour(item) { + // Future add skills and use a constants lookup file ?? + switch (item) { + case 'Green': return 0x61B329; + case 'Red': return 0xCC3333; + case 'Blue': return 0x6633FF; + default: return 0x222222; + } +} + class Item extends Phaser.GameObjects.Container { - constructor(scene, action, count, x, y, width, height) { + constructor(scene, item, index, x, y, width, height) { super(scene, x, y); this.state = 'deselect'; - this.action = action; this.scene = scene; - + this.item = item; + this.index = index; this.origX = x; this.origY = y; + this.width = width; + this.height = height; + this.colour = FindColour(item); this.box = scene.add - .rectangle(0, 0, width, height, 0x222222); + .rectangle(0, 0, width, height, this.colour); this.text = scene.add // .text(0, 0, `${action} x${count}`, TEXT.NORMAL) - .text(0, 0, `x${count}`, TEXT.NORMAL) + .text(0, 0, `${item}`, TEXT.NORMAL) .setOrigin(0.5, 0.5); this.add(this.box); @@ -31,10 +44,15 @@ class Item extends Phaser.GameObjects.Container { this.setInteractive(); } + changeOrigin(x, y) { + this.origX = x + this.width / 2; + this.origY = y + this.height / 2; + } + clickHandler() { this.scene.activeItem = this; // Set the main context to display the item info - this.scene.registry.set('itemInfo', this.action); + this.scene.registry.set('itemInfo', this.item); this.select(); } @@ -42,7 +60,7 @@ class Item extends Phaser.GameObjects.Container { this.scene.children.list.forEach((item) => { if (item.state === 'select') item.deselect(); }); - this.box.setFillStyle(COLOURS.SELECT); + // this.box.setFillStyle(COLOURS.SELECT); this.state = 'select'; } @@ -50,12 +68,12 @@ class Item extends Phaser.GameObjects.Container { this.scene.children.list.forEach((item) => { if (item.state === 'select') item.deselect(); }); - this.box.setFillStyle(0xff0000); + this.box.setFillStyle(0x222222); this.state = 'activate'; } deselect() { - this.box.setFillStyle(0x222222); + this.box.setFillStyle(this.colour); this.state = 'deselect'; } } diff --git a/client/src/scenes/item.list.js b/client/src/scenes/item.list.js index 2b4ee19a..1aafda61 100644 --- a/client/src/scenes/item.list.js +++ b/client/src/scenes/item.list.js @@ -16,15 +16,34 @@ const ITEM_WIDTH = ITEM_LIST.itemWidth(); const ITEM_HEIGHT = ITEM_LIST.itemHeight(); const INV_X = X + ITEM_WIDTH * 0.325; -const INV_Y = Y + ITEM_HEIGHT * 1.5; +const INV_Y = Y + ITEM_HEIGHT; const INV_ROWS = 3; -const INV_COLUMNS = 5; +const INV_COLUMNS = 3; -const COMB_X = INV_X + ITEM_WIDTH * 6.75; +const COMB_X = INV_X + ITEM_WIDTH * 5.75; const COMB_Y = INV_Y; const COMB_ROWS = 2; const COMB_COLUMNS = 2; +const BOX_X = X + ITEM_WIDTH * 2; +const BOX_Y = Y + ITEM_HEIGHT * 5; +const BOX_ROWS = 3; +const BOX_COLUMNS = 6; + + +const drawVbox = (graphics) => { + const boxDrawX = BOX_X - ITEM_WIDTH * 0.05; + const boxDrawY = BOX_Y - ITEM_HEIGHT * 0.05; + for (let i = 0; i <= BOX_COLUMNS; i += 1) { + const x = boxDrawX + i * ITEM_WIDTH * 1.1; + graphics.lineBetween(x, boxDrawY, x, boxDrawY + ITEM_HEIGHT * 1.1 * BOX_ROWS); + } + for (let i = 0; i <= BOX_ROWS; i += 1) { + const y = boxDrawY + i * 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; @@ -55,16 +74,40 @@ 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 * 1.25, ITEM_HEIGHT * 1.25, 0x222222); + this.setOrigin(0); + this.itemSelect = () => this.setFillStyle(0xff0000); + this.itemDeselect = () => this.setFillStyle(0x222222); + } +} + + 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)); + .concat(scene.children.list.filter(c => c instanceof CombinerHitBox || c instanceof DeleteHitBox)); let found; for (let i = 0; i < hitboxes.length; i += 1) { @@ -84,77 +127,165 @@ class ItemList extends Phaser.Scene { } updateData(parent, key, data) { - if (key === 'itemList') { + if (key === 'vbox') { this.registry.events.off('changedata', this.updateData, this); this.registry.events.off('setdata', this.updateData, this); this.scene.restart(data); } } - create(itemList) { - if (!itemList) return false; + create(vbox) { this.registry.events.on('changedata', this.updateData, this); this.registry.events.on('setdata', this.updateData, this); - this.addStatic(); - this.addItems(itemList); - this.addClickHandlers(itemList); - return this; - } + if (!vbox.bound) return false; + this.combinerItems = this.registry.get('combinerItems'); + 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'); - addStatic() { + // Static Elements const graphics = this.add.graphics(); graphics.lineStyle(5, 0x808080, 1.0); drawCombiner(graphics); drawInventory(graphics); - this.add.text(X + WIDTH / 4, Y + HEIGHT / 8, 'Inventory', TEXT.HEADER); - this.add.text(X + WIDTH * 3 / 4, Y + HEIGHT / 8, 'Combiner', TEXT.HEADER); - } + drawVbox(graphics); - addItems(itemList) { - const actions = countBy(itemList, i => i.action); + this.add.text(X + WIDTH * 0.1, Y, 'Inventory', TEXT.HEADER); + this.add.text(X + WIDTH * 0.47, Y, 'Combiner', TEXT.HEADER); + this.add.text(X + WIDTH * 0.35, Y + HEIGHT / 2, 'Varibox', TEXT.HEADER); - for (let i = 0; i < (COMB_COLUMNS * COMB_ROWS); i += 1) { - const ITEM_X = ITEM_WIDTH * 1.1 * (i % COMB_COLUMNS) + INV_X + ITEM_WIDTH * 6.75; - const ITEM_Y = ITEM_HEIGHT * 1.1 * Math.floor(i / COMB_COLUMNS) + INV_Y; + const reroll = this.add + .rectangle(WIDTH * 0.01, Y + HEIGHT * 0.775, ITEM_WIDTH * 1.25, ITEM_HEIGHT * 1.25, 0x222222) + .setInteractive() + .setOrigin(0) + .on('pointerdown', () => this.registry.get('ws').sendVboxDiscard(vbox.game)); + this.add.text(reroll.getCenter().x, reroll.getCenter().y, 'Reroll', TEXT.HEADER) + .setOrigin(0.5, 0.5); + + const combine = this.add + .rectangle(ITEM_WIDTH * 1.1 + COMB_X, ITEM_HEIGHT * 1.1 + COMB_Y, ITEM_WIDTH, ITEM_HEIGHT, 0x222222) + .setInteractive() + .setOrigin(0) + .on('pointerdown', () => { + ws.sendVboxCombine(vbox.game, 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, 'C', 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, WIDTH * 0.01, Y + HEIGHT * 0.6)); + this.add.text(del.getCenter().x, del.getCenter().y, 'Del', TEXT.HEADER) + .setOrigin(0.5, 0.5); - Object.keys(actions).forEach((action, i) => { + // Generate Items + vbox.bound.forEach((action, 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 itemBox = new Item(this, action, actions[action], ITEM_X, ITEM_Y, ITEM_WIDTH, ITEM_HEIGHT); + const itemBox = new Item(this, action, i, ITEM_X, ITEM_Y, ITEM_WIDTH, ITEM_HEIGHT); this.input.setDraggable(itemBox); this.add.existing(itemBox); }); - } - addClickHandlers(itemList) { - const ws = this.registry.get('ws'); - this.input.on('dragstart', (pointer, box) => { - if (!(box instanceof Item)) return false; - box.clickHandler(); + vbox.free.forEach((action, i) => { + const ITEM_X = ITEM_WIDTH * 1.1 * (i % BOX_COLUMNS) + BOX_X + ITEM_WIDTH * 0.5; + const ITEM_Y = ITEM_HEIGHT * 1.1 * Math.floor(i / BOX_COLUMNS) + BOX_Y + ITEM_HEIGHT * 0.5; + const itemBox = new Item(this, action, i, ITEM_X, ITEM_Y, ITEM_WIDTH, ITEM_HEIGHT); + itemBox + .setInteractive() + .on('pointerdown', () => { + ws.sendVboxAccept(vbox.game, i); + }); + 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); + hitBox.allocate(item); return true; }); - this.input.on('drag', (pointer, box, dragX, dragY) => { - if (!(box instanceof Item)) return false; - box.setPosition(dragX, dragY); + // 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; + } + 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; + }; + + // Add Handlers + this.input.on('dragstart', (pointer, item) => { + if (!(item instanceof Item)) return false; + item.clickHandler(); + 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, box) => { - if (!(box instanceof Item)) return false; - box.deselect(); + this.input.on('dragend', (pointer, item) => { + if (!(item instanceof Item)) return false; + + item.deselect(); + // Allocate to combiner if clicked without movement form inventory (return 0) + if (!Math.hypot(item.x - item.origX, item.y - item.origY)) { + const cBox = findUnallocated(); + if (cBox) allocate(item, cBox); + return true; + } + // Check for hitboxes const hitBox = itemCheckHitbox(this, pointer); - box.setPosition(box.origX, box.origY); - if (!hitBox) return false; - if (hitBox instanceof CombinerHitBox) console.log(`Moved item into slot ${hitBox.slot}`); - else ws.sendItemUse(itemList.find(li => li.action === box.action).id, hitBox.cryp.id); - hitBox.itemDeselect(); + if (hitBox) { + hitBox.itemDeselect(); + // Allocate to specific combiner slot + if (hitBox instanceof CombinerHitBox) { + allocate(item, hitBox); + return true; + } + // Allocate to cryp hitbox + if (hitBox instanceof DeleteHitBox) ws.sendVboxDrop(vbox.game, item.index); + else ws.sendVboxApply(vbox.game, hitBox.cryp.id, item.index); + } + // If the item hasn't been allocated deallocate the item + // Scene will restart if there is vbox change + deallocate(item); return true; }); + return this; } cleanUp() { diff --git a/client/src/scenes/menu.cryps.list.js b/client/src/scenes/menu.cryps.list.js index bd7e6031..bb5d77d2 100644 --- a/client/src/scenes/menu.cryps.list.js +++ b/client/src/scenes/menu.cryps.list.js @@ -113,10 +113,10 @@ class MenuCrypList extends Phaser.Scene { .setOrigin(0.5, 0.5)); const CRYP_STATS = [ cryp.stamina, - cryp.armour, - cryp.spell_shield, - cryp.phys_dmg, - cryp.spell_dmg, + cryp.red_shield, + cryp.blue_shield, + cryp.red_damage, + cryp.blue_damage, cryp.speed, ]; CRYP_STATS.forEach((stat, j) => crypStat(stat, j, crypInfo)); diff --git a/client/src/scenes/menu.js b/client/src/scenes/menu.js index 03bce852..77a1c53b 100644 --- a/client/src/scenes/menu.js +++ b/client/src/scenes/menu.js @@ -25,6 +25,7 @@ const MAIN_MENU_SCENES = [ 'ItemInfo', ]; +const NULL_UUID = '00000000-0000-0000-0000-000000000000'; class Menu extends Phaser.Scene { constructor() { @@ -37,7 +38,7 @@ class Menu extends Phaser.Scene { // When we load the menu request the latest items // Item list will restart when the data comes in - this.registry.get('ws').sendAccountItems(); + this.registry.get('ws').sendVboxState(NULL_UUID); this.scene.manager.add('MenuCrypList', MenuCrypList, true); this.scene.manager.add('MenuNavigation', MenuNavigation, true); diff --git a/client/src/scenes/passive.data.node.js b/client/src/scenes/passive.data.node.js index f1da3ef7..bb742082 100644 --- a/client/src/scenes/passive.data.node.js +++ b/client/src/scenes/passive.data.node.js @@ -2,79 +2,79 @@ const passiveNodes = [ { x: 860, y: 1011, id: 'CMED1', alloc: false, text: '5% Increased Speed for Chaos Slow skills, 5% Increased Slow Effect'}, { x: 905, y: 970, id: 'CSTAT7', alloc: false, text: '+2 Chaos Stat'}, { x: 940, y: 917.5, id: 'CSTAT8', alloc: false, text: '+2 Chaos Stat'}, - { x: 950, y: 1172.5, id: 'CMED2', alloc: false, text: '5% Increased Speed for Damaging Spells, 5% Increased Speed for Healing Skills'}, - { x: 955, y: 1120, id: 'CSDMG1', alloc: false, text: '+5% Increased Spell Damage'}, - { x: 965, y: 1082.5, id: 'CSDMG2', alloc: false, text: '+5% Increased Spell Damage'}, + { x: 950, y: 1172.5, id: 'CMED2', alloc: false, text: '5% Increased Speed for Damaging Blues, 5% Increased Speed for Healing Skills'}, + { x: 955, y: 1120, id: 'CSDMG1', alloc: false, text: '+5% Increased Blue Damage'}, + { x: 965, y: 1082.5, id: 'CSDMG2', alloc: false, text: '+5% Increased Blue Damage'}, { x: 970, y: 872.5, id: 'CSTAT9', alloc: false, text: '+2 Chaos Stat'}, { x: 975, y: 1012.5, id: 'CSTAT6', alloc: false, text: '+2 Chaos Stat'}, - { x: 995, y: 1062.5, id: 'CSDMG3', alloc: false, text: '+5% Increased Spell Damage'}, + { x: 995, y: 1062.5, id: 'CSDMG3', alloc: false, text: '+5% Increased Blue Damage'}, { x: 1000, y: 1150, id: 'CHEAL1', alloc: false, text: '+5% Increased Healing'}, { x: 1005, y: 812.5, id: 'CMED20', alloc: false, text: '5% Increased Speed for Healing skills, 5% Increased Stamina'}, { x: 1024.5, y: 1122.5, id: 'CHEAL2', alloc: false, text: '+5% Increased Healing'}, { x: 1036.5, y: 896.5, id: 'CMED19', alloc: false, text: '5% Increased Speed for Healing skills, 5% Increased Stamina'}, - { x: 1036.5, y: 1042.5, id: 'CMED18', alloc: false, text: '5% Increased Speed for Damaging Spells, 5% Increased Speed for Healing Skills'}, + { x: 1036.5, y: 1042.5, id: 'CMED18', alloc: false, text: '5% Increased Speed for Damaging Blues, 5% Increased Speed for Healing Skills'}, { x: 1036.5, y: 1082.5, id: 'CHEAL3', alloc: false, text: '+5% Increased Healing'}, { x: 1041.5, y: 772.5, id: 'CSTAT10', alloc: false, text: '+2 Chaos Stat'}, - { x: 1055, y: 1327.5, id: 'CMED3', alloc: false, text: '5% Increased Speed for Damaging Spells, 5% Increased Speed for Banish Skills'}, + { x: 1055, y: 1327.5, id: 'CMED3', alloc: false, text: '5% Increased Speed for Damaging Blues, 5% Increased Speed for Banish Skills'}, { x: 1066.5, y: 939.5, id: 'CHEAL5', alloc: false, text: '+5% Increased Healing'}, { x: 1067.5, y: 995, id: 'CHEAL4', alloc: false, text: '+5% Increased Healing'}, { x: 1070, y: 1250, id: 'CSTAT4', alloc: false, text: '+2 Chaos Stat'}, { x: 1072.5, y: 840.5, id: 'CHEAL8', alloc: false, text: '+5% Increased Healing'}, { x: 1087.5, y: 700, id: 'CSTAT11', alloc: false, text: '+2 Chaos Stat'}, - { x: 1097.5, y: 1052.5, id: 'CSDMG4', alloc: false, text: '+5% Increased Spell Damage'}, + { x: 1097.5, y: 1052.5, id: 'CSDMG4', alloc: false, text: '+5% Increased Blue Damage'}, { x: 1102.5, y: 1140, id: 'CSTAT5', alloc: false, text: '+2 Chaos Stat'}, { x: 1102.5, y: 917.5, id: 'CHEAL6', alloc: false, text: '+5% Increased Healing'}, { x: 1115, y: 1332.5, id: 'CSTAT1', alloc: false, text: '+2 Chaos Stat'}, { x: 1115, y: 875, id: 'CHEAL7', alloc: false, text: '+5% Increased Healing'}, { x: 1130, y: 640, id: 'CSTAT12', alloc: false, text: '+2 Chaos Stat'}, { x: 1145, y: 1287.5, id: 'CSTAT3', alloc: false, text: '+2 Chaos Stat'}, - { x: 1145, y: 1057.5, id: 'CSDMG5', alloc: false, text: '+5% Increased Spell Damage'}, + { x: 1145, y: 1057.5, id: 'CSDMG5', alloc: false, text: '+5% Increased Blue Damage'}, { x: 1175, y: 1332.5, id: 'CSTAT2', alloc: false, text: '+2 Chaos Stat'}, - { x: 1180, y: 1247.5, id: 'CMED10', alloc: false, text: '5% Increased Speed for Banish Spells, 5% Increased Stamina'}, + { x: 1180, y: 1247.5, id: 'CMED10', alloc: false, text: '5% Increased Speed for Banish Blues, 5% Increased Stamina'}, { x: 1180, y: 950, id: 'CMED14', alloc: false, text: '5% Increased Speed for Chaos Slow skills, 5% Increased Speed for Banish Skills'}, { x: 1195, y: 875, id: 'CHEAL9', alloc: false, text: '+5% Increased Healing'}, { x: 1196.5, y: 635, id: 'CMED21', alloc: false, text: '5% Increased Effect of Slow, 5% Increased Stamina'}, - { x: 1205, y: 1062.5, id: 'CSDMG6', alloc: false, text: '+5% Increased Spell Damage'}, + { x: 1205, y: 1062.5, id: 'CSDMG6', alloc: false, text: '+5% Increased Blue Damage'}, { x: 1212.5, y: 1200, id: 'CHEAL14', alloc: false, text: '+5% Increased Healing'}, { x: 1231.5, y: 590, id: 'CSTAT14', alloc: false, text: '+2 Chaos Stat'}, { x: 1235, y: 772.5, id: 'CHEAL12', alloc: false, text: '+5% Increased Healing'}, { x: 1240, y: 1225, id: 'CPHYS1', alloc: false, text: '+2% Reduced Physical Damage Taken'}, - { x: 1240, y: 1322.5, id: 'CMED4', alloc: false, text: '5% Increased Speed for Damaging Spells, 5% Increased Stamina'}, + { x: 1240, y: 1322.5, id: 'CMED4', alloc: false, text: '5% Increased Speed for Damaging Blues, 5% Increased Stamina'}, { x: 1258.5, y: 1107.5, id: 'CHEAL15', alloc: false, text: '+5% Increased Healing'}, { x: 1260, y: 917.5, id: 'CHEAL10', alloc: false, text: '+5% Increased Healing'}, - { x: 1263, y: 520, id: 'CLRG4', alloc: false, text: 'Damaging spells have a 20% chance to inflict slow'}, - { x: 1273.5, y: 1057.5, id: 'CMED13', alloc: false, text: '5% Increased Speed for Banish Spells, 5% Increased Speed for Damaging Spells'}, - { x: 1275, y: 1167.5, id: 'CMED11', alloc: false, text: '5% Increased Speed for Banish Spells, 5% Increased Stamina'}, - { x: 1275, y: 947.5, id: 'CMED15', alloc: false, text: '5% Increased Speed for Chaos Slow skills, 5% Increased Speed for Damaging Spells'}, - { x: 1277.5, y: 872.5, id: 'CMED16', alloc: false, text: '5% Increased Speed for Damaging Spells, 5% Increased Speed for Healing Skills'}, + { x: 1263, y: 520, id: 'CLRG4', alloc: false, text: 'Damaging blues have a 20% chance to inflict slow'}, + { x: 1273.5, y: 1057.5, id: 'CMED13', alloc: false, text: '5% Increased Speed for Banish Blues, 5% Increased Speed for Damaging Blues'}, + { x: 1275, y: 1167.5, id: 'CMED11', alloc: false, text: '5% Increased Speed for Banish Blues, 5% Increased Stamina'}, + { x: 1275, y: 947.5, id: 'CMED15', alloc: false, text: '5% Increased Speed for Chaos Slow skills, 5% Increased Speed for Damaging Blues'}, + { x: 1277.5, y: 872.5, id: 'CMED16', alloc: false, text: '5% Increased Speed for Damaging Blues, 5% Increased Speed for Healing Skills'}, { x: 1278.5, y: 812.5, id: 'CHEAL11', alloc: false, text: '+5% Increased Healing'}, { x: 1280, y: 732.5, id: 'CHEAL13', alloc: false, text: '+5% Increased Healing'}, - { x: 1281.5, y: 1002.5, id: 'CSDMG7', alloc: false, text: '+5% Increased Spell Damage'}, + { x: 1281.5, y: 1002.5, id: 'CSDMG7', alloc: false, text: '+5% Increased Blue Damage'}, { x: 1284, y: 640, id: 'CSTAT15', alloc: false, text: '+2 Chaos Stat'}, { x: 1298.5, y: 1107.5, id: 'CPHYS4', alloc: false, text: '+2% Reduced Physical Damage Taken'}, - { x: 1302.5, y: 917.5, id: 'CSDMG8', alloc: false, text: '+5% Increased Spell Damage'}, + { x: 1302.5, y: 917.5, id: 'CSDMG8', alloc: false, text: '+5% Increased Blue Damage'}, { x: 1305, y: 1307.5, id: 'CPHYS2', alloc: false, text: '+2% Reduced Physical Damage Taken'}, - { x: 1306, y: 1347.5, id: 'CSDMG10', alloc: false, text: '+5% Increased Spell Damage'}, + { x: 1306, y: 1347.5, id: 'CSDMG10', alloc: false, text: '+5% Increased Blue Damage'}, { x: 1315, y: 762.5, id: 'CMED17', alloc: false, text: '5% Increased Speed for Healing skills, 5% Increased Stamina'}, { x: 1326.5, y: 590, id: 'CSTAT16', alloc: false, text: '+2 Chaos Stat'}, { x: 1346.5, y: 635, id: 'CMED22', alloc: false, text: '5% Increased Effect of Slow, 5% Increased Stamina'}, { x: 1348.5, y: 535, id: 'CSTAT13', alloc: false, text: '+2 Chaos Stat'}, - { x: 1355, y: 1322.5, id: 'CMED5', alloc: false, text: '5% Increased Speed for Damaging Spells, 5% Increased Stamina'}, - { x: 1360, y: 1002.5, id: 'CSDMG9', alloc: false, text: '+5% Increased Spell Damage'}, + { x: 1355, y: 1322.5, id: 'CMED5', alloc: false, text: '5% Increased Speed for Damaging Blues, 5% Increased Stamina'}, + { x: 1360, y: 1002.5, id: 'CSDMG9', alloc: false, text: '+5% Increased Blue Damage'}, { x: 1399.5, y: 1107.5, id: 'CPHYS5', alloc: false, text: '+2% Reduced Physical Damage Taken'}, - { x: 1400, y: 807.5, id: 'CMED24', alloc: false, text: '5% Increased Speed for Damaging Spells, 5% Increased Speed for Healing Skills'}, + { x: 1400, y: 807.5, id: 'CMED24', alloc: false, text: '5% Increased Speed for Damaging Blues, 5% Increased Speed for Healing Skills'}, { x: 1409.5, y: 1312.5, id: 'CPHYS3', alloc: false, text: '+2% Reduced Physical Damage Taken'}, - { x: 1410, y: 1347.5, id: 'CSDMG11', alloc: false, text: '+5% Increased Spell Damage'}, - { x: 1410, y: 532.5, id: 'CMED23', alloc: false, text: '5% Increased Speed for Banish Spells, 5% Increased Effect of Slow'}, + { x: 1410, y: 1347.5, id: 'CSDMG11', alloc: false, text: '+5% Increased Blue Damage'}, + { x: 1410, y: 532.5, id: 'CMED23', alloc: false, text: '5% Increased Speed for Banish Blues, 5% Increased Effect of Slow'}, { x: 1420, y: 732.5, id: 'CSTAT19', alloc: false, text: '+2 Chaos Stat'}, { x: 1430, y: 1205, id: 'CPHYS7', alloc: false, text: '+2% Reduced Physical Damage Taken'}, - { x: 1447.5, y: 765, id: 'CMED25', alloc: false, text: '5% Increased Speed for Damaging Spells, 5% Increased Speed for Healing Skills'}, - { x: 1452.5, y: 1322.5, id: 'CMED6', alloc: false, text: '5% Increased Speed for Damaging Spells, 5% Increased Stamina'}, + { x: 1447.5, y: 765, id: 'CMED25', alloc: false, text: '5% Increased Speed for Damaging Blues, 5% Increased Speed for Healing Skills'}, + { x: 1452.5, y: 1322.5, id: 'CMED6', alloc: false, text: '5% Increased Speed for Damaging Blues, 5% Increased Stamina'}, { x: 1455, y: 1267.5, id: 'CSTAT33', alloc: false, text: '+2 Chaos Stat'}, - { x: 1455, y: 1147.5, id: 'CMED12', alloc: false, text: '5% Increased Speed for Banish Spells, 5% Increased Stamina'}, + { x: 1455, y: 1147.5, id: 'CMED12', alloc: false, text: '5% Increased Speed for Banish Blues, 5% Increased Stamina'}, { x: 1455, y: 1042.5, id: 'CSTAT35', alloc: false, text: '+2 Chaos Stat'}, { x: 1457.5, y: 975, id: 'CSTAT37', alloc: false, text: '+2 Chaos Stat'}, - { x: 1462.5, y: 825, id: 'CLRG3', alloc: false, text: '20% critical strike chance for spells'}, + { x: 1462.5, y: 825, id: 'CLRG3', alloc: false, text: '20% critical strike chance for blues'}, { x: 1485, y: 640, id: 'CSTAT17', alloc: false, text: '+2 Chaos Stat'}, { x: 1500, y: 1110, id: 'CPHYS6', alloc: false, text: '+2% Reduced Physical Damage Taken'}, { x: 1505, y: 1242.5, id: 'CSTAT32', alloc: false, text: '+2 Chaos Stat'}, @@ -85,8 +85,8 @@ const passiveNodes = [ { x: 1555, y: 732.5, id: 'CSTAT18', alloc: false, text: '+2 Chaos Stat'}, { x: 1597.5, y: 827.5, id: 'CSTAT21', alloc: false, text: '+2 Chaos Stat'}, { x: 1607.5, y: 1277.5, id: 'CSTAT30', alloc: false, text: '+2 Chaos Stat'}, - { x: 1612.5, y: 1137.5, id: 'CLRG1', alloc: false, text: 'When you take physical damage there is a 20% chance to heal for 150% of physical damage taken'}, - { x: 1612.5, y: 955, id: 'CLRG2', alloc: false, text: 'Your healing spells have a 20% chance to increase ally speed by 100%'}, + { x: 1612.5, y: 1137.5, id: 'CLRG1', alloc: false, text: 'When you take red damage there is a 20% chance to heal for 150% of red damage taken'}, + { x: 1612.5, y: 955, id: 'CLRG2', alloc: false, text: 'Your healing blues have a 20% chance to increase ally speed by 100%'}, { x: 1657.5, y: 822.5, id: 'CSTAT22', alloc: false, text: '+2 Chaos Stat'}, { x: 1657.5, y: 1312.5, id: 'CSTAT29', alloc: false, text: '+2 Chaos Stat'}, { x: 1685, y: 1187.5, id: 'CSTAT27', alloc: false, text: '+2 Chaos Stat'}, @@ -94,7 +94,7 @@ const passiveNodes = [ { x: 1690, y: 1117.5, id: 'CSTAT26', alloc: false, text: '+2 Chaos Stat'}, { x: 1705, y: 960, id: 'CSTAT24', alloc: false, text: '+2 Chaos Stat'}, { x: 1712.5, y: 850, id: 'CMED9', alloc: false, text: '5% Increased Speed for Healing skills, 5% Increased Stamina'}, - { x: 1717.5, y: 1267.5, id: 'CMED7', alloc: false, text: '5% Increased Speed for Damaging Spells, 5% Increased Stamina'}, + { x: 1717.5, y: 1267.5, id: 'CMED7', alloc: false, text: '5% Increased Speed for Damaging Blues, 5% Increased Stamina'}, { x: 1725, y: 1222.5, id: 'CSTAT28', alloc: false, text: '+2 Chaos Stat'}, { x: 1727.5, y: 1062.5, id: 'CMED8', alloc: false, text: '5% Increased Speed for Chaos Slow skills, 5% Increased Speed for Banish Skills'}, { x: 1737.5, y: 917.5, id: 'CSTAT23', alloc: false, text: '+2 Chaos Stat'}, diff --git a/client/src/scenes/statsheet.js b/client/src/scenes/statsheet.js index 325ae703..0331807b 100644 --- a/client/src/scenes/statsheet.js +++ b/client/src/scenes/statsheet.js @@ -66,11 +66,11 @@ class StatSheet extends Phaser.Scene { const CRYP_STATS = [ cryp.stamina, - cryp.armour, - cryp.spell_shield, + cryp.red_shield, + cryp.blue_shield, cryp.evasion, - cryp.phys_dmg, - cryp.spell_dmg, + cryp.red_damage, + cryp.blue_damage, cryp.speed, ]; diff --git a/client/src/socket.js b/client/src/socket.js index e947278a..2f81629d 100644 --- a/client/src/socket.js +++ b/client/src/socket.js @@ -1,6 +1,7 @@ const toast = require('izitoast'); const cbor = require('borc'); + const SOCKET_URL = process.env.NODE_ENV === 'production' ? 'wss://cryps.gg/ws' : 'ws://localhost:40000'; function errorToast(err) { @@ -44,10 +45,6 @@ function createSocket(events) { send({ method: 'account_cryps', params: {} }); } - function sendAccountItems() { - send({ method: 'account_items', params: {} }); - } - function sendAccountZone() { send({ method: 'account_zone', params: {} }); } @@ -88,6 +85,31 @@ function createSocket(events) { send({ method: 'cryp_unspec', params: { id, spec } }); } + function sendVboxState(gameId) { + send({ method: 'vbox_state', params: { game_id: gameId } }); + } + + function sendVboxAccept(gameId, i) { + send({ method: 'vbox_accept', params: { game_id: gameId, index: i } }); + } + + function sendVboxApply(gameId, crypId, index) { + send({ method: 'vbox_apply', params: { game_id: gameId, cryp_id: crypId, index } }); + } + + function sendVboxDiscard(gameId) { + send({ method: 'vbox_discard', params: { game_id: gameId } }); + } + + function sendVboxCombine(gameId, indices) { + send({ method: 'vbox_combine', params: { game_id: gameId, indices } }); + } + + + function sendVboxDrop(gameId, index) { + send({ method: 'vbox_drop', params: { game_id: gameId, index } }); + } + function sendPressR() { send({ method: 'press_r', params: { } }); @@ -110,10 +132,6 @@ function createSocket(events) { events.setActiveSkill(null); } - function sendItemUse(item, target) { - send({ method: 'item_use', params: { item, target } }); - } - function sendZoneCreate() { send({ method: 'zone_create', params: {} }); } @@ -136,7 +154,7 @@ function createSocket(events) { account = login; events.setAccount(login); sendAccountCryps(); - sendGameJoinableList(); + // sendGameJoinableList(); } function accountCryps(response) { @@ -162,16 +180,17 @@ function createSocket(events) { const [structName, game] = response; } - function accountItems(response) { - const [structName, items] = response; - events.setItems(items); - } - function zoneState(response) { const [structName, zone] = response; events.setZone(zone); } + function vboxState(response) { + const [structName, vbox] = response; + console.log(vbox); + events.setVbox(vbox); + } + // ------------- // Setup // ------------- @@ -188,11 +207,10 @@ function createSocket(events) { account_login: accountLogin, account_create: accountLogin, account_cryps: accountCryps, - account_items: accountItems, zone_create: res => console.log(res), zone_state: zoneState, zone_close: res => console.log(res), - + vbox_state: vboxState, }; function errHandler(error) { @@ -268,7 +286,6 @@ function createSocket(events) { sendAccountCreate, sendAccountDemo, sendAccountCryps, - sendAccountItems, sendAccountZone, sendGameState, sendGamePve, @@ -280,11 +297,16 @@ function createSocket(events) { sendCrypSpawn, sendCrypLearn, sendCrypForget, - sendItemUse, sendSpecForget, sendZoneCreate, sendZoneJoin, sendZoneClose, + sendVboxState, + sendVboxAccept, + sendVboxApply, + sendVboxDrop, + sendVboxCombine, + sendVboxDiscard, connect, }; } diff --git a/client/src/tutorial.js b/client/src/tutorial.js index a730df71..ecc092ed 100644 --- a/client/src/tutorial.js +++ b/client/src/tutorial.js @@ -19,7 +19,7 @@ This homepage shows your cryps, joinable online games, PVE options and your item If you have no cryps yet, press SPAWN and give your cryp a name to create one. Once you have made a cryp, click on them to visit their stat page and teach them some SKILLS. The stat page also has descriptions of each skill and their effects. -cryps have 3 basic stats: stamina, physical damage and magic damage. +cryps have 3 basic stats: stamina, red damage and magic damage. Toggle whether a cryp is selected for your team by clicking the coloured stripes next to the cryp or press 1,2,3. Once you have a team ready press the New PVE Game button to start playing. `; @@ -29,7 +29,7 @@ A cryps battle has two main phases. This first phase is called the SKILL PHASE. Your cryps are positioned on the left, your opponent's are on the right. In the centre are your cryps' SKILLS, grayed out SKILLS are currently ON COOLDOWN. A skill's cooldown reduces on every turn that cryp does not use a skill with a cooldown. -For the moment, drag ATTACK onto the opponent team to have your cryps attack them with physical damage. +For the moment, drag ATTACK onto the opponent team to have your cryps attack them with red damage. `; // const TARGET_PHASE_MESSAGE = ` diff --git a/ops/migrations/20181014141623_items.js b/ops/migrations/20181014141623_items.js deleted file mode 100644 index 97c7c8c9..00000000 --- a/ops/migrations/20181014141623_items.js +++ /dev/null @@ -1,15 +0,0 @@ -exports.up = async knex => { - return knex.schema.createTable('items', table => { - table.uuid('id').primary(); - table.uuid('account').notNullable() - table.foreign('account') - .references('id') - .inTable('accounts') - .onDelete('CASCADE'); - table.binary('data').notNullable(); - table.index('id'); - table.index('account'); - }); -}; - -exports.down = async () => {}; \ No newline at end of file diff --git a/ops/migrations/20190216123550_vbox.js b/ops/migrations/20190216123550_vbox.js new file mode 100644 index 00000000..4ed2fcae --- /dev/null +++ b/ops/migrations/20190216123550_vbox.js @@ -0,0 +1,36 @@ +const NULL_UUID = '00000000-0000-0000-0000-000000000000'; + +exports.up = async knex => { + await knex.schema.createTable('vbox', table => { + table.timestamps(); + table.uuid('id').primary(); + table.uuid('account').notNullable() + table.uuid('game').notNullable() + table.binary('data').notNullable(); + + table.foreign('game') + .references('id') + .inTable('games') + .onDelete('CASCADE'); + + table.foreign('account') + .references('id') + .inTable('accounts') + .onDelete('CASCADE'); + + table.index('id'); + table.index('account'); + table.unique(['account', 'game']); + }); + + // not really sure if this is a good idea + await knex('games').insert({ + id: NULL_UUID, + data: 'INVALID', + joinable: false, + }); + + return true; +}; + +exports.down = async () => {}; \ No newline at end of file diff --git a/ops/package.json b/ops/package.json index 6ac74db4..3ee5cdf7 100755 --- a/ops/package.json +++ b/ops/package.json @@ -11,9 +11,6 @@ "author": "", "license": "UNLICENSED", "dependencies": { - "ascii-tree": "^0.3.0", - "cli-ascii-tree": "0.0.4", - "inquirer": "^6.2.0", "knex": "^0.15.2", "pg": "^7.4.3" } diff --git a/server/COMBOS.md b/server/COMBOS.md index 3fbbe609..0f8915cd 100755 --- a/server/COMBOS.md +++ b/server/COMBOS.md @@ -55,43 +55,43 @@ Debuff `Base enemy debuff - reduce enemy speed` # Nature Type (red) # -Bite - (Attack + 3R) Deal significant `Red` damage -Strangle - (Stun + 3R) - Applies stun and physical dot -Regenerate - (Buff + 3R) Restore red defense for target ally -Snare - (Debuff + 3R) Disable enemy red skills for (3?) turns -Parry - (Block + 3R) Avoid all damage for this turn +Bite - (Attack RR) Deal significant `Red` damage +Strangle - (Stun RR) - Applies stun and physical dot +Regenerate - (Buff RR) Restore red defense for target ally +Snare - (Debuff RR) Disable enemy red skills for (3?) turns +Parry - (Block RR) Avoid all damage for this turn # Destruction Type (blue) # -Blast - (Attack + 3B) Deal significant `Blue` damage -Ruin - (Stun + 3B) - AOE stun on the enemy team for remaind of turn -Amplify - (Buff + 3B) Increased blue damage dealt by target ally -Curse - (Debuff + 3B) Decrease damage dealt by target enemy -Plague Shield - (Block + 3B) Apply dot debuff to attackers +Blast - (Attack BB) Deal significant `Blue` damage +Ruin - (Stun BB) - AOE stun on the enemy team for remaind of turn +Amplify - (Buff BB) Increased blue damage dealt by target ally +Curse - (Debuff BB) Decrease damage dealt by target enemy +Plague Shield - (Block BB) Apply dot debuff to attackers # Non-Violence Type (green) # -Heal - (Attack + 3G) Heal ally for X -Throw - (Stun + 3G) - Disable target and increase damage taken -Triage - (Buff + 3G) Buff which heals ally for X over Y turns -Purge - (Debuff + 3G) - Remove all enemy target buffs -Reflect - (Block + 3G) - Will use targetted attacks on source +Heal - (Attack GG) Heal ally for X +Throw - (Stun GG) - Disable target and increase damage taken +Triage - (Buff GG) Buff which heals ally for X over Y turns +Purge - (Debuff GG) - Remove all enemy target buffs +Reflect - (Block GG) - Will use targetted attacks on source # Chaos Type (red / blue) # -Bannish - (Attack + 1R + 2B) - deal `Hybrid Red & Blue` + enemy can't attack or be attacked 3 turns -Hex - (Stun + 2R + 1B) - enemy stunned for 3 turns -Haste - (Buff + 2R + 1B) - significantly increase ally speed -Slow - (Debuff + 1R + 2B) - significantly decrease enemy speed -Taunt - (Block + 2B + 1R) - Redirect all enemy targetted skills +Bannish - (Attack RB) - deal `Hybrid Red & Blue` + enemy can't attack or be attacked 3 turns +Hex - (Stun RB) - enemy stunned for 3 turns +Haste - (Buff RB) - significantly increase ally speed +Slow - (Debuff RB) - significantly decrease enemy speed +Taunt - (Block RB) - Redirect all enemy targetted skills # Purity Type (red / green) # -Slay - (Attack + 1G + 2R) - deal `Red` damage. Bonus damage if the target has blue defense +Slay - (Attack + 1G + 1R) - deal `Red` damage. Bonus damage if the target has blue defense Silence - (Stun + 2G + 1R) - enemy can't use blue skills for 3 turns Purify - (Buff + 2G + 1R) - remove all debuffs from target -Empower - (Buff + 1G + 2R) - Increased red damage dealt by target ally -???? - (Debuff + 1R + 2B) - ??? +Empower - (Buff + 1G + 1R) - Increased red damage dealt by target ally +???? - (Debuff RB) - ??? Shield - (Block + 2G + 1R) - Take no blue damage for the next 3 turns # Technology Type (green / blue) type - ??? maybe rework and rename # diff --git a/server/NODES.md b/server/NODES.md index 2a208ce8..a6004f8e 100644 --- a/server/NODES.md +++ b/server/NODES.md @@ -6,16 +6,16 @@ Rare `Increased Stamina` Common `Increased Evasion rating` -Common `Increased Spell Shield rating` -Common `Increased Armour rating` +Common `Increased Blue Shield rating` +Common `Increased RedShield rating` Common `Increased Healing done` Common `Increased Healing received` -Common `Increased Spell Damage` -Common `Increased Physical Damage` +Common `Increased Blue Damage` +Common `Increased Red Damage` Uncommon `Reduced hp loss penalty to evade chance` Uncommon `Increased base evasion chance per X evasion rating` -Uncommon `Increased % mitigation from armour` +Uncommon `Increased % mitigation from red_shield` Uncommon `Increased % mitigation from spell shield` Uncommon `Increased damage over time` @@ -30,11 +30,11 @@ Rare `25% hex for blast` Rare `cooldown reduction` Rare `effect duration` -Rare `increased phys dmg, 0 spell damage` -Rare `increased spell dmg, 0 phys damage` +Rare `increased phys damage, 0 spell damage` +Rare `increased spell damage, 0 phys damage` -Rare `increased phys dmg, silenced` -Rare `increased spell dmg, snared` +Rare `increased phys damage, silenced` +Rare `increased spell damage, snared` Rare `increased speed, increased durations` Rare `increased speed, increased cooldowns` diff --git a/server/README.md b/server/README.md index adab2a60..5bca346a 100644 --- a/server/README.md +++ b/server/README.md @@ -21,11 +21,11 @@ resolve phase: 2.2 <- attack (normal resolve) 1.1 <- hexed (no skills for the rest of this turn and next) -## Dmg Chart +## Damage Chart -| Physical | Magic | Modifiers | +| Red | Magic | Modifiers | | ------ | ------ | ------ | -| dmg | dmg | speed | +| damage | damage | speed | | evasion | resistance | cooldowns | | reduction | absorption? | durations | diff --git a/server/SPECS.md b/server/SPECS.md new file mode 100755 index 00000000..76e64c28 --- /dev/null +++ b/server/SPECS.md @@ -0,0 +1,166 @@ +### Specs ### + +Numbers are placeholder +`Specs get a bonus dependent on the total of Red / Green / Blue in team skills` + +# Example to meet 5 red tag bonus +In your team Cryp #1 has `Strike`, Cryp #2 has `Slay` and `Heal`, Cryp #3 has `Snare` +- RR skill `Strike` contributes 2 red tags to the total red tags (2 total) +- RG skill `Slay` contributes 1 red tag to the total red tags (3 total) +- GG skill `Heal` contirubes 0 red tags to the total red tags (3 total) +- RR skill `Snare` contirubes 2 red tags to the total red tags (5 total) + +# Specs also have `Class points` + Basic specs (Damage / Health / Defense) will generate class points + Advanced specs (Skill Specific Damage, Cooldown reduction, Increased durations, etc) will consume class points + +### Generic Specs + +(Base white skills not upgraded count as have 1 tag basic ?) + +# Basic Damage +`Base` -> 10% inc basic damage +`Bonus` -> 3 basic tags -> +10% // 6 basic tags -> +15% // 12 basic tags -> +25% +Maximum 60% inc basic damage + +# Basic % Life +`Base` -> 5% inc life +`Bonus` -> 3 basic tags -> +5% // 6 basic tags -> +10% // 12 basic tags -> +15% +Maximum 35% inc life + +# Basic Speed +`Base` -> 5% inc speed +`Bonus` -> 3 basic tags -> +10% // 6 basic tags -> +15% // 12 basic tags -> +20% +Maximum 50% inc speed + +# Basic Class Spec +`Base` -> +2 all class points +`Bonus` -> 3 basic tags -> +2 // 6 basic tags -> +4 // 12 basic tags -> +6 +Maximum +14 all class points + +# Basic Duration + +### Increased Damage Combos ### + +Generate by combining `Generic Spec (Basic Damage)` with respective RGB + +# Red Damage (Dmg + RR) +Add 5 `Nature` Class Points +`Base` -> 10% inc red dmg +`Bonus` 5 red tags -> +10% // 10 red tags -> +15% // 20 red tags -> +25% +Maximum +60% red damage + +# Blue Damage (Dmg + BB) # +Add 5 `Destruction` Class Points +`Base` -> 10% inc blue dmg +`Bonus` 5 blue tags -> +10% // 10 blue tags -> +15% // 20 blue tags -> +25% +Maximum +60% blue damage + +# Healing (Dmg + GG) # +Add 5 `Non-Violence` Class Points +`Base` -> 10% inc healing +`Bonus` 5 green tags -> +10% // 10 green tags -> +15% // 20 green tags -> +25% +Maximum +60% inc healing + +# Red damage and healing (Dmg + RG) +Add 5 `Purity` Class Points +`Base` -> 5% inc red damage and 5% inc healing +`Bonus` (2R + 2G tags) -> +5% + 5% // (5R + 5G tags) -> +10% + 10% % // (10R + 10G) tags -> +15% + 15% +Maximum +35% inc red damage and 35% inc healing + +# Red and blue damage (Dmg + RB) +Add 5 `Chaos` Class Points +`Base` -> 5% inc red damage and 5% inc healing +`Bonus` (2 red + 2 green tags) -> +5% + 5% // (5 red + 5 green tags) -> +10% + 10% % // 20 green tags -> +15% + 15% +Maximum +35% inc damage and 35% inc healing + +# Blue damage and healing (Dmg + BG) +Add 5 `??????` Class Points +`Base` -> 5% inc blue damage and 5% inc healing +`Bonus` (2B + 2G tags) -> +5% + 5% // (5B + 5G tags) -> +10% + 10% % // (10B + 10G) tags -> +15% + 15% +Maximum +35% inc blue damage and 35% inc healing + +### Increased Life Combos ### + +Generate by combining `Generic Spec (Basic Life)` with respective RGB + +# Increased % Red Shield (Basic %HP + 2R) +Add 5 Nature Class Points +`Base` -> 10% inc red shield +`Bonus` 5 red tags -> +10% // 10 red tags -> +15% // 20 red tags -> +20% +Maximum +55% inc red shield + +# Increased % Red Shield and Life (Basic %HP + 1R1G) +Add 5 Purity Class Points +`Base` -> 5% inc red shield and 5% inc life +`Bonus` (2R + 2G tags) -> +5% + 5% // (5R + 5G tags) -> +10% + 10% % // (10R + 10G) tags -> +15% + 15% +Maximum +35% inc red shield and 35% inc life + +# Increased % Blue Shield (Basic %HP + 2B) +Add 5 Destruction Class Points +`Base` -> 10% inc red shield +`Bonus` 5 blue tags -> +10% // 10 blue tags -> +15% // 20 blue tags -> +20% +Maximum +55% inc blue shield + +# Increased % Blue Shield and Life (Basic %HP + 1B1G) +Add 5 ??? Class Points +`Base` -> 5% inc red shield and 5% inc life +`Bonus` (2B + 2G tags) -> +5% + 5% // (5B + 5G tags) -> +10% + 10% % // (10B + 10G) tags -> +15% + 15% +Maximum +35% inc blue shield and 35% inc life + +# Increased % Life (Basic %HP + 2G) +Add 5 Non-Violence Class Points +`Base` -> 10% inc hp +`Bonus` 5 green tags -> +10% // 10 green tags -> +15% // 20 green tags -> +20% +Maximum +55% inc hp + +# Increased % Blue and Red Shield (Basic %HP + 1B1R) +Add 5 Chaos Class Points +`Base` -> 5% inc red shield and 5% inc life +`Bonus` (2B + 2R tags) -> +5% + 5% // (5B + 5R tags) -> +10% + 10% % // (10B + 10R) tags -> +15% + 15% +Maximum +35% inc blue shield and 35% inc red shield + +## Upgraded Attack Spec Combos + +# Increased Strike Damage (Combine Strike + Red Damage Spec x 2) +Consumes 15 Nature Class Points +`Base` -> 15% increased strike damage +`Bonus` 5 red tags -> +15% // 10 red tags -> +20% // 20 red tags -> +30% +Maximum 80% increased strike damage + +# Improved Heal (Combine Heal + Healing Spec x 2) +Consumes 15 Non-Violence Class Points +`Base` -> 15% increased heal healing +`Bonus` 5 green tags -> +15% // green red tags -> +20% // green red tags -> +30% +Maximum 80% increased heal healing + +# Increased Blast Damage (Combine Blast + Blue Spec x 2) +Consumes 15 Destruction Class Points +`Base` -> 15% increased blast damage +`Bonus` 5 blue tags -> +15% // 10 blue tags -> +20% // 20 blue tags -> +30% +Maximum 80% increased blast damage + +# Increased Slay Damage (Combine Slay + Red Damage Spec + Healing Spec) +Consumes 15 Purity Class Points +`Base` -> 15% increased slay damage +`Bonus` (2R + 2G) tags -> +15% // (5R + 5G) tags -> +20% // (10R + 10G) tags -> +30% +Maximum 80% increased slay damage + +# Increased Banish Damage (Combine Slay + Red Damage Spec + Blue Damage Spec) +Consumes 15 Chaos Class Points +`Base` -> 15% increased slay damage +`Bonus` (2R + 2B) tags -> +15% // (5R + 5B) tags -> +20% // (10R + 10B) tags -> +30% +Maximum 80% increased banish damage + +## Other Combos + +# Increased % Red Speed (Basic Speed + 2R) +Add 5 Nature Class Points +`Base` -> 15% inc red speed +`Bonus` 5 red tags -> +15% // 10 red tags -> +20% // 20 red tags -> +25% +Maximum 80% inc red speed + +# Nature Affinity (Basic Class spec + 2R) +`Base` -> 10 Nature Class Points +`Bonus` 5 red tags -> +10 // 10 red tags -> +15 // 20 red tags -> +20 +Maximum 45 Nature Class Points diff --git a/server/WORKLOG.md b/server/WORKLOG.md index ce056c8e..7922daf0 100644 --- a/server/WORKLOG.md +++ b/server/WORKLOG.md @@ -30,7 +30,7 @@ * phys is faster and chaotic * spells are slow and reliable * defensives are implicit - * armour is restored, not gained + * red_shield is restored, not gained * players can feel aggressive # ask sam @@ -56,38 +56,6 @@ taunt ## NOW -inventory + drops table - id - data - game - -drops_buy(game_id, index) - drops_get() - inventory_get() - - reduce balance - move drop into inventory - - drops_update() - inventory_update() - - -> inventory - -inventory_combine(game_id, [indices]) - inventory_get() - - new item = - match base item - match modifiers - - update inventory[base_index] - - inventory_update() - - -> inventory - - - ## SOON * clean up categories * why is the text fucked? diff --git a/server/src/cryp.rs b/server/src/cryp.rs index fdb6a338..458dc7aa 100644 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -45,14 +45,14 @@ pub enum Stat { Hp, Speed, Stamina, - PhysicalDamage, - PhysicalDamageTaken, - SpellDamage, - SpellDamageTaken, + RedDamage, + RedDamageTaken, + BlueDamage, + BlueDamageTaken, Healing, HealingTaken, - Armour, - SpellShield, + RedShield, + BlueShield, Evasion, } @@ -136,13 +136,13 @@ pub struct CrypRecover { pub struct Cryp { pub id: Uuid, pub account: Uuid, - pub phys_dmg: CrypStat, - pub spell_dmg: CrypStat, + pub red_damage: CrypStat, + pub red_shield: CrypStat, + pub blue_shield: CrypStat, + pub blue_damage: CrypStat, pub speed: CrypStat, pub stamina: CrypStat, pub hp: CrypStat, - pub armour: CrypStat, - pub spell_shield: CrypStat, pub evasion: CrypStat, pub xp: u64, pub lvl: u8, @@ -164,13 +164,13 @@ impl Cryp { return Cryp { id, account: id, - phys_dmg: CrypStat { base: 0, value: 0, stat: Stat::PhysicalDamage }, - spell_dmg: CrypStat { base: 0, value: 0, stat: Stat::SpellDamage }, + red_damage: CrypStat { base: 0, value: 0, stat: Stat::RedDamage }, + red_shield: CrypStat { base: 0, value: 0, stat: Stat::RedShield }, + blue_damage: CrypStat { base: 0, value: 0, stat: Stat::BlueDamage }, + blue_shield: CrypStat { base: 0, value: 0, stat: Stat::BlueShield }, speed: CrypStat { base: 0, value: 0, stat: Stat::Speed }, stamina: CrypStat { base: 0, value: 0, stat: Stat::Stamina }, hp: CrypStat { base: 0, value: 0, stat: Stat::Hp }, - armour: CrypStat { base: 0, value: 0, stat: Stat::Armour }, - spell_shield: CrypStat { base: 0, value: 0, stat: Stat::SpellShield }, evasion: CrypStat { base: 0, value: 0, stat: Stat::Evasion }, lvl: 0, xp: 0, @@ -238,15 +238,15 @@ impl Cryp { let evasion_max = 5; match stat { - Stat::PhysicalDamage => self.phys_dmg.set(rng.gen_range(stat_min, stat_max), &self.specs), - Stat::SpellDamage => self.spell_dmg.set(rng.gen_range(stat_min, stat_max), &self.specs), + Stat::RedDamage => self.red_damage.set(rng.gen_range(stat_min, stat_max), &self.specs), + Stat::BlueDamage => self.blue_damage.set(rng.gen_range(stat_min, stat_max), &self.specs), Stat::Speed => self.speed.set(rng.gen_range(stat_min, stat_max), &self.specs), Stat::Stamina => { self.stamina.set(rng.gen_range(stam_min, stam_max), &self.specs); self.hp.set(self.stamina.base, &self.specs) }, - Stat::SpellShield => self.spell_shield.set(rng.gen_range(stat_min, stat_max), &self.specs), - Stat::Armour => self.armour.set(rng.gen_range(stat_min, stat_max), &self.specs), + Stat::BlueShield => self.blue_shield.set(rng.gen_range(stat_min, stat_max), &self.specs), + Stat::RedShield => self.red_shield.set(rng.gen_range(stat_min, stat_max), &self.specs), Stat::Evasion => self.evasion.set(rng.gen_range(evasion_min, evasion_max), &self.specs), _ => panic!("{:?} not a rollable stat", stat), }; @@ -262,8 +262,8 @@ impl Cryp { self.xp = xp; - self.roll_stat(Stat::PhysicalDamage); - self.roll_stat(Stat::SpellDamage); + self.roll_stat(Stat::RedDamage); + self.roll_stat(Stat::BlueDamage); self.roll_stat(Stat::Speed); self.roll_stat(Stat::Stamina); @@ -306,7 +306,7 @@ impl Cryp { return Ok(self.recalculate_stats()); } - pub fn spec_remove(mut self, spec: Spec) -> Result { + pub fn spec_remove(&mut self, spec: Spec) -> Result<&mut Cryp, Error> { let find_spec = |spec_v: &Vec| spec_v.iter().position(|s| s.spec == spec.spec); match spec.level { @@ -324,18 +324,18 @@ impl Cryp { }, }; - Ok(self) + Ok(self.recalculate_stats()) } fn recalculate_stats(&mut self) -> &mut Cryp { self.stamina.recalculate(&self.specs); self.hp.recalculate(&self.specs); - self.phys_dmg.recalculate(&self.specs); - self.spell_dmg.recalculate(&self.specs); + self.red_damage.recalculate(&self.specs); + self.red_shield.recalculate(&self.specs); + self.blue_damage.recalculate(&self.specs); + self.blue_shield.recalculate(&self.specs); self.evasion.recalculate(&self.specs); - self.armour.recalculate(&self.specs); - self.spell_shield.recalculate(&self.specs); self.speed.recalculate(&self.specs); self @@ -468,24 +468,24 @@ impl Cryp { // } // Stats - pub fn phys_dmg(&self) -> u64 { - let phys_dmg_mods = self.effects.iter() - .filter(|e| e.effect.modifications().contains(&Stat::PhysicalDamage)) + pub fn red_damage(&self) -> u64 { + let red_damage_mods = self.effects.iter() + .filter(|e| e.effect.modifications().contains(&Stat::RedDamage)) .map(|cryp_effect| cryp_effect.effect) .collect::>(); - let modified_phys_dmg = phys_dmg_mods.iter().fold(self.phys_dmg.value, |acc, m| m.apply(acc)); - return modified_phys_dmg; + let modified_red_damage = red_damage_mods.iter().fold(self.red_damage.value, |acc, m| m.apply(acc)); + return modified_red_damage; } - pub fn spell_dmg(&self) -> u64 { - let spell_dmg_mods = self.effects.iter() - .filter(|e| e.effect.modifications().contains(&Stat::SpellDamage)) + pub fn blue_damage(&self) -> u64 { + let blue_damage_mods = self.effects.iter() + .filter(|e| e.effect.modifications().contains(&Stat::BlueDamage)) .map(|cryp_effect| cryp_effect.effect) .collect::>(); - let modified_spell_dmg = spell_dmg_mods.iter().fold(self.spell_dmg.value, |acc, m| m.apply(acc)); - return modified_spell_dmg; + let modified_blue_damage = blue_damage_mods.iter().fold(self.blue_damage.value, |acc, m| m.apply(acc)); + return modified_blue_damage; } pub fn skill_speed(&self, s: Skill) -> u64 { @@ -518,7 +518,7 @@ impl Cryp { ResolutionResult::Healing { amount: 0, overhealing: 0, - category: Category::PhysHeal, + category: Category::RedHeal, immunity: immunity.clone(), }; } @@ -546,12 +546,12 @@ impl Cryp { return ResolutionResult::Healing { amount: healing, overhealing, - category: Category::PhysHeal, + category: Category::RedHeal, immunity, }; } - pub fn deal_phys_dmg(&mut self, skill: Skill, amount: u64) -> ResolutionResult { + pub fn deal_red_damage(&mut self, skill: Skill, amount: u64) -> ResolutionResult { let immunity = self.immune(skill); let immune = immunity.immune; @@ -559,29 +559,29 @@ impl Cryp { return ResolutionResult::Damage { amount: 0, mitigation: 0, - category: Category::PhysDmg, + category: Category::RedDamage, immunity, }; } - let phys_dmg_mods = self.effects.iter() - .filter(|e| e.effect.modifications().contains(&Stat::PhysicalDamageTaken)) + let red_damage_mods = self.effects.iter() + .filter(|e| e.effect.modifications().contains(&Stat::RedDamageTaken)) .map(|cryp_effect| cryp_effect.effect) .collect::>(); - // println!("{:?}", phys_dmg_mods); + // println!("{:?}", red_damage_mods); - let modified_dmg = phys_dmg_mods.iter().fold(amount, |acc, m| m.apply(acc)); + let modified_damage = red_damage_mods.iter().fold(amount, |acc, m| m.apply(acc)); - // calculate amount of damage armour will not absorb - // eg 50 armour 25 dmg -> 0 remainder 25 mitigation - // 50 armour 100 dmg -> 50 remainder 50 mitigation - // 50 armour 5 dmg -> 0 remainder 5 mitigation - let remainder = modified_dmg.saturating_sub(self.armour.value); - let mitigation = modified_dmg.saturating_sub(remainder); + // calculate amount of damage red_shield will not absorb + // eg 50 red_shield 25 damage -> 0 remainder 25 mitigation + // 50 red_shield 100 damage -> 50 remainder 50 mitigation + // 50 red_shield 5 damage -> 0 remainder 5 mitigation + let remainder = modified_damage.saturating_sub(self.red_shield.value); + let mitigation = modified_damage.saturating_sub(remainder); - // reduce armour by mitigation amount - self.armour.reduce(mitigation); + // reduce red_shield by mitigation amount + self.red_shield.reduce(mitigation); // deal remainder to hp self.hp.reduce(remainder); @@ -589,12 +589,12 @@ impl Cryp { return ResolutionResult::Damage { amount: remainder, mitigation, - category: Category::PhysDmg, + category: Category::RedDamage, immunity, }; } - pub fn deal_spell_dmg(&mut self, skill: Skill, amount: u64) -> ResolutionResult { + pub fn deal_blue_damage(&mut self, skill: Skill, amount: u64) -> ResolutionResult { let immunity = self.immune(skill); let immune = immunity.immune; @@ -602,29 +602,29 @@ impl Cryp { return ResolutionResult::Damage { amount: 0, mitigation: 0, - category: Category::SpellDmg, + category: Category::BlueDamage, immunity, }; } - let spell_dmg_mods = self.effects.iter() - .filter(|e| e.effect.modifications().contains(&Stat::SpellDamageTaken)) + let blue_damage_mods = self.effects.iter() + .filter(|e| e.effect.modifications().contains(&Stat::BlueDamageTaken)) .map(|cryp_effect| cryp_effect.effect) .collect::>(); - // println!("{:?}", spell_dmg_mods); + // println!("{:?}", blue_damage_mods); - let modified_dmg = spell_dmg_mods.iter().fold(amount, |acc, m| m.apply(acc)); - let remainder = modified_dmg.saturating_sub(self.armour.value); - let mitigation = modified_dmg.saturating_sub(remainder); + let modified_damage = blue_damage_mods.iter().fold(amount, |acc, m| m.apply(acc)); + let remainder = modified_damage.saturating_sub(self.blue_shield.value); + let mitigation = modified_damage.saturating_sub(remainder); - self.armour.reduce(mitigation); + self.blue_shield.reduce(mitigation); self.hp.reduce(remainder); return ResolutionResult::Damage { amount: remainder, mitigation, - category: Category::SpellDmg, + category: Category::BlueDamage, immunity, }; } @@ -734,7 +734,7 @@ pub fn cryp_forget(params: CrypForgetParams, tx: &mut Transaction, account: &Acc pub fn cryp_unspec(params: CrypUnspecParams, tx: &mut Transaction, account: &Account) -> Result { let mut cryp = cryp_get(tx, params.id, account.id)?; - cryp = cryp.spec_remove(params.spec)?; + cryp.spec_remove(params.spec)?; return cryp_write(cryp, tx); } diff --git a/server/src/game.rs b/server/src/game.rs index cec580b3..cc060432 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -11,7 +11,6 @@ use account::Account; use rpc::{GameStateParams, GameSkillParams, GamePveParams, GamePvpParams, GameJoinParams}; use cryp::{Cryp, cryp_get}; use skill::{Skill, Cast, ResolutionResult}; -use item::{item_drop}; use zone::{node_finish}; use mob::{generate_mob_team}; @@ -685,12 +684,6 @@ pub fn game_update(game: &Game, tx: &mut Transaction) -> Result<(), Error> { result.iter().next().ok_or(format_err!("game {:?} could not be written", game))?; if game.finished() { - if let Some(t) = game.winner() { - if !t.id.is_nil() { - item_drop(tx, t.id, game.mode)?; - } - } - // check for zone update if let Some((z, i)) = game.zone { node_finish(game, z, i, tx)?; @@ -854,7 +847,7 @@ mod tests { .learn(Skill::TestTouch) .learn(Skill::TestBlock) .learn(Skill::TestParry) - .learn(Skill::TestDrain) + .learn(Skill::TestSiphon) .learn(Skill::Empower) .learn(Skill::Stun) .learn(Skill::Block) @@ -868,7 +861,7 @@ mod tests { .learn(Skill::TestTouch) .learn(Skill::TestBlock) .learn(Skill::TestParry) - .learn(Skill::TestDrain) + .learn(Skill::TestSiphon) .learn(Skill::Empower) .learn(Skill::Stun) .learn(Skill::Block) @@ -1014,12 +1007,12 @@ mod tests { let x_cryp = x_team.cryps[0].clone(); let y_cryp = y_team.cryps[0].clone(); - game.team_by_id(y_team.id).cryp_by_id(y_cryp.id).unwrap().phys_dmg.force(u64::max_value()); + game.team_by_id(y_team.id).cryp_by_id(y_cryp.id).unwrap().red_damage.force(u64::max_value()); game.team_by_id(y_team.id).cryp_by_id(y_cryp.id).unwrap().speed.force(u64::max_value()); // just in case // remove all mitigation - game.team_by_id(x_team.id).cryp_by_id(x_cryp.id).unwrap().armour.force(0); + game.team_by_id(x_team.id).cryp_by_id(x_cryp.id).unwrap().red_shield.force(0); let _x_stun_id = game.add_skill(x_team.id, x_cryp.id, Some(y_cryp.id), Skill::TestStun).unwrap(); game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::Attack).unwrap(); @@ -1088,7 +1081,7 @@ mod tests { } #[test] - fn drain_test() { + fn siphon_test() { let mut game = create_test_game(); let x_team = game.teams[0].clone(); @@ -1097,7 +1090,7 @@ mod tests { let x_cryp = x_team.cryps[0].clone(); let y_cryp = y_team.cryps[0].clone(); - let _x_drain_id = game.add_skill(x_team.id, x_cryp.id, Some(y_cryp.id), Skill::TestDrain).unwrap(); + let _x_siphon_id = game.add_skill(x_team.id, x_cryp.id, Some(y_cryp.id), Skill::TestSiphon).unwrap(); let _y_touch_id = game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::TestTouch).unwrap(); game.resolve_phase_start(); @@ -1107,7 +1100,7 @@ mod tests { game.resolve_phase_start(); - assert!(game.resolved.iter().any(|r| r.skill == Skill::DrainTick)); + assert!(game.resolved.iter().any(|r| r.skill == Skill::SiphonTick)); } #[test] diff --git a/server/src/game_target_phase.rs b/server/src/game_target_phase.rs index c7dc27f7..83412b1d 100644 --- a/server/src/game_target_phase.rs +++ b/server/src/game_target_phase.rs @@ -938,7 +938,7 @@ mod tests { .learn(Skill::TestTouch) .learn(Skill::TestBlock) .learn(Skill::TestParry) - .learn(Skill::TestDrain) + .learn(Skill::TestSiphon) .learn(Skill::Empower) .learn(Skill::Block) .create(); @@ -950,7 +950,7 @@ mod tests { .learn(Skill::TestTouch) .learn(Skill::TestBlock) .learn(Skill::TestParry) - .learn(Skill::TestDrain) + .learn(Skill::TestSiphon) .learn(Skill::Empower) .learn(Skill::Block) .create(); @@ -1104,7 +1104,7 @@ mod tests { let x_cryp = x_team.cryps[0].clone(); let y_cryp = y_team.cryps[0].clone(); - game.team_by_id(y_team.id).cryp_by_id(y_cryp.id).unwrap().phys_dmg.set(u64::max_value()); + game.team_by_id(y_team.id).cryp_by_id(y_cryp.id).unwrap().red_damage.set(u64::max_value()); let x_stun_id = game.add_skill(x_team.id, x_cryp.id, Some(y_team.id), Skill::TestStun).unwrap(); let y_attack_id = game.add_skill(y_team.id, y_cryp.id, Some(x_team.id), Skill::Attack).unwrap(); @@ -1194,7 +1194,7 @@ mod tests { } #[test] - fn drain_test() { + fn siphon_test() { let mut game = create_test_game(); let x_team = game.teams[0].clone(); @@ -1203,13 +1203,13 @@ mod tests { let x_cryp = x_team.cryps[0].clone(); let y_cryp = y_team.cryps[0].clone(); - let x_drain_id = game.add_skill(x_team.id, x_cryp.id, Some(y_team.id), Skill::TestDrain).unwrap(); + let x_siphon_id = game.add_skill(x_team.id, x_cryp.id, Some(y_team.id), Skill::TestSiphon).unwrap(); let y_touch_id = game.add_skill(y_team.id, y_cryp.id, Some(x_team.id), Skill::TestTouch).unwrap(); game.target_phase_start(); game.add_target(x_team.id, x_cryp.id, y_touch_id).unwrap(); - game.add_target(y_team.id, y_cryp.id, x_drain_id).unwrap(); + game.add_target(y_team.id, y_cryp.id, x_siphon_id).unwrap(); game.resolve_phase_start(); @@ -1218,7 +1218,7 @@ mod tests { game.target_phase_start(); - assert!(game.resolved.iter().any(|r| r.skill == Skill::DrainTick)); + assert!(game.resolved.iter().any(|r| r.skill == Skill::SiphonTick)); } #[test] diff --git a/server/src/item.rs b/server/src/item.rs index ead699e8..4f499842 100644 --- a/server/src/item.rs +++ b/server/src/item.rs @@ -17,19 +17,19 @@ use spec::{Spec, SpecType}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum ItemAction { - RerollPhysDamage, - RerollSpellDamage, + RerollRedDamage, + RerollBlueDamage, RerollSpeed, RerollStamina, - RerollArmour, - RerollSpellShield, + RerollRedShield, + RerollBlueShield, RerollEvasion, - SpecPhysDmg5, - SpecSpellDmg5, - SpecArmour5, - SpecSpellShield5, - SpecSpellEvasion5, + SpecRedDamage5, + SpecBlueDamage5, + SpecRedShield5, + SpecBlueShield5, + SpecBlueEvasion5, } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -44,24 +44,24 @@ impl Item { pub fn new(action: ItemAction, account_id: Uuid) -> Item { let id = Uuid::new_v4(); return Item { - id, - account: account_id, - action, + id, + account: account_id, + action, }; } fn apply(&mut self, tx: &mut Transaction, target: Uuid) -> Result<(), Error> { match self.action { ItemAction::RerollStamina => reroll(self, tx, target, Stat::Stamina), - ItemAction::RerollPhysDamage => reroll(self, tx, target, Stat::PhysicalDamage), - ItemAction::RerollSpellDamage => reroll(self, tx, target, Stat::SpellDamage), + ItemAction::RerollRedDamage => reroll(self, tx, target, Stat::RedDamage), + ItemAction::RerollBlueDamage => reroll(self, tx, target, Stat::BlueDamage), ItemAction::RerollSpeed => reroll(self, tx, target, Stat::Speed), - ItemAction::RerollArmour => reroll(self, tx, target, Stat::Armour), - ItemAction::RerollSpellShield => reroll(self, tx, target, Stat::SpellShield), + ItemAction::RerollRedShield => reroll(self, tx, target, Stat::RedShield), + ItemAction::RerollBlueShield => reroll(self, tx, target, Stat::BlueShield), ItemAction::RerollEvasion => reroll(self, tx, target, Stat::Evasion), - ItemAction::SpecPhysDmg5 => spec_add(self, tx, target, SpecType::PhysDamage5), - ItemAction::SpecSpellDmg5 => spec_add(self, tx, target, SpecType::SpellDamage5), + ItemAction::SpecRedDamage5 => spec_add(self, tx, target, SpecType::RedDamage5), + ItemAction::SpecBlueDamage5 => spec_add(self, tx, target, SpecType::BlueDamage5), _ => unimplemented!(), } } @@ -86,8 +86,8 @@ fn mode_drops(mode: GameMode) -> Vec<(ItemAction, usize)> { match mode { GameMode::Normal => vec![ (ItemAction::RerollStamina, 1), - (ItemAction::RerollPhysDamage, 1), - (ItemAction::RerollSpellDamage, 1), + (ItemAction::RerollRedDamage, 1), + (ItemAction::RerollBlueDamage, 1), ], GameMode::Pvp => vec![ (ItemAction::RerollSpeed, 1), @@ -96,19 +96,19 @@ fn mode_drops(mode: GameMode) -> Vec<(ItemAction, usize)> { GameMode::Zone2v2Caster | GameMode::Zone3v3MeleeMiniboss => vec![ (ItemAction::RerollEvasion, 1), - (ItemAction::RerollArmour, 1), - (ItemAction::RerollSpellShield, 1), + (ItemAction::RerollRedShield, 1), + (ItemAction::RerollBlueShield, 1), ], GameMode::Zone3v3HealerBoss => vec![ (ItemAction::RerollSpeed, 1), ], // _ => vec![ // (ItemAction::RerollStamina, 1), - // (ItemAction::RerollPhysDamage, 1), - // (ItemAction::RerollSpellDamage, 1), + // (ItemAction::RerollRedDamage, 1), + // (ItemAction::RerollBlueDamage, 1), // (ItemAction::RerollSpeed, 1), - // (ItemAction::RerollArmour, 1), - // (ItemAction::RerollSpellShield, 1), + // (ItemAction::RerollRedShield, 1), + // (ItemAction::RerollBlueShield, 1), // (ItemAction::RerollEvasion, 1), // ], } diff --git a/server/src/main.rs b/server/src/main.rs index 5c859276..570cfd98 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -26,10 +26,12 @@ mod spec; // mod passives; mod rpc; mod account; -mod item; +// mod item; mod zone; mod mob; +mod vbox; + use dotenv::dotenv; use net::{start}; diff --git a/server/src/rpc.rs b/server/src/rpc.rs index 0348613e..22e4bc61 100644 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -18,10 +18,10 @@ use net::Db; use cryp::{Cryp, cryp_spawn, cryp_learn, cryp_forget, cryp_unspec}; use game::{Game, game_state, game_pve, game_pvp, game_join, game_joinable_list, game_skill}; use account::{Account, account_create, account_login, account_from_token, account_cryps, account_zone}; -use item::{Item, ItemAction, items_list, item_use, item_create}; use skill::{Skill}; use zone::{Zone, zone_create, zone_join, zone_close}; use spec::{Spec}; +use vbox::{Vbox, vbox_state, vbox_accept, vbox_apply, vbox_discard, vbox_combine, vbox_drop}; pub struct Rpc; @@ -76,11 +76,14 @@ impl Rpc { "zone_join" => Rpc::zone_join(data, &mut tx, account.unwrap(), client), "zone_close" => Rpc::zone_close(data, &mut tx, account.unwrap(), client), "account_cryps" => Rpc::account_cryps(data, &mut tx, account.unwrap(), client), - "account_items" => Rpc::account_items(data, &mut tx, account.unwrap(), client), "account_zone" => Rpc::account_zone(data, &mut tx, account.unwrap(), client), - "item_use" => Rpc::item_use(data, &mut tx, account.unwrap(), client), - "press_r" => Rpc::press_r(data, &mut tx, account.unwrap(), client), + "vbox_state" => Rpc::vbox_state(data, &mut tx, account.unwrap(), client), + "vbox_accept" => Rpc::vbox_accept(data, &mut tx, account.unwrap(), client), + "vbox_apply" => Rpc::vbox_apply(data, &mut tx, account.unwrap(), client), + "vbox_drop" => Rpc::vbox_drop(data, &mut tx, account.unwrap(), client), + "vbox_combine" => Rpc::vbox_combine(data, &mut tx, account.unwrap(), client), + "vbox_discard" => Rpc::vbox_discard(data, &mut tx, account.unwrap(), client), _ => Err(format_err!("unknown method - {:?}", v.method)), }; @@ -251,8 +254,6 @@ impl Rpc { Ok(cryp_list) } - - fn account_create(data: Vec, tx: &mut Transaction, _client: &mut WebSocket) -> Result { match from_slice::(&data) { Ok(v) => Ok(RpcResponse { @@ -290,7 +291,7 @@ impl Rpc { let cryp = cryp_spawn(CrypSpawnParams { name }, tx, &account)?; cryp_learn(CrypLearnParams { id: cryp.id, skill: Skill::Decay }, tx, &account)?; cryp_learn(CrypLearnParams { id: cryp.id, skill: Skill::Blast }, tx, &account)?; - cryp_learn(CrypLearnParams { id: cryp.id, skill: Skill::Drain }, tx, &account)?; + cryp_learn(CrypLearnParams { id: cryp.id, skill: Skill::Siphon }, tx, &account)?; let name: String = iter::repeat(()).map(|()| rng.sample(Alphanumeric)).take(8).collect(); let cryp = cryp_spawn(CrypSpawnParams { name }, tx, &account)?; @@ -314,13 +315,6 @@ impl Rpc { }) } - fn account_items(_data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - Ok(RpcResponse { - method: "account_items".to_string(), - params: RpcResult::ItemList(items_list(tx, &account)?) - }) - } - fn account_zone(_data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { Ok(RpcResponse { method: "zone_state".to_string(), @@ -328,49 +322,6 @@ impl Rpc { }) } - fn press_r(_data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - for action in [ - ItemAction::RerollPhysDamage, - ItemAction::RerollSpellDamage, - ItemAction::RerollSpeed, - ItemAction::RerollStamina, - ItemAction::RerollArmour, - ItemAction::RerollSpellShield, - ItemAction::RerollEvasion, - ItemAction::SpecPhysDmg5, - ItemAction::SpecSpellDmg5, - ].into_iter() { - let item = Item::new(*action, account.id); - item_create(item, tx, account.id)?; - } - - let res = RpcResponse { - method: "account_items".to_string(), - params: RpcResult::ItemList(items_list(tx, &account)?) - }; - - return Ok(res); - } - - - fn item_use(data: Vec, tx: &mut Transaction, account: Account, client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - - item_use(msg.params, tx, &account)?; - - Rpc::send_msg(client, RpcResponse { - method: "account_items".to_string(), - params: RpcResult::ItemList(items_list(tx, &account)?) - })?; - - let cryps_list = RpcResponse { - method: "account_cryps".to_string(), - params: RpcResult::CrypList(account_cryps(tx, &account)?) - }; - - return Ok(cryps_list); - } - fn zone_create(_data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { // let _msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; @@ -404,6 +355,77 @@ impl Rpc { return Ok(response); } + fn vbox_state(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let response = RpcResponse { + method: "vbox_state".to_string(), + params: RpcResult::VboxState(vbox_state(msg.params, tx, &account)?) + }; + + return Ok(response); + } + + fn vbox_accept(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let response = RpcResponse { + method: "vbox_state".to_string(), + params: RpcResult::VboxState(vbox_accept(msg.params, tx, &account)?) + }; + + return Ok(response); + } + + fn vbox_discard(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let response = RpcResponse { + method: "vbox_state".to_string(), + params: RpcResult::VboxState(vbox_discard(msg.params, tx, &account)?) + }; + + return Ok(response); + } + + + fn vbox_combine(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let response = RpcResponse { + method: "vbox_state".to_string(), + params: RpcResult::VboxState(vbox_combine(msg.params, tx, &account)?) + }; + + return Ok(response); + } + + fn vbox_apply(data: Vec, tx: &mut Transaction, account: Account, client: &mut WebSocket) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let response = RpcResponse { + method: "vbox_state".to_string(), + params: RpcResult::VboxState(vbox_apply(msg.params, tx, &account)?) + }; + + Rpc::send_msg(client, RpcResponse { + method: "account_cryps".to_string(), + params: RpcResult::CrypList(account_cryps(tx, &account)?) + })?; + + return Ok(response); + } + + fn vbox_drop(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let response = RpcResponse { + method: "vbox_state".to_string(), + params: RpcResult::VboxState(vbox_drop(msg.params, tx, &account)?) + }; + + return Ok(response); + } } #[derive(Debug,Clone,Serialize,Deserialize)] @@ -422,10 +444,10 @@ pub enum RpcResult { CrypList(Vec), GameState(Game), GameJoinableList(Vec), - ItemList(Vec), - ItemUse(()), ZoneState(Zone), ZoneClose(()), + + VboxState(Vbox), } #[derive(Debug,Clone,Serialize,Deserialize)] @@ -589,24 +611,6 @@ struct AccountCrypsMsg { params: (), } -#[derive(Debug,Clone,Serialize,Deserialize)] -struct ItemListMsg { - method: String, - params: (), -} - -#[derive(Debug,Clone,Serialize,Deserialize)] -struct ItemUseMsg { - method: String, - params: ItemUseParams, -} - -#[derive(Debug,Clone,Serialize,Deserialize)] -pub struct ItemUseParams { - pub item: Uuid, - pub target: Uuid, -} - #[derive(Debug,Clone,Serialize,Deserialize)] struct ZoneCreateMsg { method: String, @@ -637,6 +641,76 @@ pub struct ZoneCloseParams { pub zone_id: Uuid, } +#[derive(Debug,Clone,Serialize,Deserialize)] +struct VboxStateMsg { + method: String, + params: VboxStateParams, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +pub struct VboxStateParams { + pub game_id: Uuid, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +struct VboxAcceptMsg { + method: String, + params: VboxAcceptParams, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +pub struct VboxAcceptParams { + pub game_id: Uuid, + pub index: usize, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +struct VboxDiscardMsg { + method: String, + params: VboxDiscardParams, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +pub struct VboxDiscardParams { + pub game_id: Uuid, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +struct VboxCombineMsg { + method: String, + params: VboxCombineParams, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +pub struct VboxCombineParams { + pub game_id: Uuid, + pub indices: Vec, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +struct VboxApplyMsg { + method: String, + params: VboxApplyParams, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +pub struct VboxApplyParams { + pub game_id: Uuid, + pub cryp_id: Uuid, + pub index: usize, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +struct VboxDropMsg { + method: String, + params: VboxDropParams, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +pub struct VboxDropParams { + pub game_id: Uuid, + pub index: usize, +} // #[cfg(test)] // mod tests { diff --git a/server/src/skill.rs b/server/src/skill.rs index 19fe728d..12ce5e17 100644 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -125,9 +125,9 @@ pub enum Effect { Triage, Decay, Regen, - Drain, + Siphon, - SpeedDrain, + SpeedSiphon, SpeedIncrease, Ko, @@ -137,13 +137,13 @@ impl Effect { pub fn immune(&self, skill: Skill) -> bool { match self { Effect::Parry => match skill.category() { - Category::Spell => false, - Category::Physical => true, + Category::Blue => false, + Category::Red => true, _ => false, }, Effect::Shield => match skill.category() { - Category::Spell => true, - Category::Physical => false, + Category::Blue => true, + Category::Red => false, _ => false, }, Effect::Banish => true, @@ -157,18 +157,18 @@ impl Effect { Effect::Hex => true, Effect::Banish => true, Effect::Silence => match skill.category() { - Category::Spell => true, - Category::Physical => false, + Category::Blue => true, + Category::Red => false, _ => false, }, Effect::Snare => match skill.category() { - Category::Spell => false, - Category::Physical => true, + Category::Blue => false, + Category::Red => true, _ => false, }, Effect::Ko => match skill.category() { - Category::SpellTick => false, + Category::BlueTick => false, _ => true, }, _ => false, @@ -177,12 +177,12 @@ impl Effect { pub fn modifications(&self) -> Vec { match self { - Effect::Empower => vec![Stat::PhysicalDamage], - Effect::Vulnerable => vec![Stat::PhysicalDamageTaken], - Effect::Block => vec![Stat::PhysicalDamageTaken], + Effect::Empower => vec![Stat::RedDamage], + Effect::Vulnerable => vec![Stat::RedDamageTaken], + Effect::Block => vec![Stat::RedDamageTaken], - Effect::Amplify => vec![Stat::SpellDamage], - Effect::Curse => vec![Stat::SpellDamageTaken], + Effect::Amplify => vec![Stat::BlueDamage], + Effect::Curse => vec![Stat::BlueDamageTaken], Effect::Haste => vec![Stat::Speed], Effect::Slow => vec![Stat::Speed], @@ -216,43 +216,43 @@ impl Effect { pub fn category(&self) -> Category { match self { // physical - Effect::Stun => Category::PhysDebuff, - Effect::Block => Category::PhysBuff, - Effect::Parry => Category::PhysBuff, - Effect::Bleed => Category::PhysDebuff, - Effect::Leech => Category::PhysDebuff, - Effect::Airborne => Category::PhysDebuff, - Effect::Untouchable => Category::PhysBuff, - Effect::Deadly => Category::PhysBuff, - Effect::Vulnerable => Category::PhysDebuff, - Effect::Fury => Category::PhysBuff, - Effect::Blind => Category::PhysDebuff, - Effect::Snare => Category::PhysDebuff, + Effect::Stun => Category::RedDebuff, + Effect::Block => Category::RedBuff, + Effect::Parry => Category::RedBuff, + Effect::Bleed => Category::RedDebuff, + Effect::Leech => Category::RedDebuff, + Effect::Airborne => Category::RedDebuff, + Effect::Untouchable => Category::RedBuff, + Effect::Deadly => Category::RedBuff, + Effect::Vulnerable => Category::RedDebuff, + Effect::Fury => Category::RedBuff, + Effect::Blind => Category::RedDebuff, + Effect::Snare => Category::RedDebuff, - Effect::Empower => Category::PhysBuff, + Effect::Empower => Category::RedBuff, // magic - Effect::Hex => Category::SpellDebuff, - Effect::Curse => Category::SpellDebuff, - Effect::Banish => Category::SpellDebuff, // todo randomise - Effect::Slow => Category::SpellDebuff, - Effect::Haste => Category::SpellBuff, - Effect::Enslave => Category::SpellDebuff, - Effect::Mesmerise => Category::SpellDebuff, - Effect::Amplify => Category::SpellBuff, - Effect::Silence => Category::SpellDebuff, + Effect::Hex => Category::BlueDebuff, + Effect::Curse => Category::BlueDebuff, + Effect::Banish => Category::BlueDebuff, // todo randomise + Effect::Slow => Category::BlueDebuff, + Effect::Haste => Category::BlueBuff, + Effect::Enslave => Category::BlueDebuff, + Effect::Mesmerise => Category::BlueDebuff, + Effect::Amplify => Category::BlueBuff, + Effect::Silence => Category::BlueDebuff, // magic immunity - Effect::Shield => Category::SpellBuff, + Effect::Shield => Category::BlueBuff, // effects over time - Effect::Triage => Category::SpellBuff, - Effect::Decay => Category::SpellDebuff, - Effect::Regen => Category::SpellBuff, - Effect::Drain => Category::SpellDebuff, + Effect::Triage => Category::BlueBuff, + Effect::Decay => Category::BlueDebuff, + Effect::Regen => Category::BlueBuff, + Effect::Siphon => Category::BlueDebuff, - Effect::SpeedDrain => Category::SpellDebuff, - Effect::SpeedIncrease => Category::SpellBuff, + Effect::SpeedSiphon => Category::BlueDebuff, + Effect::SpeedIncrease => Category::BlueBuff, Effect::Ko => Category::Ko, } @@ -283,7 +283,7 @@ impl Effect { Effect::Triage => 3, Effect::Decay => 3, - Effect::Drain => 2, + Effect::Siphon => 2, _ => { println!("{:?} does not have a duration", self); @@ -296,18 +296,18 @@ impl Effect { #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Category { - Physical, - PhysHeal, - PhysDmg, - PhysDebuff, - PhysBuff, - PhysTick, - Spell, - SpellDmg, - SpellHeal, - SpellDebuff, - SpellBuff, - SpellTick, + Red, + RedHeal, + RedDamage, + RedDebuff, + RedBuff, + RedTick, + Blue, + BlueDamage, + BlueHeal, + BlueDebuff, + BlueBuff, + BlueTick, Ko, } @@ -318,8 +318,8 @@ pub enum Skill { // ----------------- // Nature // ----------------- - Block, // reduce dmg - Parry, // avoid all dmg + Block, // reduce damage + Parry, // avoid all damage Snare, Paralyse, @@ -345,7 +345,7 @@ pub enum Skill { Heal, Triage, // hot TriageTick, - Throw, // no dmg stun, adds vulnerable + Throw, // no damage stun, adds vulnerable Charm, Calm, Rez, @@ -360,8 +360,8 @@ pub enum Skill { Amplify, Decay, // dot DecayTick, // dot - Drain, - DrainTick, + Siphon, + SiphonTick, Curse, Plague, // aoe dot Ruin, // aoe @@ -389,12 +389,12 @@ pub enum Skill { Haste, Slow, - // used by tests, no cd, no dmg + // used by tests, no cd, no damage TestTouch, TestStun, TestBlock, TestParry, - TestDrain, + TestSiphon, } impl Skill { @@ -405,8 +405,8 @@ impl Skill { // ----------------- // Nature // ----------------- - Skill::Block => None, // reduce dmg - Skill::Parry => None, // avoid all dmg + Skill::Block => None, // reduce damage + Skill::Parry => None, // avoid all damage Skill::Snare => Some(1), Skill::Paralyse => Some(1), @@ -431,7 +431,7 @@ impl Skill { Skill::Heal => None, Skill::Triage => None, // hot Skill::TriageTick => None, - Skill::Throw => Some(1), // no dmg stun, adds vulnerable + Skill::Throw => Some(1), // no damage stun, adds vulnerable Skill::Charm => Some(1), Skill::Calm => None, Skill::Rez => Some(2), @@ -443,8 +443,8 @@ impl Skill { Skill::Amplify => Some(1), Skill::Decay => None, // dot Skill::DecayTick => None, - Skill::Drain => Some(1), - Skill::DrainTick => None, + Skill::Siphon => Some(1), + Skill::SiphonTick => None, Skill::Curse => Some(1), Skill::Plague => Some(1), // aoe dot Skill::Ruin => Some(2), // aoe @@ -478,95 +478,95 @@ impl Skill { Skill::TestTouch => None, Skill::TestStun => None, Skill::TestBlock => None, - Skill::TestDrain => None, + Skill::TestSiphon => None, Skill::TestParry => None, } } pub fn category(&self) -> Category { match self { - Skill::Attack => Category::Physical, + Skill::Attack => Category::Red, // ----------------- // Nature // ----------------- - Skill::Block => Category::Physical, // reduce dmg - Skill::Parry => Category::Physical, // avoid all dmg - Skill::Snare => Category::Physical, + Skill::Block => Category::Red, // reduce damage + Skill::Parry => Category::Red, // avoid all damage + Skill::Snare => Category::Red, - Skill::Paralyse => Category::Physical, - Skill::Strangle => Category::Physical, + Skill::Paralyse => Category::Red, + Skill::Strangle => Category::Red, // Strangle - Skill::Stun => Category::Physical, + Skill::Stun => Category::Red, // ----------------- // Technology // ----------------- - Skill::Replicate => Category::Physical, - Skill::Swarm => Category::Physical, - Skill::Orbit => Category::Physical, - Skill::Repair => Category::Physical, - Skill::Scan => Category::Physical, // track? + Skill::Replicate => Category::Red, + Skill::Swarm => Category::Red, + Skill::Orbit => Category::Red, + Skill::Repair => Category::Red, + Skill::Scan => Category::Red, // track? // ----------------- // Preservation // ----------------- - Skill::Heal => Category::Physical, - Skill::Triage => Category::Spell, // hot - Skill::TriageTick => Category::SpellTick, // hot - Skill::Throw => Category::Physical, // no dmg stun, adds vulnerable - Skill::Charm => Category::Spell, - Skill::Calm => Category::Physical, - Skill::Rez => Category::Spell, + Skill::Heal => Category::Red, + Skill::Triage => Category::Blue, // hot + Skill::TriageTick => Category::BlueTick, // hot + Skill::Throw => Category::Red, // no damage stun, adds vulnerable + Skill::Charm => Category::Blue, + Skill::Calm => Category::Red, + Skill::Rez => Category::Blue, // ----------------- // Destruction // ----------------- - Skill::Blast => Category::Spell, - Skill::Amplify => Category::Spell, - Skill::Decay => Category::Spell, // dot - Skill::DecayTick => Category::SpellTick, // hot - Skill::Drain => Category::Spell, - Skill::DrainTick => Category::SpellTick, // hot - Skill::Curse => Category::Spell, - Skill::Plague => Category::Spell, // aoe dot - Skill::Ruin => Category::Spell, // aoe + Skill::Blast => Category::Blue, + Skill::Amplify => Category::Blue, + Skill::Decay => Category::Blue, // dot + Skill::DecayTick => Category::BlueTick, // hot + Skill::Siphon => Category::Blue, + Skill::SiphonTick => Category::BlueTick, // hot + Skill::Curse => Category::Blue, + Skill::Plague => Category::Blue, // aoe dot + Skill::Ruin => Category::Blue, // aoe // ----------------- // Purity // ----------------- // Skill::Precision => 1, - Skill::Empower => Category::Physical, - Skill::Slay => Category::Physical, - Skill::Shield => Category::Spell, - Skill::Silence => Category::Spell, - Skill::Inquiry => Category::Spell, - Skill::Purify => Category::Spell, - Skill::Purge => Category::Spell, + Skill::Empower => Category::Red, + Skill::Slay => Category::Red, + Skill::Shield => Category::Blue, + Skill::Silence => Category::Blue, + Skill::Inquiry => Category::Blue, + Skill::Purify => Category::Blue, + Skill::Purge => Category::Blue, // ----------------- // Chaos // ----------------- - Skill::Banish => Category::Spell, - Skill::Hex => Category::Spell, - Skill::Fear => Category::Spell, - Skill::Taunt => Category::Spell, - Skill::Pause => Category::Spell, // extend durations + Skill::Banish => Category::Blue, + Skill::Hex => Category::Blue, + Skill::Fear => Category::Blue, + Skill::Taunt => Category::Blue, + Skill::Pause => Category::Blue, // extend durations // Skill::Lag => 2, // - Skill::Haste => Category::Spell, - Skill::Slow => Category::Spell, + Skill::Haste => Category::Blue, + Skill::Slow => Category::Blue, // ----------------- // Test // ----------------- - Skill::TestTouch => Category::Physical, - Skill::TestStun => Category::Physical, - Skill::TestParry => Category::Physical, - Skill::TestBlock => Category::Physical, - Skill::TestDrain => Category::Spell, + Skill::TestTouch => Category::Red, + Skill::TestStun => Category::Red, + Skill::TestParry => Category::Red, + Skill::TestBlock => Category::Red, + Skill::TestSiphon => Category::Blue, } } @@ -574,7 +574,7 @@ impl Skill { match self { Skill::TriageTick => true, Skill::DecayTick => true, - Skill::DrainTick => true, + Skill::SiphonTick => true, _ => false, } } @@ -583,10 +583,10 @@ impl Skill { match self { // defensive block - Skill::Block => 10, // reduce dmg - Skill::Parry => 10, // avoid all dmg + Skill::Block => 10, // reduce damage + Skill::Parry => 10, // avoid all damage Skill::Snare => 10, - Skill::Shield => 10, // avoid magic dmg, + Skill::Shield => 10, // avoid magic damage, // fast phys combat Skill::Attack => 5, @@ -607,14 +607,14 @@ impl Skill { // general combat Skill::DecayTick => 2, // hot - Skill::Drain => 2, - Skill::DrainTick => 2, // hot + Skill::Siphon => 2, + Skill::SiphonTick => 2, // hot Skill::Hex => 2, Skill::Pause => 2, // extend durations Skill::Plague => 2, // aoe dot Skill::Silence => 2, Skill::Stun => 2, - Skill::Throw => 2, // no dmg stun, adds vulnerable + Skill::Throw => 2, // no damage stun, adds vulnerable Skill::TriageTick => 2, // hot Skill::Heal => 1, @@ -644,7 +644,7 @@ impl Skill { Skill::TestStun => 5, Skill::TestBlock => 10, Skill::TestParry => 10, - Skill::TestDrain => 10, + Skill::TestSiphon => 10, } } @@ -664,7 +664,7 @@ impl Skill { return resolution; } - match self.category() == Category::Physical { + match self.category() == Category::Red { true => { if let Some(evasion) = target.evade(*self) { resolution.results.push(evasion); @@ -703,7 +703,7 @@ impl Skill { Skill::Heal => heal(source, target, resolution), Skill::Triage => triage(source, target, resolution), // hot Skill::TriageTick => triage_tick(source, target, resolution), // hot - Skill::Throw => throw(source, target, resolution), // no dmg stun, adds vulnerable + Skill::Throw => throw(source, target, resolution), // no damage stun, adds vulnerable Skill::Charm => panic!("nyi"), // target casts random spell on teammate Skill::Calm => panic!("nyi"), // physical fear, taunt removal Skill::Rez => panic!("nyi"), @@ -712,11 +712,11 @@ impl Skill { // Destruction // ----------------- Skill::Blast => blast(source, target, resolution), - Skill::Amplify => amplify(source, target, resolution), // increase magic dmg + Skill::Amplify => amplify(source, target, resolution), // increase magic damage Skill::Decay => decay(source, target, resolution), // dot Skill::DecayTick => decay_tick(source, target, resolution), // dot - Skill::Drain => drain(source, target, resolution), - Skill::DrainTick => drain_tick(source, target, resolution), // hot + Skill::Siphon => siphon(source, target, resolution), + Skill::SiphonTick => siphon_tick(source, target, resolution), // hot Skill::Curse => curse(source, target, resolution), Skill::Plague => panic!("nyi"), // dot that spreads every turn Skill::Ruin => panic!("nyi"), // aoe version of blast @@ -725,9 +725,9 @@ impl Skill { // Purity // ----------------- // Skill::Precision => panic!("nyi"), - Skill::Empower => empower(source, target, resolution), // increased phys dmg - Skill::Slay => panic!("nyi"), // phys dmg mult by target magic dmg - Skill::Shield => shield(source, target, resolution), // target is immune to magic dmg and fx + Skill::Empower => empower(source, target, resolution), // increased phys damage + Skill::Slay => panic!("nyi"), // phys damage mult by target magic damage + Skill::Shield => shield(source, target, resolution), // target is immune to magic damage and fx Skill::Silence => silence(source, target, resolution), // target cannot cast spells Skill::Inquiry => panic!("nyi"), // Skill::Purify => purify(source, target, resolution), // dispel all debuffs @@ -751,7 +751,7 @@ impl Skill { Skill::TestStun => stun(source, target, resolution), Skill::TestBlock => block(source, target, resolution), Skill::TestParry => parry(source, target, resolution), - Skill::TestDrain => drain(source, target, resolution), + Skill::TestSiphon => siphon(source, target, resolution), } } @@ -780,8 +780,8 @@ impl Skill { } fn attack(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { - let amount = cryp.phys_dmg(); - resolution.results.push(target.deal_phys_dmg(Skill::Attack, amount)); + let amount = cryp.red_damage(); + resolution.results.push(target.deal_red_damage(Skill::Attack, amount)); return resolution; } @@ -828,7 +828,7 @@ fn empower(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> R } fn heal(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { - let amount = cryp.spell_dmg(); + let amount = cryp.blue_damage(); resolution.results.push(target.heal(Skill::Heal, amount)); return resolution; } @@ -858,14 +858,14 @@ fn triage(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Res } fn triage_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { - let amount = cryp.spell_dmg().wrapping_div(2); + let amount = cryp.blue_damage().wrapping_div(2); resolution.results.push(target.heal(Skill::TriageTick, amount)); return resolution; } fn blast(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { - let amount = cryp.spell_dmg(); - resolution.results.push(target.deal_spell_dmg(Skill::Blast, amount)); + let amount = cryp.blue_damage(); + resolution.results.push(target.deal_blue_damage(Skill::Blast, amount)); return resolution; } @@ -898,8 +898,8 @@ fn decay(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Reso } fn decay_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { - let amount = cryp.spell_dmg(); - resolution.results.push(target.deal_spell_dmg(Skill::DecayTick, amount)); + let amount = cryp.blue_damage(); + resolution.results.push(target.deal_blue_damage(Skill::DecayTick, amount)); return resolution; } @@ -915,28 +915,28 @@ fn curse(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Res return resolution;; } -fn drain(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { - let drain = CrypEffect { - effect: Effect::Drain, - duration: Effect::Drain.duration(), - tick: Some(Cast::new_tick(cryp, target, Skill::DrainTick)), +fn siphon(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { + let siphon = CrypEffect { + effect: Effect::Siphon, + duration: Effect::Siphon.duration(), + tick: Some(Cast::new_tick(cryp, target, Skill::SiphonTick)), }; - resolution.results.push(target.add_effect(Skill::Drain, drain)); + resolution.results.push(target.add_effect(Skill::Siphon, siphon)); return resolution;; } -fn drain_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { - let amount = cryp.spell_dmg(); - let drain_dmg = target.deal_spell_dmg(Skill::DrainTick, amount); - resolution.results.push(drain_dmg.clone()); +fn siphon_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { + let amount = cryp.blue_damage(); + let siphon_damage = target.deal_blue_damage(Skill::SiphonTick, amount); + resolution.results.push(siphon_damage.clone()); - match drain_dmg { + match siphon_damage { ResolutionResult::Damage { amount, mitigation, category: _, immunity } => { if !immunity.immune { resolution.results.push(cryp.heal(Skill::Heal, amount)); } }, - _ => panic!("drain tick dmg not dealt {:?}", drain_dmg), + _ => panic!("siphon tick damage not dealt {:?}", siphon_damage), } return resolution; @@ -960,7 +960,7 @@ fn purge(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Res if !immune { for (i, ce) in target.effects.clone().iter_mut().enumerate() { - if ce.effect.category() == Category::SpellBuff { + if ce.effect.category() == Category::BlueBuff { target.effects.remove(i); resolution.results.push(ResolutionResult::Removal { effect: ce.effect, immunity: immunity.clone() }); } @@ -976,7 +976,7 @@ fn purify(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Re if !immune { for (i, ce) in target.effects.clone().iter_mut().enumerate() { - if ce.effect.category() == Category::SpellDebuff { + if ce.effect.category() == Category::BlueDebuff { target.effects.remove(i); resolution.results.push(ResolutionResult::Removal { effect: ce.effect, immunity: immunity.clone() }); } @@ -1011,7 +1011,7 @@ mod tests { .learn(Skill::Heal) .create(); - x.deal_phys_dmg(Skill::Attack, 5); + x.deal_red_damage(Skill::Attack, 5); heal(&mut y, &mut x, Resolution::new(Skill::Heal)); } @@ -1051,7 +1051,7 @@ mod tests { .create(); // ensure it doesn't have 0 pd - x.phys_dmg.force(100); + x.red_damage.force(100); y.hp.force(500); block(&mut y.clone(), &mut y, Resolution::new(Skill::Block)); @@ -1078,13 +1078,13 @@ mod tests { .create(); // ensure it doesn't have 0 sd - x.spell_dmg.force(50); + x.blue_damage.force(50); // remove all mitigation - y.armour.force(0); - y.spell_shield.force(0); + y.red_shield.force(0); + y.blue_shield.force(0); - y.deal_phys_dmg(Skill::Attack, 5); + y.deal_red_damage(Skill::Attack, 5); let prev_hp = y.hp(); let res = Resolution::new(Skill::Triage); @@ -1118,11 +1118,11 @@ mod tests { .level(8) .create(); - x.spell_dmg.force(50); + x.blue_damage.force(50); amplify(&mut x.clone(), &mut x, Resolution::new(Skill::Amplify)); assert!(x.effects.iter().any(|e| e.effect == Effect::Amplify)); - assert_eq!(x.spell_dmg(), 100); + assert_eq!(x.blue_damage(), 100); } #[test] diff --git a/server/src/spec.rs b/server/src/spec.rs index 4fae0229..de3f793b 100644 --- a/server/src/spec.rs +++ b/server/src/spec.rs @@ -25,30 +25,30 @@ impl Spec { pub fn apply(&self, modified: u64, base: u64) -> u64 { match self.spec { - SpecType::PhysDamage5 => modified + (base * 5 / 100), - SpecType::SpellDamage5 => modified + (base * 5 / 100), + SpecType::RedDamage5 => modified + (base * 5 / 100), + SpecType::BlueDamage5 => modified + (base * 5 / 100), } } } #[derive(Debug,Copy,Clone,Serialize,Deserialize,PartialEq)] pub enum SpecType { - PhysDamage5, - SpellDamage5, + RedDamage5, + BlueDamage5, } impl SpecType { fn affects(&self) -> Stat { match *self { - SpecType::PhysDamage5 => Stat::PhysicalDamage, - SpecType::SpellDamage5 => Stat::SpellDamage, + SpecType::RedDamage5 => Stat::RedDamage, + SpecType::BlueDamage5 => Stat::BlueDamage, } } fn level(&self) -> SpecLevel { match *self { - SpecType::PhysDamage5 => SpecLevel::Common, - SpecType::SpellDamage5 => SpecLevel::Common, + SpecType::RedDamage5 => SpecLevel::Common, + SpecType::BlueDamage5 => SpecLevel::Common, } } } diff --git a/server/src/vbox.rs b/server/src/vbox.rs new file mode 100644 index 00000000..9ce99389 --- /dev/null +++ b/server/src/vbox.rs @@ -0,0 +1,406 @@ +use std::iter; +use uuid::Uuid; + +// drops +use rand::prelude::*; +use rand::{thread_rng}; +use rand::distributions::{WeightedIndex}; + +use serde_cbor::{from_slice, to_vec}; + +use postgres::transaction::Transaction; + +use failure::Error; +use failure::err_msg; + +use account::Account; +use rpc::{VboxStateParams, VboxAcceptParams, VboxDiscardParams, VboxCombineParams, VboxApplyParams, VboxDropParams}; +use skill::{Skill}; +use cryp::{cryp_get, cryp_write}; + +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +pub enum Var { + Blue, + Green, + Red, + + Attack, + Block, + Stun, + Buff, + Debuff, + + Amplify, + Banish, + Blast, + Curse, + Empower, + Haste, + Heal, + Hex, + Parry, + Purge, + Purify, + Reflect, + Ruin, + Shield, + Silence, + Slay, + Slow, + Snare, + Strangle, + Strike, + Siphon, + Survival, + Taunt, + Throw, + Toxic, + Triage, +} + +impl Var { + fn is_base(&self) -> bool { + match self { + Var::Attack | + Var::Block | + Var::Stun | + Var::Debuff | + Var::Buff => true, + _ => false, + } + } + + fn skill(&self) -> Result { + match self { + Var::Amplify => Ok(Skill::Amplify), + Var::Banish => Ok(Skill::Banish), + Var::Blast => Ok(Skill::Blast), + Var::Curse => Ok(Skill::Curse), + Var::Empower => Ok(Skill::Empower), + Var::Haste => Ok(Skill::Haste), + Var::Heal => Ok(Skill::Heal), + Var::Hex => Ok(Skill::Hex), + Var::Parry => Ok(Skill::Parry), + Var::Purge => Ok(Skill::Purge), + Var::Purify => Ok(Skill::Purify), + // Var::Reflect => Ok(Skill::Reflect), + Var::Ruin => Ok(Skill::Ruin), + Var::Shield => Ok(Skill::Shield), + Var::Silence => Ok(Skill::Silence), + Var::Slay => Ok(Skill::Slay), + Var::Slow => Ok(Skill::Slow), + Var::Snare => Ok(Skill::Snare), + Var::Strangle => Ok(Skill::Strangle), + // Var::Strike => Ok(Skill::Strike), + // Var::Survival => Ok(Skill::Survival), + // Var::Taunt => Ok(Skill::Taunt), + Var::Throw => Ok(Skill::Throw), + // Var::Toxic => Ok(Skill::Toxic), + Var::Triage => Ok(Skill::Triage), + _ => Err(err_msg("not a usable var")) + } + } +} + +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +enum ColourCode { + RR, + GG, + BB, + RG, + BR, + GB, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +pub struct Vbox { + pub id: Uuid, + pub balance: u16, + pub free: Vec, + pub bound: Vec, + pub game: Uuid, + pub account: Uuid, +} + +impl Vbox { + pub fn new(account_id: Uuid, game_id: Uuid) -> Vbox { + Vbox { + id: Uuid::new_v4(), + account: account_id, + game: game_id, + free: vec![], + bound: vec![], + balance: 0, + } + } + + pub fn fill(mut self: Vbox) -> Vbox { + let vars = vec![ + (Var::Red, 1), + (Var::Green, 1), + (Var::Blue, 1), + + (Var::Attack, 1), + (Var::Block, 1), + (Var::Buff, 1), + (Var::Debuff, 1), + (Var::Stun, 1), + ]; + + self.free = iter:: + repeat_with(|| { + let mut rng = thread_rng(); + let dist = WeightedIndex::new(vars.iter().map(|item| item.1)).unwrap(); + return vars[dist.sample(&mut rng)].0; + }) + .take(8) + .collect::>(); + + self + } + + pub fn accept(&mut self, i: usize) -> Result<&mut Vbox, Error> { + if self.bound.len() >= 9 { + return Err(err_msg("too many vars bound")); + } + + self.free.get(i).ok_or(format_err!("no var at index {:?}", i))?; + self.bound.push(self.free.remove(i)); + + Ok(self) + } + + pub fn drop(&mut self, i: usize) -> Result<&mut Vbox, Error> { + self.bound.get(i).ok_or(format_err!("no var at index {:?}", i))?; + self.bound.remove(i); + // balance update + Ok(self) + } + + pub fn combine(&mut self, mut indices: Vec) -> Result<&mut Vbox, Error> { + if indices.len() != 3 { + return Err(err_msg("exactly 3 indices required")); + } + + if !indices.iter().all(|i| self.bound.get(*i).is_some()) { + return Err(err_msg("var missing index")); + } + + // have to sort the indices and keep track of the iteration + // because when removing the elements the array shifts + indices.sort(); + let mut vars = indices + .iter() + .enumerate() + .map(|(i, index)| { + self.bound.remove(*index - i) + }) + .collect::>(); + + let base_index = vars + .iter() + .position(|v| v.is_base()) + .ok_or(err_msg("no base item selected"))?; + + let base = vars.remove(base_index); + + // fold colours into RGB + let colours = vars + .iter() + .fold([0, 0, 0], |mut acc, c| { + match c { + Var::Red => acc[0] += 1, + Var::Green => acc[1] += 1, + Var::Blue => acc[2] += 1, + _ => (), + }; + acc + }); + + let colour_code = match colours { + [2,0,0] => ColourCode::RR, + [0,2,0] => ColourCode::GG, + [0,0,2] => ColourCode::BB, + [1,1,0] => ColourCode::RG, + [0,1,1] => ColourCode::GB, + [1,0,1] => ColourCode::BR, + _ => return Err(err_msg("not a combo")), + }; + + let new = match base { + Var::Attack => match colour_code { + ColourCode::RR => Var::Strike, + ColourCode::GG => Var::Heal, + ColourCode::BB => Var::Blast, + ColourCode::RG => Var::Slay, // + ColourCode::GB => return Err(err_msg("unhandled skill combo")), + ColourCode::BR => Var::Banish, // + }, + Var::Block => match colour_code { + ColourCode::RR => Var::Parry, + ColourCode::GG => Var::Reflect, + ColourCode::BB => Var::Toxic, + ColourCode::RG => Var::Taunt, + ColourCode::GB => Var::Shield, + ColourCode::BR => return Err(err_msg("unhandled skill combo")), + }, + Var::Buff => match colour_code { + ColourCode::RR => Var::Empower, + ColourCode::GG => Var::Triage, + ColourCode::BB => Var::Amplify, + ColourCode::RG => Var::Survival, + ColourCode::GB => return Err(err_msg("unhandled skill combo")), + ColourCode::BR => Var::Haste, + }, + Var::Debuff => match colour_code { + ColourCode::RR => Var::Snare, + ColourCode::GG => Var::Purge, + ColourCode::BB => Var::Curse, + ColourCode::RG => return Err(err_msg("unhandled skill combo")), + ColourCode::GB => Var::Siphon, + ColourCode::BR => Var::Slow, + }, + Var::Stun => match colour_code { + ColourCode::RR => Var::Strangle, + ColourCode::GG => Var::Throw, + ColourCode::BB => Var::Ruin, + ColourCode::RG => return Err(err_msg("unhandled skill combo")), + ColourCode::GB => Var::Silence, + ColourCode::BR => Var::Hex, + }, + _ => panic!("wrong base {:?}", base), + }; + + self.bound.push(new); + + Ok(self) + } +} + +pub fn vbox_create(vbox: Vbox, tx: &mut Transaction, account: &Account) -> Result { + let vbox_bytes = to_vec(&vbox)?; + + let query = " + INSERT INTO vbox (id, account, game, data) + VALUES ($1, $2, $3, $4) + RETURNING id; + "; + + let result = tx + .query(query, &[&vbox.id, &account.id, &vbox.game, &vbox_bytes])?; + + result.iter().next().ok_or(format_err!("no vbox written"))?; + + // println!("{:} wrote vbox", vbox.id); + + return Ok(vbox); +} + +pub fn vbox_write(vbox: Vbox, tx: &mut Transaction) -> Result { + let vbox_bytes = to_vec(&vbox)?; + + let query = " + UPDATE vbox + SET data = $1 + WHERE id = $2 + RETURNING id, account, data; + "; + + let result = tx + .query(query, &[&vbox_bytes, &vbox.id])?; + + result.iter().next().ok_or(err_msg("no vbox row returned"))?; + + // println!("{:?} wrote vbox", vbox.id); + + return Ok(vbox); +} + + +pub fn vbox_get(tx: &mut Transaction, game_id: Uuid, account: &Account) -> Result { + let query = " + SELECT * + FROM vbox + WHERE account = $1 + AND game = $2; + "; + + let result = tx + .query(query, &[&account.id, &game_id])?; + + let returned = match result.iter().next() { + Some(row) => row, + None => return Err(err_msg("vbox not found")), + }; + + // tells from_slice to cast into a cryp + let vbox_bytes: Vec = returned.get("data"); + let vbox = from_slice::(&vbox_bytes)?; + + return Ok(vbox); +} + +pub fn vbox_state(params: VboxStateParams, tx: &mut Transaction, account: &Account) -> Result { + match vbox_get(tx, params.game_id, account) { + Ok(v) => Ok(v), + Err(e) => { + println!("{:?}", e); + vbox_create(Vbox::new(account.id, params.game_id).fill(), tx, account) + } + } +} + +pub fn vbox_discard(params: VboxDiscardParams, tx: &mut Transaction, account: &Account) -> Result { + let vbox = vbox_get(tx, params.game_id, account)?; + return vbox_write(vbox.fill(), tx); +} + +pub fn vbox_accept(params: VboxAcceptParams, tx: &mut Transaction, account: &Account) -> Result { + let mut vbox = vbox_get(tx, params.game_id, account)?; + vbox.accept(params.index)?; + return vbox_write(vbox, tx); +} + +pub fn vbox_combine(params: VboxCombineParams, tx: &mut Transaction, account: &Account) -> Result { + let mut vbox = vbox_get(tx, params.game_id, account)?; + vbox.combine(params.indices)?; + return vbox_write(vbox, tx); +} + +pub fn vbox_drop(params: VboxDropParams, tx: &mut Transaction, account: &Account) -> Result { + let mut vbox = vbox_get(tx, params.game_id, account)?; + vbox.drop(params.index)?; + return vbox_write(vbox, tx); +} + +pub fn vbox_apply(params: VboxApplyParams, tx: &mut Transaction, account: &Account) -> Result { + let mut vbox = vbox_get(tx, params.game_id, account)?; + let mut cryp = cryp_get(tx, params.cryp_id, account.id)?; + + let var = vbox.bound.remove(params.index); + + // done here because i teach them a tonne of skills for tests + let max_skills = 4; + if cryp.skills.len() >= max_skills { + return Err(format_err!("cryp at max skills ({:?})", max_skills)); + } + + let skill = var.skill()?; + cryp = cryp.learn(skill); + cryp_write(cryp, tx)?; + return vbox_write(vbox, tx); +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn combine_test() { + let mut vbox = Vbox::new(Uuid::new_v4(), Uuid::new_v4()); + vbox.bound = vec![Var::Attack, Var::Green, Var::Green]; + vbox.combine(vec![1,2,0]).unwrap(); + assert_eq!(vbox.bound[0], Var::Heal); + } +} \ No newline at end of file