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


Monday, August 21, 2017

Some Quick News

    No deep "Monday Musing" today - I've finished the Twine/ Lone Wolf project (it's got 3 more posts, should wrap up next week) and now m working on the follow-up project (that will greatly expand on the work I started) and still thinking about the Combat Cpabilities Map, and doing some thinking about Magic (esp. the "laws of magic" and how to game by laws instead of spells), and chasing down lots of little pop-up thoughts.  So a lot that I'm working on, but not a lot going to be posted this week.

Converting Lone Wolf to Twine and SugarCube - Part 7 - Finishing Equipment


Finally, An Inventory System That Actually Works

     Okay, more time as passed (you should be glad you can read this all at once instead of watching me working on it :) and I think I finally have it, one widget that will handle all my inventory needs - and cut down the hundreds of lines of code to just a few dozen!
Here it is...

:: Initialize [Widget]

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

<<widget "ManageInventory">> <<nobr>>
/*
*    Usage: this widget handles the display, removal and addition of all inventory items.
// WARNING : This widget requires several arguments to be passed.
*    <<ManageInventory  action container item quantity random_min random_max>>
* $args[0] - action: 1 - display, 2 - add item(s), 3 - remove item(s)
* $args[1] - container: 1 - Gold, 2 - Weapons, 3 - Backpack Items, 4 - Special Items, 5 - [if args[0] = 1 (display)] displays all items
* $args[2] - item:
* if adding items- the item name (no quotes needed unless the name has a space; ie, Sword and "Short Sword")
* if removing items- weapon (for a random weapon), backpack (for a random backpack item), or the item name (no quotes) if
a specific item should be searched for and removed
* $args[3] - quantity:
* # - the number of items to change
* random  - for a random number (for items this is only 1 at the moment)
* all - to remove all backpack items
* $args[4] - if a random number, the minimum
* $args[5] - if a random number, the maximum
*/
  <<switch $args[0]>>

    <<case 1>>

<<switch $args[1]>>
<<case 1>> <br> Belt Pouch: $gold Gold Crowns
<<case 2>> <br> Weapons: <<for _wi = 0; _wi < $weapons.length; _wi++>> <<print "<br>&nbsp;&nbsp;" + $weapons[_wi]>> <</for>>
<<case 3>> <br> Backpack Items: <<for _bi = 0; _bi < $backpack.length; _bi++>> <<print "<br>&nbsp;&nbsp;" + $backpack[_bi]>> <</for>>
<<case 4>> <br> Special Items: <<for _si = 0; _si < $specialitems.length; _si++>> <<print "<br>&nbsp;&nbsp;" + $specialitems[_si]>> <</for>>
<<case 5>>
<br> Belt Pouch: $gold Gold Crowns
<br> Weapons: <<for _wi = 0; _wi < $weapons.length; _wi++>> <<print "<br>&nbsp;&nbsp;" + $weapons[_wi]>> <</for>>
<br> Backpack Items: <<for _bi = 0; _bi < $backpack.length; _bi++>> <<print "<br>&nbsp;&nbsp;" + $backpack[_bi]>> <</for>>
<br> Special Items: <<for _si = 0; _si < $specialitems.length; _si++>> <<print "<br>&nbsp;&nbsp;" + $specialitems[_si]>> <</for>>
<</switch>>

<<case 2>>

  <<switch $args[1]>>

    <<case 1>>
  <<if $args[3] != "random">>You pick up $args[3] Gold Crowns, <<set $gold += $args[3]>> <<if $gold > 50>> <<set $gold = 50>> but your Belt Pouch only holds 50 Gold Crowns, <</if>> you now have $gold Gold Crowns.
  <<elseif $args[3] == "random">> <<set _goldfound = random ( $args[4], $args[5] )>> You found _goldfound Gold Crowns, <<set $gold += _goldfound>> <<if $gold > 50>> <<set $gold = 50>> but your Belt Pouch only holds 50 Gold Crowns, <</if>> you now have $gold Gold Crowns.
  <<else>> Error: Gold amount has not been defined !
  <</if>>

