Add time study tree scaffolding

This commit is contained in:
Andrei Andreev 2018-11-26 01:46:50 +03:00
parent 28a4b60d98
commit 1d62aab2c1
8 changed files with 776 additions and 22 deletions

View File

@ -9,7 +9,7 @@
<script type="text/javascript" src="javascripts/lib/jquery-3.2.1.min.js"></script>
<link href="https://fonts.googleapis.com/css?family=PT+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="stylesheets/styles.css?3">
<link rel="stylesheet" type="text/css" href="stylesheets/timestudies.css">
<link rel="stylesheet" type="text/css" href="stylesheets/time-studies.css">
<link rel="stylesheet" type="text/css" href="stylesheets/tooltips.css">
<link rel="stylesheet" type="text/css" href="stylesheets/vis.css">
<link rel="stylesheet" type="text/css" href="stylesheets/tln.css">
@ -965,7 +965,7 @@
<script type="text/javascript" src="javascripts/components/infinity/replicanti/replicanti-galaxy-button.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/eternity-tab.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/time-studies-tab.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/time-studies/time-studies-tab.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/eternity-upgrades-tab.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/eternity-milestones-tab.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/time-dilation-tab.js"></script>

View File

@ -1,4 +0,0 @@
Vue.component("time-studies-tab", {
template:
`<div>Time studies</div>`
});

View File

