Wednesday, August 30, 2017

Perception is not a skill

    Why this came to mind yesterday I do not know (well, kinda - it's a little related to another project I'm working on).  This comes from my own real-life experiences, so let me tell you a story...

    Many, many years ago I had an old Pontiac Sunbird.  It was used when I got it, and it had been very extensively used.  Still, it worked overall.  Until the day in question - when I was driving down the freeway, in the middle of the desert between Arizona on my way to New Mexico.  Everything seemed fine until all the gauges on my dashboard spiked, then dropped.
    So, instead of having a speedometer needle at the 65 mph I was driving (or 70 - I actually almost always drive the speed limit, I'm weird), it was sitting at 0.  My gas gauge, the oil temp, RPM, everything was at 0.

    Here, to drop out of real-life and into a theoretical RPG, my Perception kicked in.  I saw that something had changed.  And I knew it was not something normal or that, in fact, it had ever happened before while I've driven a vehicle.

    The next part, back to IRL, is why this post is being written: I had no idea what that meant!
    Why had my gauges all dropped?  The engine was still running, I was still moving (at a now-unknown speed), everything else seemed to be completely normal.  So what had happened?  And, what should I do?  There was no other traffic, so nobody around me was in danger.  But the thought hit me - if I pull over, will I be able to start again?  I'm several miles away from Santa Fe and the only nearby mechanic(s).  So what happened and what do I do?
    Well, I did make it to civilization - and I did find a mechanic, actually it took two, who was able to inform me that my electrical system had a short, which killed my gauges and my alternator.  It amusingly turned out to be a stupidly-easy fix.

     And that is why perception is not a skill.  Just because you see/ hear/ notice/ discover something means nothing if you can't put it in a useful context.  It's the difference between a signal and communication.  If you're sitting in the library next to me and I shout "AAAAAAHHHHHHaaahhhhhahahahah!!!!!!!!"  I have 'signaled' that there is something abnormal (hopefully) going on.  But I have not communicated anything.  If, instead of the random noise, I shouted, "Oh God help me there's a giant wombat chewing on my leg!!!"  I have now communicated, I have passed on information (granted, strange and difficult to comprehend information).

    Perception is a signal - something has changed in the environment (either an existing thing has altered, or something has been added, or something has been taken away).
    Knowledge, or Skill is communication.  This-kind-of-thing happens on the surface when these-kinds-of-things happen out of sight/ perception-range.

    Ergo: perception is not a skill.



Music I Write To - Lindsey Stirling















Monday, August 28, 2017

The Project Transition Hydra

    I don't know if any of you can relate to this, but I have a recurring problem when I'm switching from one project to another: the simple question of "what do I do next?" turns into this massive, hydra-like monster that becomes almost impossible to answer.  Case in point: I finished the Lone Wolf / Twine project a while ago, like 2 weeks ago (as I write this).  I looked at that project and I was happy.  I did not end with a fully-functional set of code, but I did learn a lot and I covered all the critical points I wanted to cover.  Happy.  My original thought was to keep refining and improving my work until I'd have this toolkit, or set of copy-and-paste bits, that someone else could use to maybe improve their own Twine story.  That was a good goal, I thought.
    Thing is, as I neared the end of the project I started to wonder.  I really respect Joe Dever for creating the Lone Wolf books and universe - I'm sad that he passed away, and that leaves me in an awkward place: since he's gone I can't really ask him for permission to use his work.  Which I feel bad about, while I did have my "pirate" side when I was younger I've found that age has mellowed me a bit.  I really want to work on projects that I can legitimately work on, that have no legal issues with my tinkering.  So pouring more effort into developing the code I was working on seemed like a waste if I couldn't control the rights to it.
    Well, easy answer, I'll just design my own system.  I'll take what I have that is not specific to the Lone Wolf setting, and then add some stuff of my own to create my own rules.  That sounded easy, but as I started to plan and organize the idea I came to a sad truth; I'm not ready to write my own rules.  From a mechanical standpoint Lone Wolf is not that complicated.  In fact, the idea of the "Choose Your Own Adventure" book is not that complicated.  And yet, the devil is in the details.  While making a story is not that hard, making a framework of your own rules that other people can use to make their own stories is a little more complicated.  And I just couldn't come up with a good set of mechanics I wanted to use.  Mind you, I had no problem coming up with mechanics - the problem was that hydra: it starts with one head, then it grows to two, and before you know it you're rolling 30d6 per round for all it's bite attacks.  I came up with too many rules, too many ideas.
    Along with that, while I was playing with Twine-SugarCube I hit some real bizarre formatting problems.  Things just were not looking like I wanted them to. I also discovered that SugarCube's widgets are great, but some of the rules I was thinking of needed to be in more pure JavaScript.  So, suddenly Twine-SugarCube seemed like a bad fit for what I wanted to do (I'll post something about the formatting in a few days, it was a trip).
    The confluence of problems threw me back on my heels.  I had to sit down and do some serious cogitating about what I really wanted to do, both for my next project and some other project ideas I've had for down the road.  Which led to some soul-searching and a whole, whole lot of reading and research.  And now, I've come to a couple of decisions:
  • First, I'm not ready to design my own rules.  I really like working with HTML, CSS and JavaScript (all of which Twine is built on).  I like learning more about the programming/ coding side than I do about making a new game from scratch.  So for now I want to keep building off of something existing, so I don't have to wear both a "game designer" and "project coder" hat.  To that end I've settled on my next project using the Dungeons and Dragons 5th edition Open Game License System Reference Document.  D&D 5th is a pretty good "rule-light" system that has room to grow and modify the base game.  Fate is really hard to program, being so story-driven.  Pathfinder is too detailed, too much programming needed.  And the other OGL systems out there I do not know very well.  So while D&D 5th is not my idea of the perfect system, it's legal for me to play with it, and it's closer than the other options I see.
  • Second, while I really like Twine, I'm going to actually create my own 'program' ('web app'?).  I have several ideas for future projects, ones that I know are beyond my current capabilities but that I think I can grow into as a coder.  They all revolve around the HTML-CSS-JavaScript trinity, and while Twine is great for what it does; I'm not sure it'll fit well with what I want to do in the future.  So I'm going to build something from the ground up for myself.  Is this re-inventing the wheel (programmer sin #1)?  Yeah, it kind of is.  But I want the experience, so I'm going to do it anyways.  Now, it's possible to use all those things in Twine (they are what Twine is made of after all), so I'm not necessarily going to make something that could not be integrated into Twine somewhere down the road.
  • Third, I want something that's professional.  I've been scouring the Web the last few days looking for open-sourced fonts, icons, graphics, music, tiles, and everything I can find to produce a sharp, professional, good-looking game/ story/ experience.  If I'm going to drive myself crazy trying to make something that's likely beyond my skills - I at least want it to be a good-looking mess :)
    Currently, I'm calling this the "Open2 Engine" in the dark corners of my mind.  It's going to be a role-playing game that could be played in a browser of on the table-top.  It's going to have a core set of rules and setting I'll develop, but other people will be able to expand on and create their own rules and details.  It's going to be a real monster of a project if I can do even half of the things I want to do.  It may be beyond me, more detail than I can handle.  Maybe, but I don't think so.  I've been reading a ton of programming books, with concepts that are pretty complicated.  A few months ago I wouldn't have understood half of them.  Now, I think I'm finally able to read between the lines and see why things work and how to tweak them.  This is going to be a huge challenge, but I like it - you only get better by pushing yourself to do more that you think you're capable of.
    In the process of coming to this new goal, I've created and thrown out a whole lot of material.  Therefore- I have nothing to post.  So I'm going to be off any kind of schedule for the next week or two until I can sort out what little I have worth keeping and where to go from here.  I have a couple of posts in mind that will go up this week, and hope to get on schedule (Mon random musings on a topic, Wed random stuff, and Fri update on the project) by the week after next at the latest.  I've almost got my feet under me, so things should hit a rhythm soon.

     Anyways, it should be an interesting ride :)


