tag:blogger.com,1999:blog-29213438483733685802024-03-13T05:54:05.168-07:00oneblogsleftoneblogsleft will cover game design/development, coding, games & computers in general, and anything else onelivesleft related too verbose to be covered by a tweetUnknownnoreply@blogger.comBlogger33125tag:blogger.com,1999:blog-2921343848373368580.post-63818912948604903382024-01-06T16:13:00.000-08:002024-01-06T16:41:53.866-08:00Modern C# & Performance (in brief)This video came up in my youtube feed the other day (it's only 12 mins long, go check it out): <div><br /><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/mGnpFGIRCjA" width="320" youtube-src-id="mGnpFGIRCjA"></iframe></div><br /></div><div>In it the presenter shows some code written in classic, old-school imperative C#, and then progressively factors it into his ideal of modern C#. </div><div><br /></div><div>The algorithm he is implementing takes a sequence of strings, looks at all the ones which can be parsed as an int, and returns the total of the two largest values. It returns -1 as an error code (so we can assume that the numbers passed in are all >= 0).</div><div><br /></div><div>So, he starts with this:</div><div><br /></div><div><div style="background-color: #0b0b18; color: #d3af86; font-family: consolas, inconsolataGo, monospace, Consolas, "Courier New", monospace; font-size: 16px; line-height: 22px; white-space: pre;"><div> </div><div> <span style="color: #98676a;">int</span> <span style="color: #a88960;">GetMagicSum0</span><span style="color: #a88f73;">(</span><span style="color: #f06431;">IEnumerable</span><span style="color: #a88f73;"><</span><span style="color: #98676a;">string</span><span style="color: #a88f73;">></span> items<span style="color: #a88f73;">)</span> <span style="color: #a88f73;">{</span></div><div> <span style="color: #f06431;">List</span><span style="color: #a88f73;"><</span><span style="color: #98676a;">int</span><span style="color: #a88f73;">></span> all <span style="color: #a38888;">=</span> <span style="color: #d38686;">new</span> <span style="color: #f06431;">List</span><span style="color: #a88f73;"><</span><span style="color: #98676a;">int</span><span style="color: #a88f73;">>()</span><span style="color: #757c50;">;</span></div><div> <span style="color: #98676a;">using</span> <span style="color: #a88f73;">(</span><span style="color: #f06431;">IEnumerator</span><span style="color: #a88f73;"><</span><span style="color: #98676a;">string</span><span style="color: #a88f73;">></span> enumerator <span style="color: #a38888;">=</span> items<span style="color: #a88f73;">.</span><span style="color: #a88960;">GetEnumerator</span><span style="color: #a88f73;">())</span> <span style="color: #a88f73;">{</span></div><div> <span style="color: #d38686;">while</span> <span style="color: #a88f73;">(</span>enumerator<span style="color: #a88f73;">.</span><span style="color: #a88960;">MoveNext</span><span style="color: #a88f73;">())</span> <span style="color: #a88f73;">{</span></div><div> <span style="color: #98676a;">int</span> number<span style="color: #757c50;">;</span></div><div> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>int<span style="color: #a88f73;">.</span><span style="color: #a88960;">TryParse</span><span style="color: #a88f73;">(</span>enumerator<span style="color: #a88f73;">.</span>Current<span style="color: #7a6852;">,</span> <span style="color: #d38686;">out</span> number<span style="color: #a88f73;">))</span></div><div> all<span style="color: #a88f73;">.</span><span style="color: #a88960;">Add</span><span style="color: #a88f73;">(</span>number<span style="color: #a88f73;">)</span><span style="color: #757c50;">;</span></div><div> <span style="color: #a88f73;">}</span></div><div> <span style="color: #a88f73;">}</span></div><br /><div> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>all<span style="color: #a88f73;">.</span>Count <span style="color: #a38888;"><</span> <span style="color: #bb9161;">2</span><span style="color: #a88f73;">)</span></div><div> <span style="color: #d38686;">return</span> <span style="color: #a38888;">-</span><span style="color: #bb9161;">1</span><span style="color: #757c50;">;</span></div><br /><div> <span style="color: #98676a;">int</span><span style="color: #a88f73;">[]</span> greatestTwo <span style="color: #a38888;">=</span> <span style="color: #a88f73;">{</span>all<span style="color: #a88f73;">[</span><span style="color: #bb9161;">0</span><span style="color: #a88f73;">]</span><span style="color: #7a6852;">,</span> all<span style="color: #a88f73;">[</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">]}</span><span style="color: #757c50;">;</span></div><div> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">]</span> <span style="color: #a38888;">></span> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">0</span><span style="color: #a88f73;">])</span> <span style="color: #a88f73;">{</span></div><div> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">0</span><span style="color: #a88f73;">]</span> <span style="color: #a38888;">=</span> all<span style="color: #a88f73;">[</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">]</span><span style="color: #757c50;">;</span></div><div> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">]</span> <span style="color: #a38888;">=</span> all<span style="color: #a88f73;">[</span><span style="color: #bb9161;">0</span><span style="color: #a88f73;">]</span><span style="color: #757c50;">;</span></div><div> <span style="color: #a88f73;">}</span></div><br /><div> <span style="color: #d38686;">for</span> <span style="color: #a88f73;">(</span><span style="color: #98676a;">int</span> i <span style="color: #a38888;">=</span> <span style="color: #bb9161;">2</span><span style="color: #757c50;">;</span> i <span style="color: #a38888;"><</span> all<span style="color: #a88f73;">.</span>Count<span style="color: #757c50;">;</span> i<span style="color: #a38888;">++</span><span style="color: #a88f73;">)</span> <span style="color: #a88f73;">{</span></div><div> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>all<span style="color: #a88f73;">[</span>i<span style="color: #a88f73;">]</span> <span style="color: #a38888;">></span> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">0</span><span style="color: #a88f73;">])</span> <span style="color: #a88f73;">{</span></div><div> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">]</span> <span style="color: #a38888;">=</span> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">0</span><span style="color: #a88f73;">]</span><span style="color: #757c50;">;</span></div><div> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">0</span><span style="color: #a88f73;">]</span> <span style="color: #a38888;">=</span> all<span style="color: #a88f73;">[</span>i<span style="color: #a88f73;">]</span><span style="color: #757c50;">;</span></div><div> <span style="color: #a88f73;">}</span></div><div> <span style="color: #d38686;">else</span> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>all<span style="color: #a88f73;">[</span>i<span style="color: #a88f73;">]</span> <span style="color: #a38888;">></span> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">])</span> <span style="color: #a88f73;">{</span></div><div> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">]</span> <span style="color: #a38888;">=</span> all<span style="color: #a88f73;">[</span>i<span style="color: #a88f73;">]</span><span style="color: #757c50;">;</span></div><div> <span style="color: #a88f73;">}</span></div><div> <span style="color: #a88f73;">}</span></div><br /><div> <span style="color: #d38686;">return</span> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">0</span><span style="color: #a88f73;">]</span> <span style="color: #a38888;">+</span> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">]</span><span style="color: #757c50;">;</span></div><div> <span style="color: #a88f73;">}</span></div></div></div><div style="background-color: #0b0b18; color: #d3af86; font-family: consolas, inconsolataGo, monospace, Consolas, "Courier New", monospace; font-size: 16px; line-height: 22px; white-space: pre;"><br /></div><br /><div>And over a few iterations ends up with this:</div><div><br /></div><div><div style="background-color: #0b0b18; color: #d3af86; font-family: consolas, inconsolataGo, monospace, Consolas, "Courier New", monospace; font-size: 16px; line-height: 22px; white-space: pre;"><br /><div> <span style="color: #98676a;">int</span> <span style="color: #a88960;">GetMagicSum3</span><span style="color: #a88f73;">(</span><span style="color: #f06431;">IEnumerable</span><span style="color: #a88f73;"><</span><span style="color: #98676a;">string</span><span style="color: #a88f73;">></span> items<span style="color: #a88f73;">)</span> <span style="color: #a38888;">=></span></div><div> items<span style="color: #a88f73;">.</span><span style="color: #a88960;">Aggregate</span><span style="color: #a88f73;">((</span>max<span style="color: #7a6852;">:</span> <span style="color: #bb9161;">0</span><span style="color: #7a6852;">,</span> next<span style="color: #7a6852;">:</span> <span style="color: #bb9161;">0</span><span style="color: #7a6852;">,</span> count<span style="color: #7a6852;">:</span> <span style="color: #bb9161;">0</span><span style="color: #a88f73;">)</span><span style="color: #7a6852;">,</span> AdvanceRaw<span style="color: #7a6852;">,</span> ProduceSum<span style="color: #a88f73;">)</span><span style="color: #757c50;">;</span></div><br /><div> <span style="color: #98676a;">int</span> <span style="color: #a88960;">ProduceSum</span><span style="color: #a88f73;">((</span><span style="color: #98676a;">int</span> max<span style="color: #7a6852;">,</span> <span style="color: #98676a;">int</span> next<span style="color: #7a6852;">,</span> <span style="color: #98676a;">int</span> count<span style="color: #a88f73;">)</span> tuple<span style="color: #a88f73;">)</span> <span style="color: #a38888;">=></span></div><div> tuple<span style="color: #a88f73;">.</span>count <span style="color: #a38888;">==</span> <span style="color: #bb9161;">2</span> <span style="color: #a38888;">?</span> tuple<span style="color: #a88f73;">.</span>max <span style="color: #a38888;">+</span> tuple<span style="color: #a88f73;">.</span>next <span style="color: #a38888;">:</span> <span style="color: #a38888;">-</span><span style="color: #bb9161;">1</span><span style="color: #757c50;">;</span></div><br /><div> <span style="color: #a88f73;">(</span><span style="color: #98676a;">int</span> max<span style="color: #7a6852;">,</span> <span style="color: #98676a;">int</span> next<span style="color: #7a6852;">,</span> <span style="color: #98676a;">int</span> count<span style="color: #a88f73;">)</span> <span style="color: #a88960;">AdvanceRaw</span><span style="color: #a88f73;">((</span><span style="color: #98676a;">int</span> max<span style="color: #7a6852;">,</span> <span style="color: #98676a;">int</span> next<span style="color: #7a6852;">,</span> <span style="color: #98676a;">int</span> count<span style="color: #a88f73;">)</span> tuple<span style="color: #7a6852;">,</span> <span style="color: #98676a;">string</span> item<span style="color: #a88f73;">)</span> <span style="color: #a38888;">=></span></div><div> int<span style="color: #a88f73;">.</span><span style="color: #a88960;">TryParse</span><span style="color: #a88f73;">(</span>item<span style="color: #7a6852;">,</span> <span style="color: #d38686;">out</span> <span style="color: #98676a;">int</span> number<span style="color: #a88f73;">)</span> <span style="color: #a38888;">?</span> <span style="color: #a88960;">Advance</span><span style="color: #a88f73;">(</span>tuple<span style="color: #7a6852;">,</span> number<span style="color: #a88f73;">)</span> <span style="color: #a38888;">:</span> tuple<span style="color: #757c50;">;</span></div><br /><div> <span style="color: #a88f73;">(</span><span style="color: #98676a;">int</span> max<span style="color: #7a6852;">,</span> <span style="color: #98676a;">int</span> next<span style="color: #7a6852;">,</span> <span style="color: #98676a;">int</span> count<span style="color: #a88f73;">)</span> <span style="color: #a88960;">Advance</span><span style="color: #a88f73;">((</span><span style="color: #98676a;">int</span> max<span style="color: #7a6852;">,</span> <span style="color: #98676a;">int</span> next<span style="color: #7a6852;">,</span> <span style="color: #98676a;">int</span> count<span style="color: #a88f73;">)</span> tuple<span style="color: #7a6852;">,</span> <span style="color: #98676a;">int</span> number<span style="color: #a88f73;">)</span> <span style="color: #a38888;">=></span> tuple <span style="color: #d38686;">switch</span></div><div> <span style="color: #a88f73;">{</span></div><div> <span style="color: #a88f73;">(</span>_<span style="color: #7a6852;">,</span> _<span style="color: #7a6852;">,</span> <span style="color: #bb9161;">0</span><span style="color: #a88f73;">)</span> <span style="color: #a38888;">=></span> <span style="color: #a88f73;">(</span>number<span style="color: #7a6852;">,</span> <span style="color: #bb9161;">0</span><span style="color: #7a6852;">,</span> <span style="color: #bb9161;">1</span><span style="color: #a88f73;">)</span><span style="color: #7a6852;">,</span></div><div> <span style="color: #a88f73;">(</span><span style="color: #f06431;">var</span> max<span style="color: #7a6852;">,</span> <span style="color: #f06431;">_</span><span style="color: #7a6852;">,</span> 1<span style="color: #a88f73;">)</span> when number > max <span style="color: #a38888;">=></span> <span style="color: #a88f73;">(</span>number<span style="color: #7a6852;">,</span> max<span style="color: #7a6852;">,</span> <span style="color: #bb9161;">2</span><span style="color: #a88f73;">)</span><span style="color: #7a6852;">,</span></div><div> <span style="color: #a88f73;">(</span>var max<span style="color: #7a6852;">,</span> _<span style="color: #7a6852;">,</span> <span style="color: #bb9161;">1</span><span style="color: #a88f73;">)</span> <span style="color: #a38888;">=></span> <span style="color: #a88f73;">(</span>max<span style="color: #7a6852;">,</span> number<span style="color: #7a6852;">,</span> <span style="color: #bb9161;">2</span><span style="color: #a88f73;">)</span><span style="color: #7a6852;">,</span></div><div> <span style="color: #a88f73;">(</span><span style="color: #f06431;">var</span> max<span style="color: #7a6852;">,</span> <span style="color: #f06431;">_</span><span style="color: #7a6852;">,</span> 2<span style="color: #a88f73;">)</span> when number > max <span style="color: #a38888;">=></span> <span style="color: #a88f73;">(</span>number<span style="color: #7a6852;">,</span> max<span style="color: #7a6852;">,</span> <span style="color: #bb9161;">2</span><span style="color: #a88f73;">)</span><span style="color: #7a6852;">,</span></div><div> <span style="color: #a88f73;">(</span><span style="color: #f06431;">var</span> max<span style="color: #7a6852;">,</span> <span style="color: #f06431;">var</span> next<span style="color: #7a6852;">,</span> 2<span style="color: #a88f73;">)</span> when number > next <span style="color: #a38888;">=></span> <span style="color: #a88f73;">(</span>max<span style="color: #7a6852;">,</span> number<span style="color: #7a6852;">,</span> <span style="color: #bb9161;">2</span><span style="color: #a88f73;">)</span><span style="color: #7a6852;">,</span></div><div> _ <span style="color: #a38888;">=></span> tuple<span style="color: #7a6852;">,</span></div><div> <span style="color: #a88f73;">}</span><span style="color: #757c50;">;</span></div><br /></div></div><br /><div>...my blog page is set up pretty narrow, so here's an image of the code that you can zoom (I have nothing against long lines, I wanted to present it as text above so it was copy-pastable):</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhEjPJR5Mqh2hVcz2tvWAweCVTTeKiwXR1UYNwNgLuXjgXuycY8KS5zeee8P_TVG7aNNBYj29bZu44g74Bp1XSo1bRoB8SSzO0h2FivQfu-oamC4WNaFEnwUzuSiBK_7hGqnlUMmLjFGoHgRPaGGCT0Vn_pb3XBZQ2TAYQ0siP4fPsxM5WZPvGV96LBFe5H" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="418" data-original-width="948" height="282" src="https://blogger.googleusercontent.com/img/a/AVvXsEhEjPJR5Mqh2hVcz2tvWAweCVTTeKiwXR1UYNwNgLuXjgXuycY8KS5zeee8P_TVG7aNNBYj29bZu44g74Bp1XSo1bRoB8SSzO0h2FivQfu-oamC4WNaFEnwUzuSiBK_7hGqnlUMmLjFGoHgRPaGGCT0Vn_pb3XBZQ2TAYQ0siP4fPsxM5WZPvGV96LBFe5H=w640-h282" width="640" /></a></div><br />Now, we can argue about which code is better or more readable, but my first thought was that the performance is going to suffer. I was then galvanized by this comment on the video:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg7aMJKBln7h0Dt4yBx2_EFwUeazJzCHqUu32iZGQSEEQQArmEw6hE463GvrbTcSfKdlP3fw2RtETU7MArQsr4t4gN5N-1Lgo5qHGPgrIXg_NAgdoSaV7RfTEVyFALhg3PJRkYYeclQHbU2dY7uPxGyyxiQywTOFXwkc_cYoOxyELzu93X4V0TKcgXJwgt7" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="241" data-original-width="958" height="162" src="https://blogger.googleusercontent.com/img/a/AVvXsEg7aMJKBln7h0Dt4yBx2_EFwUeazJzCHqUu32iZGQSEEQQArmEw6hE463GvrbTcSfKdlP3fw2RtETU7MArQsr4t4gN5N-1Lgo5qHGPgrIXg_NAgdoSaV7RfTEVyFALhg3PJRkYYeclQHbU2dY7uPxGyyxiQywTOFXwkc_cYoOxyELzu93X4V0TKcgXJwgt7=w640-h162" width="640" /></a></div><br />This seemed super unlikely to me, so I decided to measure it, yielding these initial results:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgyzsHdUmu12jNdtbX2t5zJIL0KwkDYDu7i6tIksVDG7jIPrsTiAOgAJF3VVbf-n4ztmzMk3NAf-j5z17KlFtQCgLTimncrtt07oIGqtz4LvyZTQ_ikNhJpddYq7zPp9upy6fEpL9IQLFhiIWP__HZWp1hC9BlAAOGJGjADbvVAAIXmBYVnrqvFdLfgoqid" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="47" data-original-width="231" height="65" src="https://blogger.googleusercontent.com/img/a/AVvXsEgyzsHdUmu12jNdtbX2t5zJIL0KwkDYDu7i6tIksVDG7jIPrsTiAOgAJF3VVbf-n4ztmzMk3NAf-j5z17KlFtQCgLTimncrtt07oIGqtz4LvyZTQ_ikNhJpddYq7zPp9upy6fEpL9IQLFhiIWP__HZWp1hC9BlAAOGJGjADbvVAAIXmBYVnrqvFdLfgoqid" width="320" /></a></div><br />Which bear out his response: the functional <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSum3</span> does indeed run faster than the imperative <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSum0</span>... but you knew there was a "but" coming, right? Did you notice the problem with <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSum0</span>? Go back and look at it and see if you can spot it.</div><div><br /></div><div>This is the setup for the test program btw: </div><div><br /></div><div><div style="background-color: #0b0b18; color: #d3af86; font-family: consolas, inconsolataGo, monospace, Consolas, "Courier New", monospace; font-size: 16px; line-height: 22px; white-space: pre;"><br /><div> <span style="color: #98676a;">string</span> <span style="color: #a88960;">CSharpPerformanceTest</span><span style="color: #a88f73;">(</span><span style="color: #98676a;">int</span> wordCount <span style="color: #a38888;">=</span> <span style="color: #bb9161;">100000000</span><span style="color: #a88f73;">)</span> <span style="color: #a88f73;">{</span></div><div> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>wordCount <span style="color: #a38888;"><</span> <span style="color: #bb9161;">2</span><span style="color: #a88f73;">)</span> <span style="color: #d38686;">return</span> <span style="color: #546483;">"</span><span style="color: #7d8ba3;">wordCount must be >= 2</span><span style="color: #546483;">"</span><span style="color: #757c50;">;</span></div><br /><div> <span style="color: #f06431;">System</span><span style="color: #a88f73;">.</span><span style="color: #f06431;">Random</span> rng <span style="color: #a38888;">=</span> <span style="color: #a88960;">new</span><span style="color: #a88f73;">((</span><span style="color: #98676a;">int</span><span style="color: #a88f73;">)</span>System<span style="color: #a88f73;">.</span>DateTime<span style="color: #a88f73;">.</span>Now<span style="color: #a88f73;">.</span>Ticks<span style="color: #a88f73;">)</span><span style="color: #757c50;">;</span></div><div> <span style="color: #98676a;">int</span> <span style="color: #a88960;">randomInt</span><span style="color: #a88f73;">()</span> <span style="color: #a38888;">=></span> rng<span style="color: #a88f73;">.</span><span style="color: #a88960;">Next</span><span style="color: #a88f73;">()</span> <span style="color: #a38888;">/</span> <span style="color: #bb9161;">2</span><span style="color: #757c50;">;</span> <span style="color: #555555;">// halved so don't overflow</span></div><br /><div> <span style="color: #f06431;">List</span><span style="color: #a88f73;"><</span><span style="color: #98676a;">string</span><span style="color: #a88f73;">></span> numbers <span style="color: #a38888;">=</span> <span style="color: #a88960;">new</span> <span style="color: #a88f73;">()</span><span style="color: #757c50;">;</span></div><div> <span style="color: #d38686;">for</span> <span style="color: #a88f73;">(</span><span style="color: #98676a;">int</span> i <span style="color: #a38888;">=</span> <span style="color: #bb9161;">0</span><span style="color: #757c50;">;</span> i <span style="color: #a38888;"><</span> wordCount<span style="color: #757c50;">;</span> i<span style="color: #a38888;">++</span><span style="color: #a88f73;">)</span></div><div> numbers<span style="color: #a88f73;">.</span><span style="color: #a88960;">Add</span><span style="color: #a88f73;">(</span><span style="color: #a88960;">randomInt</span><span style="color: #a88f73;">().</span><span style="color: #a88960;">ToString</span><span style="color: #a88f73;">())</span><span style="color: #757c50;">;</span></div><br /><div><span style="color: #a88f73;"> </span><span style="color: #555555;">// Then time a call to each GetMagicSumX(numbers);</span></div><br /></div></div><div><br /></div><div>Did you see what was wrong with <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSum0</span>? The problem with is is it generates a <span style="font-family: "courier new" , "courier" , monospace;">List<string></span> of the <span style="font-family: "courier new" , "courier" , monospace;">IEnumerable</span>, completely unnecessarily! Horvat does explicitly say that the algorithm isn't the point of the video - the style differences are - but I don't think pointing out that it's making a completely unneeded copy of the data really counts as algorithmic nit-picking!</div><div><br /></div><div>If we're comparing imperative vs functional then I think it's fair that we use the best possible form of each. We can assume the functional <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSum3</span> is as good as can be, as the point of the video is to advocate for it, so let's make a good version of <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSum0</span>, we'll call it <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSumA</span>. </div><div><br /></div><div>Mostly we're simply going to remove the <span style="font-family: "courier new" , "courier" , monospace;">List</span> generated at the start. However, because of how <span style="font-family: "courier new" , "courier" , monospace;">IEnumerable</span> works, setting up the <span style="font-family: "courier new" , "courier" , monospace;">greatestTwo</span> array and then iterating from index 2 would be a pain: you'd need to make an <span style="font-family: "courier new" , "courier" , monospace;">IEnumerator</span>, check <span style="font-family: "courier new" , "courier" , monospace;">Current</span> was there and parsed, <span style="font-family: "courier new" , "courier" , monospace;">MoveNext()</span>, check <span style="font-family: "courier new" , "courier" , monospace;">Current</span> again, until you got two numbers... sounds kind of loop-y. Happily we already have a loop that does all that logic, so we'll simply use it to do all the work. We set up <span style="font-family: "courier new" , "courier" , monospace;">greatestTwo</span> so that it will always be filled out by the first two numbers, by setting it to <span style="font-family: "courier new" , "courier" , monospace;">{ -1, -1 }</span>. (If we wanted to handle negative numbers we'd use <span style="font-family: "courier new" , "courier" , monospace;">int.MinValue</span> instead, but as we're only dealing with positive numbers, <span style="font-family: "courier new" , "courier" , monospace;">-1</span> is more readable). This gets us:</div><div><br /></div><div><div style="background-color: #0b0b18; color: #d3af86; font-family: consolas, inconsolataGo, monospace, Consolas, "Courier New", monospace; font-size: 16px; line-height: 22px; white-space: pre;"><br /><div> <span style="color: #98676a;">int</span> <span style="color: #a88960;">_GetMagicSumA</span><span style="color: #a88f73;">(</span><span style="color: #f06431;">IEnumerable</span><span style="color: #a88f73;"><</span><span style="color: #98676a;">string</span><span style="color: #a88f73;">></span> items<span style="color: #a88f73;">) {</span></div><div> <span style="color: #98676a;">int</span><span style="color: #a88f73;">[]</span> greatestTwo <span style="color: #a38888;">=</span> <span style="color: #a88f73;">{</span> <span style="color: #a38888;">-</span><span style="color: #bb9161;">1</span><span style="color: #7a6852;">,</span> <span style="color: #a38888;">-</span><span style="color: #bb9161;">1</span> <span style="color: #a88f73;">}</span><span style="color: #757c50;">;</span></div><br /><div> <span style="color: #d38686;">foreach</span> <span style="color: #a88f73;">(</span><span style="color: #98676a;">string</span> item <span style="color: #d38686;">in</span> items<span style="color: #a88f73;">)</span> <span style="color: #a88f73;">{</span></div><div> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>int<span style="color: #a88f73;">.</span><span style="color: #a88960;">TryParse</span><span style="color: #a88f73;">(</span>item<span style="color: #7a6852;">,</span> <span style="color: #d38686;">out</span> <span style="color: #98676a;">int</span> number<span style="color: #a88f73;">))</span> <span style="color: #a88f73;">{</span></div><div> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>number <span style="color: #a38888;">></span> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">0</span><span style="color: #a88f73;">])</span> <span style="color: #a88f73;">{</span></div><div> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">]</span> <span style="color: #a38888;">=</span> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">0</span><span style="color: #a88f73;">]</span><span style="color: #757c50;">;</span></div><div> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">0</span><span style="color: #a88f73;">]</span> <span style="color: #a38888;">=</span> number<span style="color: #757c50;">;</span></div><div> <span style="color: #a88f73;">}</span></div><div> <span style="color: #d38686;">else</span> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>number <span style="color: #a38888;">></span> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">])</span> <span style="color: #a88f73;">{</span></div><div> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">]</span> <span style="color: #a38888;">=</span> number<span style="color: #757c50;">;</span></div><div> <span style="color: #a88f73;">}</span></div><div> <span style="color: #a88f73;">}</span></div><div> <span style="color: #a88f73;">}</span></div><br /><div> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">]</span> <span style="color: #a38888;">==</span> <span style="color: #a38888;">-</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">)</span> <span style="color: #555555;">// didn't find two numbers</span></div><div> <span style="color: #d38686;">return</span> <span style="color: #a38888;">-</span><span style="color: #bb9161;">1</span><span style="color: #757c50;">;</span></div><br /><div> <span style="color: #d38686;">return</span> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">0</span><span style="color: #a88f73;">]</span> <span style="color: #a38888;">+</span> greatestTwo<span style="color: #a88f73;">[</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">]</span><span style="color: #757c50;">;</span></div><div> <span style="color: #a88f73;">}</span></div><br /><div></div></div></div><br /><div>Now, clearly I'm partisan; Horvat is bringing his best functional code to the fight, so in balance I'm going to represent imperative as best as I can. To me, that <span style="font-family: "courier new" , "courier" , monospace;">greatestTwo</span> array isn't the clearest thing to reason about in the inner <span style="font-family: "courier new" , "courier" , monospace;">if</span> block, and I'd prefer not to nest quite so deep, so I'm going to refine this to the following semantically identical function: </div><div><br /></div><div><div style="background-color: #0b0b18; color: #d3af86; font-family: consolas, inconsolataGo, monospace, Consolas, "Courier New", monospace; font-size: 16px; line-height: 22px; white-space: pre;"><br /><div> <span style="color: #98676a;">int</span> <span style="color: #a88960;">GetMagicSumA</span><span style="color: #a88f73;">(</span><span style="color: #f06431;">IEnumerable</span><span style="color: #a88f73;"><</span><span style="color: #98676a;">string</span><span style="color: #a88f73;">></span> items<span style="color: #a88f73;">)</span> <span style="color: #a88f73;">{</span></div><div> <span style="color: #98676a;">int</span> ultimate <span style="color: #a38888;">=</span> <span style="color: #a38888;">-</span><span style="color: #bb9161;">1</span><span style="color: #757c50;">;</span></div><div> <span style="color: #98676a;">int</span> penultimate <span style="color: #a38888;">=</span> <span style="color: #a38888;">-</span><span style="color: #bb9161;">1</span><span style="color: #757c50;">;</span></div><br /><div> <span style="color: #d38686;">foreach</span> <span style="color: #a88f73;">(</span><span style="color: #98676a;">string</span> item <span style="color: #d38686;">in</span> items<span style="color: #a88f73;">)</span> <span style="color: #a88f73;">{</span></div><div> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span><span style="color: #a38888;">!</span>int<span style="color: #a88f73;">.</span><span style="color: #a88960;">TryParse</span><span style="color: #a88f73;">(</span>item<span style="color: #7a6852;">,</span> <span style="color: #d38686;">out</span> <span style="color: #98676a;">int</span> number<span style="color: #a88f73;">))</span></div><div> <span style="color: #d38686;">continue</span><span style="color: #757c50;">;</span></div><br /><div> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>number <span style="color: #a38888;">></span> ultimate<span style="color: #a88f73;">)</span> <span style="color: #a88f73;">{</span></div><div> penultimate <span style="color: #a38888;">=</span> ultimate<span style="color: #757c50;">;</span></div><div> ultimate <span style="color: #a38888;">=</span> number<span style="color: #757c50;">;</span></div><div> <span style="color: #a88f73;">}</span></div><div> <span style="color: #d38686;">else</span> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>number <span style="color: #a38888;">></span> penultimate<span style="color: #a88f73;">)</span> <span style="color: #a88f73;">{</span></div><div> penultimate <span style="color: #a38888;">=</span> number<span style="color: #757c50;">;</span></div><div> <span style="color: #a88f73;">}</span></div><div> <span style="color: #a88f73;">}</span></div><br /><div> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>penultimate <span style="color: #a38888;">==</span> <span style="color: #a38888;">-</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">)</span> <span style="color: #555555;">// didn't find two numbers</span></div><div> <span style="color: #d38686;">return</span> <span style="color: #a38888;">-</span><span style="color: #bb9161;">1</span><span style="color: #757c50;">;</span></div><br /><div> <span style="color: #d38686;">return</span> ultimate <span style="color: #a38888;">+</span> penultimate<span style="color: #757c50;">;</span></div><div> <span style="color: #a88f73;">}</span></div><br /></div></div><div><br /></div><div>Comparing the readability of the imperative vs functional code is subjective, you can decide which version you find easier to reason about for yourself: below is <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSum3</span> again for comparison. It takes up 18 lines on the screen, while the above <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSumA</span> takes up 22 (I do tend to space my code out a fair bit).</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh5lbyvI9h4k4FLHXf0da2n7orXdiXB30vliPx2a6BB3oeWfEMWkf-aOjuY5Mu7KVS7nzGRjSMuIZkTdATphQlvigahm4cOl2U9-qXG_BeU5-NQT_KLCtyc5Br-XXpNIkRIEBDaiZsiJ5ItV7oQnFlMRxORl4rcX5C7ewzBsVSfZSuwWZCAvwBjs-y2oBJ2" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="418" data-original-width="948" height="282" src="https://blogger.googleusercontent.com/img/a/AVvXsEh5lbyvI9h4k4FLHXf0da2n7orXdiXB30vliPx2a6BB3oeWfEMWkf-aOjuY5Mu7KVS7nzGRjSMuIZkTdATphQlvigahm4cOl2U9-qXG_BeU5-NQT_KLCtyc5Br-XXpNIkRIEBDaiZsiJ5ItV7oQnFlMRxORl4rcX5C7ewzBsVSfZSuwWZCAvwBjs-y2oBJ2=w640-h282" width="640" /></a></div><br />However, the point of this post is not to argue about style, but about performance, and here are the results:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhnY1KOlpXkyZ3li7Vkff7ppMJpreMrqI2kbcR2UhGbbMedIwPlLeokfNze8yLXgzCzbLoWD0au3qc0qf3vDsaDs3Q5jsWLzVs_2scgM625eP2tPEjUYcJrf6hvX3ZzbyaAehOi3MzR0crXF29DtvoYqxSM2-WiHqXhhcsbgbMDY8f1ZGPyp8hCq2SNlMVW" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="105" data-original-width="231" height="145" src="https://blogger.googleusercontent.com/img/a/AVvXsEhnY1KOlpXkyZ3li7Vkff7ppMJpreMrqI2kbcR2UhGbbMedIwPlLeokfNze8yLXgzCzbLoWD0au3qc0qf3vDsaDs3Q5jsWLzVs_2scgM625eP2tPEjUYcJrf6hvX3ZzbyaAehOi3MzR0crXF29DtvoYqxSM2-WiHqXhhcsbgbMDY8f1ZGPyp8hCq2SNlMVW" width="320" /></a></div><br />With the unnecessary <span style="font-family: "courier new" , "courier" , monospace;">List</span> generation removed, the imperative code is indeed faster: <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSum3</span> takes 1.26x as long as <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSumA</span>.</div><div><br /></div><h2 style="text-align: left;">Follow-up for game developers</h2><div><br /></div><div>This section is for people who <i>really </i>care about performance, so if that's not you please don't get your knickers in a twist.</div><div>You probably noticed that in addition to <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSumA</span>, there are two successively faster versions of the function profiled above. If you're using C# for writing games then this bit is for you: <b>DO NOT USE IENUMERABLE</b>. I have never constructed an <span style="font-family: "courier new" , "courier" , monospace;">IEnumerable</span> in any game, ever. For sequences of homogenous data you need two types: <span style="font-family: "courier new" , "courier" , monospace;">List<T></span> and <span style="font-family: "courier new" , "courier" , monospace;">Array<T></span>. If your data is sitting in memory there is no reason to use an <span style="font-family: "courier new" , "courier" , monospace;">IEnumerable</span>. If it's being generated an item at a time, then you need to be dealing with that asynchronicity with code that reflects it.</div><div>There is a very handy interface called <span style="font-family: "courier new" , "courier" , monospace;">IList</span>, which will accept both <span style="font-family: "courier new" , "courier" , monospace;">List</span> and <span style="font-family: "courier new" , "courier" , monospace;">Array</span>, so is the perfect replacement here. In essence this is the only change between <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSumB</span> and <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSumA</span>:</div><div><br /></div><div><div style="background-color: #0b0b18; color: #d3af86; font-family: consolas, inconsolataGo, monospace, Consolas, "Courier New", monospace; font-size: 16px; line-height: 22px; white-space: pre;"><br /><div> <span style="color: #98676a;">int</span> <span style="color: #a88960;">GetMagicSumB</span><span style="color: #a88f73;">(</span><span style="color: #f06431;">IList</span><span style="color: #a88f73;"><</span><span style="color: #98676a;">string</span><span style="color: #a88f73;">></span> items<span style="color: #a88f73;">) </span><span style="color: #a88f73;">{</span></div><div> <span style="color: #98676a;">int</span> ultimate <span style="color: #a38888;">=</span> <span style="color: #a38888;">-</span><span style="color: #bb9161;">1</span><span style="color: #757c50;">;</span></div><div> <span style="color: #98676a;">int</span> penultimate <span style="color: #a38888;">=</span> <span style="color: #a38888;">-</span><span style="color: #bb9161;">1</span><span style="color: #757c50;">;</span></div><br /><div> <span style="color: #d38686;">for</span> <span style="color: #a88f73;">(</span><span style="color: #98676a;">int</span> i <span style="color: #a38888;">=</span> <span style="color: #bb9161;">0</span><span style="color: #757c50;">;</span> i <span style="color: #a38888;"><</span> items<span style="color: #a88f73;">.</span>Count<span style="color: #757c50;">;</span> i<span style="color: #a38888;">++</span><span style="color: #a88f73;">)</span> <span style="color: #a88f73;">{</span></div><div> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span><span style="color: #a38888;">!</span>int<span style="color: #a88f73;">.</span><span style="color: #a88960;">TryParse</span><span style="color: #a88f73;">(</span>items<span style="color: #a88f73;">[</span>i<span style="color: #a88f73;">]</span><span style="color: #7a6852;">,</span> <span style="color: #d38686;">out</span> <span style="color: #98676a;">int</span> number<span style="color: #a88f73;">))</span></div><div> <span style="color: #d38686;">continue</span><span style="color: #757c50;">;</span></div><br /><div> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>number <span style="color: #a38888;">></span> ultimate<span style="color: #a88f73;">)</span> <span style="color: #a88f73;">{</span></div><div> penultimate <span style="color: #a38888;">=</span> ultimate<span style="color: #757c50;">;</span></div><div> ultimate <span style="color: #a38888;">=</span> number<span style="color: #757c50;">;</span></div><div> <span style="color: #a88f73;">}</span></div><div> <span style="color: #d38686;">else</span> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>number <span style="color: #a38888;">></span> penultimate<span style="color: #a88f73;">)</span> <span style="color: #a88f73;">{</span></div><div> penultimate <span style="color: #a38888;">=</span> number<span style="color: #757c50;">;</span></div><div> <span style="color: #a88f73;">}</span></div><div> <span style="color: #a88f73;">}</span></div><br /><div> <span style="color: #d38686;">if</span> <span style="color: #a88f73;">(</span>penultimate <span style="color: #a38888;">==</span> <span style="color: #a38888;">-</span><span style="color: #bb9161;">1</span><span style="color: #a88f73;">)</span> <span style="color: #555555;">// didn't find two numbers</span></div><div> <span style="color: #d38686;">return</span> <span style="color: #a38888;">-</span><span style="color: #bb9161;">1</span><span style="color: #757c50;">;</span></div><br /><div> <span style="color: #d38686;">return</span> ultimate <span style="color: #a38888;">+</span> penultimate<span style="color: #757c50;">;</span></div><div> <span style="color: #a88f73;">}</span></div><br /></div></div><div><br /></div><div>As you can see the two difference in the code are the parameter type, and using a <span style="font-family: "courier new" , "courier" , monospace;">for</span> instead of a <span style="font-family: "courier new" , "courier" , monospace;">foreach</span>; the result is substantially faster code. Using <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSumB</span> as the baseline, <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSumA</span> takes 1.19x as long as it, and <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSum3</span> takes 1.5x as long.</div><div><br /></div><h3 style="text-align: left;">Bonus Round</h3><div><br /></div><div>The table of timings contains a final form of the function, <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSumC</span>, which is marginally faster again. It's clear in the original video that the point is not about optimising the algorithm, so this isn't relevant to the post in general. Therefor, an exercise for the reader: how do you modify <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSumB</span> to get <span style="font-family: "courier new" , "courier" , monospace;">GetMagicSumC</span>, and that small gain in performance? Clue: <span id="spoiler" style="display: none;">The second <span style="font-family: "courier new" , "courier" , monospace;">if</span> block</span>
<button onclick="if(document.getElementById('spoiler') .style.display=='none') {document.getElementById('spoiler') .style.display='inline'}else{document.getElementById('spoiler') .style.display='none'}" title="Click to Show/Hide Content" type="button">Show/Hide</button>
.</div><div><br /></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-62322300814603763802019-03-07T11:15:00.004-08:002021-09-29T07:43:34.605-07:00Tabletop Simulator's SteamVR binding walkthroughSteamVR's action+binding system is a very powerful tool for allowing the player to express exactly how they wish a game's controls to operate. It's also a very useful mechanism for allowing a myriad of VR hardware to all function on any game which supports it, as you can tailor the controls to the specific buttons, triggers, sticks and pads your controller has. This is a great design on Valve's part, as it means any hardware developer can make their own VR system without having to worry that with a limited consumer base no developer will write code specifically for their setup. This system means that the developer just has to makes the game work with SteamVR, after which every piece of hardware should be able to be used with their game.<br />
<br />
However, this versatility does come at a slight cost: setting up a control scheme in it is a little more involved than <i><Press the key for this action></i> that players are used to dealing with. This post will walk through the default bindings that <i>Tabletop Simulator</i> uses for Vive and Rift, which will hopefully give you an idea of how to (a) customize the controls on those platforms to suit yourself and (b) create your own bindings if you have less common hardware.<br />
<br />
You may access the controller binding interface inside VR or on your desktop in a web browser.<br />
<br />
<ul>
<li>VR: in SteamVR settings go to <i>Controller Binding</i></li>
<li>Desktop: go to <a href="http://localhost:27062/dashboard/controllerbinding.html" target="_blank">http://localhost:27062/dashboard/controllerbinding.html</a></li>
</ul>
<br />
To access it in desktop mode you must be running SteamVR in the background, and in fact running <i>Tabletop Simulator</i> in VR as well might be helpful, since the SteamVR UI is still a bit beta...<br />
<br />
<br />
The control schemes on both platforms are effectively the same on both hands, so here we'll predominantly look at the <i>Left Controller </i>(with some notes where applicable if there is some variance).<br />
<br />
These bindings are accurate to version 11.1 of <i>Tabletop Simulator</i>.<br />
<br />
N.B. If you're interested in the Rift controls it will be worth having a quick read through the Vive section, as it will cover some basic generalities not covered in the Rift section, and go into each action in more detail.<br />
<br />
<br />
<h2>
Vive</h2>
<div>
<br /></div>
<div>
<a href="https://imgur.com/a/PU7AB4p" target="_blank">[screenshot of all bindings]</a></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-5UShgd-T2MQ/XIFDy4p599I/AAAAAAAAONE/v6R1O6Dm7Bk_nb9kdWXu1xVYoppRRedcwCEwYBhgL/s1600/1.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="868" data-original-width="488" height="640" src="https://2.bp.blogspot.com/-5UShgd-T2MQ/XIFDy4p599I/AAAAAAAAONE/v6R1O6Dm7Bk_nb9kdWXu1xVYoppRRedcwCEwYBhgL/s640/1.png" width="358" /></a></div>
First up is the <span style="font-family: "courier new" , "courier" , monospace;">Grip</span> button. On Vive this is predominantly used for movement, which is bound with the <span style="font-family: "courier new" , "courier" , monospace;">Enable Movement</span> action; this is bound as a button (if you wished to add another such binding you would click on the + icon at the top right and select BUTTON).<br />
<br />
This needs to be bound under <i>Held</i>: as long as you hold the button movement will be enabled (hit "More options" when adding to show the options available other than <i>Click</i>) - movement in <i>Tabletop Simulator</i> is done by holding the button down and pulling or pushing yourself as if you had grabbed onto something.<br />
<br />
Next up is <span style="font-family: "courier new" , "courier" , monospace;">Display Tooltips</span>. This will simply cause tooltips to appear on the controllers as long as the button is held down. Doubling these on top of the move button is convenient, as it means the player has easy access to some helpful info without it getting in the way of other actions, or being context dependent. Once you are used to the controls you might want to disable tooltips; you can do so by deleting this binding (hover over it and click the trash can).<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">Button 14</span> is one of many (20!) bindings which emulate joypad buttons. This can be used in-game by turning on <i>Joypad Emulation</i> in the VR settings, allowing you to bind VR inputs in the standard control settings window. The first ten joypad buttons are listed as if they were on a 360 controller, and are assigned to the controllers here in a roughly analogous layout. The last ten buttons are simply listed with a numerical index. In effect it doesn't matter what button goes where, as long as you have a button assigned to the inputs on the controller you want to use. Note that in the default controls these buttons are assigned uniquely, scattered over both VR controllers; in this slot the right controller has <span style="font-family: "courier new" , "courier" , monospace;">Button 19</span> bound instead. As all the <span style="font-family: "courier new" , "courier" , monospace;">Button</span> bindings do the same thing (emulate joypad presses) we'll ignore them after this.<br />
<br />
Finally, there is <span style="font-family: "courier new" , "courier" , monospace;">_grip_held</span>. This, along with every other binding starting with an '<span style="font-family: "courier new" , "courier" , monospace;">_</span>' symbol, is used for the legacy binding of the old VR controls. They can be ignored, and we'll be skipping them from now on.<br />
<br />
<br />
<a name='more'></a><br />
<br />
Notice the cog icon in the bottom right of the active binding; clicking on this lets you set additional settings on the binding.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-DW__x4p6Wwc/XIFEckRqTiI/AAAAAAAAONM/2rJs4MwpxrEgqFlHJrtKn3P_ZhugXG57ACLcBGAs/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="348" data-original-width="642" height="216" src="https://4.bp.blogspot.com/-DW__x4p6Wwc/XIFEckRqTiI/AAAAAAAAONM/2rJs4MwpxrEgqFlHJrtKn3P_ZhugXG57ACLcBGAs/s400/2.png" width="400" /></a></div>
<br />
<span id="goog_522822284"></span><br />
Here you could specify some haptic feedback for when you use this input. As <i>Tabletop Simulator</i> generates relevant haptics automatically, the default bindings don't use any; they are all set to 0. If you set up a binding and are experiencing your controller unexpectedly vibrating, these settings in each binding are where to check.<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-ytir7zmUZ9E/XIFHBq0HuzI/AAAAAAAAONg/chtWnSkCEZcc12JaOWMeZMfgL2IliIoWQCLcBGAs/s1600/3.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="221" data-original-width="480" height="182" src="https://4.bp.blogspot.com/-ytir7zmUZ9E/XIFHBq0HuzI/AAAAAAAAONg/chtWnSkCEZcc12JaOWMeZMfgL2IliIoWQCLcBGAs/s400/3.png" width="400" /></a></div>
The <span style="font-family: "courier new" , "courier" , monospace;">Menu</span> button is set up for two main purposes: clicking it will activate the game's main menu, while holding it down for a few seconds will instead reset your position in the room. You can see this is done very easily; <i>Single</i> click is set to the <span style="font-family: "courier new" , "courier" , monospace;">Main Menu</span> action, and <i>Long</i> press is set to <span style="font-family: "courier new" , "courier" , monospace;">Reset Position</span>.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-EsSzwQtWn_Y/XIFHiRxjk_I/AAAAAAAAONo/7WvXJLdlTXAOxjyzIGaEBN6kMbPcbXO6QCLcBGAs/s1600/4.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="664" data-original-width="484" height="400" src="https://3.bp.blogspot.com/-EsSzwQtWn_Y/XIFHiRxjk_I/AAAAAAAAONo/7WvXJLdlTXAOxjyzIGaEBN6kMbPcbXO6QCLcBGAs/s400/4.png" width="291" /></a></div>
The <span style="font-family: "courier new" , "courier" , monospace;">Trigger</span> is set up with multiple, complementary bindings.<br />
<br />
Primarily it is used for the <span style="font-family: "courier new" , "courier" , monospace;">Grab</span> action, which is the main interaction used in <i>Tabletop Simulator</i>; it is what you use to pick up components, hit in-game buttons, and activate your currently selected tool.<br />
<br />
Here it is assigned to <i>Pull</i>: this means that it will activate when the trigger is squeezed (without having to be clicked).<br />
<br />
On the same panel you can see that <i>Click</i> is assigned to <span style="font-family: "courier new" , "courier" , monospace;">Orient Object</span>. This action depends on the type of object you are holding; for example, a card will be toggled between the orientation you picked it up, and held facing you (and hidden from other players). This works well with the above action: squeezing the trigger a little lets you hold an object, and while you do you can click the trigger to toggle.<br />
<br />
Then there is the <span style="font-family: "courier new" , "courier" , monospace;">Peek</span> action; in-game, a thing you can do to more easily view a component on the table is zoom it, turning your controller into a large-scale version of it. The <span style="font-family: "courier new" , "courier" , monospace;">Peek</span> action flips your view so you see the bottom of the component (the bottom card in a deck, for instance). Note that here the trigger is used as a button, with the action bound on <i>Held</i>.<br />
<br />
<br />
<br />
<br />
Finally there is the trackpad, which has an awful lot of bindings!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Tc3WqsplQjc/XIFNDDoaVRI/AAAAAAAAON0/M-5DIClwyNwTefGMOF5M_9GUptOmx8hAgCLcBGAs/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="782" data-original-width="970" height="514" src="https://1.bp.blogspot.com/-Tc3WqsplQjc/XIFNDDoaVRI/AAAAAAAAON0/M-5DIClwyNwTefGMOF5M_9GUptOmx8hAgCLcBGAs/s640/5.png" width="640" /></a></div>
<br />
There are two main area here: interacting with the game and its menus with the laser pointer, and the complicated context-sensitive actions which can be access via the touchpad. The top three panels in the image above deal with the former, and the bottom two the latter.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">Interface Click</span> is effectively the VR equivalent of "left mouse button". Here, the pad is set up to act as a DPad, in <i>Click</i> mode, with the action bound only to <i>Center</i>. Clicking in the middle of the touchpad will act as if you clicked on whatever you are aiming the laser pointer at.<br />
<br />
In order to have a laser pointer you either need to turn <i>Laser Always On</i> in the in-game VR settings, or bind an action to activate it. Here the touchpad is set as a DPad in <i>Touch</i> mode, with the <span style="font-family: "courier new" , "courier" , monospace;">Activate Laser Pointer</span> action bound at every point. This means whenever you touch the pad anywhere on its surface the laser will turn on.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">Pad Click</span> tells the game that the pad has been clicked. This is quite a low-level action that is currently only used for toggling the lock on the zoom object mode, and will likely be refactored at some point. The pad is simply set to act as a giant button, triggering it when clicked.<br />
<br />
Finally, there are the two bottom sections which deal with the actions related to using the pad for context sensitive commands. For the in-game context functions to work it needs to know when you are touching the pad, clicking the pad, and where (i.e. which direction or center). These two sections cover this: one for when you are merely touching the pad, and one for when you are clicking it.<br />
<br />
Note that you really only need one controller to have these bindings, so if you wanted to free up a bunch of "buttons" for use in-game then you could leave your dominant hand controller alone, but delete both of these sections from your off-hand controller. This would disable the context actions on that hand, but you could then freely use them for their joypad buttons in the control settings window; for instance, assigning pad-left to <i>copy </i>and pad-right to <i>paste</i>.<br />
<br />
<br />
<br />
<br />
That's pretty much it... apart from these two buttons in the middle of the screen:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-sRvNPzoYjlI/XIFqXu0hPVI/AAAAAAAAOPc/YojOZKAvmX4zFc_Ipp2bf08QmepW8Hq1wCLcBGAs/s1600/12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="445" data-original-width="562" height="253" src="https://1.bp.blogspot.com/-sRvNPzoYjlI/XIFqXu0hPVI/AAAAAAAAOPc/YojOZKAvmX4zFc_Ipp2bf08QmepW8Hq1wCLcBGAs/s320/12.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Which lead to these two windows:</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-uK8FOFXWACw/XIFqZWeZYnI/AAAAAAAAOPg/aZds44KYwygaz0cei6NQ3ukEsgfC1KX1ACLcBGAs/s1600/13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="672" data-original-width="1516" height="281" src="https://4.bp.blogspot.com/-uK8FOFXWACw/XIFqZWeZYnI/AAAAAAAAOPg/aZds44KYwygaz0cei6NQ3ukEsgfC1KX1ACLcBGAs/s640/13.png" width="640" /></a></div>
<br />
There isn't anything to decide here: the <span style="font-family: "courier new" , "courier" , monospace;">Pose</span> and <span style="font-family: "courier new" , "courier" , monospace;">Haptics</span> actions need to be assigned as depicted or SteamVR will not be happy.<br />
<br />
<br />
<br />
<br />
<h2>
Rift</h2>
<div>
<br /></div>
<a href="https://imgur.com/a/oAmz9xA" target="_blank">[screenshot of all bindings]</a><br />
<br />
The Rift controller is quite different in layout from the Vive controller; most notably it has a stick instead of a pad. From the top:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-VoITRbLInbk/XIFXJPaNIWI/AAAAAAAAOOM/iup1ZLU50hA_AoYejlLC3x-PeK7ceFaJgCLcBGAs/s1600/6.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="529" data-original-width="483" height="400" src="https://3.bp.blogspot.com/-VoITRbLInbk/XIFXJPaNIWI/AAAAAAAAOOM/iup1ZLU50hA_AoYejlLC3x-PeK7ceFaJgCLcBGAs/s400/6.png" width="365" /></a></div>
First up the <span style="font-family: "courier new" , "courier" , monospace;">Trigger</span>, and there is a lot going on. The primary use of the trigger on the Rift is to interact with the game and menus with the laser pointer. Here the <span style="font-family: "courier new" , "courier" , monospace;">Interface Click</span> action is assigned to the trigger under <i>Pull</i>, and then <span style="font-family: "courier new" , "courier" , monospace;">Activate Laser Pointer</span> is also assigned to the trigger under <i>Pull</i>. This might seem like a conflict, but it works because in-game there are two different thresholds for activation on these inputs; the laser pointer will activate as soon as you squeeze the trigger even a little, while the click will only occur when you pull it with intent (you can edit these thresholds in the in-game VR settings menu).<br />
<br />
The trigger is not used on the Rift for the <span style="font-family: "courier new" , "courier" , monospace;">Grab</span> action, but while you are grabbing an object you can't use the laser, so <i>Click </i>on the trigger is still a useful and intuitive place to put the <span style="font-family: "courier new" , "courier" , monospace;">Orient Object</span> action. The same is true while your controller is turned into a zoom object (no laser), so placing <span style="font-family: "courier new" , "courier" , monospace;">Peek</span> on click also works.<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-FNNvfXv5PwI/XIFX8bzEo7I/AAAAAAAAOOU/jDiJHWgXi5w35BQsbpSSySNpEkqtrcE0gCLcBGAs/s1600/7.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="808" data-original-width="482" height="640" src="https://2.bp.blogspot.com/-FNNvfXv5PwI/XIFX8bzEo7I/AAAAAAAAOOU/jDiJHWgXi5w35BQsbpSSySNpEkqtrcE0gCLcBGAs/s640/7.png" width="380" /></a></div>
The joystick is used for context actions alone (it doesn't double up on laser pointer duty like in the Vive bindings). It's slightly less versatile than the touchpad for this; theoretically it could have the dual touch+click mechanic by utilising the stick clicking in, but this feels cumbersome and ungainly in practice. Instead, on the Rift both actions are bound to the act of moving the stick in a direction.<br />
<br />
For this we use the stick as a DPad, in <i>Touch </i>mode, two times: assigning each direction's touch and click action.<br />
<br />
Note that we assign the <span style="font-family: "courier new" , "courier" , monospace;">Center Click</span> and <span style="font-family: "courier new" , "courier" , monospace;">Center Touch</span> actions separately by using the stick as a button; this is because when the Rift stick is used as a DPad in <i>Touch</i> mode it is effectively just outputting the position of the stick, and will constantly send the <i>Center</i> action while the stick is in its default position.<br />
<br />
The last section sends the <span style="font-family: "courier new" , "courier" , monospace;">Pad Click</span> action when the stick is clicked. As noted in the Vive section this might be changed in the future, but for now acts to toggle the zoom lock.<br />
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: "courier new" , "courier" , monospace;">Grip</span> is very straightforward on the Rift; it is simply used for the <span style="font-family: "courier new" , "courier" , monospace;">Grab</span> action (not counting joypad emulation or legacy bindings).</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-Es9PX5-kLuA/XIFiTd5VM-I/AAAAAAAAOOg/Vr8VqhJ3MJgR02xWrcNgjBUH8A9_OpoIwCLcBGAs/s1600/8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="130" data-original-width="482" height="86" src="https://4.bp.blogspot.com/-Es9PX5-kLuA/XIFiTd5VM-I/AAAAAAAAOOg/Vr8VqhJ3MJgR02xWrcNgjBUH8A9_OpoIwCLcBGAs/s320/8.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: left;">
<a href="https://3.bp.blogspot.com/-XuSQmIByt0M/XIFkF2PhtuI/AAAAAAAAOO8/nIbnoqOLE_I1-ZkNcHWhLbbzz0NhVKrWwCLcBGAs/s1600/9.png" imageanchor="1" style="clear: left; display: inline !important; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="412" data-original-width="484" height="340" src="https://3.bp.blogspot.com/-XuSQmIByt0M/XIFkF2PhtuI/AAAAAAAAOO8/nIbnoqOLE_I1-ZkNcHWhLbbzz0NhVKrWwCLcBGAs/s400/9.png" width="400" /></a>The <span style="font-family: "courier new" , "courier" , monospace;">X Button</span> (<span style="font-family: "courier new" , "courier" , monospace;">A Button</span> on the right controller) is used for movement. Used as a button, the <span style="font-family: "courier new" , "courier" , monospace;">Enable Movement</span> action is attached to the <i>Held </i>input.</div>
<br />
As with the Vive controls, this is a convenient place to attach the <span style="font-family: "courier new" , "courier" , monospace;">Display Tooltips</span> action (as it doesn't interfere with the movement)<br />
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
The <span style="font-family: "courier new" , "courier" , monospace;">Y Button</span> (<span style="font-family: "courier new" , "courier" , monospace;">B Button</span> on the right controller) is used as the menu button:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-J-fH4H9OLI0/XIFk_KRiQxI/AAAAAAAAOPI/xqK_wQt-A80uF9oNw38AzofflGV55-URgCLcBGAs/s1600/10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="228" data-original-width="480" height="190" src="https://4.bp.blogspot.com/-J-fH4H9OLI0/XIFk_KRiQxI/AAAAAAAAOPI/xqK_wQt-A80uF9oNw38AzofflGV55-URgCLcBGAs/s400/10.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
A single press will activate the games main menu, while holding it down for a few seconds will reset the players position instead.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Finally, the <span style="font-family: "courier new" , "courier" , monospace;">Pose</span> and <span style="font-family: "courier new" , "courier" , monospace;">Haptic</span> actions must be bound, or SteamVR will be unhappy:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-n_E5W7hp1q0/XIFl55cvqEI/AAAAAAAAOPQ/MHtRs2iH0tEYjlFj1w3ro8ARJ8UHSkigACLcBGAs/s1600/11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="446" data-original-width="600" height="296" src="https://4.bp.blogspot.com/-n_E5W7hp1q0/XIFl55cvqEI/AAAAAAAAOPQ/MHtRs2iH0tEYjlFj1w3ro8ARJ8UHSkigACLcBGAs/s400/11.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<h2 style="clear: both; text-align: left;">
Summary</h2>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Some things to watch out for:</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<ul>
<li>There are two bindings for <span style="font-family: "courier new" , "courier" , monospace;">Interface Click</span> and <span style="font-family: "courier new" , "courier" , monospace;">Activate Laser Pointer</span> - one is for an analog input like a trigger pull, the other is binary for buttons. You don't need to assign both versions of each, just the one you want to use! (so don't worry if one of each appears in <i>Suggested Actions </i>as if they are unbound).</li>
<li>Make sure to assign the <span style="font-family: "courier new" , "courier" , monospace;">Pose</span> and <span style="font-family: "courier new" , "courier" , monospace;">Haptic</span> actions or SteamVR will freak out.</li>
<li>Screenshots of all default bindings: <a href="https://imgur.com/a/PU7AB4p" target="_blank">Vive</a> | <a href="https://imgur.com/a/oAmz9xA" target="_blank">Rift</a></li>
<li><a href="https://steamcommunity.com/app/286160/discussions/5/1732081482127768388/" target="_blank">Latest info on Tabletop Simulator's VR controls</a></li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-41133610002131005132017-12-02T11:38:00.000-08:002017-12-02T11:43:25.382-08:00Debugging your TTS mods with console++ : Odds & EndsLet's wrap up this series (<a href="http://blog.onelivesleft.com/2017/09/debugging-your-tts-mods-with-console.html">which started here</a>) with some miscellaneous features of <span style="font-family: "courier new" , "courier" , monospace;">console++</span>.<br />
<br />
The <span style="font-family: "courier new" , "courier" , monospace;">console</span> module, which <span style="font-family: "courier new" , "courier" , monospace;">console++</span> is built on top of, includes a handy command: the '=' command gives you access to Lua's <span style="font-family: "courier new" , "courier" , monospace;">dynamic.eval</span> function; in other words it works as a simple expression calculator. Type '<span style="font-family: "courier new" , "courier" , monospace;">= </span>' then any mathematical expression and it will display the resulting value. You can refer to any of the variables in your program when you are a table admin, but all players can use the '<span style="font-family: "courier new" , "courier" , monospace;">=</span>' command for simple maths functions. Handy if your game is anything like Power Grid!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-VfRhj9LFu2c/WiL_hG9AM8I/AAAAAAAAGZE/zSPziXyA_1wAW-1GvYaRXdErkp1WqkeRwCLcBGAs/s1600/tut43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="360" data-original-width="196" height="400" src="https://1.bp.blogspot.com/-VfRhj9LFu2c/WiL_hG9AM8I/AAAAAAAAGZE/zSPziXyA_1wAW-1GvYaRXdErkp1WqkeRwCLcBGAs/s400/tut43.png" width="217" /></a></div>
<br />
<br />
There are also a couple of commands for interacting with the chat window output:<br />
<br />
'<span style="font-family: "courier new" , "courier" , monospace;">echo</span>' will print its parameter into the chat output (just for you); useful to include some output in a script you wish to '<span style="font-family: "courier new" , "courier" , monospace;">exec</span>' with the <span style="font-family: "courier new" , "courier" , monospace;">-q</span> or <span style="font-family: "courier new" , "courier" , monospace;">-s</span> parameters.<br />
<br />
'<span style="font-family: "courier new" , "courier" , monospace;">cls</span>' will clear the chat window.<br />
<br />
<br />
Finally, <span style="font-family: "courier new" , "courier" , monospace;">console++</span> gives all players a '<span style="font-family: "courier new" , "courier" , monospace;">shout</span>' command: this will broadcast its parameter as a message to all players, popping up at the top of their screen as well as in the chat log. It will appear in your player colour.<br />
<br />
<br />
I hope if you are using <span style="font-family: Courier New, Courier, monospace;">console++</span> that this series of posts has been helpful, or if you're not that it might have tempted you into trying it. Any feedback is greatly appreciated, especially bug reports.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-27161785823748203252017-11-27T10:33:00.001-08:002018-08-11T09:30:16.454-07:00Debugging your TTS mods with console++ : Watch ListThis will be the final installment of the tutorial series about <a href="https://github.com/onelivesleft/Console" target="_blank">console++</a> which began <a href="http://blog.onelivesleft.com/2017/09/debugging-your-tts-mods-with-console.html" target="_blank">here</a>.<br />
<br />
In this enthralling episode we'll look at the '<span style="font-family: "courier new" , "courier" , monospace;">watch</span>' command; a very useful debugging tool. '<span style="font-family: "courier new" , "courier" , monospace;">watch</span>' allows you to monitor a variable or object in your game (or even a function: more on that later), displaying a message in the chat window whenever it changes.<br />
<br />
In order to use this feature of console++ you must call '<span style="font-family: "courier new" , "courier" , monospace;">console.update()</span>' in an '<span style="font-family: "courier new" , "courier" , monospace;">onUpdate</span>' event. The example module already includes this command:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-5u3niNgDr8k/WhxFiyp6jEI/AAAAAAAAGUY/Ob0ZB3gC7sAHmI-B__pVRhe0NblXIFwDQCLcBGAs/s1600/tut31.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="244" data-original-width="529" height="183" src="https://3.bp.blogspot.com/-5u3niNgDr8k/WhxFiyp6jEI/AAAAAAAAGUY/Ob0ZB3gC7sAHmI-B__pVRhe0NblXIFwDQCLcBGAs/s400/tut31.png" width="400" /></a></div>
<br />
<br />
If you've followed this series so far then after firing up the example module in <i>Tabletop Simulator</i> you'll already be in command mode (thanks to <a href="http://blog.onelivesleft.com/2017/11/debugging-your-tts-mods-with-console.html"><span style="font-family: "courier new" , "courier" , monospace;">console.autoexec</span></a>); type '<span style="font-family: "courier new" , "courier" , monospace;">slow</span>' to enable the card display. Now if we type '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' a few times, waiting a short delay between each, we'll see the <span style="font-family: "courier new" , "courier" , monospace;">next_check</span> variable increasing:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-8of9op9uMOA/WhxG3dBbzwI/AAAAAAAAGUk/yL35Hc_GTcYVUk1KSxMzR4YrbrYdmZNvwCLcBGAs/s1600/tut32.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="664" data-original-width="390" height="400" src="https://4.bp.blogspot.com/-8of9op9uMOA/WhxG3dBbzwI/AAAAAAAAGUk/yL35Hc_GTcYVUk1KSxMzR4YrbrYdmZNvwCLcBGAs/s400/tut32.png" width="233" /></a></div>
<br />
This is a pretty awful way to check its progress however; lets use '<span style="font-family: "courier new" , "courier" , monospace;">watch</span>' instead:<br />
Type '<span style="font-family: "courier new" , "courier" , monospace;">watch /next_check</span>'. Every two seconds the <span style="font-family: "courier new" , "courier" , monospace;">next_check</span> variable will change and we'll see it do so. This is because we currently have <span style="font-family: "courier new" , "courier" , monospace;">check_delay</span> set to 2 seconds; type '<span style="font-family: "courier new" , "courier" , monospace;">fast</span>' to change <span style="font-family: "courier new" , "courier" , monospace;">check_delay</span> to 0.2, and you'll see the variable change every 0.2 seconds instead. Switch it back to 2 seconds with '<span style="font-family: "courier new" , "courier" , monospace;">slow</span>'. You wouldn't want this running after the point where you get the info you want; it's filling the chat window. To clear the watch list type '<span style="font-family: "courier new" , "courier" , monospace;">watch -c</span>'. The '<span style="font-family: "courier new" , "courier" , monospace;">-c</span>' parameter clears the watch list if it's used on its own. You may instead supply it with a variable name to only clear that variable from the watch list: here we could have typed '<span style="font-family: "courier new" , "courier" , monospace;">watch -c /next_check</span>'.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-xzn01U5aipk/WhxHmHX87YI/AAAAAAAAGUs/Yeq2dRiBVrMS0nknLIpGoCcMevganweSgCLcBGAs/s1600/tut33.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="618" data-original-width="302" height="400" src="https://3.bp.blogspot.com/-xzn01U5aipk/WhxHmHX87YI/AAAAAAAAGUs/Yeq2dRiBVrMS0nknLIpGoCcMevganweSgCLcBGAs/s400/tut33.png" width="195" /></a></div>
<br />
We can also watch objects: type '<span style="font-family: "courier new" , "courier" , monospace;">watch /dice/d6</span>' then roll the dice.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-pIneajhOQjY/WhxJpfEXNLI/AAAAAAAAGU4/oq37SvoEf5EkU0mnONVB_CF_sB7gv_I8wCLcBGAs/s1600/tut34.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="103" data-original-width="470" height="86" src="https://3.bp.blogspot.com/-pIneajhOQjY/WhxJpfEXNLI/AAAAAAAAGU4/oq37SvoEf5EkU0mnONVB_CF_sB7gv_I8wCLcBGAs/s400/tut34.png" width="400" /></a></div>
<br />
You can see it will track the objects rotation and position. This is fine for an object which you have used in your program, but not all objects will be stored in a variable. Happily we can also track objects via their GUID; right click the chess rook and in the scripting menu click on its GUID to copy it to the clipboard. Now type '<span style="font-family: "courier new" , "courier" , monospace;">watch -g </span>' and hit <span style="font-family: "courier new" , "courier" , monospace;">ctrl-v</span> to paste in the rook's GUID. Pick up the rook and move it around to see it being watched.<br />
<br />
You can see what variables you are watching by typing '<span style="font-family: "courier new" , "courier" , monospace;">watch</span>' without any parameters:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-dd2SU614GYs/WhxK3CluWKI/AAAAAAAAGVE/Igy_2Czo-McjuLERAixn-KF7fytWzu8_QCLcBGAs/s1600/tut35.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="86" data-original-width="352" height="97" src="https://4.bp.blogspot.com/-dd2SU614GYs/WhxK3CluWKI/AAAAAAAAGVE/Igy_2Czo-McjuLERAixn-KF7fytWzu8_QCLcBGAs/s400/tut35.png" width="400" /></a></div>
<br />
You can also disable the watch list temporarily by pausing it with the '<span style="font-family: "courier new" , "courier" , monospace;">-p</span>' parameter; this lets you stop the watch list outputting to the chat window without clearing it (and losing everything you are tracking). Try it now: type '<span style="font-family: "courier new" , "courier" , monospace;">watch -p</span>' and move the rook around; no updates! Do it again to unpause (use up-arrow to fetch the previous command).<br />
<br />
Grab the rook and move it around; the watch list really spams the chat window, since every tick the position changes. In a lot of cases this will be too much information, so the watch command allows you to throttle its output for each given item. Use the up-arrow key to fetch the command that added the rook to the watch list ('<span style="font-family: "courier new" , "courier" , monospace;">watch -g b27933</span>', unless your GUID is different) and then add to the end of it '<span style="font-family: "courier new" , "courier" , monospace;">-t2</span>'. This will add a 2 second throttle to the watch. Now drag the rook around and notice the difference. Notice also that this command has replaced the previous version of the item in the watch list, overwriting the old, unthrottled item; each variable or object may generally only appear once in the watch list.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-stvdESi2mpE/WhxN6S-pytI/AAAAAAAAGVU/6AhR1kDbg9Q5Uvljnr7ophrQp2F0X9j1gCLcBGAs/s1600/tut36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="168" data-original-width="332" height="201" src="https://3.bp.blogspot.com/-stvdESi2mpE/WhxN6S-pytI/AAAAAAAAGVU/6AhR1kDbg9Q5Uvljnr7ophrQp2F0X9j1gCLcBGAs/s400/tut36.png" width="400" /></a></div>
<br />
<br />
Now clear the watch list with '<span style="font-family: "courier new" , "courier" , monospace;">watch -c</span>'.<br />
<br />
As well as tracking variables and objects, we can also watch their member functions. Type '<span style="font-family: "courier new" , "courier" , monospace;">watch /dice/d6/getValue</span>', and then roll the dice:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-9q-UqNUNNYU/WhxO5DLur8I/AAAAAAAAGVc/um0l_fRURBAEpet7kpolyNO6tyKHG8sXwCLcBGAs/s1600/tut37.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="204" data-original-width="276" height="295" src="https://1.bp.blogspot.com/-9q-UqNUNNYU/WhxO5DLur8I/AAAAAAAAGVc/um0l_fRURBAEpet7kpolyNO6tyKHG8sXwCLcBGAs/s400/tut37.png" width="400" /></a></div>
<br />
Clear the list, and then try to do the same for the dice's position with '<span style="font-family: "courier new" , "courier" , monospace;">watch /dice/d6/getPosition</span>'. Argh, what's happening here?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-HISUF1lSH0k/WhxPhN8juqI/AAAAAAAAGVk/Pv7ly3JwPYIhTWsMNbMqNOq3YDyik_4GQCLcBGAs/s1600/tut38.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="574" data-original-width="318" height="400" src="https://1.bp.blogspot.com/-HISUF1lSH0k/WhxPhN8juqI/AAAAAAAAGVk/Pv7ly3JwPYIhTWsMNbMqNOq3YDyik_4GQCLcBGAs/s400/tut38.png" width="221" /></a></div>
<br />
What is happening is that every tick the watch list is calling the <span style="font-family: "courier new" , "courier" , monospace;">getPosition</span> function of the dice, and every tick it is getting a new table. Even though the table's member values are identical, the table itself is changing, so the watch list is outputting it each frame. This is definitely not useful! We can mitigate this by using the '<span style="font-family: "courier new" , "courier" , monospace;">-/</span>' parameter; this parameter specifies a member of the returned table to watch, instead of the table itself. Type '<span style="font-family: "courier new" , "courier" , monospace;">watch /dice/d6/getPosition -/x</span>'; phew, the spam has stopped. Move the dice around to see the '<span style="font-family: "courier new" , "courier" , monospace;">x</span>' value being tracked, then clear the watch list.<br />
<br />
<hr />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-AZEQsiODtUI/WhxU5eoHoXI/AAAAAAAAGV0/WVUFDdMoqhgBfkMrzBQu53h8ef3HK-IWwCLcBGAs/s1600/tut39.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="203" data-original-width="117" src="https://2.bp.blogspot.com/-AZEQsiODtUI/WhxU5eoHoXI/AAAAAAAAGV0/WVUFDdMoqhgBfkMrzBQu53h8ef3HK-IWwCLcBGAs/s1600/tut39.png" /></a></div>
As well as variables and objects, we can also watch functions. To test this out we'll need one so let's see what functions we have available with '<span style="font-family: "courier new" , "courier" , monospace;">ls -f</span>'. Ah: '<span style="font-family: "courier new" , "courier" , monospace;">dice_total</span>' is nice and simple as it has no parameters; lets watch it with '<span style="font-family: "courier new" , "courier" , monospace;">watch /dice_total</span>'. Roll the dice to see the function changing.<br />
<br />
We can also use functions with parameters; let's do that, but lets make things a little more complex, more like a situation you might find yourself in while actually debugging your mod.<br />
<br />
<div class="separator" style="clear: both;">
What does the <span style="font-family: "courier new" , "courier" , monospace;">near</span> function do? Here is its source:</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-QmVO__JXmms/WhxWCjzU10I/AAAAAAAAGWA/Pm_68XX5rI0xsR3oRNmRV-faA6reGgbjgCLcBGAs/s1600/tut40.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="132" data-original-width="590" height="87" src="https://1.bp.blogspot.com/-QmVO__JXmms/WhxWCjzU10I/AAAAAAAAGWA/Pm_68XX5rI0xsR3oRNmRV-faA6reGgbjgCLcBGAs/s400/tut40.png" width="400" /></a></div>
<br />
Not too complicated a function; it checks if two objects are near each other on the x and z axis. We're going to use it to check when the rook is near the d4.<br />
<br />
Our first problem is that we do not have a variable for the rook in our program; remember when we were watching it before we were doing so via its GUID. That's not an option here: the near function does not take a GUID as a parameter, it takes two object variables. Therefor the first thing we have to do is make one up for the rook. We'll do so using the '<span style="font-family: "courier new" , "courier" , monospace;">call</span>' command: type '<span style="font-family: "courier new" , "courier" , monospace;">call getObjectFromGUID 'b27933'</span>', and then '<span style="font-family: "courier new" , "courier" , monospace;">add /rook ~</span>'. (the '<span style="font-family: "courier new" , "courier" , monospace;">~</span>' special variable holds the last result).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-jXVEU5xhA94/WhxYDKT0WqI/AAAAAAAAGWM/XrQ0GdojrlUnXNNl0rBJPel01nzSrXyBwCLcBGAs/s1600/tut41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="276" data-original-width="404" height="272" src="https://3.bp.blogspot.com/-jXVEU5xhA94/WhxYDKT0WqI/AAAAAAAAGWM/XrQ0GdojrlUnXNNl0rBJPel01nzSrXyBwCLcBGAs/s400/tut41.png" width="400" /></a></div>
<br />
Now we have a variable holding our rook we can use it as a parameter to the near function; type '<span style="font-family: "courier new" , "courier" , monospace;">watch /near /dice/d4 /rook</span>', and then pick up the rook and move it around, over the d4 and away from it.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-yuB13iPB3Dc/WhxY6-KqJaI/AAAAAAAAGWU/OEyHENHzCvAnMhtdUOWeD-sln0bB9xFvgCLcBGAs/s1600/tut42.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="104" data-original-width="284" height="146" src="https://1.bp.blogspot.com/-yuB13iPB3Dc/WhxY6-KqJaI/AAAAAAAAGWU/OEyHENHzCvAnMhtdUOWeD-sln0bB9xFvgCLcBGAs/s400/tut42.png" width="400" /></a></div>
<br />
<br />
The <span style="font-family: "courier new" , "courier" , monospace;">near</span> function is fairly simplistic, but I hope you can see how we can write debug functions in our code, and then hook them up to problematic objects during a play session, using them to work out what is going wrong with the mod. More persistently, if you know you want to track certain variables or objects you can add '<span style="font-family: "courier new" , "courier" , monospace;">watch</span>' commands to your <span style="font-family: "courier new" , "courier" , monospace;">console.autoexec</span>, giving you feedback on events which you want to know happen (but which don't necessarily throw error messages) every time you play your mod.<br />
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-36028209743206227532017-11-23T14:55:00.000-08:002017-11-25T11:02:51.661-08:00Debugging your TTS mods with console++ : Batching and AliasingThis continues the series which <a href="http://blog.onelivesleft.com/2017/09/debugging-your-tts-mods-with-console.html" target="_blank">started here</a>.<br />
<br />
The first thing we'll do in this session (after loading the example mod in TTS and loading it into Atom) is use console++'s autoexec feature: this allows you to set a series of commands which will be executed when your mod is loaded. In order for this feature to work you need to call <span style="font-family: "courier new" , "courier" , monospace;">console.load()</span> in your <span style="font-family: "courier new" , "courier" , monospace;">onLoad </span>function; the example mod already does this as the last line:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-cOUmeJWVpEU/WhcyoaruVYI/AAAAAAAAGSQ/Gtcsnfll0X8l0kZ63526Dq4t44iJUwcPwCLcBGAs/s1600/tut24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="335" data-original-width="640" height="208" src="https://2.bp.blogspot.com/-cOUmeJWVpEU/WhcyoaruVYI/AAAAAAAAGSQ/Gtcsnfll0X8l0kZ63526Dq4t44iJUwcPwCLcBGAs/s400/tut24.png" width="400" /></a></div>
<br />
With that line present all we need to do to make console++ execute a script on startup is set it in the <span style="font-family: "courier new" , "courier" , monospace;">console.autoexec</span> variable. Add this block of code above the <span style="font-family: "courier new" , "courier" , monospace;">onLoad</span> function:<br />
<br />
<a href="https://4.bp.blogspot.com/-oSKaLlPFTG4/Whc0QOwLJZI/AAAAAAAAGSc/jE96bf-EsP4OFWB5V_t6sUGuS33MtNauQCLcBGAs/s1600/tut25.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="130" data-original-width="281" src="https://4.bp.blogspot.com/-oSKaLlPFTG4/Whc0QOwLJZI/AAAAAAAAGSc/jE96bf-EsP4OFWB5V_t6sUGuS33MtNauQCLcBGAs/s1600/tut25.png" /></a><span style="font-family: "courier new" , "courier" , monospace;"></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "courier new" , "courier" , monospace;"><br /></span></span>
<span style="font-family: "courier new" , "courier" , monospace;">console.autoexec = [[</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> cmd</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> cd /console</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">]]</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: inherit;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;">Hit </span><span style="font-family: "courier new" , "courier" , monospace;">Save And Play</span><span style="font-family: inherit;">; now when we hit enter in TTS and type a command we don't need to prefix it with '</span><span style="font-family: "courier new" , "courier" , monospace;">></span><span style="font-family: inherit;">' and don't need to remember to switch to command mode - the '</span><span style="font-family: "courier new" , "courier" , monospace;">cmd</span><span style="font-family: inherit;">' command has already done that for us. Type '</span><span style="font-family: "courier new" , "courier" , monospace;">ls</span><span style="font-family: inherit;">' to test this out:</span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-YwvaPM8ISsg/Whc1EqosXAI/AAAAAAAAGSk/T2IQbj2eWEYqtRXTYBWyV1E_FltDPS8iwCLcBGAs/s1600/tut26.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="704" data-original-width="480" height="400" src="https://3.bp.blogspot.com/-YwvaPM8ISsg/Whc1EqosXAI/AAAAAAAAGSk/T2IQbj2eWEYqtRXTYBWyV1E_FltDPS8iwCLcBGAs/s400/tut26.png" width="272" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;">You can see we are already in command mode (the command worked without a '</span><span style="font-family: "courier new" , "courier" , monospace;">></span><span style="font-family: inherit;">'), and are indeed inside the console table.</span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;">Now, the autoexec feature isn't groundbreaking: it's not doing anything we couldn't simply do with Lua commands. Instead of making an autoexec script we could have written this code:</span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-_tWsLE1sJqo/Whc2gx5X29I/AAAAAAAAGSw/Wwf6IKqVRwQbLlcnW-_P2svSyTgTHJjRwCLcBGAs/s1600/tut27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="63" data-original-width="383" height="65" src="https://3.bp.blogspot.com/-_tWsLE1sJqo/Whc2gx5X29I/AAAAAAAAGSw/Wwf6IKqVRwQbLlcnW-_P2svSyTgTHJjRwCLcBGAs/s400/tut27.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;">This accomplishes the same thing: sets you to command mode and changes your current location. It was simpler to just type the console++ commands though, no? Setting up the autoexec has an additional benefit; we can invoke it manually using the '</span><span style="font-family: "courier new" , "courier" , monospace;">exec</span><span style="font-family: inherit;">' command. Type '</span><span style="font-family: "courier new" , "courier" , monospace;">cd /</span><span style="font-family: inherit;">' to return to the root table, then '</span><span style="font-family: "courier new" , "courier" , monospace;">exec console/autoexec</span><span style="font-family: inherit;">', then '</span><span style="font-family: "courier new" , "courier" , monospace;">ls</span><span style="font-family: inherit;">': you'll see we are back in the console table. </span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;">Thus you can set up batches of instruction in strings in your program, and then run them with the '</span><span style="font-family: "courier new" , "courier" , monospace;">exec</span><span style="font-family: inherit;">' command; </span><span style="font-family: "courier new" , "courier" , monospace;">console.autoexec</span><span style="font-family: inherit;"> isn't special in this (it's only different because it is run automatically on loading) - any string variable can be executed. </span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;">As with any command which takes a variable as an argument, we can make it work on a literal string too: thus we can use '</span><span style="font-family: "courier new" , "courier" , monospace;">exec</span><span style="font-family: inherit;">' to run a sequence of commands entered as one line. To do this we need to know the use of these three punctuation marks in console++:</span></div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<ul>
<li>A string parameter prefixed with a back-tick (<span style="font-family: "courier new" , "courier" , monospace;">`</span>) is treated as a literal (instead of a path)</li>
<li>Double- and single-quotes (<span style="font-family: "courier new" , "courier" , monospace;">"</span> and <span style="font-family: "courier new" , "courier" , monospace;">'</span>) can be used to surround a parameter containing spaces in order to stop the spaces delimiting the parameter.</li>
<li>The semi-colon (<span style="font-family: "courier new" , "courier" , monospace;">;</span>) can be used to separate commands in a script instead of a new line.</li>
</ul>
<div>
So to duplicate the above autoexec script on a single line we type this:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">exec "`cmd; cd /console"</span></div>
<div>
<br />
The command line sees the surrounding double-quotes and treats everything inside them as one parameter. It then sees that that parameter starts with a back-tick and so knows it is a string literal, and then the '<span style="font-family: "courier new" , "courier" , monospace;">exec</span>' command splits that string up by its semi-colons.<br />
<br /></div>
<div>
<hr />
<br />
We'll come back to '<span style="font-family: "courier new" , "courier" , monospace;">exec'</span> later; right now we're going to look at the '<span style="font-family: "courier new" , "courier" , monospace;">alias</span>' command. '<span style="font-family: "courier new" , "courier" , monospace;">alias</span>' allows you to create your own commands from any existing commands. At its simplest this simply lets you rename commands to suit yourself: console++'s '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' and '<span style="font-family: "courier new" , "courier" , monospace;">dir</span>' commands are actually the same command: one is simply an alias of the other. If you wanted to add your own name you can use '<span style="font-family: "courier new" , "courier" , monospace;">alias</span>' to do so. Let's say you are especially verbose, and you want to type '<span style="font-family: "courier new" , "courier" , monospace;">list</span>' instead of '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>': simply enter '<span style="font-family: "courier new" , "courier" , monospace;">alias list ls</span>'.</div>
<div>
<br /></div>
<div>
This is a pretty silly example; lets make it actually useful. By default the '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' command only displays variables and tables - it does not display game objects or functions. To display everything in your current location you use the '<span style="font-family: "courier new" , "courier" , monospace;">-a</span>' option: '<span style="font-family: "courier new" , "courier" , monospace;">ls -a</span>'. Let's set up an alias for that: at the command prompt type '<span style="font-family: "courier new" , "courier" , monospace;">cd /</span>' to go to the root then '<span style="font-family: "courier new" , "courier" , monospace;">alias list ls -a</span>', and then '<span style="font-family: "courier new" , "courier" , monospace;">list</span>':</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-FBU8Q-kLejg/WhdCLKg9HJI/AAAAAAAAGTA/VMDd2f63MBkyhi1jBrQz0Y4MQtU5jyo9QCLcBGAs/s1600/tut28.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="622" data-original-width="461" height="400" src="https://4.bp.blogspot.com/-FBU8Q-kLejg/WhdCLKg9HJI/AAAAAAAAGTA/VMDd2f63MBkyhi1jBrQz0Y4MQtU5jyo9QCLcBGAs/s400/tut28.png" width="295" /></a></div>
<div>
<br /></div>
<div>
I typed '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' afterwards to compare the results: you can see our '<span style="font-family: "courier new" , "courier" , monospace;">list</span>' command displayed the functions as well as the rest, while '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' did not. If you like this behaviour you can include this alias command in your autoexec to make it available whenever you run your mod. You could even take it a step further, and alias the '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' command over the top of itself with '<span style="font-family: "courier new" , "courier" , monospace;">alias ls ls -a</span>', which would replace the default <span style="font-family: "courier new" , "courier" , monospace;">ls</span> behaviour, allowing you to just type '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' to see everything (though a better alias for this would be '<span style="font-family: "courier new" , "courier" , monospace;">alias ls ls -fov</span>', as this will allow you to toggle them off again; '<span style="font-family: "courier new" , "courier" , monospace;">-a</span>' overrides any other parameters)</div>
<div>
<br /></div>
<div>
You can check what a command does with the '<span style="font-family: "courier new" , "courier" , monospace;">help</span>' or '<span style="font-family: "courier new" , "courier" , monospace;">?'</span> command (as with <span style="font-family: "courier new" , "courier" , monospace;">ls/dir</span>, one of these is just an alias of the other). Type '<span style="font-family: "courier new" , "courier" , monospace;">? list</span>' and you'll see:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-zLh0lJT_Fuc/WhdQR_uISZI/AAAAAAAAGTQ/BQEbiKQ-qRs7QClCdyp9GtQRwZ8XGHNzQCLcBGAs/s1600/tut29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="69" data-original-width="378" height="72" src="https://1.bp.blogspot.com/-zLh0lJT_Fuc/WhdQR_uISZI/AAAAAAAAGTQ/BQEbiKQ-qRs7QClCdyp9GtQRwZ8XGHNzQCLcBGAs/s400/tut29.png" width="400" /></a></div>
<div>
<br /></div>
<div>
<hr />
<br />
Now we're going to combine these two commands, '<span style="font-family: "courier new" , "courier" , monospace;">exec</span>' and '<span style="font-family: "courier new" , "courier" , monospace;">alias</span>', to create some commands for controlling our mod. Enter these commands:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">alias slow exec -q "`set /update_cards true; set /check_delay 2.0"</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">alias fast exec -q "`set /update_cards true; set /check_delay 0.2"</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">alias off set /update_cards false</span></div>
</div>
<div>
<br /></div>
<div>
After entering these aliases we will have three new commands: '<span style="font-family: "courier new" , "courier" , monospace;">slow</span>', '<span style="font-family: "courier new" , "courier" , monospace;">fast</span>' and '<span style="font-family: "courier new" , "courier" , monospace;">off</span>'. Typing '<span style="font-family: "courier new" , "courier" , monospace;">slow</span>' or '<span style="font-family: "courier new" , "courier" , monospace;">fast</span>' will enable the card display updating to match the dice total; one being more responsive than the other. Typing '<span style="font-family: "courier new" , "courier" , monospace;">off</span>' will disable the card updating. Try them out!</div>
<div>
<br /></div>
<div>
The '<span style="font-family: "courier new" , "courier" , monospace;">-q</span>' (quiet) option tells the '<span style="font-family: "courier new" , "courier" , monospace;">exec</span>' command not to display the result of each command it is executing; just to display the final result. If we used the '<span style="font-family: "courier new" , "courier" , monospace;">-s</span>' (silent) option instead then the final result would also be suppressed. Alternatively we could have used the '<span style="font-family: "courier new" , "courier" , monospace;">-v</span>' (verbose) option to make '<span style="font-family: "courier new" , "courier" , monospace;">exec</span>' display all the commands it is executing as well as their results. If you want to you can add your own custom output by using the '<span style="font-family: "courier new" , "courier" , monospace;">echo</span>' command, which prints its arguments in the chat window.<br />
<br />
Note we don't need a back-tick before <span style="font-family: "courier new" , "courier" , monospace;">true</span> and <span style="font-family: "courier new" , "courier" , monospace;">false</span> - these are special cases. If we were assigning a string we <i>would</i> need the back-tick, or it would try to look up the string as if it were a path.</div>
<div>
<br /></div>
<div>
These are nice, but no good if you have to type them in every time you open your mod in TTS. That's OK though; we can add them to our autoexec! Remove the '<span style="font-family: "courier new" , "courier" , monospace;">cd /console</span>' command and add them in like this:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-G7gdIwRM4HY/WhdRUdX8c4I/AAAAAAAAGTc/YGUXiecGygQNCe7Ad7tFH8yWx2aFgr8wACLcBGAs/s1600/tut30.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="169" data-original-width="546" height="123" src="https://4.bp.blogspot.com/-G7gdIwRM4HY/WhdRUdX8c4I/AAAAAAAAGTc/YGUXiecGygQNCe7Ad7tFH8yWx2aFgr8wACLcBGAs/s400/tut30.png" width="400" /></a></div>
<div>
<br /></div>
<div>
Now whenever you load your mod you will start in command mode, and have these commands at your disposal.</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-76380251689708185372017-09-18T10:55:00.000-07:002017-09-18T11:09:56.051-07:00Debugging your TTS mods with console++ : FunctionsDon't just jump in here: <a href="http://blog.onelivesleft.com/2017/09/debugging-your-tts-mods-with-console.html" target="_blank">start at the beginning!</a> Once you've loaded the module in Tabletop Simulator remember to switch to the Game tab and turn on command mode with '<span style="font-family: "courier new" , "courier" , monospace;">></span>'.<br />
<br />
In the previous posts we've seen how we can display variables with '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' (or '<span style="font-family: "courier new" , "courier" , monospace;">ls -v</span>') and objects with '<span style="font-family: "courier new" , "courier" , monospace;">ls -o</span>'; the final option available is to display functions with '<span style="font-family: "courier new" , "courier" , monospace;">ls -f</span>'.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-tzP5TuyEfDA/Wb_xxet2MKI/AAAAAAAAF5I/-oGKGX0OumEeDVE4-FtE1VxEsFdNk_yxgCLcBGAs/s1600/tut14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="310" data-original-width="500" height="247" src="https://1.bp.blogspot.com/-tzP5TuyEfDA/Wb_xxet2MKI/AAAAAAAAF5I/-oGKGX0OumEeDVE4-FtE1VxEsFdNk_yxgCLcBGAs/s400/tut14.png" width="400" /></a></div>
<br />
Here you can see the functions defined in the example mod. We can do more than just display them, however; we can also call them. Type '<span style="font-family: "courier new" , "courier" , monospace;">call dice_total</span>', then roll the dice and call it again.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-fvgb4-jkFmM/Wb_yfWCpkRI/AAAAAAAAF5Q/Smr8zS1Zvr4Npt1-b8mXDKdEIOLk3d8EACLcBGAs/s1600/tut15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="310" data-original-width="500" height="247" src="https://4.bp.blogspot.com/-fvgb4-jkFmM/Wb_yfWCpkRI/AAAAAAAAF5Q/Smr8zS1Zvr4Npt1-b8mXDKdEIOLk3d8EACLcBGAs/s400/tut15.png" width="400" /></a></div>
<br />
What if we want to store the value a function outputs? console++ defines a special path for the last returned value: the '<span style="font-family: "courier new" , "courier" , monospace;">~</span>' character. You can see this if you display it with '<span style="font-family: "courier new" , "courier" , monospace;">ls ~</span>', and all other commands can use it like they would any other path; this includes '<span style="font-family: "courier new" , "courier" , monospace;">add</span>' and '<span style="font-family: "courier new" , "courier" , monospace;">set</span>'. Add a variable called <span style="font-family: "courier new" , "courier" , monospace;">total </span>using the '<span style="font-family: "courier new" , "courier" , monospace;">~</span>' path and check it's set to the right value:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-3PHUs4CO7Rs/Wb_0sAuNFxI/AAAAAAAAF5c/med6gz9Y-L449GLdr49Zxj3F2kHh3s8ogCLcBGAs/s1600/tut16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="310" data-original-width="500" height="247" src="https://2.bp.blogspot.com/-3PHUs4CO7Rs/Wb_0sAuNFxI/AAAAAAAAF5c/med6gz9Y-L449GLdr49Zxj3F2kHh3s8ogCLcBGAs/s400/tut16.png" width="400" /></a></div>
<br />
The '<span style="font-family: "courier new" , "courier" , monospace;">add</span>' and '<span style="font-family: "courier new" , "courier" , monospace;">set</span>' commands can also be used to store a result directly, by calling them with no value argument before the '<span style="font-family: "courier new" , "courier" , monospace;">call</span>' command. If you do so the the next 'call' command will store its result where you have specified. Roll the dice again and then try it by typing '<span style="font-family: "courier new" , "courier" , monospace;">set total</span>' and then '<span style="font-family: "courier new" , "courier" , monospace;">call dice_total</span>':<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-SDW_jNHNkEI/Wb_1wtqemXI/AAAAAAAAF5o/xlW0QxlTG5QK0n6I8ygLrPZ6JivOXWCfwCLcBGAs/s1600/tut17.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="310" data-original-width="500" height="247" src="https://1.bp.blogspot.com/-SDW_jNHNkEI/Wb_1wtqemXI/AAAAAAAAF5o/xlW0QxlTG5QK0n6I8ygLrPZ6JivOXWCfwCLcBGAs/s400/tut17.png" width="400" /></a></div>
<br />
<br />
Note that console++ also has a short-cut command to display the last result: simply type the result character on its own to display its contents: '<span style="font-family: "courier new" , "courier" , monospace;">~</span>'<br />
<br />
<br />
We can pass parameters to functions just as you would expect, by adding them to the '<span style="font-family: "courier new" , "courier" , monospace;">call</span>' command. Let's use the <span style="font-family: "courier new" , "courier" , monospace;">is_face_up </span>function on one of the cards. Remembers, the card objects are stores in the <span style="font-family: "courier new" , "courier" , monospace;">cards </span>table; you can see them by typing '<span style="font-family: "courier new" , "courier" , monospace;">ls -o cards</span>'. <br />
Type '<span style="font-family: "courier new" , "courier" , monospace;">call is_face_up cards/2</span>' to call <span style="font-family: "courier new" , "courier" , monospace;">is_face_up</span> on the 2 card, then flip the card and issue the command again (use up-arrow to get the last command typed instead of typing it again). <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-sKU5n5S4RZU/Wb_3QyU8dGI/AAAAAAAAF50/meSQXq402708cKUo2lmSoCHjvOyx3YLcgCLcBGAs/s1600/tut18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="600" data-original-width="1000" height="240" src="https://2.bp.blogspot.com/-sKU5n5S4RZU/Wb_3QyU8dGI/AAAAAAAAF50/meSQXq402708cKUo2lmSoCHjvOyx3YLcgCLcBGAs/s400/tut18.png" width="400" /></a></div>
<br />
<br />
Note how when we '<span style="font-family: "courier new" , "courier" , monospace;">ls -f</span>' we see only the functions defined in the mod; we don't see all the built-in Lua functions. That's not because they are not present; it's because console++ hides them under the '<span style="font-family: "courier new" , "courier" , monospace;">sys</span>' label. If you do an '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' you will see two things at the bottom of the tables that look like tables, except they are in a salmon colour instead of yellow; these are labels used to hide globals to keep things tidy. You can see what they are hiding by calling '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' on them;<br />
try it now: '<span style="font-family: "courier new" , "courier" , monospace;">ls sys -f</span>'<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-VKJ0HG1mRxE/Wb_5ZeeYl8I/AAAAAAAAF6A/2JQsTLPfyOUgC4FLzUGDHksXlhLw8mkcACLcBGAs/s1600/tut19.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="700" data-original-width="500" height="400" src="https://2.bp.blogspot.com/-VKJ0HG1mRxE/Wb_5ZeeYl8I/AAAAAAAAF6A/2JQsTLPfyOUgC4FLzUGDHksXlhLw8mkcACLcBGAs/s400/tut19.png" width="285" /></a></div>
<br />
At the point in the Lua code where you <span style="font-family: "courier new" , "courier" , monospace;">#include console++</span> it automatically scans all the globals and hides them under the `<span style="font-family: "courier new" , "courier" , monospace;">sys</span>` label, thus keeping your mod easy to interact with in the console. All these things still exist in the root table, though they may also be accessed via the label they are hidden under. i.e. '<span style="font-family: "courier new" , "courier" , monospace;">ls /math</span>' and '<span style="font-family: "courier new" , "courier" , monospace;">ls </span><span style="font-family: "courier new" , "courier" , monospace;">/sys</span><span style="font-family: "courier new" , "courier" , monospace;">/math</span>' are the same thing:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/--4fD8c6UHVw/Wb_92N4xNmI/AAAAAAAAF6M/xBSP6FhTd9wA6vZxQ9c0GyKWQIwxfYs2QCLcBGAs/s1600/tut20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="300" data-original-width="500" height="240" src="https://2.bp.blogspot.com/--4fD8c6UHVw/Wb_92N4xNmI/AAAAAAAAF6M/xBSP6FhTd9wA6vZxQ9c0GyKWQIwxfYs2QCLcBGAs/s400/tut20.png" width="400" /></a></div>
<br />
<br />
You can also create your own labels to hide things under by calling <span style="font-family: "courier new" , "courier" , monospace;">console.hide_globals</span> in the mod's Lua code. Type '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' and you'll see that as well as the '<span style="font-family: "courier new" , "courier" , monospace;">sys</span>' label there's also a '<span style="font-family: "courier new" , "courier" , monospace;">guids</span>' label; if you look at the end of the GUIDS block in the Lua code you'll see the command that created it; whenever you call <span style="font-family: "courier new" , "courier" , monospace;">console.hide_globals</span> it will scan for global variables/functions etc and hide them under the label you specify (provided they haven't already been hidden under another label).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-PJsA_7utWqo/WcACDjwY6jI/AAAAAAAAF6Y/bY-HqS-idI0NzKp9E2wjN4CEK76NfA6vACLcBGAs/s1600/tut21.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="300" data-original-width="1000" height="120" src="https://3.bp.blogspot.com/-PJsA_7utWqo/WcACDjwY6jI/AAAAAAAAF6Y/bY-HqS-idI0NzKp9E2wjN4CEK76NfA6vACLcBGAs/s400/tut21.png" width="400" /></a></div>
<br />
<br />
To come back to the point: all the built-in functions are available should you wish to call them. For example, we can get a color using <span style="font-family: "courier new" , "courier" , monospace;">stringColorToRGB </span>then display the RGB values:<br />
'<span style="font-family: "courier new" , "courier" , monospace;">call stringColorToRGB Blue</span>' then '<span style="font-family: "courier new" , "courier" , monospace;">~</span>':<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-o1GTWiixGp8/WcAH_40jc1I/AAAAAAAAF6w/x9ZN9mk84ZEX_qDcL-QT9JTmhAN_KadeQCLcBGAs/s1600/tut22.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="300" data-original-width="500" height="240" src="https://2.bp.blogspot.com/-o1GTWiixGp8/WcAH_40jc1I/AAAAAAAAF6w/x9ZN9mk84ZEX_qDcL-QT9JTmhAN_KadeQCLcBGAs/s400/tut22.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
We can then store it in a variable and use it to highlight an object:<br />
'<span style="font-family: "courier new" , "courier" , monospace;">add blue ~</span>' then '<span style="font-family: "courier new" , "courier" , monospace;">call /decks/b/highlightOn blue 100</span>'<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-x3_TjttIty4/WcAIHv6Ps_I/AAAAAAAAF60/X9XIHuO3ohMk9rkXaSqvqNm7fsajzqVBQCLcBGAs/s1600/tut23.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="300" data-original-width="1000" height="120" src="https://1.bp.blogspot.com/-x3_TjttIty4/WcAIHv6Ps_I/AAAAAAAAF60/X9XIHuO3ohMk9rkXaSqvqNm7fsajzqVBQCLcBGAs/s400/tut23.png" width="400" /></a></div>
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-9107842884677888942017-09-07T14:35:00.001-07:002017-09-07T14:42:04.893-07:00Debugging your TTS mods with console++ : Manipulating VariablesIf you haven't read it then you should check out the <a href="http://blog.onelivesleft.com/2017/09/debugging-your-tts-mods-with-console.html" target="_blank">first installment of this series</a>, which goes over how to get set up.<br />
<br />
Last time we saw how to look at our program's structure. This time we'll see how we can manipulate it in real-time by changing and adding variables. <br />
<br />
First things first though; last time we were entering commands by typing the command symbol ('>') and then the command. Let's shortcut past that by entering command mode. You can type '<span style="font-family: "courier new" , "courier" , monospace;">>cmd</span>' at the prompt to enter command mode, and when in command mode you can type '<span style="font-family: "courier new" , "courier" , monospace;">exit</span>' to leave it. When you are in command mode the console will interpret all your inputs as commands, so you no longer need to prefix everything with '<span style="font-family: "courier new" , "courier" , monospace;">></span>'. <br />
But we're not going to type those! Instead, an even easier way to enter and exit command mode is to just type the command character on it's own; if you hit '<span style="font-family: "courier new" , "courier" , monospace;">></span>' then enter it will toggle you in and out of command mode. Try it now:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-hLUhpUH3ixM/WbGlIjtHf2I/AAAAAAAAF2w/MSWsTq4SlcY9GgXALSdm7qmKwJpj-sfywCLcBGAs/s1600/tut7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="200" data-original-width="500" height="160" src="https://1.bp.blogspot.com/-hLUhpUH3ixM/WbGlIjtHf2I/AAAAAAAAF2w/MSWsTq4SlcY9GgXALSdm7qmKwJpj-sfywCLcBGAs/s400/tut7.png" width="400" /></a></div>
<br />
After I entered command mode I typed '<span style="font-family: "courier new" , "courier" , monospace;">cd cards</span>' (just like last time), and you can see that while in command mode any commands you perform will end with a prompt which includes our current path.<br />
<br />
OK, so now we can simply type commands without a prefix, let's try a new one out: the '<span style="font-family: "courier new" , "courier" , monospace;">tgl</span>' (or '<span style="font-family: "courier new" , "courier" , monospace;">toggle</span>') command flips a boolean. Let's go back to the root table with '<span style="font-family: "courier new" , "courier" , monospace;">cd /</span>' then remind ourselves what is there with '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>'.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-OlBo4SQ2fX0/WbGmAPO41PI/AAAAAAAAF24/sw4-gWliXIErQloB65nqjXTF_6IecNszACLcBGAs/s1600/tut8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="268" data-original-width="500" height="213" src="https://2.bp.blogspot.com/-OlBo4SQ2fX0/WbGmAPO41PI/AAAAAAAAF24/sw4-gWliXIErQloB65nqjXTF_6IecNszACLcBGAs/s400/tut8.png" width="400" /></a></div>
<br />
At the bottom you can see the four global variables in the mod, and the bottom one is a boolean, currently set to false. If we look at <a href="https://gist.github.com/onelivesleft/d909638a22a5ccb13b5c56d68b1f1607" target="_blank">the code</a> we can see what it does:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-M4ITW9lhKWQ/WbGmus4QqAI/AAAAAAAAF3A/KMNZEJL_KIglyWJSDqeHre8GE-WcEzQrgCLcBGAs/s1600/tut9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="239" data-original-width="489" height="193" src="https://3.bp.blogspot.com/-M4ITW9lhKWQ/WbGmus4QqAI/AAAAAAAAF3A/KMNZEJL_KIglyWJSDqeHre8GE-WcEzQrgCLcBGAs/s400/tut9.png" width="400" /></a></div>
<br />
<span style="font-family: "courier new" , "courier" , monospace;">update_cards </span>acts as a sentinel on the <span style="font-family: "courier new" , "courier" , monospace;">onUpdate</span> event. Let's turn it on so the mod actually does what it's supposed to do. Type: '<span style="font-family: "courier new" , "courier" , monospace;">tgl update_cards</span>' and see what happens.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-W-FwwdxC6So/WbGnjN9LFPI/AAAAAAAAF3I/R3yIPS28bCsip_H1FpniVUWxVmE00uxnwCLcBGAs/s1600/tut10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="986" data-original-width="1528" height="257" src="https://1.bp.blogspot.com/-W-FwwdxC6So/WbGnjN9LFPI/AAAAAAAAF3I/R3yIPS28bCsip_H1FpniVUWxVmE00uxnwCLcBGAs/s400/tut10.png" width="400" /></a></div>
<br />
Well, that isn't good. The mod clearly doesn't work right, and worse, because the bug is in the <span style="font-family: "courier new" , "courier" , monospace;">onUpdate</span> event it is spamming errors every time it engages. Investigating will be impossible while the console is being filled with error messages, so turn it off again: in the console hit up-arrow to get the previous command and enter to execute it; this will toggle the <span style="font-family: "courier new" , "courier" , monospace;">update_cards</span> boolean back off, disabling the faulty <span style="font-family: "courier new" , "courier" , monospace;">onUpdate</span> event code. Note this technique; it's always a good idea to wrap timer-event driven code inside a boolean like this so that if things go wrong you can easily disable the code without aborting from the faulty session.<br />
<br />
So what's the problem? The error is on line <span style="font-family: "courier new" , "courier" , monospace;">1542</span>, so go to the global script in Atom and hit <span style="font-family: "courier new" , "courier" , monospace;">ctrl-g</span> and enter <span style="font-family: "courier new" , "courier" , monospace;">1542 </span>to go to the offending line number. The Atom plugin will take into account any <span style="font-family: "courier new" , "courier" , monospace;">#includes</span> in the code, and will take you to the correct line, which in this case is the one that reads '<span style="font-family: "courier new" , "courier" , monospace;">cards[count].flip()</span>'.<br />
<br />
The error is suggesting that we're trying to index a nil value, so either <span style="font-family: "courier new" , "courier" , monospace;">cards</span> is <span style="font-family: "courier new" , "courier" , monospace;">nil</span>, or the individual location of <span style="font-family: "courier new" , "courier" , monospace;">cards[count]</span> is <span style="font-family: "courier new" , "courier" , monospace;">nil</span>. Let's have a look: type '<span style="font-family: "courier new" , "courier" , monospace;">ls cards</span>' at the console.<br />
Nothing? Oh, the cards are game objects; try '<span style="font-family: "courier new" , "courier" , monospace;">ls -o cards</span>'. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-9FOW8aYf1EI/WbGpsS3shdI/AAAAAAAAF3U/s1E72RLuUkst5E21SLT1mTDhPhIS-Qb_QCLcBGAs/s1600/tut11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="268" data-original-width="500" height="213" src="https://2.bp.blogspot.com/-9FOW8aYf1EI/WbGpsS3shdI/AAAAAAAAF3U/s1E72RLuUkst5E21SLT1mTDhPhIS-Qb_QCLcBGAs/s400/tut11.png" width="400" /></a></div>
<br />
There they are; <span style="font-family: "courier new" , "courier" , monospace;">cards</span> is clearly not <span style="font-family: "courier new" , "courier" , monospace;">nil</span>. Let's look again at the code - what is the <span style="font-family: "courier new" , "courier" , monospace;">cards</span> table supposed to do? You can see in the <span style="font-family: "courier new" , "courier" , monospace;">onUpdate</span> event it is looping over the numbers 2 to 10 - the card values on the table. The table has been set up such that to access the <i>2 of Spades</i> we use <span style="font-family: "courier new" , "courier" , monospace;">cards[2]</span>, to get the <i>3 of Diamonds</i> we use <span style="font-family: "courier new" , "courier" , monospace;">cards[3]</span>, etc. In the <span style="font-family: "courier new" , "courier" , monospace;">onUpdate</span> event we are looping over each of the cards from 2 to 10 and possibly flipping them. However, look at the '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' output: the cards are not indexed from 2 to 10, they are indexed from 1 to 9. Looks like the table has been set up wrong! There is no <span style="font-family: "courier new" , "courier" , monospace;">cards[10]</span> so when it tries to access it on the last iteration of the loop it doesn't find anything, thus the error message. <br />
Before we jump back into the code to try to track this down, let's confirm that that is the problem; we'll add a card at that index and see if the code works. To add a variable we use the '<span style="font-family: "courier new" , "courier" , monospace;">add</span>' command. We'll set it to the card that's not being looped over: <span style="font-family: "courier new" , "courier" , monospace;">cards[1]</span>. <br />
Type '<span style="font-family: "courier new" , "courier" , monospace;">add cards/10 cards/1</span>', and then '<span style="font-family: "courier new" , "courier" , monospace;">ls cards</span>' again.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-ACG4Vw9vZ_U/WbGrxh3G2mI/AAAAAAAAF3g/F7ISQa39ipYA-FnIIJGODY3nzuYmxv3RwCLcBGAs/s1600/tut12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="310" data-original-width="500" height="247" src="https://1.bp.blogspot.com/-ACG4Vw9vZ_U/WbGrxh3G2mI/AAAAAAAAF3g/F7ISQa39ipYA-FnIIJGODY3nzuYmxv3RwCLcBGAs/s400/tut12.png" width="400" /></a></div>
<br />
You can see it has a card at index 10, and it has the same GUID as the one in index 1. Turn on the code by toggling the boolean: '<span style="font-family: "courier new" , "courier" , monospace;">tgl update_cards</span>'. No errors - nice! The update code is working, but our table is still set up wrong; all the indexes are off by one (as you can tell by the card 3 being face up, but the total of the dice only being 2). Select the dice and hit '<span style="font-family: "courier new" , "courier" , monospace;">r</span>' to roll them. The cards will update, but always to the wrong value.<br />
<br />
We now know what to fix in the code though, and on investigation you can see the part that sets up the table:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-XnefZsw3q5g/WbGs-D91UII/AAAAAAAAF3s/5OcVB_JfjkUugCzdp6aFRROOVq9JkQuewCLcBGAs/s1600/tut13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="90" data-original-width="536" height="65" src="https://4.bp.blogspot.com/-XnefZsw3q5g/WbGs-D91UII/AAAAAAAAF3s/5OcVB_JfjkUugCzdp6aFRROOVq9JkQuewCLcBGAs/s400/tut13.png" width="400" /></a></div>
<br />
<br />
This looks pretty straight-forward; the table is created with a <span style="font-family: "courier new" , "courier" , monospace;">null</span> value in index 1, so that when it loops over the GUIDs of the cards it will add them to slot 2, 3, 4... etc. all the way up to 10. However, if you are more familiar with Lua than the programmer of this code you will know that <span style="font-family: "courier new" , "courier" , monospace;">nil</span> does not behave just like <span style="font-family: "courier new" , "courier" , monospace;">null</span> does in other languages; in Lua when you assign <span style="font-family: "courier new" , "courier" , monospace;">nil</span> you are actually deleting that entry, so this code is no different from typing '<span style="font-family: "courier new" , "courier" , monospace;">cards = {}</span>'... it makes an empty table. The fix should be simple: change the line to '<span style="font-family: "courier new" , "courier" , monospace;">cards = {'dummy'}</span>' and hit <i>Save And Play</i>. Now we can go back into command mode with '<span style="font-family: "courier new" , "courier" , monospace;">></span>' and type '<span style="font-family: "courier new" , "courier" , monospace;">ls -o cards</span>' to see that they are correctly indexed, 2-10. (An exercise to the reader: where is the 'dummy' card?)<br />
<br />
Toggle the <span style="font-family: "courier new" , "courier" , monospace;">update_cards</span> variable to turn on the event code. No errors, and the card being displayed matches the total of the dice! Select the dice and hit '<span style="font-family: "courier new" , "courier" , monospace;">r</span>' to roll them, and you can watch the cards flip to match. <br />
<br />
It feels a bit clunky though; the cards are updating very slowly. Type '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' again to see what variables we have to play with: sure enough, there's a '<span style="font-family: "courier new" , "courier" , monospace;">check_delay</span>' number set to 1.5 seconds. Let's set it to a shorter delay using the '<span style="font-family: "courier new" , "courier" , monospace;">set</span>' command. Type '<span style="font-family: "courier new" , "courier" , monospace;">set check_delay 0.3</span>' then select the dice and roll them. Much more responsive! If you were writing this code you could easily play with varying values for <span style="font-family: "courier new" , "courier" , monospace;">check_delay</span> until you found the one you liked the best, and then amend the code afterwards to match.<br />
<br />
That covers the basics of manipulating variables in the run-time environment. You can see how we can use it to debug our code in a controlled manner, and also to help in refining control variables. We've now looked at simple variables and objects; next time we'll look at the third of the triad: functions.
<br />
<br />
<div style="padding-bottom: 54%; position: relative;">
<iframe allowfullscreen="" frameborder="0" height="100%" scrolling="no" src="https://gfycat.com/ifr/FragrantDifficultDegus" style="left: 0; position: absolute; top: 0;" width="100%"></iframe></div>
Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-2921343848373368580.post-32469151528481566292017-09-04T15:28:00.000-07:002017-12-02T11:17:26.125-08:00Debugging your TTS mods with console++ : Introduction<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
The typical cycle for debugging a game in Tabletop Simulator is to play it, wait for an error message to emerge, use that error message to find the problem, write a potential fix, then start again. This is less than ideal in a lot of ways: the error message can be a good clue for what the problem is, but sometimes it can be extremely vague (sometimes it doesn't even have a line number!). Potentially more time-consuming is that when your game is getting fairly well developed the bugs become hidden further in; it may take a lot of setup to get to the specific circumstances that trigger the bug. Typically this means that once you think you've written a fix you then need to play the game out exactly the same was as before, coordinating any playtesters to do what they did before, until the bug manifests again... or does not, and is <i>potentially</i> fixed - but how do you know for sure?<br />
<br />
<a href="https://github.com/onelivesleft/TTSConsole" target="_blank">console++</a> gives you live access to your mod's structure, treating it as if it were a file system. It lets you examine your variables, functions and objects, and also lets you update them, all without leaving the game.<br />
<br />
<a href="https://1.bp.blogspot.com/-tHNcC3q181k/Wa3Foe4v5bI/AAAAAAAAF00/VGZkRhotrAMrZhSNx1Vcm2WwbN9zXQu0QCLcBGAs/s1600/console%252B%252B_table.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="708" data-original-width="822" height="275" src="https://1.bp.blogspot.com/-tHNcC3q181k/Wa3Foe4v5bI/AAAAAAAAF00/VGZkRhotrAMrZhSNx1Vcm2WwbN9zXQu0QCLcBGAs/s320/console%252B%252B_table.png" width="320" /></a>To follow along to this tutorial you need to get console++ from the link above and subscribe to the <a href="http://steamcommunity.com/sharedfiles/filedetails/?id=1128219748&result=8" target="_blank">example mod on the Steam Workshop</a>. Once you've loaded it into Tabletop Simulator, save it to your own hard disk so you can save your edits. <br />
<br />
<a href="https://gist.github.com/onelivesleft/d909638a22a5ccb13b5c56d68b1f1607" target="_blank">Source code is visible here.</a><br />
<div class="separator" style="clear: both; text-align: left;">
This post will cover navigating and displaying your program using the two basic commands in console++: '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' and '<span style="font-family: "courier new" , "courier" , monospace;">cd</span>'. As a quick aside, I'll be using the unix-like command names during this tutorial ('<span style="font-family: "courier new" , "courier" , monospace;">ls</span>'), but console++ includes DOS-like alternatives, so if you prefer DOS you can use those instead. In this case, anywhere I say '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' you can type '<span style="font-family: "courier new" , "courier" , monospace;">dir</span>' instead.</div>
<br />
What does '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' do? Well, we could ask for help on it, but lets do the impetuous thing and just type it into the console; make sure you are on the Game tab of the console, not the Global tab it defaults to (also it would be a good idea to hit the cog and disable autohide if you have it turned on). <br />
<br />
So, hit enter, type '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' and hit enter again. Congrats, you just said '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>'; the console is just the chat window after all, and you didn't tell it you wanted to use a command! To use a command you type the prefix '<span style="font-family: "courier new" , "courier" , monospace;">></span>' before it, so lets try again, typing '<span style="font-family: "courier new" , "courier" , monospace;">>ls</span>'. Much more interesting, and if you looked at the source file above it should look a little familiar:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-3gkJs7TPjhI/Wa3L7NJibMI/AAAAAAAAF1c/kIf3OEPnKEodpVn53txswYpcR4skLgfFwCLcBGAs/s1600/tut3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="268" data-original-width="1000" height="105" src="https://3.bp.blogspot.com/-3gkJs7TPjhI/Wa3L7NJibMI/AAAAAAAAF1c/kIf3OEPnKEodpVn53txswYpcR4skLgfFwCLcBGAs/s400/tut3.png" width="400" /></a></div>
<br />
You can see the variables we defined in the source are there; the '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' command lists all the tables and variables in your current location. Right now we're in the root of our program ('<span style="font-family: "courier new" , "courier" , monospace;">/</span>') which is the global level. You can see at the top of the listing some labels in yellow: these are tables. Let's see what's in them; type '<span style="font-family: "courier new" , "courier" , monospace;">>ls decks</span>'. Nothing there? That's because by default the '<span style="font-family: "courier new" , "courier" , monospace;">ls</span>' command only displays tables and basic variables (numbers, strings and booleans); to get it to show game objects we need to use the '<span style="font-family: "courier new" , "courier" , monospace;">-o</span>' option. Try '<span style="font-family: "courier new" , "courier" , monospace;">>ls decks -o</span>' and you'll see:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-tuFk7TB58CM/Wa3NyutekUI/AAAAAAAAF1o/dzKU8-cHxns4bHyP6mC_2elIgq8xpefiwCLcBGAs/s1600/tut4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="268" data-original-width="500" height="171" src="https://2.bp.blogspot.com/-tuFk7TB58CM/Wa3NyutekUI/AAAAAAAAF1o/dzKU8-cHxns4bHyP6mC_2elIgq8xpefiwCLcBGAs/s320/tut4.png" width="320" /></a></div>
<br />
There are our deck objects: table entries <span style="font-family: "courier new" , "courier" , monospace;">a</span>, <span style="font-family: "courier new" , "courier" , monospace;">b</span>, and <span style="font-family: "courier new" , "courier" , monospace;">c</span>. We can see their GUIDs listed next to them; right click a deck on the table and check the Scripting submenu to see it matches up. <br />
<br />
We can refer to each of these decks by their path: if you type '<span style="font-family: "courier new" , "courier" , monospace;">>ls decks/a</span>' you'll get the listing for only deck <span style="font-family: "courier new" , "courier" , monospace;">a</span>. Now, if we were debugging something to do with these decks, and were going to type their names a lot it would be tedious to have to keep specifying their location. Instead we can change our current location to suit our needs, with the '<span style="font-family: "courier new" , "courier" , monospace;">cd</span>' command. Type '<span style="font-family: "courier new" , "courier" , monospace;">>cd decks</span>' and then '><span style="font-family: "courier new" , "courier" , monospace;">ls -o</span>'. As you can see we have changed the table we are in; we can now refer to the decks simply by their letters: '<span style="font-family: "courier new" , "courier" , monospace;">>ls a</span>'. Note that when we specify an exact path we do not need the '<span style="font-family: "courier new" , "courier" , monospace;">-o</span>' option.<br />
<br />
Paths behave just as you would expect for a file-system; you can specify relative paths from your current location or absolute paths from the root table (by starting your path with a '<span style="font-family: "courier new" , "courier" , monospace;">/</span>') You can refer to a parent table by using '<span style="font-family: "courier new" , "courier" , monospace;">..</span>' and your current table with '<span style="font-family: "courier new" , "courier" , monospace;">.</span>'<br />
<br />
Let's go back to the root table: type either '><span style="font-family: "courier new" , "courier" , monospace;">cd ..</span>' to go up one level (we are only one level below the root) or '><span style="font-family: "courier new" , "courier" , monospace;">cd /</span>'. This example mod is clearly very small and simple, but you can imagine in a big game you will have lots of variables and tables; simply typing '><span style="font-family: "courier new" , "courier" , monospace;">ls</span>' and scrolling up and down looking for things could be cumbersome. In that situation it would be good to use a wildcard to trim down what you're looking at: try '<span style="font-family: "courier new" , "courier" , monospace;">>ls c*</span>'<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-1TDu_clgwzE/Wa3QtCva4qI/AAAAAAAAF10/32p1yeRT4AsCTeWrAK_1q0cl-Pdn6gfqgCLcBGAs/s1600/tut5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="268" data-original-width="500" height="171" src="https://3.bp.blogspot.com/-1TDu_clgwzE/Wa3QtCva4qI/AAAAAAAAF10/32p1yeRT4AsCTeWrAK_1q0cl-Pdn6gfqgCLcBGAs/s320/tut5.png" width="320" /></a></div>
<br />
That's the basics of navigating and examining your program from within TTS with console++, except to return to what was said at the top of the post about asking for help: you can type '><span style="font-family: "courier new" , "courier" , monospace;">help</span>' or more simply '><span style="font-family: "courier new" , "courier" , monospace;">?</span>' to get help on any command, and the commands with more complicated options will also support a '<span style="font-family: "courier new" , "courier" , monospace;">-?</span>' option to provide more detailed information. If you type '<span style="font-family: "courier new" , "courier" , monospace;">>? ls</span>' you'll see it has a few options we haven't tried yet. To wrap up lets try '<span style="font-family: "courier new" , "courier" , monospace;">>ls <span style="font-family: "courier new" , "courier" , monospace;">-ar</span></span>' ...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-0CjUl4dzLUI/Wa3TJcYGNGI/AAAAAAAAF2A/nPMPkva49o8zOSVgOZ6f9xN69QU_k3SdgCLcBGAs/s1600/tut6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="512" data-original-width="500" height="320" src="https://4.bp.blogspot.com/-0CjUl4dzLUI/Wa3TJcYGNGI/AAAAAAAAF2A/nPMPkva49o8zOSVgOZ6f9xN69QU_k3SdgCLcBGAs/s320/tut6.png" width="312" /></a></div>
<br />
<br />
Next time we'll look at actually interacting with your program by modifying variables in real time.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-43402882413763903642017-08-24T14:11:00.000-07:002018-12-23T11:48:32.428-08:00Atom Tabletop Simulator package, experimental #include system<span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;">A note about the experimental additions, specifically the </span><span style="background-color: white; color: #3e3e3e; font-size: 13px;"><span style="font-family: "courier new" , "courier" , monospace;">#include</span></span><span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;">:</span><br />
<br style="background-color: white; color: #3e3e3e; font-family: Verdana, Arial, Tahoma, Calibri, Geneva, sans-serif; font-size: 13px;" />
<span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;">You can </span><span style="background-color: white; color: #3e3e3e; font-size: 13px;"><span style="font-family: "courier new" , "courier" , monospace;">#include</span></span><span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;"> other files into your source; when you hit </span><span style="background-color: white; color: #3e3e3e; font-size: 13px;"><span style="font-family: "courier new" , "courier" , monospace;">Save And Play</span></span><span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;"> the plugin will go and grab those files and inline them in your code at the point you have placed them. The files are looked for inside the folder specified, or you can specify a complete path to their location.</span><br />
<br style="background-color: white; color: #3e3e3e; font-family: Verdana, Arial, Tahoma, Calibri, Geneva, sans-serif; font-size: 13px;" />
<span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;">For example, on the game I'm working my </span><span style="background-color: white; color: #3e3e3e; font-size: 13px;"><span style="font-family: "courier new" , "courier" , monospace;">Global.-1.ttslua</span></span><span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;"> is now simply:</span><br />
<br />
<div class="bbcode_container" style="background-color: white; color: #3e3e3e; font-family: Verdana, Arial, Tahoma, Calibri, Geneva, sans-serif; font-size: 13px; margin: 5px 20px 20px; padding: 0px;">
<pre class="bbcode_code" style="background: none repeat-x rgb(242, 246, 248); border: 1px inset; direction: ltr; font-size: 12px; height: 60px; line-height: 12px; overflow: auto; padding: 6px;">-- workshop id = 945458382
#include Shard/shard</pre>
</div>
<span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;">When I hit Save And Play it will look for the shard file in the folder location specified in the settings: by default this is your </span><span style="background-color: white; color: #3e3e3e; font-family: "courier new" , "courier" , monospace; font-size: 13px;">USER_FOLDER</span><span style="background-color: white; color: #3e3e3e; font-size: 13px;"><span style="font-family: "courier new" , "courier" , monospace;">/Documents/Tabletop Simulator</span></span><span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;">. So in my case it will get the file:</span><br />
<br />
<div class="bbcode_container" style="background-color: white; color: #3e3e3e; font-family: Verdana, Arial, Tahoma, Calibri, Geneva, sans-serif; font-size: 13px; margin: 5px 20px 20px; padding: 0px;">
<pre class="bbcode_code" style="background: none repeat-x rgb(242, 246, 248); border: 1px inset; direction: ltr; font-size: 12px; height: 36px; line-height: 12px; overflow: auto; padding: 6px;">C:\Users\onelivesleft\Documents\Tabletop Simulator\Shard\shard.ttslua</pre>
</div>
<span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;">- </span><span style="background-color: white; color: #3e3e3e; font-size: 13px;"><span style="font-family: "courier new" , "courier" , monospace;">Shard\shard.ttslua</span></span><span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;"> being specified by the </span><span style="background-color: white; color: #3e3e3e; font-size: 13px;"><span style="font-family: "courier new" , "courier" , monospace;">#include</span><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"> </span></span><br />
<br style="background-color: white; color: #3e3e3e; font-family: Verdana, Arial, Tahoma, Calibri, Geneva, sans-serif; font-size: 13px;" />
<span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;">My </span><span style="background-color: white; color: #3e3e3e; font-family: "courier new" , "courier" , monospace; font-size: 13px;">shard.ttslua</span><span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;"> file looks like this:</span><br />
<div class="bbcode_container" style="background-color: white; color: #3e3e3e; font-family: Verdana, Arial, Tahoma, Calibri, Geneva, sans-serif; font-size: 13px; margin: 5px 20px 20px; padding: 0px;">
<div class="bbcode_description" style="margin: 0px; padding: 0px;">
Code:</div>
<pre class="bbcode_code" style="background: none repeat-x rgb(242, 246, 248); border: 1px inset; direction: ltr; font-size: 12px; height: 288px; line-height: 12px; overflow: auto; padding: 6px;">#include constants
#include guids
#include globals
#include levels
#include !/utils
#include admin
#include game
#include draft
#include rewards
#include dice
#include expansions
--NO_SAVE_OR_LOAD = true -- To remove any state for clean upload to workshop
--DEBUG_DISPLAY = true
game_started = false
loaded = false
function onload(saved_data)
current_level = nil
...</pre>
</div>
<span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;">When the plugin fetches it, it will then look through it inlining the includes it has, and so on. This time it won't look in the folder specified in the settings, it will look in the same folder as the file which included them. My file structure looks like this (and is a git repo!):</span><br />
<span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-DuOCF-jrzbA/WZ88VDnrQGI/AAAAAAAAFtw/sWyI2VXet00TIy198M1kB1sq7Rvwj8GOACLcBGAs/s1600/folder.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="384" data-original-width="282" height="320" src="https://4.bp.blogspot.com/-DuOCF-jrzbA/WZ88VDnrQGI/AAAAAAAAFtw/sWyI2VXet00TIy198M1kB1sq7Rvwj8GOACLcBGAs/s320/folder.png" width="235" /></a></div>
<span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;"><br /></span>
<span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;">Almost all the files included by </span><span style="background-color: white; color: #3e3e3e; font-family: "courier new" , "courier" , monospace; font-size: 13px;">shard.ttslua</span><span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;"> are in the same folder as it, so I only need their name.</span><br />
<span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;"><br /></span>
<span style="color: #3e3e3e;"><span style="background-color: white; font-size: 13px;"><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;">Note the line: </span><span style="font-family: "courier new" , "courier" , monospace;">#include !/utils</span><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"> - the </span><span style="font-family: "courier new" , "courier" , monospace;">!/</span><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"> is an identifier that specifies the folder specified in the package settings. In this way I can have a utils library sitting in the root of my TTS folders, accessible by all my games. You may have a vector library you wish to include in all your objects' code; now, this is easy to do. You may also use the symbol </span><span style="font-family: "courier new" , "courier" , monospace;">~/</span><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"> to start a path from your user home folder (even on Windows), or specify a full path on the </span><span style="font-family: "courier new" , "courier" , monospace;">#include</span><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"> line if you want to.</span></span></span><br />
<span style="color: #3e3e3e;"><span style="background-color: white; font-size: 13px;"><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"><br /></span></span></span>
<span style="color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"><span style="background-color: white; font-size: 13px;">In practice how this works is it takes all the text in the files you are including and dumps them into the source at the point they are included. This means if you have a bunch of functions you want your objects to have access to then you can, but they are in effect being repeated in every object. </span></span><br />
<span style="color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"><span style="background-color: white; font-size: 13px;"><br /></span></span>
<span style="color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"><span style="background-color: white; font-size: 13px;">As it stands this means all your modules are sharing the same namespace; a global in one is accessible in all. I'm OK with this (it's the Lua spirit), but if you want to make them behave like modules you can, you just have to do it explicitly. </span></span><br />
<br />
<ol>
<li><span style="color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"><span style="font-size: 13px;">Start the module by declaring itself as a table.</span></span></li>
<li><span style="color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"><span style="font-size: 13px;">Prefix all it's "globals" and function with that table.</span></span></li>
</ol>
<br />
<span style="color: #3e3e3e;"><span style="background-color: white; font-size: 13px;"><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;">Let's say you have a module to greet the players, saved as </span><span style="font-family: "courier new" , "courier" , monospace;">greetings.ttslua</span><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"> :</span></span></span><br />
<div class="bbcode_description" style="background-color: white; color: #3e3e3e; font-family: Verdana, Arial, Tahoma, Calibri, Geneva, sans-serif; font-size: 13px; margin: 0px; padding: 0px;">
<br /></div>
<pre class="bbcode_code" style="background: none repeat-x rgb(242, 246, 248); border: 1px inset; color: #3e3e3e; direction: ltr; font-size: 12px; height: 144px; line-height: 12px; overflow: auto; padding: 6px;">greetings = {}
greetings.default_greeting = "Hello!"
function greetings.greet(message)
local message = message or greetings.default_greeting
for p, player in pairs(getSeatedPlayers()) do
printToColor(message, player, rgb)
end
end</pre>
<span style="color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"><span style="background-color: white; font-size: 13px;"><br /></span></span>
<span style="background-color: white; color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif; font-size: 13px;">Then you can use it in your program by:</span><br />
<div class="bbcode_description" style="background-color: white; color: #3e3e3e; font-family: Verdana, Arial, Tahoma, Calibri, Geneva, sans-serif; font-size: 13px; margin: 0px; padding: 0px;">
<br /></div>
<pre class="bbcode_code" style="background: none repeat-x rgb(242, 246, 248); border: 1px inset; color: #3e3e3e; direction: ltr; font-size: 12px; height: 96px; line-height: 12px; overflow: auto; padding: 6px;">#include greetings
greetings.greet()
greetings.greet("Hi!")
greetings.default_greeting = "Yo!"
greetings.greet()</pre>
<span style="color: #3e3e3e; font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"><span style="background-color: white; font-size: 13px;"><br /></span></span><span style="color: #3e3e3e;"><span style="background-color: white; font-size: 13px;"><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;">When you get the script from TTS the atom plugin will automatically strip out the inlined code and give you what you expect. Note that the package commands like </span><span style="font-family: "courier new" , "courier" , monospace;">Go To Function</span><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"> will take into account all your includes, and when you use them will jump to the correct place. If you are in your </span><span style="font-family: "courier new" , "courier" , monospace;">Global.-1.ttslua</span><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"> file and type a line number into the </span><span style="font-family: "courier new" , "courier" , monospace;">Go To Function</span><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"> dialog it is smart enough to know which line you are referring to and take you to it. i.e. even though the program I listed at the top of this post only has 3 lines when I see it in the editor, when TTS says there's a bug on line </span><span style="font-family: "courier new" , "courier" , monospace;">4345 </span><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;">I can hit </span><span style="font-family: "courier new" , "courier" , monospace;">ctrl-g</span><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"> and type </span><span style="font-family: "courier new" , "courier" , monospace;">4345</span><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;">, and it will take me to the correct line (opening up the relevant included file if necessary).</span></span></span><br />
<span style="color: #3e3e3e;"><span style="background-color: white; font-size: 13px;"><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;"><br /></span></span></span>
<span style="color: #3e3e3e;"><span style="background-color: white; font-size: 13px;"><span style="font-family: "verdana" , "arial" , "tahoma" , "calibri" , "geneva" , sans-serif;">EDIT: As of plugin version 11.0.2 you may optionally enclose your include in `<` and `>`; if you do so then the inserted text will be enclosed within a `do`...`end` block, keeping local variables contained. Example:</span></span></span><br />
<br />
<div class="bbcode_container" style="background-color: white; color: #3e3e3e; font-family: verdana, arial, tahoma, calibri, geneva, sans-serif; font-size: 13px; margin: 5px 20px 20px; padding: 0px;">
<pre class="bbcode_code" style="background: none repeat-x rgb(242, 246, 248); border: 1px inset; direction: ltr; font-size: 12px; height: 60px; line-height: 12px; overflow: auto; padding: 6px;">#include <Console/Console++></pre>
</div>
Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-2921343848373368580.post-46706672278868114682017-04-06T13:07:00.002-07:002017-08-24T14:17:43.241-07:00Shard changes after 01/04/17<br />
<ul>
<li><b>Energy Shield</b><br />Passive and active now grant +2 TN against ranged (instead of +1 soak dice)</li><br />
<li><b>Wrist Blaster</b><br />Now a reaction, can be activated on any coach's turn (ranged 1 dam 4)</li><br />
<li><b>Pulse Pistol</b><br />Increased attack roll to AGI+1 (up from AGI)</li><br />
<li><b>Pulse SMG</b><br />Increased attack roll to AGI+1 (up from AGI)</li><br />
<li><b>Pulse Cannon</b><br />Increased attack roll to AGI+2 (up from AGI+1)</li><br />
<li><b>Launcher</b><br />Increased attack roll to AGI+2 (up from AGI+1)</li><br />
<li><b>Moment of Truth</b><br />Buffed built in actions (was just a [move]). Now reads:</li>
</ul>
<blockquote class="tr_bq">
<ul>
If you only have one player who is not KO'd or incapacitated then this card may be played on them (even if this is not their card). They may take any or all of the actions listed below (instead of choosing just one of them). // [[move] or [attack] or [use]]
</ul>
</blockquote>
<ul>
<li><b>Passing Play</b></li>
Buffed ability text to:</ul>
<blockquote class="tr_bq">
<ul>If this player passes the Shard this turn roll [dice:+1] for the pass. If the receiver catches the Shard and has not been activated this turn then when you activate them you may have them take a [move] instead of playing a card. </ul>
</blockquote>
<ul>
<li><b>Coruin Hlest - Steal</b><br />Fixed so he can steal from an adjacent player without having to move first.</li><br />
<li><b>Shadow Dancer - The Weaver</b><br />Nerfed: removed riposte ability of parry.</li><br />
<li><b>From Dawning Minutiae</b><br />Buffed their passive (before it only triggered if they were discarding to stand up):</li>
</ul>
<blockquote class="tr_bq">
<ul>
When a coach wants to play a card belonging to a player who is adjacent to From Dawning Minutiae they must first discard another matching card from their hand.
</ul>
</blockquote>
<ul>
<li><b>Gaffer Jones</b><br />Changed his ability for attaching cards to Automata (added turn restriction, but allowed any Basic card):</li>
</ul>
<blockquote class="tr_bq">
<ul>
[react] : During Gaffer Jones' activation, once per turn, attach a card from your hand to an Automaton (discarding any card already there). The card must match Gaffer Jones or be Basic.
</ul>
</blockquote>
<ul>
<li><b>Gaffer Jones - Automaton</b><br />Gave Automata an in-built Use ability:</li>
</ul>
<blockquote class="tr_bq">
<ul>
[use] : Move [mp] then [melee]
</ul>
</blockquote>
<ul>
<li><b>The King</b><br />Removed his passive that made removing an injury not cost a star. Instead gave him:</li>
</ul>
<blockquote class="tr_bq">
<ul>
When flipping a card for this player from the Critical Strike deck flip an extra card and choose which one they get. Use this ability after any other abilities which deal with cards flipped from the Critical Strike deck.
</ul>
</blockquote>
<ul>
<li><b>Pickles - Deploy</b><br />Gave Pickles' Emblem card (which was a standard Deploy: [use]+[move]) the Team trait.</li><br />
<li><b>Switchback - Energy Shackle</b><br />Fixed wording, using written "attack" and "move" instead of icons. Now reads:</li>
</ul>
<blockquote class="tr_bq">
<ul>
[use] : Make a [target:2] on an enemy player. If you hit then they are held immobile, unable to move or attack, for as long as Switchback stays upright in the same square or until the start of your next turn, whichever is sooner.
</ul>
</blockquote>
<br />
Clarified the jargon on "teammate" and "ally": an ally is another unit in your team, while a teammate is an ally who is also a player. Used the correct one of these terms on these players:<br />
<ul>
<li><b>Oubliette - Barricade</b></li>
<li><b>Webspinner</b></li>
<li><b>With Unfailing Anticipation - Recall</b></li>
</ul>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-4718672910443507122017-03-26T14:21:00.001-07:002017-08-24T14:17:43.244-07:00Shard changes after 25/03/17<u>Blister</u><br />
<br />
Removed the range restriction on her emblem card ability. It now reads:<br />
<blockquote class="tr_bq">
<span style="font-family: "arial" , "helvetica" , sans-serif;">Choose an enemy player who Blister can see who is not in cover and has an empty adjacent square under the LOS. Warp Blister to that square then [melee] them. </span></blockquote>
<br />
<u>Havok</u><br />
<br />
Changed his emblem card ability to:<br />
<blockquote class="tr_bq">
<span style="font-family: "arial" , "helvetica" , sans-serif;"><strike>Place the Suppression marker in any square. Whenever an enemy enters a square within 2 squares of it Havok may make a [ranged] against them if able. Havok gains [dice:+1] when making a ranged attack against anyone within 2 squares of the Suppression marker. Remove it from the board at the end of your next turn.</strike> </span> </blockquote>
<blockquote class="tr_bq">
<span style="font-family: "arial" , "helvetica" , sans-serif;"><strike>Place the Suppression marker in any square; if Havok is Downed remove it. Whenever an enemy enters a square within 2 squares of it Havok makes a [ranged] against them if able.</strike></span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><strike>If it is still on the board at the start of your next turn then Havok makes a [ranged] against one enemy within 2 squares of it, then remove it.</strike></span> </blockquote>
<blockquote class="tr_bq">
<span style="font-family: "arial" , "helvetica" , sans-serif;">Place the Suppression marker in any square; if Havok is Downed remove it. The first time in a turn an enemy enters a square within 2 squares of it Havok makes a [ranged] against them if able.<br />If it is still on the board at the start of your next turn then Havok may make a [ranged] against one enemy within 2 squares of it, then remove it regardless. <br /><i>A player may only take one [attack] action per turn.</i> </span> </blockquote>
<br />
<br />
<u>Rules</u><br />
<br />
Adding this rule:<br />
<blockquote class="tr_bq">
<span style="font-family: "arial" , "helvetica" , sans-serif;">Whenever you make any roll for a player carrying the Shard they will fumble it if you roll as many [crit] as their Agility; scatter it from their square. When they attempt to pass the Shard the roll is first checked against the thrower then the receiver (scattering from the relevant square if applicable)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "arial" , "helvetica" , sans-serif;">"Fumble TN" is no longer a thing: adjacent enemy players no longer affect this mechanic.</span></blockquote>
<br />
<strike> Considering adding this rule:</strike><br />
<blockquote class="tr_bq">
<span style="font-family: "arial" , "helvetica" , sans-serif;"><strike>Whenever the Shard carrier makes any roll (other than to contain the Shard), if they roll as many [crit] as their Agility then the Shard will surge.</strike></span></blockquote>
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-6358907702831213932017-01-04T06:20:00.002-08:002021-02-04T14:41:49.243-08:00Standout Games of 2016<h2>
The Witness</h2>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://cdn.ndtv.com/tech/gadgets/the_witness_2016_01.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="250" src="http://cdn.ndtv.com/tech/gadgets/the_witness_2016_01.jpg" width="400" /></a></div>
<br />
<br />
Finding a screenshot for <i>The Witness</i> was hard. Well, I should say <i>choosing</i> one was hard, because the game is unendingly beautiful to look at; as you explore it every new horizon you walk over yields some new work of art. You could take pretty much any screenshot that wasn't a close-up of a puzzle panel and it would look good on this page.<br />
<br />
That's not why <i>The Witness</i> is on this list though (though it certainly helps); <i>The Witness</i> perfectly accomplishes what every good puzzle game tries to do: make the player feel like a genius. Over and over you'll be stuck, and confused, and uncertain, and then your brain will click, will make the magical click noise in your head as the bulb flashes on and you <i>get it</i>. There is lots and lots to <i>get</i>, extending even beyond the game you think you're playing. <br />
<br />
I love <i>Metroidvania </i>games; a genre named after its two main antecedents, <i>Metroid</i> and <i>Castlevania</i>. In these games you explore a generally open environment, and as you progress you gain power-ups that affect your ability to traverse the terrain. For example, a ledge you walked past on the first screen was too high to jump to, but once you get the <i>double-jump</i> power up you can go back and see what's up there. This type of gameplay just pushes my brain's buttons, and so too does <i>The Witness</i>, because it's a <i>Metroidvania</i> game in disguise. There are no power-ups to collect, not in the game: the power-ups are inside your head. As your neurons evolve to overcome the puzzles in front of you they unlock locations you had previously deemed inaccessible. Puzzles you had to ignore because they just didn't make any sense snap into focus, and you spend your time wandering over the island again and again, round and round, and it never gets dull because (a) it's beautiful and (b) you're a genius.<br />
<br />
<br />
<h2>
Audioshield</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://cdn.uploadvr.com/wp-content/uploads/2016/04/audioshield-screenshot-2.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://cdn.akamai.steamstatic.com/steam/apps/412740/ss_a5e5620dc467e0239bb2e2919a15e1484f3e5797.600x338.jpg?t=1568409238" width="400" /></a></div>
<br />
It was hard to find a screenshot for <i>Audioshield</i> too, but that's because there are barely any online. Worse, while its visuals are great when you're playing it, they hardly look great in 2D on a monitor. <i>Audioshield</i> is a VR game; VR for Virutal Reality. Wearing a headset, like those giant face-lawnmowers you saw in the 90's, except these days they look more like something from The Matrix and are awesome instead of naff. <br />
<br />
<i>Audioshield</i> is the game that pushed me over the edge into buying a Vive. <i>Space Pirate Trainer</i> was the first VR game I played, and for Shock & Awe it's hard to beat; the visuals are amazing, the soundtrack makes you feel instantly badass, and then you pick up the pistols and double-down. But <i>Space Pirate Trainer</i> isn't on this list and <i>Audioshield</i> is, because the former makes you feel like a badass for 10 minutes, while the latter fills you with joy for hours and hours. <i>Audioshield</i> is the VR game I have the most time played in by a factor of ten (discounting <a href="http://store.steampowered.com/app/570600"><i>Binary Trigger</i></a> for obvious reasons).<br />
<br />
Superficially <i>Audioshield</i> appears to be a rhythm game, like <i>Guitar Hero</i> or <i>Osu</i>. Blue and orange orbs fly at you, and you punch them in time to the music. However, this isn't quite accurate. You <i>can</i> play it this way; in fact the developer patched in a more extreme difficulty just for the players who want this, but this is not what I like about it. <i>Audioshield</i> is not a <i>rhythm</i> game; <i>Audioshield</i> is a <i>dancing</i> game.<br />
<br />
Playing <i>Audioshield</i> is like the best bits of being a slightly drunk in a nightclub; dancing without inhibition to thumping tunes, working out your body, (your aggression if you want), like you're in the start of Blade, forever, but without all the crap bits of being in a nightclub: no smoke, no drunks, no shitty DJs, no trying to look cool so someone will have sex with you, no worrying about finding someone to have sex with you, no vampires ripping your throat open... None of that shit. It's just dancing and punching and the best music you can think of because you're the DJ and what you want to dance to is what gets played.<br />
<br />
<br />
<h2>
Slither.io</h2>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://i.ytimg.com/vi/KEP0Jpe6WMU/maxresdefault.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://i.ytimg.com/vi/KEP0Jpe6WMU/maxresdefault.jpg" width="400" /></a></div>
<div>
<br /></div>
<i>Slither.io</i> is the spiritual successor to <i>Agar.io</i>. You start off as a little tiny snake, which moves towards your mouse pointer. You'll eat the little coloured blobs of light that are lying around (perhaps not realising they are being shat out by all the other snakes), and like in the ancient Nokia game, they'll make you grow longer. <i>Slowly</i>, ever so <i>slowly</i>, but you'll grow longer and fatter. Of course, the arena is full of other snakes all doing the same as you, all controlled by another player, all wanting to get bigger, to be the <i>biggest</i>. <br />
<br />
The shining lance of game design that makes this game amazing is the very simple, very brutal rule at the core of its gameplay: no matter how big you are, no matter how many other snakes you've killed, if your snake runs face-first into any other snake, you die. No hitpoints. No extra lives. You die, and will be reborn the smallest little snake you can imagine. Again.<br />
You can loop around over your own body as much as you like, but touch any other snake with your nose and you're dead, and when you die you leave behind all the blobs of light you've eaten so far. If you want to get big in <i>Slither.io</i>, and get big quick, killing big snakes and eating their remains is the way to do it.<br />
<br />
The beauty of this mechanic is in the interaction between your size and everyone else's. Once you are a tidal behemoth, leviathan, roaming the edges of the arena like some vast oil tanker, you have the same problem the oil tanker does: you are no longer maneuverable. You take an age to turn. You can speed boost for years (you speed boost by holding down the mouse button, but it uses up your girth so smaller snakes can't do it very long, while monsters can do it practically forever), but always you are afraid, because one little knock, one dunt on your nose and you're toast, and some little shit is going to eat your corpse. Meanwhile the tiny little snakes can only speed boost in short spurts, but they are so very agile, turning on a dime. They're also without fear: what does the newborn care of death? If it tries to kill you and dies, it gets reborn the same size; no loss. Baby snakes will happily kamikaze face-first into a giant, because it's 50/50 who dies, and they have everything to gain and nothing to lose, while the giant can win the equivalent of a grain of rice, but stands to lose everything.<br />
<br />
The other striking thing about <i>Slither.io</i> is the emergent behavior you start to notice once you've been playing for a while. The threat behavior of two similarly sized snakes, using speed-boost like a cat making itself larger. Players meet like animals in the wild; each trying to maneuver against the other for an advantage, but unwilling to engage an even or unfavorable fight. Then there's the subtleties of snake combat, of coiling, the <i>Qix</i>-like mini game of trapping a smaller snake within your coil then shaving territory off in infinitesimal slices, while they try to out-bluff you with their speed boost and peg you as you turn in. The risk/reward of killing your captured prey while other snakes draw near, hoping to catch you in your weakened state; if you are a long snake and are spread out you are hard to kill, but while coiled up you take up no space and can easily be enveloped by another. Few things are as satisfying as killing a snake which is about to kill another smaller snake (and then killing the smaller snake too). Crucially, none of this is designed, or encoded; it comes about purely from the game's one simple rule.<br />
<br />
I'm going to stop writing about <i>Slither.io</i> now (I could go on), but I realize I've written more about this free web game (<a href="http://slither.io/">go play it yourself now!</a>) than the commercial giants listed above it.<br />
<br />
<br />
<h2>
Overwatch</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://d1u1mce87gyfbn.cloudfront.net/media/wallpaper/lineup-wide.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="210" src="https://d1u1mce87gyfbn.cloudfront.net/media/wallpaper/lineup-wide.jpg" width="400" /></a></div>
<br />
If there's a game on this list that you're going to have heard of, regardless of how little you pay attention to video games, regardless if you just got back from a year-long sabatical in Zaire, it's <i>Overwatch</i>. Blizzard's class-based FPS emerged and then blew-up this year, in part due to its slick-yet-cool-yet-simple gameplay, and in part due to these <a href="https://www.youtube.com/watch?v=FqnKB22pOC0">amazing</a>. <a href="https://www.youtube.com/watch?v=to8yh83jlXg">shorts</a>. I mean <i>blew. up.</i> I saw a guy wearing an <i>Overwatch</i> jacket coming out of the train station this morning.<br />
<br />
I think in the last couple of months my interest has waned (perhaps because I've been busy with the Vive), but for a few months in the middle of the year I played a shit-ton of <i>Overwatch</i>. It's just so, so... FUN! It's probably the most beginner friendly FPS there has ever been, thanks to a wide array of different-feeling characters, some of which you don't even need to be able to aim with (they might have a weapon that locks on to targets, or build turrets that shoot automatically), but while easy for noobs it also packs a ton of depth for the more ardent fans. <br />
<br />
Get ready for a lot more <i>Overwatch</i> in 2017.<br />
<br />
<br />
<h2>
Flight</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.freegameaccess.com/images/flight-big.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="275" src="https://i.ytimg.com/vi/d1PGvSIgyFg/hqdefault.jpg" width="400" /></a></div>
<div>
<br /></div>
<div>
<i>Flight</i> is a simple little flash game I sat and played through when I had nothing better to do. I think the word to use is: <i>charming</i>. It's just so nice, and sweet, and... charming. You will be charmed. I don't want to say much about it since discovering the game pretty much is the game, so just go <a href="http://armorgames.com/play/7598/flight">play it yourself</a>. But whatever you do, don't throw the plane backwards, and when you do throw the plane backwards don't say I didn't warn you.<br />
<br />
<br />
<h2>
INSIDE</h2>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://cdn3-www.craveonline.com/assets/uploads/2016/07/Inside-Ending-2.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="http://cdn.gamer-network.net/2016/usgamer/INSIDE_shot_10.png" width="400" /></a></div>
<div>
<br /></div>
<div>
<i>INSIDE</i> is horrific. Horrible. Horrifying. It tells a story, but the story is not concerned with narrative or character development. The story is about projecting emotions onto you, the player, and those emotions are oppression, ignorant cruelty, inescapable horror. It depicts fascism, not as an artform to portray to the player why fascism is bad, or how fascism works, but instead to use fascism as a tool - fascism is the tool here, not the subject - a tool to push the player's face in. It's a terror-realm, not of jump-scares, but of a reality fashioned from the building blocks of nightmares. The nightmares of grown-ups, and the nightmares of their childhood.</div>
<div>
<br /></div>
<div>
I got <i>INSIDE </i>as a surprise Christmas present, having never heard of it (though having googled it since have discovered it was something of an Indie darling). Made by the same team who created <i>Limbo</i>, it feels most like a modern take on <i>Another World</i>. Modern in that it looks amazing, with its flat textures and faceless protagonist. Modern in that its control interface is seemless, responsive, slick. Modern in that it can be this polished mechanically while still having the ugliest, rawest soul you could dream of.</div>
<div>
<br /></div>
<div>
I don't know. I've read a bit on what people think the game means, what it could mean, what the developers intended it to mean... to me it still means what it meant as I was playing it: it puts you in the nightmare. The nightmare you can't wake up from, where you're being chased, where you know that whatever is chasing you is going to catch you and there's nothing you can do about it. The nightmare of when you're a kid and a dead animal is the most terrifying thing imaginable. The nightmare of your body. The nightmare of the ordered masses. It's well worth your time, if you're so inclined*</div>
<div>
<br /></div>
<div>
*Not if you have a phobia about water as it has extended sections like that so don't do it you'll die.</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-20592262123172363162016-12-24T06:46:00.000-08:002016-12-24T09:58:28.889-08:00Running out of space on your system SSD? Meet Junction.My PC is getting on a bit these days; I got it back when SSDs had got to the point of being reliable, but only just, so it only has a 120GB SSD for a system drive, and a 1TB HDD for storage. After a year or so I found out that 120GB is very barely enough to run Windows from. In fact, the only stuff I have on it is Windows and Dota (because Dota had performance issues when run from the HDD). This wasn't always the case: I had previously installed a few applications on it. To make space by moving them I could have uninstalled them then reinstalled them on the HDD; that's an annoying amount of work. Worse however; some developers have taken to make their installers totally foolproof, by which I mean they remove any options for the user to make: they install to C: and don't give you another choice (Office 365 does this, as does Oculus). When you don't have enough space on C: to install them this is problematic. Happily, there's a Windows command that comes to the rescue: Junction.<br />
<div>
<br /></div>
<div>
Junction is used to make a "junction point" on a drive. A junction point is sort of like a Shortcut to a folder, except it is handled at a much lower level. To Windows, it's as if the folder it points to is actually at the location it sits in. With it you can make Windows think a program is installed in C: when it is actually held on D: (or any other drive).</div>
<div>
<br />
<h2>
</h2>
<h2>
Moving software to HDD</h2>
<br /></div>
<div>
Let's say you have Photoshop installed on your SSD, but it takes up a ton of space that you want to reclaim (because the SSD is full and Windows is starting to choke on not having any free MB). Photoshop will typically be installed in <span style="font-family: "courier new" , "courier" , monospace;">c:\program files\adobe</span>. To free up the space you could uninstall photoshop entirely, then run the installer again and put it on D:, but that's a lot of time wasted (and potentially a lot of bandwidth if it has to re-download a bunch of stuff). Instead you can move the <span style="font-family: "courier new" , "courier" , monospace;">adobe</span> folder to D: and use a junction point to make Windows think it's still happily running from C:. To do this you need to:</div>
<div>
<br /></div>
<div>
<ol>
<li>Make sure the application in question is not running. Close all open windows, and make sure it hasn't left any icon in the system tray.</li>
<br /><a href="https://4.bp.blogspot.com/-DEnd2kX8u7c/WF6upne0SnI/AAAAAAAAEM0/TlbK9MfRu2cFcLxNb1_DNpUp4KC0w-ocgCLcB/s1600/screenshot1.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="222" src="https://4.bp.blogspot.com/-DEnd2kX8u7c/WF6upne0SnI/AAAAAAAAEM0/TlbK9MfRu2cFcLxNb1_DNpUp4KC0w-ocgCLcB/s320/screenshot1.png" width="320" /></a>
<li>Move the folder from its location on C: (your SSD) to a location on D: (your HDD). Make a <span style="font-family: "courier new" , "courier" , monospace;">D:\program files</span> folder and then <i>using the right mouse button </i>drag the <span style="font-family: "courier new" , "courier" , monospace;">adobe</span> folder over. By using the right mouse button, when you release it a menu will pop up allowing you to select Move.</li>
<br /><div class="separator" style="clear: both; text-align: center;">
</div>
<li>Now you need to make the junction point. Open the Start menu and type <span style="font-family: "courier new" , "courier" , monospace;">cmd</span> but <b>don't</b> push <span style="font-family: "courier new" , "courier" , monospace;">enter</span>. When it displays the cmd icon, right-click it and Run As Administrator. In the cmd window type:</li>
<br />
<h3>
Windows 7</h3>
<pre>cd "\program files"
junction adobe "d:\program files\adobe"</pre>
<h3>
<br />
Windows 10 <span style="font-weight: normal;"><span style="font-size: x-small;">(Junction command was removed from Windows 10; use mklink instead)</span></span></h3>
<pre>cd "\program files"
mklink /j adobe "d:\program files\adobe"
</pre>
<a href="https://1.bp.blogspot.com/-6Cx3CmhwUcw/WF6vTU7xz1I/AAAAAAAAEM8/jP4elZVlW8AxoWssJWz6twn8yY9sR8otACLcB/s1600/screenshot2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="200" src="https://1.bp.blogspot.com/-6Cx3CmhwUcw/WF6vTU7xz1I/AAAAAAAAEM8/jP4elZVlW8AxoWssJWz6twn8yY9sR8otACLcB/s200/screenshot2.png" width="99" /></a><a href="https://1.bp.blogspot.com/-otTnu2OlDUc/WF6vTcd1MCI/AAAAAAAAEM4/9UsIdYBh-ZQh2ZUK2RVSTzozep3NtxPfgCLcB/s1600/screenshot3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="168" src="https://1.bp.blogspot.com/-otTnu2OlDUc/WF6vTcd1MCI/AAAAAAAAEM4/9UsIdYBh-ZQh2ZUK2RVSTzozep3NtxPfgCLcB/s320/screenshot3.png" width="320" /></a><br />
<br />
<div class="separator" style="clear: both;">
</div>
<li>Close the cmd window. You should now be able to run Photoshop as normal.<br /><h3>
</h3>
</li>
</ol>
<br />
<h2>
Installing to HDD</h2>
<br />
For a program that only lets you install on C: you either need to make enough room by moving other folders first, or set up the junction before running the installer. For example, Oculus installs in <span style="font-family: "courier new" , "courier" , monospace;">c:\program files\oculus</span> so before installing you can make an oculus folder on D: and then make a junction to it as above; when you run the installer it should see that the oculus folder already exists and install into it.<br />
<br />
<ol>
<li>Make sure there is no folder at <span style="font-family: "courier new" , "courier" , monospace;">c:\program files\oculus</span>. If you have tried to install and failed it may have left one.</li>
<li>Make folder <span style="font-family: "courier new" , "courier" , monospace;">d:\program files\oculus</span></li>
<li>In a cmd window:<br /><br /><h3>
Windows 7</h3>
<pre>cd "\program files"
junction oculus "d:\program files\oculus"</pre>
<br /><h3>
Windows 10</h3>
<pre>cd "\program files"
mklink /j oculus "d:\program files\oculus"</pre>
</li>
</ol>
</div>
<div>
<br />
<h2>
Making Space</h2>
<br />
If you want to go on a space-saving spree a good program to use is <a href="https://www.jam-software.com/treesize_free/">treesize</a>. This will scan your SSD and display all the folders on it sorted by how much space they are taking up, allowing you to quickly see which ones are worth moving over to the HDD. You can use this technique on most programs you find in <span style="font-family: "courier new" , "courier" , monospace;">c:\program files</span> and <span style="font-family: "courier new" , "courier" , monospace;">c:\program files (x86)</span>, though you should avoid any Windows folders as they can be a bit temperamental.</div>
<blockquote class="tr_bq">
</blockquote>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-3493332127098248702015-04-23T09:05:00.001-07:002015-06-08T19:07:25.869-07:00Dota 2 - Common Support Laning Mistakes<br />
<div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;">
<br /></div>
<br />
Supporting in Dota is <i>hard</i>. You need good game knowledge, good map awareness, a fundamentally team-play driven mindset and the ability to eek by with the bare minimum of items. Learning to play support well takes a lot of time and effort, as you have to grow your capabilities in multiple directions: you need to learn how to support your teammate in lane, how to gank, how to counter-gank, how to ward, how to find whatever farm you can... it's easy to get caught going down one track (warding for instance) and neglecting others.<br />
<br />
Which is to say; it's fairly easy to sink a ton of hours into learning support and still miss out on some things which are actually pretty fundamental. This post will cover some of the basics supports should, or, more accurately, should <i>not</i> be doing in the laning stage; the kind of things that you see <i>all the time</i> in public matchmaking, and that can drive carry teammates up the wall. An unhappy carry is a distracted carry, and no-one want them making mistakes late-game because they've had their morale upset early. Carries are delicate creatures!<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img src="http://www.onelivesleft.com/misc/dotamistakesheader.png" width="80%" /></div>
<br />
<h2>
1. Ineffectual Leeching</h2>
<br />
<a href="http://www.onelivesleft.com/misc/leeching.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="http://www.onelivesleft.com/misc/leeching.png" height="320" width="267" /></a>This is the #1 thing a support does that incenses their lane-mate:
standing behind the carry, possibly attempting to deny creeps. It's
hard to emphasize how terrible this actually is. Hiding behind the
carry, the support contributes nothing to the lane: they do not harass
the enemy hero(es), and are not in a position to act if a potential kill
situation occurs. Crucially, they also take half the XP from the
lane. They are literally making the lane worse for the carry than if
they <i>weren't there at all</i>. A support who spends the first few
minutes roaming around accomplishing nothing is actually better for the
lane than one who sits in it, actively having a negative impact (by
stealing experience from the core). The normal outcome to a two-vs-one
lane where the support does this is that the enemy hero gains a level
advantage, then kills one or both of the dual lane.<br />
<br />
<br />
<a name='more'></a>What should Lina have done? She should have waited in the woods, out of sight of the lane, until the enemy creep wave had passed (so that she didn't draw aggro from them), and then emerged into the lane <i>behind</i> the Death Prophet, and started right-clicking her.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.onelivesleft.com/misc/zone.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://www.onelivesleft.com/misc/zone.png" height="317" width="400" /></a></div>
<br />
<br />
Trading hits is fine (Lina should have bought adequate regen), and the outcome will be that the Lina <i>zones</i> the Death Prophet: that is, Death Prophet is made to retreat back under her tower, outside of XP range. If Death Prophet were to stay in lane with low hitpoints then Troll + Lina will be able to kill her, so she has to hide.<br />
<br />
<br />
<a href="http://www.onelivesleft.com/misc/noxp.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://www.onelivesleft.com/misc/noxp.png" height="320" width="267" /></a>Once Death Prophet has been beaten back Lina can stand between her and the creep line, keeping her from gaining levels. Note that Lina has to dodge out of the lane as each new creep wave arrives, so as not to interfere with it, but can then return behind it again to keep Death Prophet from getting anything.<br />
<br />
In this situation Lina can stand far enough from the creep line so that she doesn't take XP (giving Troll solo XP will power him up quickly), or she can stand close enough so that she does split the XP. It's fine to take XP from Troll as long as they are both getting more out of the lane than Death Prophet is. If the Lina is good she may be able to use windows when Death Prophet is absent to stack nearby camps.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
By zoning Death Prophet the Lina will end up under-levelled, but the Death Prophet will be much <i>more </i>under-levelled. Once the Troll has powered up enough, he can go spend some time in the jungle, leaving Lina to get solo XP and farm in the lane.</div>
<br />
<br />
<h2>
2. Tardiness</h2>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/warding.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="http://www.onelivesleft.com/misc/warding.png" height="200" width="165" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">"Don't mind me, I'm<br />
just checking the rune..."</td></tr>
</tbody></table>
It may not be apparent from the screenshots above, but Dire is actually running a trilane here. Where then is Crystal Maiden?<br />
<br />
She's warding bot. Having a ward covering the bot rune isn't bad, and it gives some protection to the solo offlane from mid ganks, but crucially Crystal Maiden didn't have to be here to do this: she could just have given the offlane hero a ward in the fountain and let them place it themselves (hold <i>ctrl</i> and use the ward on your allied hero to split the stack and give them one ward from it, keeping one for yourself).<br />
<br />
Imagine Crystal Maiden had been with Lina as the creep waves initially met, hiding in the woods. Death Prophet came all the way up to the creep line; Frostbite into Light Strike Array with all three heroes right-clicking would have been the easiest First Blood in the world, comfortable enough to finesse the kill onto the Troll. This would have practically won the lane on it's own: Death Prophet would get nothing out of it (if she ever came back near it she would die again), and a free-farm Troll is a happy Troll.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.onelivesleft.com/misc/rip.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://www.onelivesleft.com/misc/rip.png" height="317" width="400" /></a></div>
<br />
<br />
After the kill Crystal Maiden can stick around nearby, stacking camps or jungling with Frostbite. If Death Prophet shows her face again they can repeat the kill. Once it's clear that Death Prophet isn't going to come back (a sensible player won't), Crystal Maiden can go roam and/or jungle. Of course, for this to have happened she had to have been in lane as the game began, instead of being half-way across the map.<br />
<br />
<br />
<h2>
3. Denying</h2>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/deny.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="http://www.onelivesleft.com/misc/deny.png" height="186" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Gotta pad that stats page</td></tr>
</tbody></table>
A lot of supports think they can help their carry out by denying creeps for them. It's harsh to criticize someone for trying to be helpful, but this is almost always detrimental; the carry will be trying to maintain creep equilibrium. That is, they will be trying to hold the creep line in a specific place, near their tower but outside its range. By hitting their creeps the support will generally by doing something which (a) the carry has no trouble doing themselves, (b) interferes with the balance of the creep line which the carry is trying to maintain, and (c) might throw of the timing of the carry's swing.<br />
<br />
The only time a support in lane next to a carry should be denying creeps is when they are actively trying to drag the line back nearer their tower, in which case all allies in the lane should be attacking their creeps as soon as the creeps get to half-health or lower. Except in that circumstance, supports should leave the creeps alone.<br />
<br />
As an aside: when a support is harassing the enemy hero(es), they need to be careful not to do it within aggro range of the creep line. If they take aggro on to themselves they will likely cause the lane to push.<br />
<br />
<br />
<h2>
4. Pulling</h2>
The Stack-Pull and Chain-Pull (and also Single-Pull) are important tools in the support players arsenal, techniques which they need to learn. Equally important to knowing<i> how </i>to pull, though, is knowing <i>when </i>to pull, and when <i>not</i> to.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.onelivesleft.com/misc/badpull.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://www.onelivesleft.com/misc/badpull.png" height="161" width="400" /></a></div>
<br />
Here Crystal Maiden has stacked the pull camp and is pulling with it. However, the creep line was already in the perfect place: Troll was holding it just outside of tower range where it is very safe to farm, with no disruption to his last-hitting. By pulling here Crystal Maiden forces the Troll to have to last-hit under tower, inevitably resulting in some missed CS and likely pushing the lane.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/undertower.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://www.onelivesleft.com/misc/undertower.png" height="216" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Last hitting under tower is full of excitement.</td></tr>
</tbody></table>
<br />
A good support will use a Stack-Pull or Chain-Pull to pull a pushed lane back nearer their tower: it is a recovery mechanism for when their lane becomes pushed out. It should not be used when the creep line is in the correct place already.<br />
<br />
<a href="http://www.onelivesleft.com/misc/suboptimal.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="http://www.onelivesleft.com/misc/suboptimal.png" height="320" width="308" /></a>
Communication can solve this issue. It's good to have the <code>>Pulling Creeps</code> chat message assigned to the wheel, or bound to a key (the command is "<code>chatwheel_say 50"</code>). If the support lets the carry know what they intend then the carry can respond if they don't want it to happen. In fact, with good coordination, the carry can start pushing the lane, allowing the support to perform the pull and get some farm, while keeping the creep line out from under the tower, but they can only do this if they have some warning.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<h2>
Summary</h2>
Follow these handy tips for improved lane performance! Standard Dota disclaimer applies (There's An Exception To Everything In Dota).<br />
<br />
Previous Dota posts:<br />
<ul>
<li><a href="http://blog.onelivesleft.com/2014/03/dota-2-quickndirty-support-primer.html">Quick'n'Dirty Support Primer</a> </li>
<li><a href="http://blog.onelivesleft.com/2014/01/dota-2-ocd-item-slots-or-how-to-stop.html">OCD Item Slots (or How To Stop Forgetting Where Your Dust Is) </a></li>
</ul>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-85634271306865077232014-12-10T17:55:00.002-08:002015-06-08T19:06:49.786-07:00Grand Theft Auto 5's bullying is a problem its creators must finally address<br />
<br />
<div style="text-align: center;">
<a href="http://4.bp.blogspot.com/-16zoH6uwBOw/VIjz4cXdJJI/AAAAAAAADNQ/SRDM4Bb54A4/s1600/ljr0JTi.png"><img border="0" height="225" src="https://4.bp.blogspot.com/-16zoH6uwBOw/VIjz4cXdJJI/AAAAAAAADNQ/SRDM4Bb54A4/s1600/ljr0JTi.png" width="400" /></a></div>
<br />
<br />
<br />
The doctrine of "if you don't like it, just don't buy it" is a useful defense for cynical purveyors of shoddy products. They would prefer that you move along and keep your opinions to yourself.<br />
<br />
When used in video games, this cheap line usefully reduces the relationship between products and humans to a base financial transaction. The product has no meaning for those who do not consume it, or so we are supposed to believe.<br />
<br />
<a name='more'></a>Fans of the product can be relied upon to take up the same call. "Leave us alone to have our fun," they say. Don't look too closely.<br />
<br />
So it's no surprise to see Take-Two trot out this old defense, when its Grand Theft Auto 5 (2013) is once again held up as a nasty example of privileged clichés.<br />
<br />
Take-Two wants to frame a decision by Australian retailers (to take the game off their shelves) as an issue of consumer rights and free speech. It wants you to be mad at the gall of the petition that questions GTA 5's portrayal of the brutal murder of the homeless.<br />
<br />
Take-Two and publishing sub-brand Rockstar want to be able to say whatever they like about whomever they like, without consequence. At the same time as they reduce GTA 5 to a mere purchasing choice, they also want you to believe that the game is an essential work of art that is being stifled by a mob of censors.<br />
<br />
But this is really about a corporation profiting from the depiction of some of the most brutalized members of our society, being further brutalized, for fun. It's about a game that displays incredibly bad taste, that has made ignorant choices, and that is happy to trade on the misery of the victims who it portrays so mercilessly.<br />
<br />
Look, let's get one thing straight. We live in a free society. GTA 5 should be available for any adult who wants to buy it, though any retailer who wants to sell it. But that doesn't mean its makers ought to be allowed to feel comfortable dismissing its critics in the most derisory fashion imaginable.<br />
<br />
<br />
<div style="text-align: center;">
<a href="http://prod.cloud.rockstargames.com/ugc/gta5photo/7004/A0yXj1o39U63HV9FpKVhTg/0_0.jpg"><img border="0" height="225" src="https://lh3.googleusercontent.com/proxy/hOw4Slrs_pOPyO2fJU3_5Q5Qeqo_zqahtp25kvKM_cHGlT6KCMW7x9D8rz7uuQV-guflSiayUDZf0HUMbS8y4YA0nDZ2g-0OmAGWSIT6Fpp04cr81GygdIxsl3r6DAdt9akkfULoxacY" width="400" /></a></div>
<br />
What I personally find repulsive about this game is the pleasure it offers in portraying the savaging of a class of people who are already victims, in real life. This is where GTA 5 shows a lack of judgment. I take issue with the portrayal of the homeless being abused and murdered, because the indigent are already victims, and it's just not right to take your fun in abusing victims.<br />
<br />
I know a lot of people desperately want to believe that killing a vagrant in GTA 5 is the same as killing any other character, but it's really not. Unlike gangsters or cops or business dudes or hot dog vendors, the homeless, as a class, are despised, marginalized and abused in real life, all the time. This means that GTA 5 takes its pleasure in humiliating and abusing victims of humiliation and abuse.<br />
<br />
In what kind of world is that not worthy of debate, above and beyond the ignorant cry of "if you don't like it, don't buy it"?<br />
<br />
<blockquote class="tr_bq">
<div style="text-align: right;">
<blockquote class="tr_bq">
<h4>
This game reinforces hard ideas about the worthlessness of the homeless</h4>
</blockquote>
</div>
</blockquote>
Yeah, I know these aren't real people. They are just cartoon characters. My point is not that the on-screen vagrants being murdered are real, nor that the game will prompt people to go out and murder vagrants. My point is that this portrayal of them reinforces hard ideas about the worthlessness of the homeless, in ways that are unique to this class of characters in the game. My point is that it is deeply distasteful to gleefully portray victims being shat upon by privilege.<br />
<br />
This is why I find recent comments from Take-Two chairman and CEO Karl Slatoff so callous. Here is a game made by a company largely run by privileged, well-educated, wealthy people, and it mostly profits from portrayals of people who are none of those things.<br />
<br />
Take-Two wants you to believe that the game has a soul akin to the movies it so desperately apes. But actually, it has very little to say about urban life that has not been said before. It is a skillful farrago of jokes, action-sequences and visual shocks. Its merits are mostly technical. It is a play-pen for violent fantasies.<br />
<br />
And that is okay. The world demands such things, just as it demands silly musical theater and sexy novels about bondage and movies about magical teenagers. But if you're going to make a product that is bought by millions of people, you really ought to have a better defense for its failings than a thinly veiled invitation to just fuck off and worry about something else.<br />
<br />
I admire the women who organized that petition, not because I agree with all their demands to retailers, nor because I want to see the game banned. I admire them because they dare to give us the perspectives of people who have suffered abuse while homeless, about a game that portrays the same abuse as a matter of entertainment.<br />
<br />
This is something we absolutely should be talking about. It's one thing for games to portray the slaughter of soldiers and gangsters and even vanilla members of the public. It's another to show us victims being kicked in the teeth, and then pretend this is not worth talking about.<br />
<br />
It's a shame that Take-Two is so uninterested in such a conversation, that when the company does address the issue it tries to fob the world off with some weak piss about free speech.<br />
<br />
I suspect that Take-Two is secretly terrified that the world might wake up tomorrow and figure out that, yes, this aspect of the game is in very poor taste and perhaps we should be having a bigger conversation about how this company portrays the poor in games that are overwhelmingly consumed by the well off.<br />
<br />
This game has made choices about what is acceptable and what is not; poor ones, in my opinion. Of course, the line of acceptability is always on the move. As a boy, I watched musicals in which white men blacked up and sang songs about the good ol' South. I watched comedies in which particular races and nations were portrayed as dishonest, cowardly or shiftless. I watched dramas in which women were repeatedly presented as deranged or simpletons or both. In the intervening years, the line has moved. It's not cool to make entertainments like that any more.<br />
<br />
If you did make entertainments that mocked minorities and women, would it be fine and dandy to just say ‘if you don't like it, don't buy it." I don't think so. This is the sort of line we hear from entitled sophomores, peering up from their heartless philosophies. We understand now that "punching down" is what bullies do; that the jokes that mock people for being what they are, just aren't funny.<br />
<br />
<blockquote class="tr_bq">
<h4>
"Punching down is what bullies do"</h4>
</blockquote>
<br />
I take issue with where Take-Two and Rockstar have drawn their line. I don't think it's okay to depict vagrants being murdered for kicks because I believe that deprivation is (very, very often) a horrible crime perpetrated against poor and desperate people. <br />
<br />
You can draw your own arbitrary moral line to illustrate that Take-Two's choices are by no means unchangeable, that through a different lens, they look pretty weird. So, for example, why can you not murder children in GTA 5? I am certain that there are plenty of people who would love to do that. I am certain that were it a reality in the game, its continued inclusion would be defended vigorously by hordes of brave libertarians. If Take-Two is so concerned about free speech, why isn't it railing against the Entertainment Software Ratings Board to allow more freedoms to depict atrocities against the defenseless?<br />
<br />
Many of the people who make this game, of course, have children, probably don't really love seeing children being hurt, might even make the argument that children are helpless and vulnerable and that it would be an extremely poor artistic choice to have children murdered in a game, even though it's been done many times in the movies. You can see where I'm going with this.<br />
<br />
The homeless in GTA 5 are adults, but they represent a deep vulnerability that the game's other adult characters do not share. Vagrants are almost always living under the threat of violence and intimidation. They have very limited choices.<br />
<br />
Take-Two has lots of choices. <a href="http://blog.onelivesleft.com/2013/10/grand-theft-auto-v-loading-screens.html">It has chosen to represent women in manner which is far from equitable</a>. It has chosen to bat away questions about its own decisions with lazy arguments about "freedom" and "choice."<br />
<br />
It's time for Take-Two and Rockstar to quit with the <a href="http://www.polygon.com/2014/12/10/7364823/gta-5s-vicious-misogyny-ought-to-be-addressed-not-ignored">smoke-and-mirrors routine</a>, and address the real problem here: the nature of some of the content in GTA 5 is nasty stuff that merits a more thoughtful response than "if you don't like it, don't buy it."<br />
<br />
<div style="text-align: center;">
<a href="http://s4.postimg.org/em0pn6fgt/1320250546720.jpg"><img border="0" height="225" src="https://lh6.googleusercontent.com/proxy/4iuxPP2egT_HxKYJcLt7173f1PzC-A0TMDaZzNCllQ-99RCyQPyIjRFp1Sd4hkDGl4KeD5Mk3ra40jMANdPMOWjeRbgc" width="400" /></a></div>
<!-- Blogger automated replacement: "https://images-blogger-opensocial.googleusercontent.com/gadgets/proxy?url=http%3A%2F%2F4.bp.blogspot.com%2F-16zoH6uwBOw%2FVIjz4cXdJJI%2FAAAAAAAADNQ%2FSRDM4Bb54A4%2Fs1600%2Fljr0JTi.png&container=blogger&gadget=a&rewriteMime=image%2F*" with "https://4.bp.blogspot.com/-16zoH6uwBOw/VIjz4cXdJJI/AAAAAAAADNQ/SRDM4Bb54A4/s1600/ljr0JTi.png" --><!-- Blogger automated replacement: "https://images-blogger-opensocial.googleusercontent.com/gadgets/proxy?url=http%3A%2F%2Fs4.postimg.org%2Fem0pn6fgt%2F1320250546720.jpg&container=blogger&gadget=a&rewriteMime=image%2F*" with "https://lh6.googleusercontent.com/proxy/4iuxPP2egT_HxKYJcLt7173f1PzC-A0TMDaZzNCllQ-99RCyQPyIjRFp1Sd4hkDGl4KeD5Mk3ra40jMANdPMOWjeRbgc" --><!-- Blogger automated replacement: "https://images-blogger-opensocial.googleusercontent.com/gadgets/proxy?url=http%3A%2F%2Fprod.cloud.rockstargames.com%2Fugc%2Fgta5photo%2F7004%2FA0yXj1o39U63HV9FpKVhTg%2F0_0.jpg&container=blogger&gadget=a&rewriteMime=image%2F*" with "https://lh3.googleusercontent.com/proxy/hOw4Slrs_pOPyO2fJU3_5Q5Qeqo_zqahtp25kvKM_cHGlT6KCMW7x9D8rz7uuQV-guflSiayUDZf0HUMbS8y4YA0nDZ2g-0OmAGWSIT6Fpp04cr81GygdIxsl3r6DAdt9akkfULoxacY" -->Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-2921343848373368580.post-50375447203857153992014-03-15T18:10:00.001-07:002015-06-08T19:06:37.464-07:00Dota 2 - Quick'n'Dirty Support PrimerThis is a rough outline of things I do most games as support. If you are clueless about how to play support then following this will set you on the right path. After you do it a few times you'll start to work out what is going on, and should diverge off it as you see fit. This is for hard support, i.e. farm position 5.<br />
<br />
Farm priority is a system to designate who gets farm when more than one player is in the same place.
Each player has a number: the lowest number gets the farm. So 1 is the hard carry in the
safe lane, 2 is mid, 3 is the offlane or jungle, 4 and 5 are typically
supports. The 4 saves up and gets Mek, while the 5 buys wards.<br />
<br />
Start items I buy for practically every support hero is: Courier, Wards, Tango, Salve, Clarity.<br />
<br />
<a name='more'></a>At the beginning of the game, go ward enemy side of mid. For Radiant this is just above the ramp leading to top rune, for Dire it's the spot next to Ancients. These spots will give vision on the top rune as well as help your mid tremendously, especially if they are playing as or against Pudge.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table style="width: 100%;"><tbody>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_3a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_3a.jpg" title="" width="229" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Ward here if you're Radiant...</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_5a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_5a.jpg" title="" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...or here if you're Dire.</td></tr>
</tbody></table>
</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
</div>
<br />
If on Radiant ward the spot to the right of the medium camp next to the river that gives
rune vision, or maybe a lane-ward instead (just on the ramp down to
river, below enemy tower).<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table style="width: 100%;"><tbody>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_13a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_13a.jpg" title="" width="229" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">This spot will protect your safe lane<br />
and give vision on bot Rune.</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_25a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_25a.jpg" title="" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">While this one will give vision on the enemy<br />
offlane and warn of incoming TPs.</td></tr>
</tbody></table>
</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
</div>
<br />
If on Dire ward to the right of the left large camp, which blocks it (to stop Radiant pulling it) and gives you vision of incoming ganks, or alternatively (again) a lane ward.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table style="width: 100%;"><tbody>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_29a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_29a.jpg" title="" width="229" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Eat the tree to enhance vision.</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_17a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_17a.jpg" title="" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Dire equivalent of Radiant lane ward above.</td></tr>
</tbody></table>
</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
</div>
<br />
At 3 mins upgrade the courier. From then on you can worry about what you're gonna buy. You should be buying Observer wards whenever they come off cooldown. The first set you buy after the game starts can either go where the original wards went, replacing them, or you can progress to more central wards. Usually this means somewhere with vision of the Rosh pit entrance, and somewhere with vision mid. Warding the entrances to your jungle are also good. I tend to ward aggressively, i.e. I'll put the ward on the enemy side of the river. This is a little dangerous (if you get caught you'll die).<br />
<br />
Typical spots for Radiant:
Above the ramp leading from Rosh pit to dire mid T1. About 300 units from the start of the
ramp, middle of the path. This spot can't be seen by the usual rune-spot deward sentry. Mid I usually put it in the same spot I'd have used earlier: just above the ramp leading from dire mid T1 to top rune.<br />
<br />
<br />
<table style="width: 100%;"><tbody>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_6a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_6a.jpg" title="" width="229" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Rosh pit vision that's hard to deward.</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_3a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_3a.jpg" title="" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">This one looks familiar...</td></tr>
</tbody></table>
</td></tr>
</tbody></table>
<br />
For dire:
For Rosh pit I usually use the turtle spot. For mid it's the rune spot to the right of Radiant Ancients.<br />
<br />
<br />
<table style="width: 100%;"><tbody>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_9a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_9a.jpg" title="" width="229" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The turtle spot.</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_5a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_5a.jpg" title="" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">So does this one...</td></tr>
</tbody></table>
</td></tr>
</tbody></table>
<div>
<br />
If you want to play less aggressively and use the normal, safe ward spots, swap those over, Dire for Radiant and Radiant for Dire.</div>
<div>
<br />
<br /></div>
Ideally
you'll be able to grab some farm by stacking & pulling or chain
pulling. The theory is that having good ward coverage will allow you to
take good fights, and you'll get gold from those. Early pick-ups to
squeeze between wards: Brown Boots, Stick (possibly into Wand, though
naked Stick is generally fine), Sage's Mask (can be made into Urn or Eul's). You might want to pick up Stick before Boots if they have a spammy
lineup against you.<br />
After that start working toward actual boots. In a
game that's going well that's easy. In a game that's tight or going
badly you might be stuck on Brown Boots for the duration: that's fine;
just keep buying those wards.<br />
After that your items are more hero dependent. Urn is good
for mana, gives you a great ability for fights, and is dirt cheap.
Alternatively Drums will also give you good mana and will beef you up
decently, but are a bit pricier. Another option would be Soul Ring, but I like it less than Urn.<br />
<br />
Then I'll almost always go Force Staff. IT'S THE BEST ITEM IN THE
GAME. Apart from all the awesome stuff you can do with it, it also
gives you a much needed escape mechanism; as a support you'll tend to
(a) not have one by default and (b) be squishy as hell. As the 5 you won't generally be buying Mek - that's usually for the 4 (or even the 3).<br />
<br />
As towers fall your ward spots move. After the Radiant T1 bot
tower has fallen I like to ward the lower junction below the large
camp, just down-left of the magic bush. Gives you good vision for
defending/attacking the T2. For mid, once T1 goes the Ancient spot is
still good, but you now also want a hill ward. Another good spot is
above the ramp toward ancients just next to the T2. If you want to ward top then the junction into the
secret shop from top lane is pretty good, or the junction to secret shop
itself, but mid and bot are usually where vision will aide you more.<br />
<br />
<br />
<table style="width: 100%;"><tbody>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_35a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_35a.jpg" title="" width="229" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">After Radian bot T1 falls.</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_60a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_60a.jpg" title="" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">After Radiant mid T1 falls.</td></tr>
</tbody></table>
</td></tr>
</tbody></table>
<br />
<br />
On dire side, for attacking/defending the top T2 the hill is
great. Also good is having one somewhere in the jungle left of their
mid T2 (espec after mid T1 has fallen). Atop the steps leading from
dire secret shop to mid is good, as is the spot below that, just above
the steps leading down to dire ancients.<br />
<br />
<br />
<table style="width: 100%;"><tbody>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_18a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_18a.jpg" title="" width="229" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">After Dire top T1 falls.</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_20a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_20a.jpg" title="" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">After Dire mid T1 falls.</td></tr>
</tbody></table>
</td></tr>
</tbody></table>
<br />
Later on you should try to be carrying wards with you: they
are great for attacking highground into the enemy base. You can run up
to the base wall, mid-way between two lanes, and put a ward inside the
base above you (if you get it in the right place). It won't be seen by
towers. It will get dewarded if they see you doing it, but it can be
worth it even if it only gives you 10s of vision. Dropping a ward where
the T3 was after it's destroyed is also good.<br />
<br />
<br />
<table style="width: 100%;"><tbody>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_44a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_44a.jpg" title="" width="229" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Between top and mid</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_45a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_45a.jpg" title="" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Between mid and bot.</td></tr>
</tbody></table>
</td></tr>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_52a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_52a.jpg" title="" width="229" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Between top and mid.</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/misc/dotawardguide_53a.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://www.onelivesleft.com/misc/dotawardguide_53a.jpg" title="" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Between mid and bot.</td></tr>
</tbody></table>
</td></tr>
</tbody></table>
<br />
If you're doing well you'll be needing more items, but at that
point it's usually situational. Eul's is great if you want to be more
mobile and have another disable. Ghost Scepter is relatively cheap and can greatly increase your survivability. Agh's depends on the hero, but has a
solid build-up and could be worth it. Necrobook is great. Scythe is
amazing but usually you'll need to be stomping to get that.<br />
<br />
If the enemy have an annoying invis hero (or multiple) it's
also a good idea, once your tank has bulked up a bit,
to buy them a Gem. You should of course be buying sentries and Dust earlier on, squeezed between wards, though other players helping to buy those will help a lot.<br />
<br />
I've not covered Smoke or dewarding; this is the quick and dirty primer, and they are more advanced tactics. Q&D though; smoke your team to get to Rosh without being seen, or to get to the enemy team with surprise on your side. Dewarding: keep an eye on enemy supports, pretend you're them and imagine where you'd put the ward, then put a sentry near it.<br />
<br />
Once you've got used to warding, read up on pretty much every spot available in the <a href="http://www.team-dignitas.net/articles/blogs/DotA/1092/Dota-2-Ultimate-Guide-to-Warding/">Dota 2: Ultimate Guide to Warding</a>. Then you can read <a href="http://www.teamliquid.net/forum/dota-2-strategy/377503-guide-how-to-play-support-by-burningsera">this</a>.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-10640240395727192692014-01-29T05:56:00.000-08:002014-01-29T05:56:37.638-08:00Dota 2 - OCD Item Slots (or How To Stop Forgetting Where Your Dust Is)Following a discussion on reddit, and a missed kill last night, this is just a short post on how I arrange my inventory. The exact positions are set based on where the hotkey for that slot is; some keys are easier/faster to push than others, so which slot is used for each thing can vary by player based on their fingers and preference, but the point of the post is not which individual slots are used; rather, it is to advocate keeping things consistent so that when you need to use something in a hurry you can do so instinctively, rather than having to think about it and possibly losing your opportunity.<br />
<br />
<a name='more'></a>So, as I'm buying items in Dota I'm moving them around in my inventory based on a semi-arbitrary pattern that I'm now used to. The inventory is, as you are doubtless aware, a collection of 6 boxes arranged in two rows of three. I'll refer to them by location, i.e. top-left, bottom-middle, etc. Due to how my hotkeys are arranged*, it's easier for me to push the keys for the top row of items than the bottom row, and so the items in the top row tend to be the often used items, mobility items and the emergency items (items you want to use in a hurry on short notice).<br />
<br />
My six types of slot are, in order of easiest-to-activate first: Emergency, Escape/Initiate, Boots, TP, Truesight/Misc, and Wards.<br />
<br />
Top-right is where I put my Stick/Wand. This slot is thus the
Emergency slot. I now instinctively hit the hotkey for this slot whenever I notice I'm on very low health. If I'm stunned and taking damage I'll start hammering it. Items which may replace Stick in this slot: Mek, Drums. Possibly Bottle, or some other consumable heal, but I'll tend to put those in the Misc slot as they aren't instant enough. <br />
<br />
Top-middle is the mobility slot: Escape/Initiate. Force Staff or Blink Dagger go here. I'm used to double-tapping this when I'm in trouble (to Force Staff out of danger). If the hero I'm playing doesn't get a mobility item, I'll put whatever their main item is here instead. For example, Diffusal Blade on Riki (double-tap to remove Dust).<br />
<br />
The top-left slot is where I keep my boots. You always get boots, and boots almost always have an active that you might need to use in a pinch (quickly get mana from arcanes, tread switch for HP or mana, phase shift).<br />
<br />
TP scrolls go in the bottom-right. We're getting to the items that you have more time to think about using, but sometimes you do need to double-tap your TP to get the hell out just before they land the killing blow, so TP gets priority over the following two slots. If I upgrade to Boots of Travel then I move them into this slot too: my hotkey for teleporting is thus always the same, and I free up an easier to hit button in the top-left for some other item.<br />
<br />
Bottom-middle is the Truesight/Misc slot. What this means is that, if the other team has invis heroes, Sentries/Dust/Gem/Necro go in this slot. If they don't then I use it for whatever else I need to. Usually that's things like Bottle, Soul Ring, Urn, Mek, Drums, Euls, Smoke.<br />
<br />
Finally, bottom-left is the Ward slot. I play a lot of support, so this slot tends to be fairly dedicated, but if I'm not supporting I can use it for items that don't have an active like Stout, PMS, Vanguard, OoV, or just components in general.<br />
<br />
Of course, things might not fall out exactly this way. I might have Force Staff AND Blink Dagger, or Urn, Wand, Mek and Drums, or any number of other configurations, but in general I keep my items to this pattern and it means that in any given situation I can react a little bit faster. If I'm about to die I hit the Emergency key and then the Escape key. If someone goes invis in front of me I hit the Truesight key. Etc.<br />
<br />
<br />
* I use 1,2,3 for the top three item slots, and z,x,c for the bottom three. This means the physical layout of the keys matches the layout of the item grid, so I never have to think about which hotkey handles which box. It also gives me really easy access to the top three slots, at the expense of three control groups. I tend not to play heroes that need control groups, so it's a worthwhile trade for me, and I still have reasonable access to the groups on 4 and 5 if I need them. <br />
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-84231935549043263752013-11-02T18:14:00.000-07:002014-01-29T05:57:07.543-08:00Progenitor revision 2013.11.03 changelog<b><u><span style="font-family: "Courier New", Courier, monospace;">Progenitor changelog 2013.11.03</span></u></b><br />
<br />
<br />
<i><span style="font-family: "Courier New",Courier,monospace;">Nerfs/buffs:</span></i><br />
<br />
<table>
<tbody>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Adaptive Shielding</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Decreased Energy from <b>+0 </b>to <b>-1.</b></span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Atom Reclaimer</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Decreased Energy Requirement from <b>3 </b>to <b>2</b>.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Charibdis</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Now only lets you destroy cards owned by the player with the most Energy.<b></b></span><br />
<a name='more'></a></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Chronotic Schism</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">No longer removes itself from the game. Purge effect is now triggered automatically on Purging.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Effector </span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Decreased Energy from <b>+0 </b>to <b>-1.</b></span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Feedback Lance</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Increased Energy from <b>+0 </b>to <b>+1</b>. Now causes target to discard cards rather than you to draw them.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Heavy Duty Displacer</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Decreased Energy from <b>+0 </b>to <b>-1.</b></span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Leak</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Reworded so that discard counts as happening. Now goes into play for opponent when you gain it.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Nanohole Projector</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Increased Energy Requirement from <b>1 </b>to <b>2</b>. Increased Energy from <b>+0</b> to <b>+1</b>.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Nexus</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Removed Energy (is no longer an Energy card so no longer effects itself).</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Resonator</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Added <i>may</i> to both effects, and second is contingent on first: you may only draw if opponent does not discard.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Salvage Rig</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Decreased Energy from <b>+1</b> to <b>+0</b>. Increased Energy Requirement from <b>2</b> to <b>3</b>.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;"><b>Teles Beacon</b></span></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Decreased Energy Requirement from <b>2 </b>to <b>1</b>. </span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Trojan</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Increased Energy from <b>-1 </b>to <b>0</b>.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Warp Gremlin</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Is now attracted to player with most Energy. Moves to next player during purge.</span></td></tr>
</tbody></table>
<br />
<br />
<i><span style="font-family: "Courier New", Courier, monospace;">Rewording:</span></i><br />
<br />
<table>
<tbody>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Attrition</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Formatting fixes.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">High Energy Effector</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Added clarification/help text.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Wits</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Reworded for timing/clarity.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"></td><td style="padding: 0.5em; vertical-align: top;"><br /></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"></td><td style="padding: 0.5em; vertical-align: top;"><br /></td></tr>
</tbody></table>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-7301241147578784682013-10-05T07:32:00.000-07:002013-10-07T06:25:40.393-07:00Grand Theft Auto V - Loading ScreensThese are all the images that slideshow past as you wait for the game to load. Anomalies detected...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table style="margin-left: -40px; width: 100%;"><tbody>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-TBic28rS-08/UlAcX-JOpAI/AAAAAAAAAk8/x8Bk3W8I9Ks/s1600/gtav01.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Hot female cop arresting hot hippy chick" border="0" src="http://1.bp.blogspot.com/-TBic28rS-08/UlAcX-JOpAI/AAAAAAAAAk8/x8Bk3W8I9Ks/s1600/gtav01.png" title="Hot female cop arresting hot hippy chick" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Do not appear in game</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-6hi99Zumn8o/UlAcV30_hZI/AAAAAAAAAko/RHy7EAPk6UI/s1600/gtav02.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Franklin & Chop" border="0" src="http://3.bp.blogspot.com/-6hi99Zumn8o/UlAcV30_hZI/AAAAAAAAAko/RHy7EAPk6UI/s1600/gtav02.png" title="Franklin & Chop" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Playable character and<br />
trainable dog</td></tr>
</tbody></table>
</td></tr>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-Ek-V_Mx9LQw/UlAcWelPIdI/AAAAAAAAAkw/jek-u_M3E8s/s1600/gtav03.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Wade" border="0" src="http://3.bp.blogspot.com/-Ek-V_Mx9LQw/UlAcWelPIdI/AAAAAAAAAkw/jek-u_M3E8s/s1600/gtav03.png" title="Wade" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Character</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-WAAI-1Q73ek/UlAcXrvrQJI/AAAAAAAAAk4/V0RhZ8FTPL8/s1600/gtav04.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Trevor" border="0" src="http://4.bp.blogspot.com/-WAAI-1Q73ek/UlAcXrvrQJI/AAAAAAAAAk4/V0RhZ8FTPL8/s1600/gtav04.png" title="Trevor" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Playable character</td></tr>
</tbody></table>
</td></tr>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-pUut8Hh-odc/UlAcYfkSVNI/AAAAAAAAAlI/0cSsi-YpH_U/s1600/gtav05.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Dave" border="0" src="http://1.bp.blogspot.com/-pUut8Hh-odc/UlAcYfkSVNI/AAAAAAAAAlI/0cSsi-YpH_U/s1600/gtav05.png" title="Dave" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Character</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-WB3AJGgIZL8/UlAcaKZdrxI/AAAAAAAAAlU/6RoJUOERxWM/s1600/gtav06.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Michael" border="0" src="http://4.bp.blogspot.com/-WB3AJGgIZL8/UlAcaKZdrxI/AAAAAAAAAlU/6RoJUOERxWM/s1600/gtav06.png" title="Michael" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Playable character</td></tr>
</tbody></table>
</td></tr>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-tj_KQKdJJ4Y/UlAcZ-tnYyI/AAAAAAAAAlQ/uDX5uYUmBqg/s1600/gtav07.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Ron" border="0" src="http://2.bp.blogspot.com/-tj_KQKdJJ4Y/UlAcZ-tnYyI/AAAAAAAAAlQ/uDX5uYUmBqg/s1600/gtav07.png" title="Ron" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Character</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-hWmYmkZdERY/UlAca5zRd_I/AAAAAAAAAlg/L-LMeCK-cCM/s1600/gtav08.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Franklin" border="0" src="http://2.bp.blogspot.com/-hWmYmkZdERY/UlAca5zRd_I/AAAAAAAAAlg/L-LMeCK-cCM/s1600/gtav08.png" title="Franklin" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Playable character</td></tr>
</tbody></table>
</td></tr>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-EN92DvI_Fpc/UlAccS_wbLI/AAAAAAAAAls/St0f3cRdpAA/s1600/gtav09.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Hot bikini chick" border="0" src="http://1.bp.blogspot.com/-EN92DvI_Fpc/UlAccS_wbLI/AAAAAAAAAls/St0f3cRdpAA/s1600/gtav09.png" title="Hot bikini chick" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Does not appear in game</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-Kp6nppmyO5Y/UlAccMQi1BI/AAAAAAAAAlo/QsPrDtH9VYc/s1600/gtav10.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Trevor" border="0" src="http://2.bp.blogspot.com/-Kp6nppmyO5Y/UlAccMQi1BI/AAAAAAAAAlo/QsPrDtH9VYc/s1600/gtav10.png" title="Trevor" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Playable character</td></tr>
</tbody></table>
</td></tr>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-SNWyUgn9DEY/UlAcdNn-LJI/AAAAAAAAAl4/DvUVgmPPwAw/s1600/gtav11.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Lester" border="0" src="http://1.bp.blogspot.com/-SNWyUgn9DEY/UlAcdNn-LJI/AAAAAAAAAl4/DvUVgmPPwAw/s1600/gtav11.png" title="Lester" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Character</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-lorzgUDLjnI/UlAcdXsCXeI/AAAAAAAAAl8/rCYCY-ugx5k/s1600/gtav12.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Michael" border="0" src="http://2.bp.blogspot.com/-lorzgUDLjnI/UlAcdXsCXeI/AAAAAAAAAl8/rCYCY-ugx5k/s1600/gtav12.png" title="Michael" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Playable character</td></tr>
</tbody></table>
</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
In summation:<br />
<br />
<table border="0" style="border-collapse: collapse; text-align: center; width: 100%;"><tbody>
<tr><td><b>Characters </b></td><td><i> in loadscreens </i> </td><td><i> that appear in game </i> </td><td><i> who are playable</i></td></tr>
<tr><td><i> Male </i></td><td>6</td><td>6</td><td>3</td></tr>
<tr><td><i> Female </i></td><td>3</td><td>0</td><td>0</td></tr>
<tr><td><i> Canine </i></td><td>1</td><td>1</td><td>0</td></tr>
<tr><td><i> Total </i></td><td>10</td><td>7</td><td>3</td></tr>
</tbody></table>
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-51636383069348291352013-09-23T03:21:00.002-07:002013-10-31T07:52:48.294-07:00Progenitor - Tempo: Purging & TurnoverIn the last couple of posts I've discussed some <a href="http://blog.onelivesleft.com/2013/09/progenitor-windows-of-opportunity.html">key mechanics of Progenitor</a>, and how <a href="http://blog.onelivesleft.com/2013/09/progenitor-tempo-gaining-losing-cards.html">gaining cards affects tempo</a>. Here I'll put the two together, analysing the tempo gains/losses of a <i>purge</i> and a <i>turnover</i>. First up, let's look at purging.<br />
<br />
When you purge you get to gain cards from the board, trigger any purge effects, then destroy all cards you have in play. Each of these steps can change your tempo, so working out the tempo change for a purge is probably the most convoluted calculation applicable to Progenitor.<br />
<br />
First, you add up the tempo you get for any cards you gain, which is the Energy Requirement of each card plus one. Then add any tempo changes from purge effects. Finally, you destroy all cards you have in play, and thus subtract tempo for all the Energy and Primes you lose. It doesn't matter how a card leaves play, be it an opponent destroying them with a card effect or you purging, you still lose the tempo in exactly the same way. This would seem to imply that building up and purging is a massive waste of time, as almost all the tempo you gain with your plays will be lost again as soon as you purge, but the purge is letting you gain cards from the board, and it's this gain of cards which is the overall tempo increase you are scoring.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table style="float: right;"><tbody>
<tr><td><a href="http://www.onelivesleft.com/cardgame/cards/Charge.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img alt="0ER, +1, [starting deck is made of these]" border="0" height="200" src="http://www.onelivesleft.com/cardgame/cards/Charge.png" title="Charge" width="143" /></a></td>
<td><a href="http://www.onelivesleft.com/cardgame/cards/Supply%20Shuttle.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img alt="1ER, +1, +1 Prime." border="0" height="200" src="http://www.onelivesleft.com/cardgame/cards/Supply%20Shuttle.png" title="Supply Shuttle" width="143" /></a></td></tr>
</tbody></table>
Let's look at the example I used before, where you have 2 <b>Charge</b> (0ER, +1, [starting deck is made of these]) cards and a <b>Supply Shuttle</b> (1ER, +1, +1 Prime) in play, giving you a total of 3 Energy and 2 Primes. When you purge in this sitation you have four different options as to what you take from the board. You may gain:<br />
<a name='more'></a><ol>
<li>One 1ER card and one 2ER card</li>
<li>Two 1ER cards</li>
<li>One card with 3ER or less</li>
<li>No cards at all (if you wish) </li>
</ol>
The tempo you score per card is equal to the card's Energy Requirement + 1, so these different combinations are worth, respectively: 5 tempo, 4 tempo, 2-4 tempo (depending on ER), and 0 tempo. Notice that the highest tempo you can gain (5 tempo) is equal to the amount of tempo your cards are providing + 1; this will always be the case (assuming you have enough Primes to use all your Energy), as you have the tempo from your cards in play plus your turn. The 4 tempo from the cards is also what you will lose when you destroy all your cards in play. In effect, gaining the cards from the purge is you banking the points you've been playing to that point.<br />
<br />
It should also be clear that you should always try to use all of the Energy and Primes at your disposal, as to do less is to lose tempo. If you picked option 2 above then you would be wasting 1 tempo: you may as well have not played the second <b>Charge</b>, and thus been able to purge a turn or two earlier.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table style="float: right;"><tbody>
<tr><td><a href="http://www.onelivesleft.com/cardgame/cards/Energy%20Condenser.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img alt="3ER, +2, played: draw a card, purged: draw a card" border="0" height="200" src="http://www.onelivesleft.com/cardgame/cards/Energy%20Condenser.png" title="Energy Condenser" width="143" /></a></td>
<td><a href="http://www.onelivesleft.com/cardgame/cards/Mass%20Fabricator.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img alt="3ER, +2, +2 Primes." border="0" height="200" src="http://www.onelivesleft.com/cardgame/cards/Mass%20Fabricator.png" title="Mass Fabricator" width="143" /></a></td></tr>
</tbody></table>
Next example: you have 3 <b>Charge</b>, 2 <b>Energy Condenser</b> (3ER, +2, played: draw a card, purged: draw a card) and a <b>Mass Fabricator</b> (3ER, +2, +2 Primes) in play.<br />
<br />
When you purge you have a total of 9 Energy and 3 Primes. The three steps of the purge go like this:<br />
<ol>
<li>You gain 3 more <b>Mass Fabricator</b>s, utilising all your Energy and Primes. Each gain of a <b>Mass Fabricator</b> is worth 4 tempo, so this gives a total of +12 tempo.</li>
<li>You trigger both your <b>Energy Condenser</b>s, drawing a card for each: +2 tempo.</li>
<li>You destroy everything in play, losing 9 Energy and 2 Primes: -11 tempo.</li>
</ol>
So the total tempo swing for the purge is +12 +2 -11 = +3 tempo. This may not seem like much, but remember you'll have been accruing tempo for every play leading up to this point. Every Energy Condenser gave you +3 tempo when you played it, and your Mass Fabricator gave you +4. If we take two points in the game, point A (just before you started to build up cards in play) and point B (right after the purge), between the two points you have played 3 <b>Charge</b> (+1 tempo each), 2 <b>Energy Condenser</b> (+3 tempo each) and a <b>Mass Fabricator</b> (+4 tempo), and then purged them (+3 tempo). Your tempo at point B is <u>16</u> higher than at point A. Assuming you've played them in sequence without doing anything else, that's 16 tempo over 7 turns: you've averaged more than 2 tempo per turn over that window.<br />
<br />
Of course, we should really extend that window backward to include you getting those cards in hand. If you had to draw them all one at a time with the draw action then it would add 6 turns on to our timeframe, but two of your plays were <b>Energy Condenser</b> (played: draw you a card), so that lowers it to 4 turns. Adding on those 4 turns still gives you a respectable 1.5 tempo per turn. Not bad, but clearly not as good as the 2 tempo/turn above. This should show why having some good card draw like <b>Materiel</b> (2ER, +0, played: target player draws three cards) or <b>Entropy Engine</b> (3ER, +0, At the start of your turn draw a card) is desirable. <b>Entropy Engine</b> would negate all 4 turns of drawing, as well as the two turns to draw and play it, putting you back up to double.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table style="width: 100%;"><tbody>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://onelivesleft.com/cardgame/cards/Materiel.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="2ER, +0, played: Target player draws three cards." border="0" height="320" src="http://onelivesleft.com/cardgame/cards/Materiel.png" title="Materiel" width="229" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Use this to make an opponent trigger a costly turnover...</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://onelivesleft.com/cardgame/cards/Entropy%20Engine.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="3ER, +0, At the beginning of your turn draw a card." border="0" height="320" src="http://onelivesleft.com/cardgame/cards/Entropy%20Engine.png" title="Entropy Engine" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...but be careful you don't trigger one on yourself with this!</td></tr>
</tbody></table>
</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
</div>
<br />
As to the turnover, well, that's pretty simple: any time you discard a card from your hand it costs you 1 tempo: you lose the draw that gave it to you. Note you lose this point of tempo when you play the card too, but you are counting on the positive tempo the card provides when played to outweigh the loss; when you simply discard a card you don't get this good stuff.<br />
<br />
A turnover happens when you have to draw a card and have no cards left in your draw pile; when it happens you must reveal your hand to all players, discard it, then shuffle your discard pile to form your draw pile before finally drawing. Thus, a turnover costs you tempo equal to the number of cards in your hand.<br />
<br />
This means that an opponent sitting with a dozen cards in hand, but less than 3 in their draw pile, is ripe for being hit with a <b>Materiel</b>, which will cause a massive tempo swing. If they have literally a dozen cards in hand, you'll hit them for 8, 9 or 10 tempo, depending on how many cards they have in their draw pile. The sweet spot for this is when they have exactly 2 left: <b>Materiel</b> makes them draw three, so they'll draw the two remaining cards, then discard their hand of 14, before shuffling up and drawing the final card of the three, leaving them with only a single card in hand (+3 -14 = -11, but they get +1 for your play).<br />
<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"><b>Art assets on prototype cards are the property of their respective owners, and are used without permission.</b></span>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-79885669997514795702013-09-19T10:41:00.000-07:002013-10-31T07:46:41.477-07:00Progenitor - Tempo: Gaining & Losing CardsUp until now tempo has dealt purely with cards as they enter and leave play. A card which gives you +2 Energy is worth 2 tempo while it's in play Destroying a card which would provides an opponent +1 Energy causes them to lose 1 tempo. But how is tempo affected by adding cards to our deck, or losing them from it?<br />
<br />
When you gain a card you put it into your discard pile. If you gain it as part of a purge action then it is never in play: it goes straight from the board into your discard pile. It may appear that this has no effect on tempo at all. This is not the case!<br />
<br />
<a name='more'></a>Remember, <a href="http://blog.onelivesleft.com/2013/08/progenitor-synopsis.html">Progenitor is a deck-building card game</a>. In essence, the basic point of the game is to add cards to your deck. Remember too that the concept of tempo is as a measurement of how well you are doing. In the abstract, gaining cards should be treated as the scoring action in Progenitor, as creating a deck is the goal of the game. Therefor, whenever you gain a card, you gain tempo.<br />
<br />
How much is gaining a card worth? Within the framework of the basic rules, what you need to do to gain a card is (a) have enough Energy to meet the card's Energy Requirement and (b) take a purge action. As <a href="http://blog.onelivesleft.com/2013/09/progenito-tempo-in-play.html">each point of Energy is worth 1 tempo</a>, and 1 turn (the purge) is <a href="http://blog.onelivesleft.com/2013/08/progenitor-tempo.html">worth 1 tempo</a>, the amount of tempo you score when you gain a card is equal to the card's Energy Requirement plus one.<br />
<br />
Conversely, if you were to lose a card you would lose the same amount of tempo. Thus, card effects which allow you to steal cards from your opponents are extremely powerful, as when you take the card you gain the tempo while simultaneously causing your opponent to lose it; it's worth double the points!<br />
<br />
Let's look at an example: I play <b>Heavy Duty Displacer </b>(3ER, -1, played: Take a card at random from target opponent's hand), and manage to get a <b>Mass Fabricator </b>(3ER, +2, +2 Primes).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table style="width: 100%;"><tbody>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://onelivesleft.com/cardgame/cards/Heavy%20Duty%20Displacer.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="3ER, -1, played: Take one card at random from target opponent's hand." border="0" height="320" src="http://onelivesleft.com/cardgame/cards/Heavy%20Duty%20Displacer.png" title="Heavy Duty Displacer" width="229" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">You'll want a Blink Drive in hand...</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://onelivesleft.com/cardgame/cards/Mass%20Fabricator.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="3ER, +2, +2 Primes." border="0" height="320" src="http://onelivesleft.com/cardgame/cards/Mass%20Fabricator.png" title="Mass Fabricator" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...so they don't steal something as nice as this.</td></tr>
</tbody></table>
</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
</div>
<br />
The <b>Mass Fabricator</b> costs 3ER, and so me taking it gives me 3+1 = 4. At the same time, my opponent loses the card and so loses 4 tempo. On top of that, you are taking it from their hand, meaning they suffer -1 draw, and <b>Heavy Duty Displacer</b> actually makes you <i>take</i> the card, not <i>gain </i>it - it goes straight into your hand, not onto your discard pile, and so effectively gives you +1 draw. After factoring in <b>Heavy Duty Displacer</b>'s -1 Energy, and the standard -1 for playing a card (because you no have one less card in hand) this gives a total tempo swing of +8; pretty good!<br />
<br />
8 tempo is a big play. How does that line up with other 3 Energy Requirement offensive cards? Well, here is <b>Laser Salvo</b>'s big, big brother: <b>CREWs</b> (3ER, +1, played: destroy up to two target cards).<br />
<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/cardgame/cards/CREWs.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="3ER, +1, played: Destroy up to two target cards." border="0" height="320" src="http://www.onelivesleft.com/cardgame/cards/CREWs.png" title="CREWs" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Boom, boom.</td></tr>
</tbody></table>
<br />
My opponent has two <b>Mass Fabricator</b>s in play. They are each providing +2 Energy and +2 Primes, so are worth 4 tempo while in play. If I play <b>CREWs</b> and destroy them both I'll cost him 8 tempo, while giving myself +1 Energy. Less 1 for the play makes that an 8 tempo swing!<br />
<br />
Swings of 8 tempo are much bigger than what we've looked at in previous examples. Indeed, if we look at the other 3ER card in question here, <b>Mass Fabricator</b>, we see it's only worth 4 tempo while in play. Why are the two offensive cards seemingly so powerful in comparison?<br />
<br />
There are two things to consider here. Firstly, the worth of the offensive cards is dictated purely by the target, and in these examples the <b>Mass Fabricator</b> is an extremely valuable one. The <b>Heavy Duty Displacer</b> takes a card <i>at random</i>, and so could just as easily deliver you a 1ER or 2ER card (or even one with 0ER). The possible tempo swings for a <b>Heavy Duty Displacer</b> are 2/4/6/8 tempo for Energy Requirements of 0/1/2/3. Similarly, the value of the <b>CREWs</b> depends on what you destroy, with the 8 demonstrated here being fairly optimal.<br />
<br />
When you look at other 3ER cards the averages of these values are still comparatively high, however, and this brings us to "Secondly". <i>Secondly</i>, not to post a spoiler about an upcoming blog post, but offensive cards are less good in multiplayer games than they are in 1v1, and I'll explain why in the aforementioned future post.<br />
<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"><b>Art assets on prototype cards are the property of their respective owners, and are used without permission.</b></span>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-41678439636830622102013-09-13T03:03:00.000-07:002013-09-15T04:51:49.696-07:00Progenitor - Windows Of OpportunityIn games like Progenitor it's not uncommon for a player to get into a commanding position: a place of power which can make them seemingly unassailable. In Progenitor this manifests in two ways:<br />
<ol>
<li><u>Play Presence</u>.<br />By having a lot of cards in play you can have a plethora of effects continually providing you with benefits. Defensive cards can stop any opponent from messing with you, while utility cards like <b>Permutator</b> (2ER, +2, At the beginning of your turn you may look at the top card of your draw pile, then discard it or put it back.) or <b>Entropy Engine</b> (3ER, +0, At the beginning of your turn draw a card) can give you extensive card draw or a way to lock-down / control your adversaries.</li>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table style="width: 100%;"><tbody>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://onelivesleft.com/cardgame/cards/Permutator.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="+2, 2ER, At the beginning of your turn you may look at the top card of your draw pile, then discard it or put it back." border="0" height="320" src="http://onelivesleft.com/cardgame/cards/Permutator.png" title="Permutator" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Having this in play is pretty nice...</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://onelivesleft.com/cardgame/cards/Entropy%20Engine.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="+0, 3ER, At the beginning of your turn draw a card." border="0" height="320" src="http://onelivesleft.com/cardgame/cards/Entropy%20Engine.png" title="Entropy Engine" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...but not as nice as this.</td></tr>
</tbody></table>
</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
</div>
<li><u>Hand Size.</u><br />Your hand is your materiel, your ammunition. The more cards you've got in hand the more options you have in your turn. Add to that the presence of defensive cards like <b>Blink Drive</b> (2ER, -1, You may play this card when an opponent targets you or your cards to nullify opponent's card, played: Draw a card) and a large hand of cards can give you a large psychological advantage, as well as the actual advantage of having lots of answers to your opponents plays.</li>
</ol>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/cardgame/cards/Blink%20Drive.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="-1, 2ER, You may play this card when an opponent targets you or your cards to nullify opponent's card, played: draw a card." border="0" height="320" src="http://www.onelivesleft.com/cardgame/cards/Blink%20Drive.png" title="Blink Drive" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Simply having this in your deck can deter opponents.</td></tr>
</tbody></table>
There are two key play mechanics in Progenitor aimed at redressing these advantages. That is not to say it removes them: after all, a player who has played well enough to get into a position of power deserves to be rewarded for it. They do, however, provide a couple of ways through, a couple of chinks in the armour that a canny player can take advantage of to pull a game back in their direction.<br />
<br />
<a name='more'></a><br />
First is the <i>purge.</i> If you remember <a href="http://blog.onelivesleft.com/2013/08/progenitor-synopsis.html">from the synopsis</a>, purging is one of the three basic actions you may can choose from in your turn. When a player purges they take three steps in order:<br />
<ol>
<div class="separator" style="clear: both; text-align: center;">
</div>
<table style="float: right;"><tbody>
<tr><td><a href="http://www.onelivesleft.com/cardgame/cards/Charge.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img alt="+1, 0ER, [starting deck is made of these]" border="0" height="200" src="http://www.onelivesleft.com/cardgame/cards/Charge.png" title="Charge" width="143" /></a></td>
<td><a href="http://www.onelivesleft.com/cardgame/cards/Supply%20Shuttle.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img alt="+1, 1ER, +1 Prime." border="0" height="200" src="http://www.onelivesleft.com/cardgame/cards/Supply%20Shuttle.png" title="Supply Shuttle" width="143" /></a></td></tr>
</tbody></table>
<li>They work out their total Energy, and how many Primes they have. They then gain cards from the board up to those limits: they may gain up to their Primes number of cards, and the total Energy Requirement of the cards they gain may not exceed their Energy. For example, if they have two <b>Charge</b> cards and a <b>Supply Shuttle </b>in play then they have a total of 3 Energy and 2 Primes. They may gain:<br /><br /> <ul>
<li>One 1ER card and one 2ER card</li>
<li>Two 1ER cards</li>
<li>One card with 3ER or less</li>
<li>No cards at all (if they wish) </li>
</ul>
<br />
</li>
<li>They check for any purged effects on the cards they have in play, and trigger them in whatever order they want.</li>
<br />
<li>They destroy all cards they have in play.</li>
</ol>
Step 3 is the important one here: in order to gain cards from the board... that is, in order to build their deck <i>in this deck-building card game</i>, they must reset their Play Presence to zero. Every time a player wishes to improve their deck they will have to start building up again from scratch.<br />
<br />
<br />
The second mechanic is the <i>turnover</i>. This occurs when a player runs out of cards in their draw pile. If a player needs to draw a card (either because they have taken a Draw action, or because a card effect tells them to) and they have no cards in their draw pile then they do this:<br />
<ol>
<li>Reveal their hand to all players then discard it.</li>
<li>Shuffle their discard pile: it becomes their draw pile.</li>
<li>Draw a card.</li>
</ol>
Here step 1 is the crux: when a player draws all the way through their deck and then has to draw again they lose their hand, regardless of how many cards are in it. A sensible player will of course play all the cards they want to from their hand before drawing from the empty draw pile, but card effects can cause them to draw when they do not wish to (see <b>Blink Drive </b>and <b>Entropy Engine</b> above), and there is nothing stopping their opponents from playing cards to make them draw. "Wasting" a <b>Materiel </b>on an opponent may feel wrong, but watching them go from a 13 card hand to a 3 card hand* will give you the warm-and-fuzzies.<br />
<br />
<br />
N.B. Causing a turnover at inopportune times can be useful outwith destroying their hand. The turnover is the point at which all the cards they've gained on this pass though their deck will be shuffled in, ready to be drawn and used. When playing it's desirable to purge just before the turnover in order to get the new cards operational as soon as possible. Making someone trigger a turnover before they are ready can result in them having to go all the way through their deck again in order to shuffle in their latest purchases.<br />
<br />
<br />
*In this hypothetical situation, where you cause the opponent to go from a 13 card hand to a 3 card hand you have played <b>Materiel</b> on them when their draw pile was empty and caused them to discard 13 cards then draw 3: <i>that's a 10 point loss of tempo!</i><br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.onelivesleft.com/cardgame/cards/Materiel.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img alt="2ER, +0, played: target player draws three cards." border="0" height="320" src="http://www.onelivesleft.com/cardgame/cards/Materiel.png" title="Materiel" width="228" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Not just a pretty... great way to
fill your hand; can also be the best offensive card in the game.</td></tr>
</tbody></table>
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"><b>Art assets on prototype cards are the property of their respective owners, and are used without permission.</b></span>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-32810474328415008892013-09-11T12:57:00.000-07:002013-09-12T07:13:02.640-07:00Progenitor revision 2013.09.11 changelog<b><u><span style="font-family: "Courier New", Courier, monospace;">Progenitor changelog 2013.09.11</span></u></b><br />
<br />
<br />
<i><span style="font-family: "Courier New",Courier,monospace;">Nerfs/buffs:</span></i><br />
<br />
<table>
<tbody>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">AM Shell</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Added '<b>playable</b>' when playing with a card from hand to stop instant win with </span><span style="font-family: "Courier New",Courier,monospace;">All-In</span><span style="font-family: "Courier New",Courier,monospace;">.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Adaptive Shielding</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Opponent may discard card with same Energy Requirement to stop gain.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Blink Drive</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Increased Energy Requirement from <b>1</b> to <b>2</b>.</span><br />
<a name='more'></a></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">CAM Dusting</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Removed discard effect.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Control Substrate</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Now only affects <b>one </b>card.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Diplomatic Module</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Added '<b>to</b>' so no opponent is chosen when played with no cards in hand.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Haywire Rig</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Reworded for clarity.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Heavy Duty Displacer</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Lowered Energy from <b>+1 </b>to <b>+0</b>.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Hot Disassemble</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Lowered card draw from <b>three </b>cards to <b>two</b> cards.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Leech Snare</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Increased Energy from <b>+1 </b>to <b>+2</b>.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Probability Matrix</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Added option to discard.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Reality Shift</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Opponent gets to choose which cards.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Replicator</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Effect only happens <b>once per turn</b>.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Retribution Net</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">Lowered Energy Requirement from <b>2 </b>to <b>1</b>.</span></td></tr>
</tbody></table>
<br />
<br />
<i><span style="font-family: "Courier New", Courier, monospace;">Added 5 new Progenitor cards:</span></i><br />
<br />
<table>
<tbody>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Chronotic Schism</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">You may play cards in your discard pile as though they were in your hand. If a card would be put in your discard pile, remove it from the game instead.</span><br />
<span style="font-family: "Courier New",Courier,monospace;"><br />purged: Recall this card.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Duplication Array</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">When an opponent plays a card you may put into play a card with the same title.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Reality Selecter</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">When you purge you may select any number of cards you have in play. Those cards are nullified and are not destroyed by the purge.</span><br />
<span style="font-family: "Courier New",Courier,monospace;"><br />This card is not destroyed by a purge.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Stellar Sink</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">At the beginning of each player's turn they discard the top card of their draw pile.</span></td></tr>
<tr><td style="padding: 0.5em; vertical-align: top;"><b><span style="font-family: "Courier New",Courier,monospace;">Universe Gate</span></b></td><td style="padding: 0.5em; vertical-align: top;"><span style="font-family: "Courier New",Courier,monospace;">played: All players take the top card from the draw pile of the player to their right.</span></td></tr>
</tbody></table>
<br />
<br />
<i><span style="font-family: "Courier New",Courier,monospace;">Added <b>Excessive </b>symbol to the 10 most unfriendly Progenitor cards; First Time Setup rules will be updated to remove these instead of a random selection.</span></i>Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-2921343848373368580.post-90528930257060641972013-09-09T05:21:00.001-07:002013-09-12T07:14:08.623-07:00In case you were wondering what a 5-star rating is worth...Just received this email:<br />
<br />
<blockquote style="background: #eee; padding: 1em;">
<span style="font-size: 12.0pt;">Dear Android developer,<br /><br /><br />Please let us introduce our new services that can make your app look more appealing and will eventually lead to more downloads.<br /><br /><br /><b><u>1. Positive ratings</u><br /><br />- Adding 100 5-star ratings and 10 positive comments to your Google Play page<br />Price: $135</b></span><br />
<div class="MsoNormal" style="margin-bottom: 12.0pt;">
<b><span style="font-size: 12.0pt;">- Adding 1.000 ratings with 100 comments<br />Price: $950</span></b></div>
<div class="MsoNormal" style="margin-bottom: 12.0pt;">
<b><u><span style="font-size: 12.0pt;">2. Facebook likes</span></u></b><b><span style="font-size: 12.0pt;"><br /><br />- Adding 25.000 Facebook likes to your Facebook page<br />Price: $250</span></b></div>
<div class="MsoNormal" style="margin-bottom: 12.0pt;">
<b><span style="font-size: 12.0pt;">- Adding 100.000 Facebook likes <br />Price: $750</span></b><br />
<a name='more'></a></div>
<div class="MsoNormal" style="margin-bottom: 12.0pt;">
<b><u><span style="font-size: 12.0pt;">3. Google +1’s</span></u></b><b><span style="font-size: 12.0pt;"><br /><br />- Adding 1.000 Google +1’s to your Google Play page or any other page<br />Price: $100</span></b></div>
<div class="MsoNormal" style="margin-bottom: 12.0pt;">
<b><span style="font-size: 12.0pt;">- Adding 20.000 +1’s<br />Price: $1.200</span></b></div>
<div class="MsoNormal" style="margin-bottom: 12.0pt;">
<b><u><span style="font-size: 12.0pt;">4. YouTube views & likes</span></u></b><b><span style="font-size: 12.0pt;"><br /><br />- Adding 25.000 views and 1.000 Likes to your YouTube video<br />Price: $150</span></b></div>
<div class="MsoNormal" style="margin-bottom: 12.0pt;">
<b><span style="font-size: 12.0pt;">- Adding 100.000 views and 4.000 Likes<br />Price: $500</span></b><span style="font-size: 12.0pt;"></span></div>
<div class="MsoNormal" style="margin-bottom: 12.0pt;">
<b><u><span style="font-size: 12.0pt;">5. iOS ratings</span></u></b><b><span style="font-size: 12.0pt;"><br /><br />- Adding 100 5-star ratings to your iOS app<br />Price: $220</span></b></div>
<div class="MsoNormal" style="margin-bottom: 12.0pt;">
<b><span style="font-size: 12.0pt;">- Adding 1.000 5-star ratings to your iOS app <br />Price: $1.900</span></b><span style="font-size: 12.0pt;"></span></div>
<div class="MsoNormal" style="margin-bottom: 12.0pt;">
<span style="font-size: 12.0pt;">If
you are interested in any of the above mentioned services, please don’t
hesitate to reply to this email. It’s possible to do a free trial.<i><br /></i></span></div>
</blockquote>
<br />
Pigment has 3.4 stars in the <a href="http://www.amazon.com/One-Lives-Left-Pigment/dp/B0085MNZ66">Amazon Appstore</a> (129 reviews thanks to Amazon's <a href="http://blog.onelivesleft.com/2012/11/the-amazon-faotd-experience.html">FAotD</a>), and 4.8 in <a href="https://play.google.com/store/apps/details?id=com.onelivesleft.pigment">Google Play</a> over only 8 reviews, but somehow I don't think I'll be taking them up on their offer. What a depressing email.<br />
<br />
<br />
N.B. The conversion table looks like this: <br />
<br />
<table border="1" style="border-collapse: collapse; text-align: center;"><tbody>
<tr><td> Dollars </td><td> Play 5-star reviews </td><td> iOS 5-star reviews </td><td> Youtube views </td><td> Facebook Likes </td><td> Google+ Likes </td></tr>
<tr><td>$4</td><td>3</td><td>2</td><td>666</td><td>400</td><td>40</td></tr>
</tbody></table>
<br />
So, a G+ Like is 10 times more valuable than a Facebook Like? I'm guessing that's less about how effective they are for marketing and more about it being harder to spoof Google than Facebook.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2921343848373368580.post-54930309215166289652013-09-09T01:48:00.000-07:002013-10-31T07:38:16.765-07:00Progenitor - Tempo: In Play<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table style="float: right;"><tbody>
<tr><td><a href="http://www.onelivesleft.com/cardgame/cards/Laser%20Salvo.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img alt="+0, 1ER, played: Destroy target card." border="0" height="200" src="http://www.onelivesleft.com/cardgame/cards/Laser%20Salvo.png" title="Laser Salvo" width="143" /></a></td>
<td><a href="http://www.onelivesleft.com/cardgame/cards/Charge.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img alt="+1, 0ER, [starting deck is made of these]" border="0" height="200" src="http://www.onelivesleft.com/cardgame/cards/Charge.png" title="Charge" width="143" /></a></td></tr>
</tbody></table>
When using tempo to analyse plays in a game we need to look at how the play changes the state of the game, as opposed to the theoretical numbers we used previously. For example, if you were to make a play that would destroy an opponent's card, we no longer consider this card destruction worth a flat 2 tempo (your opponent loses a draw and a play); instead we need to look at the card we are going to destroy.<br />
<br />
Let's say you play a <b>Laser Salvo</b> (1ER, +0, played: destroy target card). If you destroy an opponent's <b>Charge</b> (0ER, +1, [starting deck is made of these]), what have they lost? They have lost 1 Energy. Ignore that they had to draw and play the <b>Charge</b>; that is irrelevant. The direct change to the state of the game is that they now have 1 less Energy than they did before. You might think that your play was therefor worth 1 tempo, and it would be, except that them losing 1 Energy is not the only change in the state of the game: you now have 1 less card in hand than you did before.<br />
Therefor, your play was worth 0 tempo: you have set them back by a point (of Energy) at the expense of a point of your own (a card in hand; a draw). <br />
<br />
Scoring your tempo is an interesting idea: obviously, the more tempo you gain the better, so you would want this to be high. If it were a computer game (or you were particularly bloody minded) you might be able to derive stats after the game was over, like your average tempo per turn. While it is clearly a good rule of thumb that the bigger-the-better for tempo/turn, during play it won't actually be key that this number is bigger than some goalpost figure: rather, it is important simply that it be bigger than your opponents'. Note that in a heads-up, 1v1 game the tempo score is zero-sum: any time you gain tempo your opponent loses it, and vice-versa; it's only in a multiplayer game that having a goalpost is possible; I'll look at tempo in multiplayer in a later post. Let's look at 1v1 now, as it's much simpler. In 1v1 tempo can be looked at as a peg moving up and down a number line:<br />
<a name='more'></a><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://vector-magz.com/wp-content/uploads/2013/09/number-line-clip-art.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10" border="0" height="180" src="http://vector-magz.com/wp-content/uploads/2013/09/number-line-clip-art.png" title="A Number Line" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A Number Line: useful for infants and game theorists.</td></tr>
</tbody></table>
<br />
At the start of the game the tempo peg is at 0. You go first and draw a card: the peg moves up to 1. Your opponent does likewise: the peg moves back to 0*. You play <b>Charge </b>and the peg stays put: you gained an Energy, but lost a card from hand (<b>Charges</b> are not good cards!). Your opponent draws again and the peg moves down to -1. You draw and it goes back up to 0.<br />
Let's fast-forward a few turns! The peg is at 0 (you just drew), and your opponent plays <b>Materiel</b> (2ER, +0, played: target player draws three cards) targetting themselves. This is worth 2 tempo: they draw three cards, gaining 3 tempo, but lose one because they played a card from their hand: the peg moves left two slots to -2. You are now losing!<br />
<br />
Of course, maximising tempo gain isn't the be-all-and-end-all of playing
Progenitor: if it was then the game would be extremely boring. Lots of
other considerations must be factored in to your decision making; with
two questions held above all others: "Can I win the game?" and "Can my
opponent win the game and how do I stop them?". It doesn't matter how
much tempo you lose doing it, making a play that wins you the game is
the best play you can make. Having said that, analysing tempo is an
extremely useful tool when deciding what play to make in most
circumstances throughout the game. <br />
<br />
<a href="http://www.onelivesleft.com/cardgame/cards/Ion%20Charge.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="+0, 1ER, played: recall target card, draw a card." border="0" height="200" src="http://www.onelivesleft.com/cardgame/cards/Ion%20Charge.png" title="Ion Charge" width="143" /></a>Let's examine the scenario I wrote about in the <a href="http://blog.onelivesleft.com/2013/09/progenitor-tempo-take-two.html">previous post</a>: bouncing your own <b>Laser Salvo </b>with an <b>Ion Charge</b> and playing it again. Using what we've learned in this post, it's obvious that the strength of the play depends entirely on what cards you destroy. The linking play, the <b>Ion Charge </b>to recall the <b>Laser Salvo</b>, is always worth 1 tempo: you get the <b>Laser Salvo</b> back (effectively a card draw), and also draw a card, but lose a card from hand. The <b>Laser Salvo</b> provides nothing while it is in play, and so recalling it incurs no loss of tempo. Had it +1 Energy (instead of the +0 it actually has) then recalling it would cost a tempo, resulting in the play being worth 1-1=0 tempo gained, but since it does only have +0 Energy it is a card with no worth while in play. Conversely, in your hand it can be played to generate variable amounts of tempo, depending on what you destroy with it. Thus, when asking yourself "should I <b>Ion Charge </b>my own <b>Laser Salvo</b>?", what you need to work out is how much tempo you can generate with the <b>Laser Salvo</b>.<br />
<br />
Let's look at one last example:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table style="width: 100%;"><tbody>
<tr><td><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: right; float: right; margin-bottom: 1em; margin-right: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://onelivesleft.com/cardgame/cards/Materiel.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="+0, 2ER, played: target player draws three cards." border="0" height="320" src="http://onelivesleft.com/cardgame/cards/Materiel.png" title="Materiel" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Solid card draw</td></tr>
</tbody></table>
</td><td><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-left: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://onelivesleft.com/cardgame/cards/Energy%20Condenser.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="+2, 3ER, played: draw a card, purged: draw a card." border="0" height="320" src="http://onelivesleft.com/cardgame/cards/Energy%20Condenser.png" title="Energy Condenser" width="230" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Excellent economy </td></tr>
</tbody></table>
</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
</div>
You've got your <b>Laser Salvo </b>and you're looking to blow up something of your opponent's. They have in play a <b>Materiel </b>and an <b>Energy Condenser </b>(+2, 3ER, played: draw a card, purged: draw a card). <b>Materiel </b>isn't providing anything: when they played it they got to draw three cards (+2 tempo for them), but now that it's in play it's almost valueless, and if you destroyed it it would net you 0 tempo. <b>Energy Condenser</b>, on the other hand, is chugging out a very nice +2 Energy. It will also draw them a card when they purge. Thus it is currently worth 3 tempo (2 Energy + a card draw); destroying it will bump the tempo peg up the number line 3 places, a pretty substantial swing!**<br />
<br />
Now let's say that your opponent also has an <b>Ion Charge </b>in their deck. They just drew three cards from the <b>Materiel</b>, increasing the odds that they'll have it in their hand. They won't be using it to recall your <b>Laser Salvo </b>(obviously!); if they play it it'll be to recall one of their own cards. If they recall the <b>Materiel</b>, they are setting up a +2 tempo card draw - pretty big; <b>Materiel </b>is a great choice for <b>Ion Charge</b>. <b>Energy Condenser</b>, though, is not: yes, they draw a card when they play it, but instead of recalling it and playing it they could just take a Draw action to draw the same card. Bouncing it and replaying it doesn't gain them any tempo.<br />
<br />
In this situation destroying the <b>Materiel </b>becomes a 2 tempo play: by putting it in their discard pile before they can recall it with <b>Ion Charge </b>you stop them from getting to play it again. Note that you wouldn't move the tempo peg right two spaces for playing your <b>Laser Salvo </b>- what you are doing is stopping them moving it two left.<br />
<br />
So, destroying the <b>Energy Condenser </b>is worth 3 tempo, while destroying the <b>Materiel</b> is worth 2: which do you pick? It depends! Though the 3 tempo play is bigger numerically, other conditions might make it less advantageous. Are they about to purge, or are they trying to build up a lot of energy first? Are you racing to buy a card? Have you more cards in hand and are they looking to catch up, draw cards to get as much ammunition as you? Maybe they have another card in their deck they are trying to get to as quickly as possible? One consideration that could sway you would be that if these cards are the only notable cards in play and you destroy the <b>Materiel </b>then that leaves only the <b>Energy Condenser </b>and your <b>Laser Salvo </b>in play: there would no longer be any useful targets for their <b>Ion Charge</b>. You will have turned it into a dead draw, and a point of tempo loss for them, unless another useful recall target presents itself.<br />
<br />
<br />
* Notice that assuming you both make equal plays for the entire game, averaging the tempo score over every turn would give you a mean tempo score of 0.5, not 0. Going first is advantageous!<br />
<br />
** The play is worth 3 tempo, but you wouldn't actually move the peg 3 spaces. When the opponent played <b>Energy Condenser </b>they scored the "played: draw a card" moved the peg left 1 space, and the +2 Energy moved it left a further 2, before bumping it back to the right once because they lost a card from hand. The final tempo point the <b>Energy Condenser </b>would make, the "purged: draw a card", would not have moved the peg: it would only move it when the opponent purged and they drew the card.<br />
Thus, when you destroy the <b>Energy Condenser</b>, you move the peg right 2 spaces, for the 2 energy you just removed, and because it is gone the opponent will no longer be able to score the card draw when they purge. So you are scoring 2 tempo directly, and preventing a -1 tempo from happening. <br />
<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"><b>Art assets on prototype cards are the property of their respective owners, and are used without permission.</b></span>Unknownnoreply@blogger.com0