<<case 2>>
<span class="wpnreplace">What weapon slot do you want to replace?
<<link " <br> Replace the $weapons[0] with $args[2]">> <<set $weapons[0] = $args[2]>> <<replace .wpnreplace>> You now have a $weapons[0] and a $weapons[1] <</replace>> <</link>>
<<link " <br> Replace the $weapons[1] with $args[2]">> <<set $weapons[1] = $args[2]>> <<replace .wpnreplace>> You now have a $weapons[0] and a $weapons[1] <</replace>> <</link>>
<<link " <br> Do not pick up the items">> <<replace .wpnreplace>> You leave your items unchanged <</replace>> <</link>>
</span>

<<case 3>>
<span class="backreplace">What backpack item do you want to replace?
<<link " <br> Replace the $backpack[0] with $args[2]">> <<set $backpack[0] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Replace the $backpack[1] with $args[2]">> <<set $backpack[1] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Replace the $backpack[2] with $args[2]">> <<set $backpack[2] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Replace the $backpack[3] with $args[2]">> <<set $backpack[3] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Replace the $backpack[4] with $args[2]">> <<set $backpack[4] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Replace the $backpack[5] with $args[2]">> <<set $backpack[5] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Replace the $backpack[6] with $args[2]">> <<set $backpack[6] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Replace the $backpack[7] with $args[2]">> <<set $backpack[7] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Do not pick up the items">> <<replace .backreplace>> You leave your items unchanged <</replace>> <</link>>
</span>

<<case 4>>
<<if $specialitems.contains ($args[2])>> You already have a $args[2]!
<<else>>
<<run $specialitems.push ($args[2])>> You now have a $args[2].
<</if>>

  <</switch>>

<<case 3>>

  <<switch $args[1]>>

    <<case 1>>
<<if $args[3] != "random">> You have lost $args[3] Gold Crowns, <<set $gold = $gold - $args[3]>> <<if $gold < 0>> <<set $gold = 0>> <</if>> you now have $gold Gold Crowns.
<<elseif $args[3] == "random">> <<set _goldlost = random ( $args[4], $args[5] )>> You have lost _goldlost Gold Crowns, <<set $gold = $gold - _goldlost>> <<if $gold < 0>> <<set $gold = 0>> <</if>> you now have $gold Gold Crowns.
<<else>> Error: Gold amount has not been defined !
<</if>>

<<case 2>>
<<if $args[3] == "random">>
<<set _randomslot = random (1, 2)>>
<<if _randomslot == 1>> <<set $weapons[0] = "-empty-">>You lose your first weapon!
<<elseif _randomslot == 2>> <<set $weapons[1] = "-empty-">>You lose your second weapon!
<</if>>
<<elseif $args[3] == 2>>
<<set $weapons = [ "-empty-", "-empty-" ]>> You have lost all your weapons!
<</if>>

<<case 3>>
<<if $args[3] == "random">>
<<set _randomslot = random (0, 7)>> You lost a backpack item!
<<if _randomslot == 0>> <<set $backpack[0] = "-empty-">>
<<elseif _randomslot == 1>> <<set $backpack[1] = "-empty-">>
<<elseif _randomslot == 2>> <<set $backpack[2] = "-empty-">>
<<elseif _randomslot == 3>> <<set $backpack[3] = "-empty-">>
<<elseif _randomslot == 4>> <<set $backpack[4] = "-empty-">>
<<elseif _randomslot == 5>> <<set $backpack[5] = "-empty-">>
<<elseif _randomslot == 6>> <<set $backpack[6] = "-empty-">>
<<elseif _randomslot == 7>> <<set $backpack[7] = "-empty-">>
<</if>>
<</if>>
<<if $args[3] == "all">>
<<set $backpack = [ "-empty-", "-empty-", "-empty-", "-empty-", "-empty-", "-empty-", "-empty-", "-empty-" ]>> You have lost all backpack items!
<</if>>