Friday, August 25, 2017

Converting Lone Wolf to Twine and SugarCube - Part 9 - Combat Rounds




    In the last section I managed to create a widget that would set up my fights by calculating all the relevant stats and abilities and gear. Now, I actually need to play out the fight turn-by-turn. In Lone Wolf this is done by calculating the difference between the two Combat Skill values, then ‘rolling’ a random number between 0 and 9, then reading off the chart for how much Endurance both Lone Wolf and his opponent lose. Here's the chart....


    Now, let me say in advance, this is not how I would handle this. The chart is neat, I like how it gives you both health totals at once - so you're both attacking at the same time instead of typical D&D “I swing, wait for you to swing, I swing, wait, yawn, etc...” But, having 4 values (column, row, player health, enemy health) all on the same table looks like it's going to be a little bit of a headache to code. Let's see...

-time passes-

    Okay, I've been showing some of my mistakes along the way here - and with this code I made a lot of them. So many, which took me so long to untangle, that I'm actually going to skip showing you the bad parts and focus on what I got to work. I do want to show you one thing though, a screenshot from some of the bad code...



    If you read each line, you'll notice that the math doesn't add up. That was one of the big problems I was having (after fixing the wall of red error text). I fixed most all of those problems by spitting things up, which is where I want to pick up talking about my code.

    Widgets are great, they are re-usable so that you can write one block of code just one time and call it over and over down the road. Another really cool thing about widgets - they can call other widgets, and they can even call themselves. My Inventory widget from a few sections back - I was using it to print out the inventory after it had just changed the inventory. A widget (or ‘function’ in JavaScript parlance) calling itself is called ‘recursive’ (I believe) - and that's another great way you can re-use code to cut down on the typing.
    The combat system has 3 connected widgets. <<CombatTurn>> calls <<Attack>> which calls <<TurnEnd>> and if everybody's still alive then <<TurnEnd>> calls <<Attack>> and continues the cycle until somebody falls and the combat is over.

    Let's start with <<CombatTurn>>

/*
* <<CombatTurn $enemy $player CanEscape>>
*/
<<widget "CombatTurn">> <<nobr>>
<<set $lwEnd = $args[1].Endurance>>
<<set $eEnd = $args[0].Endurance>>
<<set $CSdiff = $args[1].CombatSkill - $args[0].CombatSkill>>
Your CS difference is $CSdiff
<br> You attack your opponent...
<<Attack>>
<</nobr>> <</widget>>

    This just sets up the two main stats of CombatSkill and Endurance. Now, I wrote this as a little stub to test the turn-by-turn combat system; in the final story this would actually be a part of the <<CombatSetup>> widget that I spent the last section working on. The next widget, <<Attack>> does the really heavy lifting...

<<widget "Attack">> <<nobr>>
<<set _atkroll = random (0, 9)>>
<<switch Math.clamp($CSdiff, -100, 100)>>

<<case -11 -12 -13 -14>>
<<if _atkroll == 1>> <<set $eDmg = 0>> <<set $lwDmg = 100>> <<TurnEnd>> <</if>>
<<if _atkroll == 2>> <<set $eDmg = 0>> <<set $lwDmg = 100>> <<TurnEnd>> <</if>>
<<if _atkroll == 3>> <<set $eDmg = 0>> <<set $lwDmg = 8>> <<TurnEnd>> <</if>>
<<if _atkroll == 4>> <<set $eDmg = 0>> <<set $lwDmg = 8>> <<TurnEnd>> <</if>>
<<if _atkroll == 5>> <<set $eDmg = 1>> <<set $lwDmg = 7>> <<TurnEnd>> <</if>>
<<if _atkroll == 6>> <<set $eDmg = 2>> <<set $lwDmg = 6>> <<TurnEnd>> <</if>>
<<if _atkroll == 7>> <<set $eDmg = 3>> <<set $lwDmg = 5>> <<TurnEnd>> <</if>>
<<if _atkroll == 8>> <<set $eDmg = 4>> <<set $lwDmg = 4>> <<TurnEnd>> <</if>>
<<if _atkroll == 9>> <<set $eDmg = 5>> <<set $lwDmg = 3>> <<TurnEnd>> <</if>>
<<if _atkroll == 0>> <<set $eDmg = 6>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>

<<case -10 -9>>
<<if _atkroll == 1>> <<set $eDmg = 0>> <<set $lwDmg = 100>> <<TurnEnd>> <</if>>
<<if _atkroll == 2>> <<set $eDmg = 0>> <<set $lwDmg = 8>> <<TurnEnd>> <</if>>
<<if _atkroll == 3>> <<set $eDmg = 0>> <<set $lwDmg = 7>> <<TurnEnd>> <</if>>
<<if _atkroll == 4>> <<set $eDmg = 1>> <<set $lwDmg = 7>> <<TurnEnd>> <</if>>
<<if _atkroll == 5>> <<set $eDmg = 2>> <<set $lwDmg = 6>> <<TurnEnd>> <</if>>
<<if _atkroll == 6>> <<set $eDmg = 3>> <<set $lwDmg = 6>> <<TurnEnd>> <</if>>
<<if _atkroll == 7>> <<set $eDmg = 4>> <<set $lwDmg = 5>> <<TurnEnd>> <</if>>
<<if _atkroll == 8>> <<set $eDmg = 5>> <<set $lwDmg = 4>> <<TurnEnd>> <</if>>
<<if _atkroll == 9>> <<set $eDmg = 6>> <<set $lwDmg = 3>> <<TurnEnd>> <</if>>
<<if _atkroll == 0>> <<set $eDmg = 7>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>

<<case -8 -7>>
<<if _atkroll == 1>> <<set $eDmg = 0>> <<set $lwDmg = 8>> <<TurnEnd>> <</if>>
<<if _atkroll == 2>> <<set $eDmg = 0>> <<set $lwDmg = 7>> <<TurnEnd>> <</if>>
<<if _atkroll == 3>> <<set $eDmg = 1>> <<set $lwDmg = 6>> <<TurnEnd>> <</if>>
<<if _atkroll == 4>> <<set $eDmg = 2>> <<set $lwDmg = 6>> <<TurnEnd>> <</if>>
<<if _atkroll == 5>> <<set $eDmg = 3>> <<set $lwDmg = 5>> <<TurnEnd>> <</if>>
<<if _atkroll == 6>> <<set $eDmg = 4>> <<set $lwDmg = 5>> <<TurnEnd>> <</if>>
<<if _atkroll == 7>> <<set $eDmg = 5>> <<set $lwDmg = 4>> <<TurnEnd>> <</if>>
<<if _atkroll == 8>> <<set $eDmg = 6>> <<set $lwDmg = 3>> <<TurnEnd>> <</if>>
<<if _atkroll == 9>> <<set $eDmg = 7>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 0>> <<set $eDmg = 8>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>

<<case -6 -5>>
<<if _atkroll == 1>> <<set $eDmg = 0>> <<set $lwDmg = 6>> <<TurnEnd>> <</if>>
<<if _atkroll == 2>> <<set $eDmg = 1>> <<set $lwDmg = 6>> <<TurnEnd>> <</if>>
<<if _atkroll == 3>> <<set $eDmg = 2>> <<set $lwDmg = 5>> <<TurnEnd>> <</if>>
<<if _atkroll == 4>> <<set $eDmg = 3>> <<set $lwDmg = 5>> <<TurnEnd>> <</if>>
<<if _atkroll == 5>> <<set $eDmg = 4>> <<set $lwDmg = 4>> <<TurnEnd>> <</if>>
<<if _atkroll == 6>> <<set $eDmg = 5>> <<set $lwDmg = 4>> <<TurnEnd>> <</if>>
<<if _atkroll == 7>> <<set $eDmg = 6>> <<set $lwDmg = 3>> <<TurnEnd>> <</if>>
<<if _atkroll == 8>> <<set $eDmg = 7>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 9>> <<set $eDmg = 8>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 0>> <<set $eDmg = 9>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>

