Bpen's simple 1 vs 1 battle system


Well, relatively simple. (mostly kludgy and inefficient)

A few years ago, I did an arena-combat game for renpy. It was inspired by RPGMaker’s system, which allows the creation of JRPG-style game. On the user side, it is made up little more than of several different editable lists - globals (such as names, identifiers, etc), characters (and their stats and growth curves), enemies (and their stats, usable skills, etc), a single block of skills and attacks (which both players and enemies can use) and an inventory list.

I tried porting the renpy game into choicescript, but ran into the problem that the python-based script allowed me to just $new_variable = value pretty much anywhere while now I must remember and predefine every variable. The inventory list alone is pretty tiring to re-encode, not to mention the special abilities/spells/etc. Each of them checks if active_player = “hero” or “enemy” and with two all-but-identical code to check for success, damage, etc. Better than having separate label enemy_firebolt and hero_firebolt?

Still, the ideal is that once it’s done, it would be plug-and-play. All that’s need to customize it is to change the lists - the ‘meat’ of the script, the combat system, is actually quite short and does not need any further editing.

The process looks something like:

Bpen’s simple 1 vs 1 combat script

Step 1:
Select the player character
– > *set hero_class “knight”
– > gosub to “knight”
– > etc.

Step 2:
Define the player’s stats and abilities
– > *set DEF, ATK, SPD, END, LCK etc
– > *set hero_hp and hero_hp
– > *set max_hero_hp and max_hero_mp as hero_hp and hero_mp
– > *set equipped items (hero_wpn_dmg, hero_wpn_ac, hero_wpn_dsc, etc.)
– > *set all status (poisoned, bleeding, stunned , etc.) to 0
– > *set inventory items
– > return

Step 2:
Do the same, but for the chosen enemy
– > as above

Step 3:
Offer the player a choice of what to do
– > Simple attack
----- > set act_id 101 (simple attack)
– > Special Action
----- > goto special menu
-----------> Action 1
-------------- > set act_id for this action
-----------> Action 2
– > Use item
----- > goto item menu
---------- > drink health potion
---------- > drink mana potion
– > etc

Step 4:
Begin the combat script

Display Combat Report

Step 4a. Roll initiative to see who goes first
– > set active player = “hero” or “enemy”
– > goto (active player) turn

Step 5: (Assuming hero wins init)
Execute player_turn
– > check status
----- > Poisoned? Stunned? Regenerating?
----- > Add or subtract to hp/mp

Goto the actions list
– > perform chosen action
– > if act_id = 101
– > goto simple_attack
– > etc.

Goto the label to execute the action
(assuming simple attack)
– > if active_player = “hero”
----- > roll to hit
----- > if hit succeeds
---------- > check for critical hit
-------------- > double damage
---------- > do normal damage
---------- > check if enemy_hp < 1
-------------- > goto you_lose
----- > if hit fails
----- > say ‘you missed’
– > goto continue_player_turn

Back to hero_turn
– > check who won initiative
– > if enemy, then go back to player action choice
– > else, go to enemy_turn

Step 6:
Initiate enemy combat
– > check status
----- > Poisoned? Stunned? Regenerating?
----- > Add or subtract to hp/mp

Get enemy_ai
– > gotoref (enemy class)_ai

Get action from the pattern list
– > roll dice
– > if enemy_hp < max_hp/2, drink potion
----- > if enemy_mp < max_mp/2, 50/50 chance of drink another potion
– > if roll_dice = 1: evade
----- > set act_id 201
– > if roll_dice = 3: heavy attack
----- > set act_id 301
– > if roll_dice = 6: special/magic attack
----- > set act_id 301
– > etc.
– > else do a simple attack
– > go back to enemy_turn

Goto the actions list
– > perform chosen action
– > if act_id = 101
– > goto simple_attack
– > etc.

Goto the label to execute the action
(assuming simple attack)
– > if active_player = “enemy”
----- > roll to hit
----- > if hit succeeds
---------- > check for critical hit
-------------- > double damage
---------- > do normal damage
---------- > check if hero_hp < 1
-------------- > goto you_lose
----- > if hit fails
----- > say ‘you missed’
– > goto continue_enemy_turn