<<case 4>>  // remove a special item - not actually used in LW !!

<<else>> Error: Argument for items to remove was not properly defined !

  <</switch>>

    <<default>>
      Error: No state was passed as the first argument to this widget!

  <</switch>>

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


Let me walk through it with you...

<<widget "ManageInventory">> <<nobr>>
/*
* Usage: this widget handles the display, removal and addition of all inventory items.
// WARNING : This widget requires several arguments to be passed.
* <<ManageInventory action container item quantity random_min random_max>>
* $args[0] - action: 1 - display, 2 - add item(s), 3 - remove item(s)
* $args[1] - container: 1 - Gold, 2 - Weapons, 3 - Backpack Items, 4 - Special Items, 5 - [if args[0] = 1 (display)] displays all items
* $args[2] - item:
* if adding items- the item name (no quotes needed unless the name has a space; ie, Sword and "Short Sword")
* if removing items- weapon (for a random weapon), backpack (for a random backpack item), or the item name (no quotes) if
a specific item should be searched for and removed
* $args[3] - quantity:
* # - the number of items to change
* random - for a random number (for items this is only 1 at the moment)
* all - to remove all backpack items
* $args[4] - if a random number, the minimum
* $args[5] - if a random number, the maximum
*/


     I'm calling this widget <<ManageInventory>>, and putting the <<nobr>> to keep my output pretty. This is in my first Passage, with the “Widget” tag to prevent any problems (per the SugarCube documentation). At the start of it I've got some HTML comments to make notes to myself (and anyone else) about how to use this widget. Taz says documentation is good - and this widget is kind of complicated, it's going to take up to 6 arguments.
     First argument will be what to do: display the inventory, add to it or remove from it. There could be another kind, to “use an item” but I'm going to put that in another section [the Lone Wolf items can only be used at certain times].
     After what we're doing, the next argument is what container/ type of items are we working on: gold, weapons, backpack items, or special items.
     Argument after that is the specific item, or a generic placeholder (the order matters, if you skip this the index of arguments moves - which would be bad).
     Final arguments are the specific number of items to work on, or “random” and then the following arguments as the minimum and maximum number.
     Takes a bit of getting used to, but isn't too bad. Not everything in this list is done right now, my Lone Wolf book only does limited inventory management - I will fill in the blanks after I get done with this conversion and move on to making my own story.

So let's look at the variables we'll be working with:

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


     I'm defining arrays for each container (except gold, since it's just a number). I've put that “-empty-” in each available slot so that when I print the contents each empty slot will show up as something. I could have just used “--” or “_” for something shorter (and I may change it to that later). It just needs to print something the player can see and not be totally empty. Except for the Special Items, since they do not get checked or moved like the weapons and backpack items (they mostly just sit quietly in the background).

     Okay, so what happens when we want to display the inventory?

<<switch $args[0]>>

<<case 1>>

<<switch $args[1]>>

<<case 1>> <br> Belt Pouch: $gold Gold Crowns

<<case 2>> <br> Weapons: <<for _wi = 0; _wi < $weapons.length; _wi++>> <<print "<br>&nbsp;&nbsp;" + $weapons[_wi]>> <</for>>

<<case 3>> <br> Backpack Items: <<for _bi = 0; _bi < $backpack.length; _bi++>> <<print "<br>&nbsp;&nbsp;" + $backpack[_bi]>> <</for>>

<<case 4>> <br> Special Items: <<for _si = 0; _si < $specialitems.length; _si++>> <<print "<br>&nbsp;&nbsp;" + $specialitems[_si]>> <</for>>