<<case -4 -3>>
<<if _atkroll == 1>> <<set $eDmg = 1>> <<set $lwDmg = 6>> <<TurnEnd>> <</if>>
<<if _atkroll == 2>> <<set $eDmg = 2>> <<set $lwDmg = 5>> <<TurnEnd>> <</if>>
<<if _atkroll == 3>> <<set $eDmg = 3>> <<set $lwDmg = 5>> <<TurnEnd>> <</if>>
<<if _atkroll == 4>> <<set $eDmg = 4>> <<set $lwDmg = 4>> <<TurnEnd>> <</if>>
<<if _atkroll == 5>> <<set $eDmg = 5>> <<set $lwDmg = 4>> <<TurnEnd>> <</if>>
<<if _atkroll == 6>> <<set $eDmg = 6>> <<set $lwDmg = 3>> <<TurnEnd>> <</if>>
<<if _atkroll == 7>> <<set $eDmg = 7>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 8>> <<set $eDmg = 8>> <<set $lwDmg = 1>> <<TurnEnd>> <</if>>
<<if _atkroll == 9>> <<set $eDmg = 9>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 0>> <<set $eDmg = 10>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>

<<case -2 -1>>
<<if _atkroll == 1>> <<set $eDmg = 2>> <<set $lwDmg = 5>> <<TurnEnd>> <</if>>
<<if _atkroll == 2>> <<set $eDmg = 3>> <<set $lwDmg = 5>> <<TurnEnd>> <</if>>
<<if _atkroll == 3>> <<set $eDmg = 4>> <<set $lwDmg = 4>> <<TurnEnd>> <</if>>
<<if _atkroll == 4>> <<set $eDmg = 5>> <<set $lwDmg = 4>> <<TurnEnd>> <</if>>
<<if _atkroll == 5>> <<set $eDmg = 6>> <<set $lwDmg = 3>> <<TurnEnd>> <</if>>
<<if _atkroll == 6>> <<set $eDmg = 7>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 7>> <<set $eDmg = 8>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 8>> <<set $eDmg = 9>> <<set $lwDmg = 1>> <<TurnEnd>> <</if>>
<<if _atkroll == 9>> <<set $eDmg = 10>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 0>> <<set $eDmg = 11>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>

<<case 0>>
<<if _atkroll == 1>> <<set $eDmg = 3>> <<set $lwDmg = 5>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 2>> <<set $eDmg = 4>> <<set $lwDmg = 4>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 3>> <<set $eDmg = 5>> <<set $lwDmg = 4>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 4>> <<set $eDmg = 6>> <<set $lwDmg = 3>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 5>> <<set $eDmg = 7>> <<set $lwDmg = 2>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 6>> <<set $eDmg = 8>> <<set $lwDmg = 2>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 7>> <<set $eDmg = 9>> <<set $lwDmg = 1>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 8>> <<set $eDmg = 10>> <<set $lwDmg = 0>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 9>> <<set $eDmg = 11>> <<set $lwDmg = 0>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 0>> <<set $eDmg = 12>> <<set $lwDmg = 0>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>

<<case 1 2>>
<<if _atkroll == 1>> <<set $eDmg = 4>> <<set $lwDmg = 5>> <br> case 1, 2 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 2>> <<set $eDmg = 5>> <<set $lwDmg = 4>> <br> case 1, 2 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 3>> <<set $eDmg = 6>> <<set $lwDmg = 3>> <br> case 1, 2 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 4>> <<set $eDmg = 7>> <<set $lwDmg = 3>> <br> case 1, 2 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 5>> <<set $eDmg = 8>> <<set $lwDmg = 2>> <br> case 1, 2 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 6>> <<set $eDmg = 9>> <<set $lwDmg = 2>> <br> case 1, 2 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 7>> <<set $eDmg = 10>> <<set $lwDmg = 1>> <br> case 1, 2 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 8>> <<set $eDmg = 11>> <<set $lwDmg = 0>> <br> case 1, 2 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 9>> <<set $eDmg = 12>> <<set $lwDmg = 0>> <br> case 1, 2 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 0>> <<set $eDmg = 14>> <<set $lwDmg = 0>> <br> case 1, 2 roll _atkroll <<TurnEnd>> <</if>>

<<case 3 4>>
<<if _atkroll == 1>> <<set $eDmg = 5>> <<set $lwDmg = 4>> <<TurnEnd>> <</if>>
<<if _atkroll == 2>> <<set $eDmg = 6>> <<set $lwDmg = 3>> <<TurnEnd>> <</if>>
<<if _atkroll == 3>> <<set $eDmg = 7>> <<set $lwDmg = 3>> <<TurnEnd>> <</if>>
<<if _atkroll == 4>> <<set $eDmg = 8>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 5>> <<set $eDmg = 9>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 6>> <<set $eDmg = 10>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 7>> <<set $eDmg = 11>> <<set $lwDmg = 1>> <<TurnEnd>> <</if>>
<<if _atkroll == 8>> <<set $eDmg = 12>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 9>> <<set $eDmg = 14>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 0>> <<set $eDmg = 16>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>

<<case 5 6>>
<<if _atkroll == 1>> <<set $eDmg = 6>> <<set $lwDmg = 4>> <<TurnEnd>> <</if>>
<<if _atkroll == 2>> <<set $eDmg = 7>> <<set $lwDmg = 3>> <<TurnEnd>> <</if>>
<<if _atkroll == 3>> <<set $eDmg = 8>> <<set $lwDmg = 3>> <<TurnEnd>> <</if>>
<<if _atkroll == 4>> <<set $eDmg = 9>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 5>> <<set $eDmg = 10>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 6>> <<set $eDmg = 11>> <<set $lwDmg = 1>> <<TurnEnd>> <</if>>
<<if _atkroll == 7>> <<set $eDmg = 12>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 8>> <<set $eDmg = 14>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 9>> <<set $eDmg = 16>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 0>> <<set $eDmg = 18>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>

<<case 7 8>>
<<if _atkroll == 1>> <<set $eDmg = 7>> <<set $lwDmg = 4>> <<TurnEnd>> <</if>>
<<if _atkroll == 2>> <<set $eDmg = 8>> <<set $lwDmg = 3>> <<TurnEnd>> <</if>>
<<if _atkroll == 3>> <<set $eDmg = 9>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 4>> <<set $eDmg = 10>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 5>> <<set $eDmg = 11>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 6>> <<set $eDmg = 12>> <<set $lwDmg = 1>> <<TurnEnd>> <</if>>
<<if _atkroll == 7>> <<set $eDmg = 14>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 8>> <<set $eDmg = 16>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 9>> <<set $eDmg = 18>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 0>> <<set $eDmg = 100>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>

<<case 9 10>>
<<if _atkroll == 1>> <<set $eDmg = 8>> <<set $lwDmg = 3>> <<TurnEnd>> <</if>>
<<if _atkroll == 2>> <<set $eDmg = 9>> <<set $lwDmg = 3>> <<TurnEnd>> <</if>>
<<if _atkroll == 3>> <<set $eDmg = 10>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 4>> <<set $eDmg = 11>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 5>> <<set $eDmg = 12>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 6>> <<set $eDmg = 14>> <<set $lwDmg = 1>> <<TurnEnd>> <</if>>
<<if _atkroll == 7>> <<set $eDmg = 16>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 8>> <<set $eDmg = 18>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 9>> <<set $eDmg = 100>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 0>> <<set $eDmg = 100>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>

