Combat and combat training

Twine Version: 2.3.16
Story Format: 2.36.1

I have built several things already into my game, such as time/date and using it to set events (https://intfiction.org/t/using-time-and-date-to-set-events), managing stats so they don’t exceed a maximum or fall below a minimum (https://intfiction.org/t/minimum-and-maximum-stats-settings), assigning a name to stats (https://intfiction.org/t/giving-stats-a-name-as-well-as-a-value), adding a passage header only to specified passages (https://intfiction.org/t/passageheader-in-sugarcube) and so forth.

But now I want to implement a combat system, hopefully without having to change any of these things.

In my StoryInit, for the main character, I have (among others)

<<set $Strength to 50>>
<<set $Strength_max to 200>>
<<set $Strength_min to 0>>

with the equivalent stats for Health, Stamina, Speed, and Melee. Plus I have this:

/* CHARACTERS */
<<character "you" "$name" "img/people/you/' + $Gender + ' avatar.png">>

/* OLEG */
<<character "oleg" "$Oleg" "img/people/military/oleg/oleg_avatar.png.png">>
<<set $olegStrength to 75>>
<<set $olegSpeed to 30>>
<<set $olegMelee to 4>>
<<set $olegHP to 150>>
<<set $olegHP_max to 200>>
<<set $olegHP_min to 0>>

In widgets, I have this:

<<widget "strength_change">>
<<set $Strength to $Strength += $args[0]>>
<<if $Strength gte $Strength_max>><<set $Strength to $Strength_max>>
  <<elseif $Strength lte $Strength_min>><<set $Strength to $Strength_min>>
<<endif>>
<</widget>>

including one for each of the stats set up in the previous code. And this for the character Oleg:

<<widget "olegHP_change">>
<<set $olegHP to $olegHP += $args[0]>>
<<if $olegHP gte $olegHP_max>><<set $olegHP to $olegHP_max>>
  <<elseif $olegHP lte $olegHP_min>><<set $olegHP to $olegHP_min>>
<<endif>>
<</widget>>

Oleg is a character that I want to use to train my main character. In a real fight, if health goes to 0 death occurs, but in training with Oleg, 0 simply means defeat, but with whatever bonus was gotten from the training.

I want different training to increase a different stat: Strengtth, Speed or Stamina, plus I would like to have different types of training: Unarmed, Knife, Sword etc.

Plus, I want to have training for Attack and also for Defence.

I want the training to look exactly like real combat.

I want all of this, so that in a fight, I can use some formula to establish the strength, speed, stamina and health (HP) of my character versus the strength, speed, stamina and health (HP) of the opponent, also taking into account what weapon is being used, and whether they are attacking or defending. (I have not yet implemented an inventory system to my liking, nor do I yet have the relative strengths of different weapons.)

I hope to have a list of options during the fight like this (maybe even more options):

<<button "<<scicon caret-right>> Fight">><<stat_change -10>><<goto "Test Combat">><</button>><br>
<<button "<<scicon caret-right>> Talk">><<stat_change -10>><<goto "Test Combat">><</button>><br>
<<button "<<scicon caret-right>> Flee">><<stat_change -10>><<goto "Test Combat">><</button>><br>
<<button "<<scicon caret-right>> Bribe">><<stat_change -10>><<goto "Test Combat">><</button>><br>
<<button "<<scicon caret-right>> Beg">><<stat_change -10>><<goto "Test Combat">><</button>><br>
<<button "<<scicon caret-right>> Surrender">><<stat_change -10>><<goto "Test Combat">><</button>><br>

I have just left the possible changes to stats as stat_change to try and keep this post as small as possible. It links back to itself (Test Combat) only for testing purposes. For the real combat, it will be whatever passage I need to create for it.

I did copy this from a combat post, but it doesn’t work for me. I changed the names, but that was all.

<<set _combined_melee to $oleg_melee + $Melee>>
<<set _round_result to random(1,_combined_melee)>>
<<if _round_result lte $Melee>>You successfully swing your sword through Oleg's defences.<<olegHP_change -5>><<melee_change +1>><br>
<<else>>Oleg wields his mortal hammer too powerfully for you to stop it. You're hit, you're bruised, and you start to regret to have ever left your comfortable house.<<health_change -3>><<melee_change +1>><</if>>

I get this: Error: << set>>: bad evaluation: random max parameter must be an integer

That is beyond my comprehension at this stage. I’ve messed up somewhere, but don’t know where or what.

Possibly I am trying to reinvent the wheel here, but some combat systems I did find confused me, and my current coding skills are not up to that job. Doing it the way I have outlined above at least makes some sort of sense to me, even if it isn’t elegant or concise.

Any and all assistance is welcome, even if it is only on some elements of this monster.

As for the error message: in your character creation you spell the variable as $olegMelee, but in the combat code it’s $oleg_melee (plus, the same code uses $Melee, which is not defined, at least not in the code you’ve shown). I copied your code, changed $oleg_melee to $olegMelee and added <<set $Melee to 4>> in StoryInit, and the test combat works as intended.

Since you said you’ve just started working on the combat systems, here are some things to consider (maybe you already did, but just in case this is helpful to you or someone else):

Before coding everything, it’s a good idea to write down all the options you want to have and think about how they interact with each other. What will Attack, Defense etc. do? Do you want to have limits on training (e.g. no more that +10 total stat gains per in-game day). (I usually draw mock-ups of combat screens with all the options I want to include beforehand.)

Consider giving the player more choices during combat, and make them matter. I like having options like Talk, Bribe, Surrender, etc. (especially that your game is not just about combat) - but does it mean if I actually choose to fight, I only have one option (“Fight”) every single turn?

If you add options like attacking/defending, give the player a reason to use them. This would probably require adding some variability to the opponent’s behaviour - for example, they may go for a big attack every couple of turns (or at random intervals, but with some hint beforehand - you could have a line like “Oleg prepares for an all-out attack”, and then if his next attack hits, it deals double damage). As a player, I love figuring out when to use which skills in turn-based combat and defending successfully against this big attack would feel great.

How would Speed and Stamina figure in the big picture? Would I be able to make a character that’s really fast, but has low strength? How would I use that to my advantage in combat? (for example - are weapons stat-based, so while attacking with knife, you use speed, and strength for swords? Or does Speed determine who goes first, and Strength - the damage? It’s different from the example you’re using, where both characters “go” at once and you compare their attack values, so that only one character gets damaged per turn, but you mentioned it only being an example). If you feel some stats are just different flavours of the same thing and don’t add any player agency, don’t be afraid to ditch them completely.

Consider having min-max damage for attacks, and having them deal a random damage value from the range - this makes things more interesting in my opinion.

Consider adding different combat messages, even for the same outcome (like 5 different ways to say “your attack hits”, with one being randomly chosen each time). Reading on turn-based, text-based combat on the Internet, I noticed players really paying attention to how varied/interesting the combat messages are.

Okay, that’s a lot of stuff, but I enjoy writing turn-based combat in Twine and couldn’t stop myself. If you want help with any of the points I mentioned, let me know! And, of course, this is all just hints and suggestions, certainly biased because of the way I tend to write my combat systems, so I won’t be hurt if you don’t use any of it!

Good luck with your project - it seems very ambitious, esp. for your first game, but also interesting.

2 Likes

Thanks for picking up on that error. I have fixed it.

The game that I have in mind is a mystery/adventure with adult themes, set in a pre-gunpowder world (but with some breaks from actual history). I want to set it all within a city and the surrounding countryside, with many NPCs to interact with. There will be a main threat to explore, interact with and either defeat or be defeated by (multiple ending choices - you can go down a bad path or a good one or anything in between, possibly even failing to deal with the main threat at all but not being defeated by them either), plus many minor threats that may or may not be associated with the main one, ie smugglers or random bandits etc. But so that options don’t become too obvious for game-savvy players, there will also be a few NPCs who aren’t involved in any crime or deals.

The combat will only be a small part of all that, and will crop up (sometimes as a random event) as the player goes into dubious areas. If detected the player may have to fend off muggers, rapists, thugs etc. The occasional murder attempt may also crop up. At this stage in game design, the player will only be reacting to these things, and not instigating any of it.

Currently, the player would choose from a limited array of options to build a character: Gender, political stance, religious and sexual orientation. Your choice in these things opens or limits certain interactions. Stats such as strength, speed, stamina, charisma etc will be assigned as the default low-mid range level, so that maybe after a beating, they will have to train to defend themselves. (I would like to be able to have points that the player could distribute among the physical stats, (strength, stamina etc) but that is another post for another day. It is beyond me at this stage.)

In a melee fight, strength and stamina will be important. (I haven’t figured out ranged fighting yet.)
To flee or maybe avoid, speed and stamina will be important.
Charsima is important if the player wants to attempt to talk their way out of a fight.
Stealth is also a stat that might be used to avoid detection and avoid a fight altogether.

The fight itself will depend on the weapon (if any) that the player has, any training with that weapon, versus the weapon that the opponent has and their opposing stat values. I want to be able to combine the player’s strength, speed and stamina into a combat stat, combined with their choice of weapon and any training for melee that they have. This will be matched against the opponent’s rating in the same areas. Different weapons deal different damage. There is no magic in this game.

The attack and defence values would come in if the player was making a move or reacting to one.

So sorry for the wall of text, but that is what I am aiming for, and I will address the specific coding requirements in additional posts as they come up. For now, I am wondering if I am on the right track with how to assign stats to the player and to NPC opponents from the original post. This is a very ambitious project, especially for an inexperienced game maker (this is my first one), but I have played many games and have drawn from that experience to try and make a game I would like to play.

1 Like

Thanks for taking the time to explain your systems. This indeed looks like a big and ambitious game, so here’s a bit of advice taken from personal experience (something often mentioned here, and rightfully so, but it especially matters for big games like yours): make absolutely sure you only use story variables (starting with $) when absolutely necessary. Use temporary variables (starting with _) for things that are only used in a single passage and you don’t need to keep track of them later. For things that are set up initially and never change, use “setup” objects. Using the example of Oleg’s stats, I would change them to the following:

<<set setup.olegStrength to 75>>
<<set setup.olegSpeed to 30>>
<<set setup.olegMelee to 4>>
<<set $olegHP to 150>>
<<set setup.olegHP_max to 200>>
<<set setup.olegHP_min to 0>>

EDIT: for better clarity, you could also gather all of Oleg’s static data into a single object:

<<set setup.oleg to {
	Strength: 75,
	Speed: 30,
	Melee: 4,
	HP_max: 200,
	HP_min: 0
}
>>

Then if you need to refer to one of those stats, you can do the following:

<<if _x > setup.oleg.Speed>> ...

(This is based on an assumption that only Oleg’s HP changes during gameplay, all of his other stats remain the same)

This is because big Sugarcube projects start gettting really slow when they have to keep track of too many story variables. I’ve been there with my massive RPG project - I initially used story variables for pretty much everything, and at some point the game became super slow, and on weaker devices borderline unplayable (10+ seconds for each passage transition). I had to spend a week or two changing some of the static data (enemy objects, item properties etc.) to setup objects. So, learn from my mistakes and don’t do the same thing!

(Also, make sure you do use story variables for things that have to be tracked across multiple passages and may change, like player’s stats. Otherwise, they won’t be saved and will reset on each save/reload. I’ve made this mistake as well when fixing my big project - put too much stuff in setup objects, and as a result saving and loading the game started to change the entire map layout, which was procedurally generated. And I had to fix stuff again).

This is not to discourage you - with proper planning, Twine can handle really big games, just make sure to plan properly from the start to avoid headaches further down the line!

1 Like

Thank you very much for that. I was unaware of the setup variable.
Just so I fully understand it, let me say it back to you:

<<set $Strength to 75>>

Is setting the player’s strength to 75. And this variable $Strength, can be increased in game by training. This increase is permanent, at least until the next round of training.

<<set setup.olegStrength to 75>>

sets the NPC Oleg’s strength to 75, and this won’t change because there is no need for it to change. Oleg doesn’t train.

<<set $olegHP to 150>>

sets Oleg’s HP to 150.

If I use

<<if _x > setup.olegHP>>

during training, his HP will change during a training session, whenever the player lands a blow, but the next time the player trains, Oleg will be back to 150HP.
x is damage Oleg takes during training.

Do I have that all right? If so, how would I write the x component? How would I make it clear what x is?

Also, many of you have expressed concern over the proposed size and scope of my game, and as I am aware that you all know what you’re talking about, I am going to scale it back for a demo/sample game to get everything working as I want it before tackling the big one.

The <<if _x > setup.oleg.Speed>> I used is just an example of how to refer to a single parameter of an object, if you choose to have one “oleg” object with multiple parametres (I recommend that - it gives better readability, plus adding stats for new characters is easier - just copy the stat block, change “oleg” to another name and adjust the values.) The _x is just an example (temporary) variable.

Since Oleg’s HP may change during the training session, it’s the one parameter you want to use a story variable for - $olegHP, so that changes are properly tracked. To have him recover at the start of a new training session, when initiating training, you should add the following:

<<set $olegHP to setup.olegHP_max>> if you use separate variables for each of Oleg’s stats (my first example in the earlier post)

or <<set $olegHP to setup.oleg.HP_max>> if you use one object with multiple parametres (my second example).

This will set Oleg’s HP to 200 (I assume you want him to start each session with max HP, even though in your example he starts with 150? If not, you can simply <<set $olegHP to 150>>, or add a new setup variable for Oleg’s starting HP).

Otherwise, you’ve got the distinction between player’s and Oleg’s strength correctly, the first one can change and is tracked, the other is static.

I think working on a sample game first is a good idea - you’ll have something to show / send to testers earlier, and later you can either keep working on expanding that game or, if you come up with better versions of some systems, start a fresh new project, while having the first one already in your portfolio.