<<case 5>>
<br> Belt Pouch: $gold Gold Crowns
<br> Weapons: <<for _wi = 0; _wi < $weapons.length; _wi++>> <<print "<br>&nbsp;&nbsp;" + $weapons[_wi]>> <</for>>
<br> Backpack Items: <<for _bi = 0; _bi < $backpack.length; _bi++>> <<print "<br>&nbsp;&nbsp;" + $backpack[_bi]>> <</for>>
<br> Special Items: <<for _si = 0; _si < $specialitems.length; _si++>> <<print "<br>&nbsp;&nbsp;" + $specialitems[_si]>> <</for>>
<</switch>>


     Our first <<switch>> looks at $args[0], the first argument that says what we're doing. A “1” means we're displaying the inventory (not actually changing it). The next <<switch>> looks at $args[1], the second argument of what containers to display. 1 is Gold, 2 is Weapons, 3 the Backpack, 4 Special Items and 5 the whole inventory. For each container we just use a <<for>> loop to read each slot and print it's contents (with the default -empty- if it's empty). Easy.


Adding Items To Inventory

     Looking at the stuff you have is bound to make you want more. So how do you pick up stuff?

<<case 2>>

<<switch $args[1]>>

<<case 1>>

<<if $args[3] != "random">>You pick up $args[3] Gold Crowns,
<<set $gold += $args[3]>>
<<if $gold > 50>> <<set $gold = 50>> but your Belt Pouch only holds 50 Gold Crowns,
<</if>> you now have $gold Gold Crowns.

<<elseif $args[3] == "random">>
<<set _goldfound = random ( $args[4], $args[5] )>> You found _goldfound Gold Crowns,
<<set $gold += _goldfound>>
<<if $gold > 50>> <<set $gold = 50>> but your Belt Pouch only holds 50 Gold Crowns,
<</if>> you now have $gold Gold Crowns.

<<else>> Error: Gold amount has not been defined !

<</if>>


     Back to the first <<switch>>, here if $args[0] == 2 then we are adding an item to the inventory. A second <<switch>> and $args[1] == 1 says that we are adding to the gold. Then we check $args[3], the argument for the quantity, and if it is a number we add it, if it is the word “random” we read $arg[4] and $arg[5] for the minimum and maximum to randomly pick from.
     Now, in here we're not looking at $arg[2], the specific item, since there is only one item, gold. That argument should still be “gold” or some word, just to keep the arguments index in the right order. Also, there is an <<else>> that will print an error. It isn't a very useful error right now (if you think about the conditions when it would trigger), but it's good to anticipate where things could go wrong and how you could tell yourself what happened (a lesson I keep trying to remember :). You could remove this after finishing your code, I'd leave it in so that even your reader could see where a mistake was. And if you make it say something useful it'll help your reader to report that mistake to you to be fixed.
     This is actually just the code I was using in the “Change Gold” Passage from before, if it ain't broke don't fix it.

<<case 2>>

<span class="wpnreplace">What weapon slot do you want to replace?

<<link " <br> Replace the $weapons[0] with $args[2]">>
<<set $weapons[0] = $args[2]>>
<<replace .wpnreplace>> You now have a $weapons[0] and a $weapons[1]
<</replace>>
<</link>>

<<link " <br> Replace the $weapons[1] with $args[2]">>
<<set $weapons[1] = $args[2]>>
<<replace .wpnreplace>> You now have a $weapons[0] and a $weapons[1]
<</replace>>
<</link>>