<<case 11 12 13 14>>
<<if _atkroll == 1>> <<set $eDmg = 9>> <<set $lwDmg = 3>> <<TurnEnd>> <</if>>
<<if _atkroll == 2>> <<set $eDmg = 10>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 3>> <<set $eDmg = 11>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 4>> <<set $eDmg = 12>> <<set $lwDmg = 2>> <<TurnEnd>> <</if>>
<<if _atkroll == 5>> <<set $eDmg = 14>> <<set $lwDmg = 1>> <<TurnEnd>> <</if>>
<<if _atkroll == 6>> <<set $eDmg = 16>> <<set $lwDmg = 1>> <<TurnEnd>> <</if>>
<<if _atkroll == 7>> <<set $eDmg = 18>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 8>> <<set $eDmg = 100>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 9>> <<set $eDmg = 100>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>
<<if _atkroll == 0>> <<set $eDmg = 100>> <<set $lwDmg = 0>> <<TurnEnd>> <</if>>

<</switch>>

<</nobr>> <</widget>>


    Yeah, this took a lot more code that I'd like - it's another one I'm having a hard time figuring out how to simplify.
    The first thing I need is to take the difference between the player's and enemy's Combat Skills so I can random-roll the right table. That's easy, I just used a <<switch>>, the new part is the variable that <<switch>> is evaluating- Math.clamp($CSdiff, -100, 100).
    $CSdiff is my variable. One of the errors I kept running into though was that SugarCube was reading it as a string of text and not as a number. That was confusing <<switch>>. The function Math.clamp forces that variable to be treated as a number, and it's arguments are the variable, the minimum value and maximum value (both of which are far higher that I'll need).
    With the correct Combat Skill difference, now I need a random number and to look up that result - I'm going to take out one section, for the $CSdiff == 0 or an even fight...

<<case 0>>
<<if _atkroll == 1>> <<set $eDmg = 3>> <<set $lwDmg = 5>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 2>> <<set $eDmg = 4>> <<set $lwDmg = 4>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 3>> <<set $eDmg = 5>> <<set $lwDmg = 4>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 4>> <<set $eDmg = 6>> <<set $lwDmg = 3>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 5>> <<set $eDmg = 7>> <<set $lwDmg = 2>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 6>> <<set $eDmg = 8>> <<set $lwDmg = 2>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 7>> <<set $eDmg = 9>> <<set $lwDmg = 1>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 8>> <<set $eDmg = 10>> <<set $lwDmg = 0>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 9>> <<set $eDmg = 11>> <<set $lwDmg = 0>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>
<<if _atkroll == 0>> <<set $eDmg = 12>> <<set $lwDmg = 0>> <br> case 0 roll _atkroll <<TurnEnd>> <</if>>

    So <<case 0>> of my <<switch>> means both characters have the same Combat Skill value. I roll up a random number, then I <<if>> two variables for how much damage the enemy takes $eDmg and how much damage our hero “Lone Wolf” takes $lwDmg. Then there is a weird line that prints the case # and random roll #. That is debugging code. After fixing a ton of errors I was getting funky math, like my screenshot above, so I started printing this line out to see where the widget was getting it's numbers from. Which showed me something else about ‘splitting things up’ - when I had 2 or more combats on the same Passage the math went haywire. As soon as I moved them to different Passages, everything worked the right way. This was unexpected. It's also not a problem - I was running a ton of fights on the same Passage as a quick way to test the system, in the actual story there would only be one combat on each Passage. So I don't think this is a big deal, but I'm still not sure exactly why it happens. JavaScript/ SugarCube is a little weird about exactly how and when things load, from what reading I have been able to understand, but since this doesn't seem like it will be a problem outside of testing I'm not worried about it. I also wonder if I should put this code in a Passage instead of a Widget - maybe that would fix the issue? Again, I don't plan on running this down at the moment but I am going to be careful about watching for it to happen again.
    Now, this is only 1 <<case>>, there are 13 total - so this is a really long block of code, and the reason why I'd rather resolve combat some other way. It's a lot of <<switch/ if>>ing that was a pain to type.

    Once I know how much damage everybody takes, then I call the next widget, <<TurnEnd>>...

<<widget "TurnEnd">> <<nobr>>

<<set $lwEnd = $lwEnd - $lwDmg>>
<<set $eEnd = $eEnd - $eDmg>>
<br> Damage: Foe $eDmg Lone Wolf $lwDmg
<br> Endurance: Foe $eEnd Lone Wolf $lwEnd

<<if $lwEnd <= 0>>
<br> You have been killed, your quest ends here !
<<elseif $eEnd <= 0>>
<br> You have defeated your foe!
<br> You have $lwEnd Endurance remaining !
<<else>>
<<linkreplace "Attack">> <<Attack>> <</linkreplace>>
<</if>>

<</nobr>> <</widget>>

    This is pretty straight-forward. First I take both characters' Endurance and subtract the damage they took, then I display that (so that there is something on the screen, my last message was debug, not for the player to see).
    Then I check if the player is dead. If not I check if the enemy is dead. If not I print a link to the <<Attack>> widget for the next round. This section needs some work. If the player is killed I need to send them back to the beginning of the fight somehow so they can try it again, or try to avoid it (I really don't want to make the player start over from the beginning, that seems a bit too harsh (and yeah, I know it's “old school,” I started gaming back in the “old school” - but being historical doesn't automatically make it good)). Also, in some fights the player has the choice to escape - which needs to be a link here after the Attack; and is another thing I don't like about this system, I think it's a little harsh to forbid the player from trying to escape, except sometimes. I'd like it to always be an option. Escape should be easy to add, it's just another attack round, but Lone Wolf can't do any damage, he can only take damage. If he survives the fight ends. So if I copy the <<TurnEnd>> widget and just remove the line that deals damage to the enemy, I pretty much have it done already.
Still, despite missing a few pieces, I do have a working system...

 







    So now I am at a strange place.

    I'm not actually done. I have a lot of pieces of scattered code that all need to be brought together, missing parts need to be added, everything needs to be double-checked, and it all needs to be made pretty and put into an actual story - not the current several dozen un-related testing passages.
    But, this isn't my story, and these aren't my rules. All of this work has been on Joe Dever's fantastic setting and rules, and sadly he passed away - so I can't even ask him for permission to do this (granted, I could try to get in touch with his estate, somehow). That's why I've been limiting the book material to the dry rules and not any of the story or art. So far this has been a mental exercise and learning experience, not actually creating a game.
    So I'm hesitant to go on working with this code. I've put a lot of hours into this so far, and a whole lot of frustration, and I want something that is going to be mine, and more importantly something that I can share with you, gentle reader.

    Which means, despite the fact that Lone Wolf's story is not quite done, it's time for this project to end. I have learned so much, and it has been great to re-visit something I loved from my youth, now it is time to go forward. So my next posting is going to be a new project - what you ask?  Well, I'm not sure yet, still doing some research into a few ideas.  As soon as I decide on the next steps to take, I will let you know.
    Look forward to seeing you then :)


Wednesday, August 23, 2017

Converting Lone Wolf to Twine and SugarCube - Part 8 - Combat Setup


The Lone Wolf Combat System

     This is it, the last of the major game rules I need to implement. Once I finish this I'll pretty much have everything coded to track and do (that is, all my variables and widgets) - I'll just need to clean things up, make them pretty, and put them in with the text. I'm finally getting close to being finished with this project (I can't describe that feeling well enough).

     So here are the book's combat rules...


Rules for Combat


There will be occasions on your adventure when you have to fight an enemy. The enemy’s COMBAT SKILL and ENDURANCE points are given in the text. Lone Wolf’s aim in the combat is to kill the enemy by reducing his ENDURANCE points to zero while losing as few ENDURANCE points as possible himself.
At the start of a combat, enter Lone Wolf’s and the enemy’s ENDURANCE points in the appropriate boxes on the Combat Record section of your Action Chart.
The sequence for combat is as follows.

1. Add any extra points gained through your Kai Disciplines to your current COMBAT SKILL total.
2. Subtract the COMBAT SKILL of your enemy from this total. The result is your Combat Ratio. Enter it on the Action Chart.

Example

