Merge branch 'combos'

This commit is contained in:
ntr 2019-02-21 15:03:21 +11:00
commit 9b68fd0a09
30 changed files with 1388 additions and 588 deletions

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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' }
]
},
};

View File

@ -29,7 +29,7 @@ function renderCryps() {
},
},
scene: [
Background,
// Background,
Header,
],
};

View File

@ -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);

View File

@ -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';
}
}

View File

@ -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);
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() {

View File

@ -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));

View File

@ -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);

View File

@ -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'},

View File

@ -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,
];

View File

@ -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,
};
}

View File

@ -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 = `

View File

@ -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 () => {};

View File

@ -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 () => {};

View File

@ -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"
}

View File

@ -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 #

View File

@ -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`

View File

@ -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 |

166
server/SPECS.md Executable file
View File

@ -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

View File

@ -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?

View File

@ -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<Cryp, Error> {
pub fn spec_remove(&mut self, spec: Spec) -> Result<&mut Cryp, Error> {
let find_spec = |spec_v: &Vec<Spec>| 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::<Vec<Effect>>();
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::<Vec<Effect>>();
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::<Vec<Effect>>();
// 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::<Vec<Effect>>();
// 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<Cryp, Error> {
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);
}

View File

@ -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]

View File

@ -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]

View File

@ -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)]
@ -53,15 +53,15 @@ impl Item {
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),
// ],
}

View File

@ -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};

View File

@ -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<u8>, tx: &mut Transaction, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
match from_slice::<AccountCreateMsg>(&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<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
Ok(RpcResponse {
method: "account_items".to_string(),
params: RpcResult::ItemList(items_list(tx, &account)?)
})
}
fn account_zone(_data: Vec<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
Ok(RpcResponse {
method: "zone_state".to_string(),
@ -328,49 +322,6 @@ impl Rpc {
})
}
fn press_r(_data: Vec<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
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<u8>, tx: &mut Transaction, account: Account, client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
let msg = from_slice::<ItemUseMsg>(&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<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
// let _msg = from_slice::<ZoneCreateMsg>(&data).or(Err(err_msg("invalid params")))?;
@ -404,6 +355,77 @@ impl Rpc {
return Ok(response);
}
fn vbox_state(data: Vec<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
let msg = from_slice::<VboxStateMsg>(&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<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
let msg = from_slice::<VboxAcceptMsg>(&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<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
let msg = from_slice::<VboxDiscardMsg>(&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<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
let msg = from_slice::<VboxCombineMsg>(&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<u8>, tx: &mut Transaction, account: Account, client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
let msg = from_slice::<VboxApplyMsg>(&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<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
let msg = from_slice::<VboxDropMsg>(&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<Cryp>),
GameState(Game),
GameJoinableList(Vec<Game>),
ItemList(Vec<Item>),
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<usize>,
}
#[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 {

View File

@ -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<Stat> {
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]

View File

@ -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,
}
}
}

406
server/src/vbox.rs Normal file
View File

@ -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<Skill, Error> {
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<Var>,
pub bound: Vec<Var>,
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::<Vec<Var>>();
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<usize>) -> 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::<Vec<Var>>();
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<Vbox, Error> {
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<Vbox, Error> {
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<Vbox, Error> {
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<u8> = returned.get("data");
let vbox = from_slice::<Vbox>(&vbox_bytes)?;
return Ok(vbox);
}
pub fn vbox_state(params: VboxStateParams, tx: &mut Transaction, account: &Account) -> Result<Vbox, Error> {
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<Vbox, Error> {
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<Vbox, Error> {
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<Vbox, Error> {
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<Vbox, Error> {
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<Vbox, Error> {
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);
}
}