<<link " <br> Do not pick up the items">>
<<replace .wpnreplace>> You leave your items unchanged <</replace>> <</link>>
</span>


     Okay, we're still adding items $args[0] == 1, now we're moving on to adding a weapon $args[1] == 2. This gets a little tricky, in an unexpected way. So, we print out 2 lines, one for each weapon slot, showing what the player has in that slot, asking if they want to replace it with the item to be picked up. If they do (by clicking on the <<link>>) we trigger a <<replace>> that replaces the whole block of text. It looks like this...


     There's the list of items you can pick up (a sword, dagger or axe), so you click on the one you want...


     So, what slot do I want to replace with the Sword? Both are empty. Or, I could choose not to pick up the item (though, that will get rid of the original link, so I won't be able to pick it up again - I'm not sure how to fix that, not too worried about it at the moment). But I want it, so I pick whichever slot..


     Great, now I have a Sword. But I have another empty slot, so why not pick up one of the other weapons?


     Okay, now I can get the Dagger. I can put it in the empty slot, or replace the Sword, whatever I want (and which took a whole ton of code the last way I did this).


     So now I have a Dagger, um... twice? That looks a little confusing...
     Let's talk about something in CSS...

CSS IDs and Classes

     When we do a <<replace>> we need some kind of target, something to be replaced. Before this I had been using IDs (like <span id="replace_this">). An ID is supposed to be unique on the page, in Twine I'm trying to make it unique to the whole story. One Id to one element. But there is another selector, a “Class.” A Class is meant to belong to several items, so <span class="warning_message"> could be used for every place there needs to be some kind of warning (like we did way back at the beginning).
     So why do I bring this up now? Well, that funny thing above where each line had the same text, that's because the <<ManageInventory>> widget is using a class: <span class="wpnreplace"> and <<replace .wpnreplace>> (for the <<replace>> when you target an Id you use #name and a class is targeted with .name).
     Well, that's silly - why not use an Id instead then?
     Um... that looks even weirder...




     Because the Id is unique, each link to pick up an item keeps overwriting the one before it, and doesn't get rid of the links for what slot to put the item in (so you could keep picking up the same item forever). That really didn't work right. So I changed it to using a class and while it looks funny, there is no "infinite item glitch" that way. I really do need to revisit this and see how to clean it up some more, which will be one of the long list of things to fix down the road (I have a feeling it will be an easy fix, I'm just a little overwhelmed with this inventory stuff right now).


Okay, where was I again?

     Oh yeah, now we can add a weapon, we just let the player choose where.

     Adding a Backpack Item is pretty much exactly the same thing, we just have 8 Backpack slots compared to only 2 Weapons.

<<case 3>>
<span class="backreplace">What backpack item do you want to replace?
<<link " <br> Replace the $backpack[0] with $args[2]">> <<set $backpack[0] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Replace the $backpack[1] with $args[2]">> <<set $backpack[1] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Replace the $backpack[2] with $args[2]">> <<set $backpack[2] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Replace the $backpack[3] with $args[2]">> <<set $backpack[3] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Replace the $backpack[4] with $args[2]">> <<set $backpack[4] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Replace the $backpack[5] with $args[2]">> <<set $backpack[5] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Replace the $backpack[6] with $args[2]">> <<set $backpack[6] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Replace the $backpack[7] with $args[2]">> <<set $backpack[7] = $args[2]>> <<replace .backreplace>> You have gained a $args[2] <</replace>> <</link>>
<<link " <br> Do not pick up the items">> <<replace .backreplace>> You leave your items unchanged <</replace>> <</link>>
</span>





     Again, the presentation is not the greatest - I'm going to need to work on making everything look neat and easy to read. But it does work, and after the last several days of banging my head against the wall on this, I'm okay with ugly-but-works for now.

     Adding a Special Item is stupidly easy.

<<case 4>>
<<if $specialitems.contains ($args[2])>> You already have a $args[2]!
<<else>>
<<run $specialitems.push ($args[2])>> You now have a $args[2].
<</if>>


     The system tells you if you already have one, or adds it to the array if you don't. Thankfully Special Items do not get moved around, either you have them or you don't.

     And that's it - now we have an inventory system that will let you view your items and add items, including letting you replace existing items with new ones. All at a fraction of the code I was working on before, and all from one central source.

Taking Out The Trash

     So we can add items in our new inventory system, what about getting rid of them?

     Well, that's not a problem either. All the removals are $args[0] == 3, so our first <<switch>> looks for that, and again a second <<switch>> looks at $args[1] for what container we're removing an item from.

<<if $args[3] != "random">> You have lost $args[3] Gold Crowns,
<<set $gold = $gold - $args[3]>>
<<if $gold < 0>> <<set $gold = 0>> <</if>> you now have $gold Gold Crowns.
<<elseif $args[3] == "random">>
<<set _goldlost = random ( $args[4], $args[5] )>> You have lost _goldlost Gold Crowns,
<<set $gold = $gold - _goldlost>> <<if $gold < 0>> <<set $gold = 0>> <</if>> you now have $gold Gold Crowns.
<<else>> Error: Gold amount has not been defined !
<</if>>


     Again, using the old code, removing gold is easy, and checks for a specific number or a random number. Just like the code to add, but in reverse.

<<if $args[3] == "random">>
<<set _randomslot = random (1, 2)>>
<<if _randomslot == 1>> <<set $weapons[0] = "-empty-">>You lose your first weapon!
<<elseif _randomslot == 2>> <<set $weapons[1] = "-empty-">>You lose your second weapon!
<</if>>
<<elseif $args[3] == 2>>
<<set $weapons = [ "-empty-", "-empty-" ]>> You have lost all your weapons!
<</if>>


     The code to remove a Weapon is a little funny right now. All I'm testing for is 2 things - weather to remove 1 random weapon or weather to remove all of them. Because, in the Lone Wolf books that's all that ever happens. I can and should set up a way to remove a specific item (like just a Sword, if you had one) and will, later. Again, I just want to get my basic code working, breathe, and then expand on and refine it.

<<if $args[3] == "random">>
<<set _randomslot = random (0, 7)>> You lost a backpack item!
<<if _randomslot == 0>> <<set $backpack[0] = "-empty-">>
<<elseif _randomslot == 1>> <<set $backpack[1] = "-empty-">>
<<elseif _randomslot == 2>> <<set $backpack[2] = "-empty-">>
<<elseif _randomslot == 3>> <<set $backpack[3] = "-empty-">>
<<elseif _randomslot == 4>> <<set $backpack[4] = "-empty-">>
<<elseif _randomslot == 5>> <<set $backpack[5] = "-empty-">>
<<elseif _randomslot == 6>> <<set $backpack[6] = "-empty-">>
<<elseif _randomslot == 7>> <<set $backpack[7] = "-empty-">>
<</if>>
<</if>>
<<if $args[3] == "all">>
<<set $backpack = [ "-empty-", "-empty-", "-empty-", "-empty-", "-empty-", "-empty-", "-empty-", "-empty-" ]>> You have lost all backpack items!
<</if>>


     Backpack Items are the same way, we either clear one random slot or all of them - again, all that ever happens in the game. Again, needs to be fleshed out down the road.

     Thankfully, you never lose Special Items - so I'm totally ignoring that section :)

     And that's it - view, add and remove items ; what else do you really need an inventory system to do?
     One last comment before we finally move on from Inventory (thankfully)...

Test, Test, Test

     Writing code is complicated, you need to think it through, write it a piece at a time, and then test it a lot. While working on this system I made a few Passages just to play with adding and removing items...







     This is a vital step. I made a ton of errors, both large and small, I had the whole unexpected display difference between Ids and Classes, there was a lot of work to refine this (and it's not even finished, so there's still more work to do!). Trying to write everything out and then wade through the errors would be a disaster. It would be so frustrating, so overwhelming, so hard to track exactly where an error came from - no fun. So write a bit, make a Passage and test it, write some more, update the test Passage and test, rinse, wash, repeat. That will make your coding into a series of small, manageable disasters - and give you some small, regular success to see that you really are getting somewhere. Coding is work, make it as easy on yourself as possible.

     Okay, that's it. The inventory system will still need some work, but it's close enough to done that I want to move on and get the last chunk of the game rules out of the way... Combat.