Lone Wolf (COMBAT SKILL 15) is ambushed by a Winged Devil (COMBAT SKILL 20). He is not given the opportunity to evade combat, but must stand and fight as the creature swoops down on him. Lone Wolf has the Kai Discipline of Mindblast, so he adds 2 points to his COMBAT SKILL, giving a total COMBAT SKILL of 17.

He subtracts the Winged Devil’s COMBAT SKILL from his own, giving a Combat Ratio of −3 (17 − 20 = −3). −3 is noted on the Action Chart as the Combat Ratio.


3. When you have your Combat Ratio, pick a number from the Random Number Table.
4. Turn to the Combat Results Table. Along the top of the chart are shown the Combat Ratio numbers. Find the number that is the same as your Combat Ratio and cross-reference it with the random number that you have picked (the random numbers appear on the side of the chart). You now have the number of ENDURANCE points lost by both Lone Wolf and his enemy in this round of combat. (E represents points lost by the enemy; LW represents points lost by Lone Wolf.)

Example

The Combat Ratio between Lone Wolf and Winged Devil has been established as -3. If the number taken from the Random Number Table is a 6, then the result of the first round of combat is:

• Lone Wolf loses 3 ENDURANCE points
• Winged Devil loses 6 ENDURANCE points


• On the Action Chart, mark the changes in ENDURANCE points to the participants in the combat.
• Unless otherwise instructed, or unless you have an option to evade, the next round of combat now starts.
• Repeat the sequence from Stage 3.


This process of combat continues until the ENDURANCE points of either the enemy or Lone Wolf are reduced to zero or below, at which point that combatant is declared dead. If Lone Wolf is dead, the adventure is over. If the enemy is dead, Lone Wolf proceeds but with his ENDURANCE points possibly reduced.
A summary of Combat Rules appears in the back of this book.

Evasion of Combat


During your adventure you may be given the chance to evade combat. If you have already engaged in a round of combat and decide to evade, calculate the combat for that round in the usual manner. All points lost by the enemy as a result of that round are ignored, and you make your escape. Only Lone Wolf may lose ENDURANCE points during that round, but then that is the risk of running away! You may only evade if the text of the particular section allows you to do so.



    So, while this is kind of simple, it's also fairly complex. Yeah, that made a lot of sense :) Okay, while this looks simple, it's actually fairly complex. One of the first hurdles is the combat table. Once you find the difference between the Combat Skills of Lone Wolf and the Enemy, then you use that number as a reference on this combat table...


    Along the top you find the CS difference (in this case it is negative, therefore the Enemy is higher than LW). Then you pick a random number from 0 to 9, and cross-reference the box to find out how much Endurance both LW and the Enemy lose. That's one turn, keep picking new random numbers on the same column until somebody's dead.
    So I'm going to need some kind of <<if>> or <<switch>> to get the difference; then within that a <<random>> to get the results.

    Now, there are only 2 stats: Combat Skill and Endurance. Endurance is easy, it only goes down during the fight and doesn't matter at all until it reaches 0. A simple variable.
    Combat Skill is more complicated. First, there is the basic combat skill that we got at the beginning of the character creation. Then, add 2 if the player chose Weaponskill and if the player has the correct weapon. Add 2 more if the player has Mindblast and if the Enemy is not immune to it. Subtract 2 if the Enemy has Mindblast and if the player does not have Mindshield. Add 2 each if the player has a chainmail waistcoat and/ or helmet. Also, if the player has a Strength Potion and if they want to use it, add 2 more. All of which has to be figured out before the fight starts to get that fight's CS - which may be different in the next fight (due to lost/ gained equipment) so needs to be re-calculated each fight.
    I see a lot of <<if>> macros in my future.

    Also, we need to track if you can and want to escape, who's alive at the end of every round (and what to do if the player dies? send them back a Passage? re-start the book?), and according to the rules Health Potions can only be used at the end of a fight, so we'll need to check and offer that as well.

    So far I've got a workable system (I kind of hesitate to call it “good”) for tracking everything the player has - but I'm also going to need to track some things the enemies can have. So how do I organize their stats?


I, Object


    At the beginning I made an Object, my first list of Disciplines. Then I figured out it would be easier to just track disciplines in an Array. But, now I need to track a bunch of different stats for my monsters - and here is where I think an Object will work perfectly. Something like this...