@ -0,0 +1,239 @@
const remMixin = {
methods: {
rem(value) {
return value + "rem";
}
}
};
Vue.component("time-studies-tab", {
mixins: [remMixin],
data: function() {
const layout = TimeStudyTreeLayout.instance;
return {
width: this.rem(layout.width),
height: this.rem(layout.height),
studies: layout.studies,
connections: layout.connections
};
},
computed: {
tabStyleObject: function() {
return {
width: this.width,
height: this.height
};
}
},
template:
`<div class="l-time-studies-tab" :style="tabStyleObject">
<time-study
v-for="(setup, index) in studies"
:key="'study' + index"
:setup="setup"
/>
<svg :style="tabStyleObject" class="l-time-study-connection">
<time-study-connection
v-for="(setup, index) in connections"
:key="'connection' + index"
:setup="setup"
/>
</svg>
</div>`
});
Vue.component("time-study", {
mixins: [remMixin],
props: {
setup: Object
},
computed: {
styleObject: function() {
return {
top: this.rem(this.setup.top),
left: this.rem(this.setup.left)
};
}
},
template:
`<div class="o-time-study l-time-study" :style="styleObject">
<hint-text>{{setup.study._id}}</hint-text>
</div>`
});
Vue.component("time-study-connection", {
mixins: [remMixin],
props: {
setup: Object
},
template:
`<line
:x1="rem(setup.x1)"
:y1="rem(setup.y1)"
:x2="rem(setup.x2)"
:y2="rem(setup.y2)"
class="o-time-study-connection" />`
});
class TimeStudyRow {
constructor(layout, items) {
this.layout = layout;
this.items = items;
}
get width() {
const itemCount = this.items.length;
const layout = this.layout;
return itemCount * layout.itemWidth + (itemCount - 1) * layout.spacing;
}
itemPosition(column, treeLayout) {
const layout = this.layout;
const treeWidth = treeLayout.width;
const rowLeft = (treeWidth - this.width) / 2;
return rowLeft + column * layout.itemWidth + column * layout.spacing;
}
}
class TimeStudyRowLayout {
constructor(props) {
this.itemWidth = props.itemWidth;
this.itemHeight = props.itemHeight;
this.spacing = props.spacing;
}
}
class TimeStudySetup {
constructor(props) {
this.study = props.study;
this.row = props.row;
this.column = props.column;
}
setPosition(layout) {
this.top = layout.itemPosition(this.row);
const row = layout.rows[this.row];
this.left = row.itemPosition(this.column, layout);
this.width = row.layout.itemWidth;
this.height = row.layout.itemHeight;
}
}
class TimeStudyConnectionSetup {
constructor(from, to) {
this.from = from;
this.to = to;
}
/**
* @param {TimeStudySetup[]} studies
*/
setPosition(studies) {
const from = studies.find(study => study.study === this.from);
const to = studies.find(study => study.study === this.to);
this.x1 = from.left + from.width / 2;
this.y1 = from.top + from.height / 2;
this.x2 = to.left + to.width / 2;
this.y2 = to.top + to.height / 2;
}
}
class TimeStudyTreeLayout {
constructor() {
this.spacing = 4;
const normalRowLayout = new TimeStudyRowLayout({
itemWidth: 17,
itemHeight: 8,
spacing: 3
});
const normalRow = (...items) => new TimeStudyRow(normalRowLayout, items);
const TS = id => TimeStudy(id);
const EC = id => TimeStudy.eternityChallenge(id);
/**
* @type {TimeStudyRow[]}
*/
this.rows = [
normalRow(null, TS(11), null),
normalRow(TS(21), TS(22)),
normalRow(TS(33), TS(31), TS(32), null),
normalRow(TS(41), TS(42)),
normalRow(null, TS(51), EC(5)),
normalRow(null, TS(61), TS(62)),
normalRow(TS(71), TS(72), TS(73)),
normalRow(TS(81), TS(82), TS(83)),
normalRow(TS(91), TS(92), TS(93)),
normalRow(TS(101), TS(102), TS(103)),
normalRow(EC(7), TS(111), null),
normalRow(TS(121), TS(122), TS(123)),
normalRow(EC(6), TS(131), TS(132), TS(133), EC(8)),
normalRow(TS(141), TS(142), TS(143)),
normalRow(null, EC(9), TS(151), null, EC(4)),
normalRow(TS(161), TS(162)),
normalRow(TS(171)),
normalRow(EC(1), EC(2), EC(3)),
normalRow(TS(181)),
normalRow(EC(10)),
normalRow(TS(191), TS(192), TS(193)),
normalRow(TS(201)),
normalRow(TS(211), TS(212), TS(213), TS(214)),
normalRow(TS(221), TS(222), TS(223), TS(224), TS(225), TS(226), TS(227), TS(228)),
normalRow(TS(231), TS(232), TS(233), TS(234)),
normalRow(EC(11), EC(12)),
normalRow(TimeStudy.dilation),
normalRow(TimeStudy.timeDimension(5), TimeStudy.timeDimension(6)),
normalRow(TimeStudy.timeDimension(7), TimeStudy.timeDimension(8)),
normalRow(TimeStudy.reality),
];
/**
* @type {TimeStudySetup[]}
*/
this.studies = [];
for (let rowIndex = 0; rowIndex < this.rows.length; rowIndex++) {
const row = this.rows[rowIndex];
for (let columnIndex = 0; columnIndex < row.items.length; columnIndex++) {
const study = row.items[columnIndex];
if (study === null) continue;
this.studies.push(new TimeStudySetup({
study: study,
row: rowIndex,
column: columnIndex
}));
}
}
/**
* @type {TimeStudyConnectionSetup[]}
*/
this.connections = TimeStudy.allConnections
.map(c => new TimeStudyConnectionSetup(c.from, c.to));
this.width = this.rows.map(row => row.width).max();
const heightNoSpacing = this.rows.map(r => r.layout.itemHeight).sum();
this.height = heightNoSpacing + (this.rows.length - 1) * this.spacing;
for (let study of this.studies) {
study.setPosition(this);
}
for (let connection of this.connections) {
connection.setPosition(this.studies);
}
}
itemPosition(row) {
const rows = this.rows.slice(0, row);
const heightNoSpacing = rows.map(r => r.layout.itemHeight).sum();
return heightNoSpacing + rows.length * this.spacing;
}
static get instance() {
if (this._instance === undefined) {
this._instance = new TimeStudyTreeLayout();
}
return this._instance;
}
}

View File

@ -5,5 +5,5 @@ Vue.component("hint-text", {
}
},
template:
`<div v-if="isVisible" class="o-hint-text l-hint-text"><slot /></div>`
`<div v-show="isVisible" class="o-hint-text l-hint-text"><slot /></div>`
});

View File