Back to enemy_turn
– > check who won initiative
– > if enemy, then goto hero_turn
– > else, go go back to player action choice

Step 8:
Check if won or lost

Step 9:
Check hero xp
– > *set xp_adj hero_xp+100
– > *set level_inc hero_level*hero_level
– > *set next_level 100*level_inc

This creates an xp curve that goes
100, 300, 600, 1500, 2500, etc.

– > if hero_xp >= next_level
– > you gained a level
– > add max_hp/mp bonus
– > every two levels, allow a stat increase

Step 10:
Give the player a choice
– > fight again
– > rest
– > select different enemy
– > select different player class
– > quit


It’s still all sorts of borked right now, with only the Knight, Goblin, Orc, and simple attacks functioning correctly. I have no idea how the hay to even BEGIN turning this into a team or multiple-enemy system.

Help? Any ideas?


You have to input
*create title


You have to * create mp


… um, what do you mean?

If anything in the game crashes due to a need for mp, then it should be because it’s supposed to call for hero_mp or enemy_mp instead. :-? Though technically I haven’t written yet any parts that require checking for if (active player)_mp < 1 […]


You add a line of text at the beggining saying
*create title
without it it doesnt allow you to check stats
and also same with mp to allow progress


I gtg its mid night


… thanks, but the process outline isn’t the script.

*create title creates a variable named title
which still needs to *set title as “text” or a number.

  • otherwise the game will crash because it says the variable has been set and has no value.

There’s a semi-working demo link up there. :wink:


Okay, on further prodding, please allow me to explain a bit about what the stats are and how they are used.

This battle system uses only five main stats
HP/MP - and once max_hp and max_hp are set, should only ever be altered by level-ups

ATK - influences to hit chance
DEF - influences to evade chance

SPD - influences evade, directly used for init check
END - influences recover from status effects
LCK - influences critical hit chance

plus three additional values

Hero_name - player name, once called from input
Hero_class - the type of character, with each class having a different action chain
Hero_level - is used all over the place, and enemies tend to scale to hero level

plus three status values


plus two inventory values


Then four more from the inventory
hero_wpn_atk - added to player ATK
hero_wpn_dmg - is the actual base damage for attacks
hero_arm_ac - added to player DEF
hero_arm_dr - absorbs a portion of enemy attack DMG

The enemy has the same set of stats, only with the prefix enemy_ .

The thing is, these are all that’s needed to create very different enemy encounters from mooks to bosses. Alter HP and MP, and it’ll take a lot more turns before going down. High enemy DEF makes it a pain to hit the enemy without high-ATK weapons (which, once an inventory script is running, may trade off low damage for higher accuracy or rate of fire… such as knives or submachine guns). High enemy LCK is a serious threat when paired with Special Actions that allow them to attack multiple times per turn.

In the game, the character/definition blocks look like

*label player_class_1

*label player_class_1

*label player_class_1


*label enemy_class_1
*label enemy_class_ai

*label enemy_class_2
*label enemy_class_ai

*label enemy_class_3
*label enemy_class_ai

... etc.

Placed at the end of an active script, well away from anything that might get them to run, they should be called only with a *gosub. The lists won’t affect anything in the narrative or any other game scripts. The labeled definition blocks print out nothing to the screen.

Now, a problem is being able to reach these lists from anywhere, whenever the combat script needs them.

Assuming that the mygame.js goes

nav = new SceneNavigator([

  • will it be possible to *gosub to something in the lists while still in scene “ch1”?

If not, it seems that globalscript mod is needed.


DO NOT use the *Create command.
This intrigues me, I’ll have a closer look later today.
I’m convinced it’s entirely possible to make a multiple enemy (and ally) system.

You cannot *gosub to a scene, only a label.


I had to use *create for this to make sure there’s no need to mess with mygame.js when testing out the file though. Keep everything in one document while switching between PC and working on it on a mobile.

A more polished final version will hopefully be less bloated with unnecessary *create, *temp, and *sets.

Anyway, just to confirm:

Say that in ch1, I run a *goto combat_script, but that label doesn’t exist in ch1.txt.

Will the game search for the label in combat_script.txt, or goto only if there’s one in a prior scene (in this case, intro.txt, completed with *finish), or will it crash with fail and fire like the Hindenburg?


It’ll error, “bad label”