<<set $enemy = {
CS: #,
End: #,
Mindblast: true/false,
Mindshield: true/false,
}>>

    And there's my little generic enemy. Their combat stats, plus if they have mindblast and/ or mindshield (which I'll apply to the player when I check before combat). All I have to do is copy this little block and fill in the specific names and numbers.
    Now, to reference the numbers you use the variable name and a “.” like - $enemy.CS or $enemy.Mindblast - which makes it easy to get to the values. If you pass the enemy as a argument (let's say $args[1]) you do the exact same thing - $args[1].CS or $args[1].Mindblast - so that's easy too.


Let's get ready to ruummmbbbbllllleeeeee........


    Okay, I need to get my stats together and check for all that stuff, so let's make another handy widget that we can call as needed...

<<widget CombatSetup>> <<nobr>> You prepare for combat...

// pass the enemy variable as $args[0]

/* player base CS */
<<set _cbtCS = $CombatSkill>>

/* weaponskill */
<<if $Disciplines.contains ("Weaponskill")>>
<<if $weapons.contains ($WpnProf)>>
<<set _cbtCS += 2>> <br> Having the weapon you're trained in will give you an edge!
<</if>>
<</if>>

/* no weapon */
<<if $weapons[0] == “-empty-”>>
<<if $weapons[1] == “-empty-">>
<<set _cbtCS -= 4>> <br> Not having any weapons will make the fight harder!
<</if>>
<</if>>

/* player Mindblast */
<<if $Disciplines.contains ("Mindblast")>>
<<if $args[0].Mindshield == “false”>>
<<set _cbtCS += 2>> <br> Your Mindblast staggers your foe!
<<elseif $args[0].Mindshield == “true”>>
<br> Your foe is immune to your Mindblast!
<</if>>
<</if>>

/* enemy Mindblast */
<<if $args[0].Mindblast == “true”>>
<<if $Disciplines.contains ("Mindshield")>>
<br> Your foe tries to use Mindblast on you, but you resist with Mindshield !
<<else>>
<<set _cbtCS -= 2>> <br> Your foe's Mindblast makes it difficult to fight!
<</if>>
<</if>>

/* player items (armor, helmet) */
<<if $specialitems.contains ("Chainmail Waistcoat")>>
<<set _cbtCS += 2>> <br> Your Chainmail Waistcoat will help protect you!
<</if>>
<<if $specialitems.contains ("Helmet")>>
<<set _cbtCS += 2>> <br> Your Helmet will help protect you!
<</if>>

/* player does have and wants to use potion */
//check the whole container to offer option
<<if $backpack.contains ("Strength Potion")>>
<br> Do you want to drink one of your Strength Potions to help with the fight? <br>
<span id="usestr">
// yes
<<link “ Yes”>>
//check each slot to determin which to use
<<if $backpack[0] == “Strength Potion”
<<set _cbtCS += 2>> <<set $backpack[0] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<</replace>>
<<elseif $backpack[1] == “Strength Potion”
<<set _cbtCS += 2>> <<set $backpack[1] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<</replace>>
<<elseif $backpack[2] == “Strength Potion”
<<set _cbtCS += 2>> <<set $backpack[2] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<</replace>>
<<elseif $backpack[3] == “Strength Potion”
<<set _cbtCS += 2>> <<set $backpack[3] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<</replace>>
<<elseif $backpack[4] == “Strength Potion”
<<set _cbtCS += 2>> <<set $backpack[4] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<</replace>>
<<elseif $backpack[5] == “Strength Potion”
<<set _cbtCS += 2>> <<set $backpack[5] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<</replace>>
<<elseif $backpack[6] == “Strength Potion”
<<set _cbtCS += 2>> <<set $backpack[6] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<</replace>>
<<elseif $backpack[7] == “Strength Potion”
<<set _cbtCS += 2>> <<set $backpack[7] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<</replace>>
<</if>> <</link>>

//no
<<link “ No”>>
<<replace #usestr>> You decide that you don't need any help defeating this enemy!
<</replace>> <</link>>

</span>
<</if>>

The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The Enemy's Combat Skill is $args[0].CS
<<if _cbtCS > $args[0].CS>> <<set _diff = _cbtCS - $args[0].CS>>
<br> You are stronger than your enemy! (+ _diff)
<<elseif _cbtCS < $args[0].CS>> <<set _diff = $args[0].CS - _cbtCS>>
<br> You are weaker than your enemy! (- _diff)
<<else>>
<br> You are evenly matched with your enemy!
<</if>>
<br> Your Endurance is $Endurance
<br> The Enemy's Endurance is $args[0].End

<</nobr>> <</widget>>

    And all that beautiful code above? Doesn't work.



     When I ran it I got all sorts of “unexpected token” errors in my <<if>> statements. What does that mean? Darned if I know. The error documentation is, well, as far as I can tell, non-existent. It seemed like at least some of the problem was from accessing the $enemy object. So I decided to break my code into two parts, the first part would just be trying to read everything from the player side. After some fiddling I came up with this...

<<nobr>>
<<set $CombatSkill to 10>>
<<set $Endurance to 20>>

<<set $Disciplines to []>>
<<set $WpnProf = "none">>

<<set $gold = 0>>
<<set $weapons = [ "-empty-", "-empty-" ]>>
<<set $backpack = [ "-empty-", "-empty-", "-empty-", "-empty-", "-empty-", "-empty-", "-empty-", "-empty-" ]>>
<<set $specialitems = []>>


<<widget CombatSetup>> <<nobr>> You prepare for combat...

<<set _cbtCS = $CombatSkill>>

<<if $Disciplines.includes ("Weaponskill")>>
<<if $weapons.includes ($WpnProf)>>
<<set _cbtCS += 2>> <br> Having the weapon you're trained in will give you an edge!
<</if>>
<</if>>

<<if $weapons.includes ("-empty-", 1)>>
<<if $weapons.includes ("-empty-", 0)>>
<<set _cbtCS -= 4>> <br> Not having any weapons will make the fight harder!
<</if>>
<</if>>

<<if $Disciplines.includes ("Mindblast")>>
<<set _cbtCS += 2>> <br> Your Mindblast staggers your foe!
<</if>>

<<if $Disciplines.includes ("Mindshield")>>
<br> Your foe tries to use Mindblast on you, but you resist with Mindshield !
<</if>>

<<if $specialitems.contains ("Chainmail Waistcoat")>>
<<set _cbtCS += 2>> <br> Your Chainmail Waistcoat will help protect you!
<</if>>

<<if $specialitems.contains ("Helmet")>>
<<set _cbtCS += 2>> <br> Your Helmet will help protect you!
<</if>>

<<if $backpack.contains ("Strength Potion")>>
<br> Do you want to drink one of your Strength Potions to help with the fight? <br>
<<print $backpack>>
<span id="usestr">
<<link "Yes">>
<<if $backpack.includes ("Strength Potion", 7)>>
<<set _cbtCS += 2>> <<set $backpack[7] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<<print $backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> Your Endurance is $Endurance
<</replace>>
<<elseif $backpack.includes ("Strength Potion", 6)>>
<<set _cbtCS += 2>> <<set $backpack[6] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<<print $backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> Your Endurance is $Endurance
<</replace>>
<<elseif $backpack.includes ("Strength Potion", 5)>>
<<set _cbtCS += 2>> <<set $backpack[5] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<<print $backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> Your Endurance is $Endurance
<</replace>>
<<elseif $backpack.includes ("Strength Potion", 4)>>
<<set _cbtCS += 2>> <<set $backpack[4] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<<print $backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> Your Endurance is $Endurance
<</replace>>
<<elseif $backpack.includes ("Strength Potion", 3)>>
<<set _cbtCS += 2>> <<set $backpack[3] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<<print $backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> Your Endurance is $Endurance
<</replace>>
<<elseif $backpack.includes ("Strength Potion", 2)>>
<<set _cbtCS += 2>> <<set $backpack[2] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<<print $backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> Your Endurance is $Endurance
<</replace>>
<<elseif $backpack.includes ("Strength Potion", 1)>>
<<set _cbtCS += 2>> <<set $backpack[1] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<<print $backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> Your Endurance is $Endurance
<</replace>>
<<elseif $backpack.includes ("Strength Potion", 0)>>
<<set _cbtCS += 2>> <<set $backpack[0] = "-empty-">>
<<replace #usestr>> You feel the potion energize you!
<<print $backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> Your Endurance is $Endurance
<</replace>>
<</if>> <</link>>

<<link "No">>
<<replace #usestr>> You decide that you don't need any help defeating this enemy!
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> Your Endurance is $Endurance
<</replace>> <</link>>

</span>
<<else>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> Your Endurance is $Endurance
<</if>>

<</nobr>> <</widget>>
<</nobr>>

    Instead of splitting the code over two Passages, I dumped it all into one, and then started testing things one scenario at a time, here are all the tests I built:

Let's start testing...

No weapons
<<CombatSetup>>

Weapons, no skill
<<set $weapons = [ "Sword", "Axe" ]>>
<<CombatSetup>>

Skill, not for weapons carried
<<set $Disciplines to ["Weaponskill"]>> <<set $WpnProf = "Dagger">>
<<CombatSetup>>

Skill and weapon in slot 1
<<set $WpnProf = "Axe">>
<<CombatSetup>>

Skill and weapon in slot 0
<<set $WpnProf = "Sword">>
<<CombatSetup>>

Skilled, equipped, Mindblast
<<set $Disciplines to ["Weaponskill", "Mindblast"]>>
<<CombatSetup>>

Skilled, equipped, Mindblast, Mindshield
<<set $Disciplines to ["Weaponskill", "Mindblast", "Mindshield"]>>
<<CombatSetup>>

Skilled, equipped, Mindblast, Mindshield, Armor
<<set $specialitems = ["Chainmail Waistcoat"]>>
<<CombatSetup>>

Skilled, equipped, Mindblast, Mindshield, Armor, Helmet
<<set $specialitems = ["Chainmail Waistcoat", "Helmet"]>>
<<CombatSetup>>

Skilled, equipped, Mindblast, Mindshield, Armor, Helmet, Potion in slot 2
<<set $backpack = [ "-empty-", "Strength Potion", "-empty-", "-empty-", "-empty-", "-empty-", "-empty-", "-empty-" ]>>
<<CombatSetup>>







    Finally, I got all of this to work right. So I know how to access the player, no errors. Now I just needed to be able to get the enemy information...

<<set $enemy1 = {
CS: 10,
End: 10,
Mindblast: "true",
Mindshield: "true",
}>>

<<set $enemy2 = {
CS: 15,
End: 15,
Mindblast: "false",
Mindshield: "true",
}>>

<<set $enemy3 = {
CS: 10,
End: 20,
Mindblast: "false",
Mindshield: "false",
}>>

<<widget "EnemyStats">> <<nobr>>

<br> The enemy's Combat Skill is $args[0].CS

<br> The enemy's Endurance is $args[0].End

<<if $args[0].Mindblast == "true">> <br> The enemy has Mindblast
<<elseif $args[0].Mindblast == "false">> <br> The enemy does not have Mindblast
<</if>>

<<if $args[0].Mindshield == "true">> <br> The enemy has Mindshield
<<elseif $args[0].Mindshield == "false">> <br> The enemy does not have Mindshield
<</if>>

<</nobr>> <</widget>>




    And that worked just fine for the enemies. But I got to thinking, instead of making separate variables for the enemy's Mindshield/ Mindblast - could I just give the enemy Object an Array like I do for the player? So I decided to try it...

<<set $enemy4 = {
CS: 44,
End: 44,
Disciplines: [],
}>>

<<set $enemy5 = {
CS: 55,
End: 55,
Disciplines: ["Mindblast"],
}>>

<<set $enemy6 = {
CS: 66,
End: 66,
Disciplines: ["Mindblast", "Mindshield"],
}>>


<<widget "EnemyStatsArray">> <<nobr>>

<br> The enemy's Combat Skill is $args[0].CS

<br> The enemy's Endurance is $args[0].End

<<if $args[0].Disciplines.includes ("Mindblast")>> <br> The enemy has Mindblast
<<else>> <br> The enemy does not have Mindblast
<</if>>

<<if $args[0].Disciplines.includes ("Mindshield")>> <br> The enemy has Mindshield
<<else>> <br> The enemy does not have Mindshield
<</if>>

<</nobr>> <</widget>>

    And hot diggidy dog it actually worked! That's a much nicer way to handle it, and it lets my monsters have the same options as the players, which is not a big deal for this Lone Wolf project but will be great when I spin off this code into my own project. While I was adding the disciplines I also realized that I could add just about anything, including the name and description of the enemy to make things more customized (instead of “enemy”/ "foe" everything).

<<set $enemy7 = {
CS: 66,
End: 66,
Disciplines: ["Mindblast", "Mindshield"],
Name: "Giak",
Description: "A short, foul, twisted servent of the Darklords who delights in murder and mayhem.",
}>>


<<widget "EnemyStatsFull">> <<nobr>>

<br> Your enemy is a $args[0].Name

<br> $args[0].Description

<br> The $args[0].Name's Combat Skill is $args[0].CS

<br> The $args[0].Name's Endurance is $args[0].End

<<if $args[0].Disciplines.includes ("Mindblast")>> <br> The $args[0].Name has Mindblast
<<else>> <br> The $args[0].Name does not have Mindblast
<</if>>

<<if $args[0].Disciplines.includes ("Mindshield")>> <br> The $args[0].Name has Mindshield
<<else>> <br> The $args[0].Name does not have Mindshield
<</if>>

<</nobr>> <</widget>>


    I've got two separate pieces for the player and enemy stats, so now for the combined widget, the one that finally gets all the right info to setup the fight...
 
/*
*   <<CombatSetup $enemy $player>>
*/
<<widget CombatSetup>> <<nobr>>
<br> You prepare for combat...
<br> Your enemy is a $args[0].Name
<br> $args[0].Description

<<set _cbtCS = $args[1].CombatSkill>>

<<if $args[1].Disciplines.includes ("Weaponskill")>>
<<if $args[1].weapons.includes ($args[1].WpnProf)>>
<<set _cbtCS += 2>> <br> Having the weapon you're trained in will give you an edge!
<</if>>
<</if>>

<<if $args[1].weapons.includes ("-empty-", 1)>>
<<if $args[1].weapons.includes ("-empty-", 0)>>
<<set _cbtCS -= 4>> <br> Not having any weapons will make the fight harder!
<</if>>
<</if>>

<<if $args[1].Disciplines.includes ("Mindblast")>>
<<if $args[0].Disciplines.includes ("Mindshield")>> <br> The $args[0].Name resists with Mindshield !
<<else>>
<<set _cbtCS += 2>> <br> Your Mindblast staggers your foe!
<</if>>
<</if>>

<<if $args[0].Disciplines.includes ("Mindblast")>>
<br> The $args[0].Name has Mindblast
<<if $args[1].Disciplines.includes ("Mindshield")>>
, but you resist with Mindshield !
<<else>>  <<set _cbtCS -= 2>>
<br> You struggle to fight through the mental assault!
<</if>>
<</if>>

<<if $args[1].specialitems.contains ("Chainmail Waistcoat")>>
<<set _cbtCS += 2>> <br> Your Chainmail Waistcoat will help protect you!
<</if>>

<<if $args[1].specialitems.contains ("Helmet")>>
<<set _cbtCS += 2>> <br> Your Helmet will help protect you!
<</if>>

<<if $args[1].backpack.contains ("Strength Potion")>>
<br> Do you want to drink one of your Strength Potions to help with the fight? <br>
<<print $args[1].backpack>> <br>
<span id="usestr">
<<link "Yes">>
<<if $args[1].backpack.includes ("Strength Potion", 7)>>
  <<set _cbtCS += 2>> <<set $args[1].backpack[7] = "-empty-">>
  <<replace #usestr>>
<br> You feel the potion energize you!
<br> <<print $args[1].backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The $args[0].Name's Combat Skill is $args[0].CS
<br> Your Endurance is $args[1].Endurance
<br> The $args[0].Name's Endurance is $args[0].End
  <</replace>>
<<elseif $args[1].backpack.includes ("Strength Potion", 6)>>
  <<set _cbtCS += 2>> <<set $args[1].backpack[6] = "-empty-">>
  <<replace #usestr>>
<br> You feel the potion energize you!
<br> <<print $args[1].backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The $args[0].Name's Combat Skill is $args[0].CS
<br> Your Endurance is $args[1].Endurance
<br> The $args[0].Name's Endurance is $args[0].End
  <</replace>>
<<elseif $args[1].backpack.includes ("Strength Potion", 5)>>
  <<set _cbtCS += 2>> <<set $args[1].backpack[5] = "-empty-">>
  <<replace #usestr>>
<br> You feel the potion energize you!
<br> <<print $args[1].backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The $args[0].Name's Combat Skill is $args[0].CS
<br> Your Endurance is $args[1].Endurance
<br> The $args[0].Name's Endurance is $args[0].End
  <</replace>>
<<elseif $args[1].backpack.includes ("Strength Potion", 4)>>
  <<set _cbtCS += 2>> <<set $args[1].backpack[4] = "-empty-">>
  <<replace #usestr>>
<br> You feel the potion energize you!
<br> <<print $args[1].backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The $args[0].Name's Combat Skill is $args[0].CS
<br> Your Endurance is $args[1].Endurance
<br> The $args[0].Name's Endurance is $args[0].End
  <</replace>>
<<elseif $args[1].backpack.includes ("Strength Potion", 3)>>
  <<set _cbtCS += 2>> <<set $args[1].backpack[3] = "-empty-">>
  <<replace #usestr>>
<br> You feel the potion energize you!
<br> <<print $args[1].backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The $args[0].Name's Combat Skill is $args[0].CS
<br> Your Endurance is $args[1].Endurance
<br> The $args[0].Name's Endurance is $args[0].End
  <</replace>>
<<elseif $args[1].backpack.includes ("Strength Potion", 2)>>
  <<set _cbtCS += 2>> <<set $args[1].backpack[2] = "-empty-">>
  <<replace #usestr>>
<br> You feel the potion energize you!
<br> <<print $args[1].backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The $args[0].Name's Combat Skill is $args[0].CS
<br> Your Endurance is $args[1].Endurance
<br> The $args[0].Name's Endurance is $args[0].End
  <</replace>>
<<elseif $args[1].backpack.includes ("Strength Potion", 1)>>
  <<set _cbtCS += 2>> <<set $args[1].backpack[1] = "-empty-">>
  <<replace #usestr>>
<br> You feel the potion energize you!
<br> <<print $args[1].backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The $args[0].Name's Combat Skill is $args[0].CS
<br> Your Endurance is $args[1].Endurance
<br> The $args[0].Name's Endurance is $args[0].End
  <</replace>>
<<elseif $args[1].backpack.includes ("Strength Potion", 0)>>
  <<set _cbtCS += 2>> <<set $args[1].backpack[0] = "-empty-">>
  <<replace #usestr>>
<br> You feel the potion energize you!
<br> <<print $args[1].backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The $args[0].Name's Combat Skill is $args[0].CS
<br> Your Endurance is $args[1].Endurance
<br> The $args[0].Name's Endurance is $args[0].End
  <</replace>>
 <</if>> <</link>>

<<link "No">>
<<replace #usestr>>
<br> You decide that you don't need any help defeating this enemy!
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The $args[0].Name's Combat Skill is $args[0].CS
<br> Your Endurance is $args[1].Endurance
<br> The $args[0].Name's Endurance is $args[0].End
<</replace>> <</link>>

</span>
<<else>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The $args[0].Name's Combat Skill is $args[0].CS
<br> Your Endurance is $args[1].Endurance
<br> The $args[0].Name's Endurance is $args[0].End
<</if>>

<</nobr>> <</widget>>


    Here are what the player and enemy Objects look like...

<<set $player8 = {
CombatSkill: 10,
Endurance: 20,
Disciplines: ["Weaponskill", "Mindblast", "Mindshield"],
WpnProf: "Sword",
gold: 0,
weapons: [ "Sword", "Axe" ],
backpack: [ "-empty-", "Strength Potion", "-empty-", "Strength Potion", "-empty-", "-empty-", "-empty-", "-empty-" ],
specialitems: ["Helmet", "Chainmail Waistcoat"],
}>>

<<set $enemy3 = {
CS: 10,
End: 20,
Disciplines: ["Mindshield", "Mindblast"],
Name: "Giak",
Description: "A short, foul, twisted servent of the Darklords who delights in murder and mayhem.",
}>>



Let me go over some of that code to talk about what it does...

/*
* <<CombatSetup $enemy $player>>
*/
<<widget CombatSetup>> <<nobr>>
<br> You prepare for combat...
<br> Your enemy is a $args[0].Name
<br> $args[0].Description

<<set _cbtCS = $args[1].CombatSkill>>

    Above I introduced the enemy and set a local variable that will be the final Combat Skill score. I made it local, expecting to add to this widget so it would handle all of the combat system - but in hindsight I should have made it global since I'm actually going to end up using several widgets together for the combat system.

<<if $args[1].Disciplines.includes ("Weaponskill")>>
<<if $args[1].weapons.includes ($args[1].WpnProf)>>
<<set _cbtCS += 2>> <br> Having the weapon you're trained in will give you an edge!
<</if>>
<</if>>

    An easy check, does the player's Array of disciplines have the “Weaponskill” ability, and if so does their weapons Array have the weapon they are skilled with - it was a much better idea to use a separate variable to hold the skilled weapon; if you remember that far back I originally made 9 different ‘weaponskill’ disciplines, which was clunky. I am still mixing cases a lot (some properties are upper-case and others lower) which I really need to smooth out. It's confusing enough writing this stuff, I have to pick a rule for upper or lower and stick with it (having a list of the variables to copy-and-paste helps though).
    Using the Objects is great, the ObjectName.ArrayName.includes ("thing") syntax is easy to remember, works great, and you can include the index number if you want a specific slot/ spot or leave it out to search the whole Array. It also ties each inventory to each character (pc or npc) so I can easily make and track multiple characters (in a future project, always looking ahead). I can also build monsters just like players, giving them a base skill and adding gear or disciplines, to make a varity of challenges. This was one of the better ideas I've had on this project. (which I am proud of until I think about how few of my ideas so far have been good :)

<<if $args[1].weapons.includes ("-empty-", 1)>>
<<if $args[1].weapons.includes ("-empty-", 0)>>
<<set _cbtCS -= 4>> <br> Not having any weapons will make the fight harder!
<</if>>
<</if>>

    If you don't have any weapons at all (and here is where I like that I used “-empty-” to initialize the inventory slots - it's easy to check for that phrase and it makes the code easy for me to read) then you have a penalty to your combat skill score.