@ -138,4 +138,14 @@ Array.prototype.last = function(predicate) {
/**
* @type {number[]}
*/
Array.dimensionTiers = Array.range(1, 8);
Array.dimensionTiers = Array.range(1, 8);
Array.prototype.sum = function() {
if (this.length === 0) return 0;
return this.reduce(Number.sumReducer);
};
Array.prototype.max = function() {
if (this.length === 0) return 0;
return this.reduce((a, b) => Math.max(a, b));
};

View File

@ -573,3 +573,489 @@ $(document).ready(function() {
}
});
});
const TimeStudyType = {
NORMAL: 0,
EC: 1,
DILATION: 2
};
const TimeStudyPath = {
NORMAL: 0,
INFINITY: 1,
TIME: 2,
ACTIVE: 3,
PASSIVE: 4,
IDLE: 5,
LIGHT: 6,
DARK: 7
};
class TimeStudyInfo {
constructor(props) {
this._id = props.id;
this._cost = props.cost;
this.type = TimeStudyType.NORMAL;
this.incomingConnections = [];
}
get id() {
return this._id;
}
get cost() {
return this._cost;
}
get isBought() {
return player.timestudy.studies.includes(this._id);
}
}
TimeStudyInfo.studies = function() {
const allProps = [
{
id: 11,
cost: 1
},
{
id: 21,
cost: 1
},
{
id: 22,
cost: 1
},
{
id: 31,
cost: 1
},
{
id: 32,
cost: 1
},
{
id: 33,
cost: 1
},
{
id: 41,
cost: 1
},
{
id: 42,
cost: 1
},
{
id: 51,
cost: 1
},
{
id: 61,
cost: 1
},
{
id: 62,
cost: 1
},
{
id: 71,
cost: 1
},
{
id: 72,
cost: 1
},
{
id: 73,
cost: 1
},
{
id: 81,
cost: 1
},
{
id: 82,
cost: 1
},
{
id: 83,
cost: 1
},
{
id: 91,
cost: 1
},
{
id: 92,
cost: 1
},
{
id: 93,
cost: 1
},
{
id: 101,
cost: 1
},
{
id: 102,
cost: 1
},
{
id: 103,
cost: 1
},
{
id: 111,
cost: 1
},
{
id: 121,
cost: 1
},
{
id: 122,
cost: 1
},
{
id: 123,
cost: 1
},
{
id: 131,
cost: 1
},
{
id: 132,
cost: 1
},
{
id: 133,
cost: 1
},
{
id: 141,
cost: 1
},
{
id: 142,
cost: 1
},
{
id: 143,
cost: 1
},
{
id: 151,
cost: 1
},
{
id: 161,
cost: 1
},
{
id: 162,
cost: 1
},
{
id: 171,
cost: 1
},
{
id: 181,
cost: 1
},
{
id: 191,
cost: 1
},
{
id: 192,
cost: 1
},
{
id: 193,
cost: 1
},
{
id: 201,
cost: 1
},
{
id: 211,
cost: 1
},
{
id: 212,
cost: 1
},
{
id: 213,
cost: 1
},
{
id: 214,
cost: 1
},
{
id: 221,
cost: 1
},
{
id: 222,
cost: 1
},
{
id: 223,
cost: 1
},
{
id: 224,
cost: 1
},
{
id: 225,
cost: 1
},
{
id: 226,
cost: 1
},
{
id: 227,
cost: 1
},
{
id: 228,
cost: 1
},
{
id: 231,
cost: 1
},
{
id: 232,
cost: 1
},
{
id: 233,
cost: 1
},
{
id: 234,
cost: 1
},
];
const studies = [];
for (let props of allProps) {
studies[props.id] = new TimeStudyInfo(props);
}
return studies;
}();
/**
* @returns {TimeStudyInfo}
*/
function TimeStudy(id) {
return TimeStudyInfo.studies[id];
}
class ECTimeStudyInfo {
constructor(props) {
this._id = props.id;
this.type = TimeStudyType.EC;
this.incomingConnections = [];
}
get isBought() {
return player.eternityChallUnlocked === this._id;
}
}
ECTimeStudyInfo.studies = Array.range(1, 12).map(id => new ECTimeStudyInfo({ id: id }));
TimeStudy.eternityChallenge = function(id) {
return ECTimeStudyInfo.studies[id - 1];
};
class DilationTimeStudy {
constructor(props) {
this._id = props.id;
this.type = TimeStudyType.DILATION;
this.incomingConnections = [];
}
get isBought() {
return player.dilation.studies.includes(this._id);
}
}
TimeStudy.dilation = new DilationTimeStudy({ id: 1 });
DilationTimeStudy.tdStudies = Array.range(5, 4).map(tier => new DilationTimeStudy({ id: tier - 3 }));
TimeStudy.timeDimension = function(tier) {
return DilationTimeStudy.tdStudies[tier - 5];
};
TimeStudy.reality = new DilationTimeStudy({ id: 6 });
class TimeStudyConnection {
constructor(from, to, override) {
this._from = from;
this._to = to;
this._override = override;
}
get from() {
return this._from;
}
get to() {
return this._to;
}
get isOverridden() {
return this._override !== undefined && this._override();
}
get isSatisfied() {
return this.isOverridden || this._from.isBought;
}
}
/**
* @type {TimeStudyConnection[]}
*/
TimeStudy.allConnections = function() {
const TS = id => TimeStudy(id);
const EC = id => TimeStudy.eternityChallenge(id);
const connections = [
[TS(11), TS(21)],
[TS(11), TS(22)],
[TS(21), TS(31)],
[TS(21), TS(33)],
[TS(22), TS(32)],
[TS(31), TS(41)],
[TS(32), TS(42)],
[TS(41), TS(51)],
[TS(42), TS(51)],
[TS(42), EC(5)],
[TS(51), TS(61)],
[EC(5), TS(62)],
[TS(61), TS(71)],
[TS(61), TS(72)],
[TS(61), TS(73)],
[TS(71), TS(81)],
[TS(72), TS(82)],
[TS(73), TS(83)],
[TS(81), TS(91)],
[TS(82), TS(92)],
[TS(83), TS(93)],
[TS(91), TS(101)],
[TS(92), TS(102)],
[TS(93), TS(103)],
[TS(101), TS(111)],
[TS(102), TS(111)],
[TS(103), TS(111)],
[TS(111), EC(7)],
[TS(111), TS(121)],
[TS(111), TS(122)],
[TS(111), TS(123)],
[TS(121), TS(131)],
[TS(122), TS(132)],
[TS(123), TS(133)],
[TS(121), EC(6)],
[TS(123), EC(8)],
[TS(131), TS(141)],
[TS(132), TS(142)],
[TS(133), TS(143)],
[TS(141), TS(151)],
[TS(142), TS(151)],
[TS(143), TS(151)],
[TS(143), EC(4)],
[TS(151), EC(9)],
[TS(151), TS(161)],
[TS(151), TS(162)],
[TS(161), TS(171)],
[TS(162), TS(171)],
[TS(171), EC(1)],
[TS(171), EC(2)],
[TS(171), EC(3)],
[EC(1), TS(181)],
[EC(2), TS(181)],
[EC(3), TS(181)],
[TS(181), EC(10)],
[EC(10), TS(191)],
[EC(10), TS(192)],
[EC(10), TS(193)],
[TS(192), TS(201)],
[TS(191), TS(211)],
[TS(191), TS(212)],
[TS(193), TS(213)],
[TS(193), TS(214)],
[TS(211), TS(221)],
[TS(211), TS(222)],
[TS(212), TS(223)],
[TS(212), TS(224)],
[TS(213), TS(225)],
[TS(213), TS(226)],
[TS(214), TS(227)],
[TS(214), TS(228)],
[TS(221), TS(231)],
[TS(222), TS(231)],
[TS(223), TS(232)],
[TS(224), TS(232)],
[TS(225), TS(233)],
[TS(226), TS(233)],
[TS(227), TS(234)],
[TS(228), TS(234)],
[TS(231), EC(11)],
[TS(232), EC(11)],
[TS(233), EC(12)],
[TS(234), EC(12)],
[EC(11), TimeStudy.dilation],
[EC(12), TimeStudy.dilation],
[TimeStudy.dilation, TimeStudy.timeDimension(5)],
[TimeStudy.timeDimension(5), TimeStudy.timeDimension(6)],
[TimeStudy.timeDimension(6), TimeStudy.timeDimension(7)],
[TimeStudy.timeDimension(7), TimeStudy.timeDimension(8)],
[TimeStudy.timeDimension(8), TimeStudy.reality]
]
.map(props => new TimeStudyConnection(props[0], props[1], props[2]));
for (let connection of connections) {
connection.to.incomingConnections.push(connection);
}
return connections;
}();

View File

@ -0,0 +1,37 @@
.l-time-studies-tab {
align-self: center;
position: relative;
margin: 0 -1000%;
}
.l-time-study {
position: absolute;
z-index: 1;
}
.l-time-study-connection {
position: absolute;
left: 0;
top: 0;
z-index: 0;
}
.o-time-study {
color: #b341e0;
background: black;
border: .1rem solid #b341e0;
font-weight: bold;
font-family: Typewriter, serif;
width: 17rem;
height: 8rem;
transition-duration: 0.2s;
cursor: pointer;
border-radius: .4rem;
font-size: 1.05rem;
animation: studyGlowIn 7s infinite;
}
.o-time-study-connection {
stroke-width: 1rem;
stroke: #444444;
}

View File

@ -1,14 +0,0 @@
.o-time-study {
color: #b341e0;
background: black;
border: .1rem solid #b341e0;
font-weight: bold;
font-family: Typewriter, serif;
width: 17rem;
height: 8rem;
transition-duration: 0.2s;
cursor: pointer;
border-radius: .4rem;
font-size: 1.05rem;
animation: studyGlowIn 7s infinite;
}