<<if $args[1].Disciplines.includes ("Mindblast")>>
<<if $args[0].Disciplines.includes ("Mindshield")>> <br> The $args[0].Name resists with Mindshield !
<<else>>
<<set _cbtCS += 2>> <br> Your Mindblast staggers your foe!
<</if>>
<</if>>

    Do you, $args[1] since the player is the second variable passed, have mindblast and if so does the enemy $args[0] have mindshield?

<<if $args[0].Disciplines.includes ("Mindblast")>>
<br> The $args[0].Name has Mindblast
<<if $args[1].Disciplines.includes ("Mindshield")>>
, but you resist with Mindshield !
<<else>> <<set _cbtCS -= 2>>
<br> You struggle to fight through the mental assault!
<</if>>
<</if>>

    And the same question of mindblast/shield but in reverse.

<<if $args[1].specialitems.contains ("Chainmail Waistcoat")>>
<<set _cbtCS += 2>> <br> Your Chainmail Waistcoat will help protect you!
<</if>>

<<if $args[1].specialitems.contains ("Helmet")>>
<<set _cbtCS += 2>> <br> Your Helmet will help protect you!
<</if>>

    Then there are the two armor pieces, which go in the ‘special items’ Array.

<<if $args[1].backpack.contains ("Strength Potion")>>
<br> Do you want to drink one of your Strength Potions to help with the fight? <br>
<<print $args[1].backpack>> <br>
<span id="usestr">

    Now, the Strength Potion is a little tricky. First I just check to see if you have one. The <<print>> was for debugging, I wanted to be able to see the player's inventory before and after to make sure the potion was being seen and removed correctly.
    The following code I'm abbreviating, it's very repetative. First there's the link if you want to use your potion (you don't have to after all). Then for each backpack slot I have an <<if>> that looks for a potion. It starts looking at backpack[7] - the last slot - first, counting down, because it acted funny when I tried counting up from 0. Once the potion is applied the <<replace>> overwrites the link to use the potion (I'm trying not to write any more infinite potion glitches). Then, there is the final summary of the stats involved, which ends what this widget was designed to do (the actual fight will be next). It is really repetative since I had to duplicate a lot of code for each of the 8 backpack slots - I'm sure there's a better way to do this but I haven't figured it out yet (will look for one though, I'm sick of writing massive walls of code).

<<link "Yes">>
<<if $args[1].backpack.includes ("Strength Potion", 7)>>
<<set _cbtCS += 2>> <<set $args[1].backpack[7] = "-empty-">>
<<replace #usestr>>
<br> You feel the potion energize you!
<br> <<print $args[1].backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The $args[0].Name's Combat Skill is $args[0].CS
<br> Your Endurance is $args[1].Endurance
<br> The $args[0].Name's Endurance is $args[0].End
<</replace>>

<<elseif $args[1].backpack.includes ("Strength Potion", 6)>>
<<set _cbtCS += 2>> <<set $args[1].backpack[6] = "-empty-">>
<<replace #usestr>>
<br> You feel the potion energize you!
<br> <<print $args[1].backpack>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The $args[0].Name's Combat Skill is $args[0].CS
<br> Your Endurance is $args[1].Endurance
<br> The $args[0].Name's Endurance is $args[0].End
<</replace>>
<<elseif $args[1].backpack.includes ("Strength Potion", 5)>>
<<set _cbtCS += 2>> <<set $args[1].backpack[5] = "-empty-">>
<<replace #usestr>> .... ..... ....

    Finally, if you didn't want to use a potion or if you didn't have one, the widget prints out your final combat stats.

<<link "No">>
<<replace #usestr>>
<br> You decide that you don't need any help defeating this enemy!
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The $args[0].Name's Combat Skill is $args[0].CS
<br> Your Endurance is $args[1].Endurance
<br> The $args[0].Name's Endurance is $args[0].End
<</replace>> <</link>>

</span>
<<else>>
<br> The battle begins!
<br> Your Combat Skill for this fight is _cbtCS
<br> The $args[0].Name's Combat Skill is $args[0].CS
<br> Your Endurance is $args[1].Endurance
<br> The $args[0].Name's Endurance is $args[0].End
<</if>>

<</nobr>> <</widget>>


    Now that we can set up the fight - next we actually need each round of combat...