Parameters issue

I’m putting together code for a combat system. *params is perfect for this as I can just input the enemy’s stats when they appear in the narrative and then the code will run the fight scene using those stats. I’ve put a simple version here to illustrate what I mean, and then the full version beneath that.

Simple Version
*gosub_scene fight fight "Godzilla" 10 5

fight.txt

*label fight

*params enemy health damage

${enemy} has {health} health and deals you ${damage} damage!

*return

Output:

Godzilla has 10 health and deals you 5 damage!

Actual Version, Beware All Ye Who Enter
*gosub_scene fight fight "Godzilla" 3 85 5 10 10 10 100 2 3 "Godzilla's radioactive aura burns your skin." "You shoot godzilla with a rocket launcher." "Godzilla blasts you with a laser beam." "Godzilla continues to destroy Tokyo." "Godzilla topples over!" "Godzilla grabs your rocket and chucks it back at you." "Godzilla lets loose a roar. A good opportunity to jam a rocket into their mouth." "You reflect back Godzilla's laser with a big mirror." "You and Godzilla scream at each other for a few minutes, uselessly." "Godzilla destroys a building. You can't sit around on your thumbs this whole fight." "You wave your arms at Godzilla, trying to get their attention." "Godzilla glares at you angrily, but does nothing." "You send a probe to take readings on Godzilla's radioactive energy output." "You've taken down Godzilla, but their massive bulk lands on top of you!" "You've taken down Godzilla before they could destroy Tokyo!" "Godzilla incinerates you easily." "Mothra arrives to distract Godzilla!"

fight.txt

*label fight

*params enemy enemy_gender strike bide taunt health health_max enemy_health enemy_min_damage enemy_power fatigue_flavour strike_flavour enemy_strike_flavour enemy_still_alive_flavour enemy_struck_down_flavour enemy_counters_you_flavour enemy_struck_while_taunting_flavour enemy_countered_flavour stalemate_flavour enemy_taunt_flavour taunt_flavour enemy_useless_bide_flavour study_flavour pyrrhic_flavour win_flavour lose_flavour resolution_flavour

*comment enemy: string
*comment enemy_gender: number
*comment strike: number
*comment bide: number
*comment taunt: number
*comment health: number
*comment health_max: number
*comment enemy_health: number
*comment enemy_min_damage: number
*comment enemy_power: number
*comment fatigue_flavour: string
*comment strike_flavour: string
*comment enemy_strike_flavour: string
*comment enemy_still_alive_flavour: string
*comment enemy_struck_down_flavour: string
*comment enemy_counters_you_flavour: string
*comment enemy_struck_while_taunting_flavour: string
*comment enemy_countered_flavour: string
*comment stalemate_flavour: string
*comment enemy_taunt_flavour: string
*comment taunt_flavour: string
*comment enemy_useless_bide_flavour: string
*comment study_flavour: string
*comment pyrrhic_flavour: string
*comment win_flavour: string
*comment lose_flavour: string
*comment resolution_flavour: string

*comment Power 1: 1-2
*comment Power 2: 1-4
*comment Power 3: 2-5
*comment Power 4: 4-7
*comment Power 5: 5-10

*temp threshold_1 0
*temp threshold_2 0
*temp focused_random 0
*rand focused_random 1 100
*temp jester_taunt false
*temp resolution_chance 30
*temp resolution_check 0
*temp revision_used false
*temp taunted false
*temp enemy_taunted false
*temp study_action 0
*temp studied false

*set threshold_1 (strike)
*set threshold_2 (strike + bide)

*if (testing)
  Enemy: ${enemy}
  [n/]Enemy Gender: ${enemy_gender}
  [n/]Strike: ${strike}
  [n/]Bide: ${bide}
  [n/]Taunt: ${taunt}
  [n/]Health: ${health}
  [n/]Health Max: ${health_max}
  [n/]Enemy Health: ${enemy_health}
  [n/]Enemy Min Damage: ${enemy_min_damage}
  [n/]Enemy Power: ${enemy_power}
  [n/]Fatigue Flavour: ${fatigue_flavour}
  [n/]Strike Flavour: ${strike_flavour}
  [n/]Enemy Strike Flavour: ${enemy_strike_flavour}
  [n/]Enemy Still Alive Flavour: ${enemy_still_alive_flavour}
  [n/]Enemy Struck Down Flavour: ${enemy_struck_down_flavour}
  [n/]Enemy Counters You Flavour: ${enemy_counters_you_flavour}
  [n/]Enemy Struck While Taunting Flavour: ${enemy_struck_while_taunting_flavour}
  [n/]Enemy Countered Flavour: ${enemy_countered_flavour}
  [n/]Stalemate Flavour: ${stalemate_flavour}
  [n/]Enemy Taunt Flavour: ${enemy_taunt_flavour}
  [n/]Taunt Flavour: ${taunt_flavour}
  [n/]Enemy Useless Bide Flavour: ${enemy_useless_bide_flavour}
  [n/]Study Flavour: ${study_flavour}
  [n/]Pyrrhic Flavour: ${pyrrhic_flavour}
  [n/]Win Flavour: ${win_flavour}
  [n/]Lose Flavour: ${lose_flavour}
  [n/]Resolution Flavour: ${resolution_flavour}

*if (class = 2)
  As a Luck Bender, you already know @{enemy_gender his|her|their} inclinations.
  
  Strike: ${strike}%
  [n/]Bide: ${bide}%
  [n/]Taunt: ${taunt}%

*label next_turn

*comment *if (choice_randomtest)
*comment   *set enemy_health 0
*comment   *goto resolution

*comment Power 1: 1-2
*comment Power 2: 1-4
*comment Power 3: 2-5
*comment Power 4: 4-7
*comment Power 5: 5-10

*temp damage 1
*temp enemy_damage 1
*temp shield_smasher_damage 1
*temp redirection_damage 1
*temp enemy_action 1
*temp enemy_random 1
*rand enemy_random 1 100
*if (enemy_taunted)
  *if (jester)
    *set jester_taunt true
  *set enemy_random 1
  *set enemy_taunted false
*if (enemy_random <= threshold_1)
  *set enemy_action 1
*if ((enemy_random > threshold_1) and (enemy_random <= threshold_2))
  *set enemy_action 2
*if (enemy_random > threshold_2)
  *set enemy_action 3
*temp study_random 1
*if (studied)
  *set enemy_action study_action
*set studied false
*if (class = 1)
  *rand damage 1 20
*if (class != 1)
  *rand damage 1 10
*if ((punisher) and (taunted))
  *set damage @{class=1 20|10}
*if (enemy_power = 1)
  *rand enemy_damage 1 2
*if (enemy_power = 2)
  *rand enemy_damage 1 4
*if (enemy_power = 3)
  *rand enemy_damage 2 5
*if (enemy_power = 4)
  *rand enemy_damage 4 7
*if (enemy_power = 5)
  *rand enemy_damage 5 10
*rand redirection_damage 1 20
*if ((jester) and (jester_taunt))
  *set enemy_damage enemy_min_damage
*rand resolution_check 1 100
*if (resolution_chance > 100)
  *set resolution_chance 100
*if (shield_smasher)
  *set shield_smasher_damage (damage / 2)
  *set shield_smasher_damage round(shield_smasher_damage)
*if (tough)
  *set enemy_damage (enemy_damage / 2)
  *set enemy_damage round(enemy_damage)

*if (testing)
  Damage: ${damage}
  [n/]Enemy Damage: ${enemy_damage}
  [n/]Redirection Damage: ${redirection_damage}
  [n/]Enemy Action: ${enemy_action}
  [n/]Threshold 1: ${threshold_1}
  [n/]Threshold 2: ${threshold_2}

*set health -1
${fatigue_flavour} You take 1 damage. You have ${health} health remaining.
*if (flesh_flayer)
  *set enemy_health -1
  [n/][FLESH FLAYER] ${enemy} takes 1 damage. @{enemy_gender He has|She has|They have} ${enemy_health} health remaining.
*if (health <= 0)
  *goto resolution

*if ((punisher) and (taunted))
  [PUNISHER] While taunted, your strikes deal maximum damage!

*if ((focused) and (taunted))
  *if (focused_random >= 66)
    *set taunted false
    [FOCUSED] You are able to shake off your enemy's taunts! @{punisher Your strikes still deal maximum damage.|}
  *if (focused_random < 66)
    [FOCUSED] You are unable to shake off your enemy's taunts.
  *rand focused_random 1 100

*fake_choice
  # Strike.
    *set taunted false
    
    *if (enemy_action = 1)
      *set enemy_health (enemy_health - damage)
      *set health (health - enemy_damage)
      
      ${strike_flavour} ${enemy_strike_flavour}
      
      You deal ${enemy} ${damage} damage.
      [n/]${enemy} deals you ${enemy_damage} damage.
      *if (tough)
        [n/][TOUGH] Half damage!
      *if ((jester) and (jester_taunt))
        [n/][JESTER] Minimum damage!
        *set jester_taunt false
      
      *if (enemy_health > 0)
        ${enemy_still_alive_flavour} @{enemy_gender He has|She has|They have} ${enemy_health} health remaining.
      *if (enemy_health <= 0)
        ${enemy_struck_down_flavour}
    *if (enemy_action = 2)
      *set health (health - enemy_damage)
      
      ${enemy_counters_you_flavour} You take ${enemy_damage} damage.
      *if (shield_smasher)
        [SHIELD SMASHER] ${enemy} still takes ${shield_smasher_damage} damage from your strike! @{enemy_gender He has|She has|They have} ${enemy_health} health remaining.
        *if (enemy_health <= 0)
          ${enemy_struck_down_flavour}
      
    *if (enemy_action = 3)
      *set enemy_health (enemy_health - damage)
      
      ${enemy_struck_while_taunting_flavour} You deal ${enemy} ${damage} damage.
      
      *if (enemy_health > 0)
        @{enemy_gender He has|She has|They have} ${enemy_health} health remaining.
      *if (enemy_health <= 0)
        ${enemy_struck_down_flavour}
  *selectable_if (taunted = false) # Bide.
    *if (enemy_action = 1)
      *if (redirection)
        [REDIRECTION] You deal more damage to enemies when you bide.
        *if (enemy_damage >= redirection_damage)
          *set enemy_health (enemy_health - enemy_damage)
        *if (redirection_damage > enemy_damage)
          *set enemy_health (enemy_health - redirection_damage)
      *if not(redirection)
        *set enemy_health (enemy_health - enemy_damage)
        
      ${enemy_countered_flavour}
      
      *if (redirection)
        *if (enemy_damage >= redirection_damage)
          ${enemy} takes ${enemy_damage} damage.
        *if (redirection_damage > enemy_damage)
          ${enemy} takes ${redirection_damage} damage.
      *if not(redirection)
        ${enemy} takes ${enemy_damage} damage.
      *if (enemy_health > 0)
        ${enemy_still_alive_flavour} @{enemy_gender He has|She has|They have} ${enemy_health} health remaining.
      *if (enemy_health <= 0)
        ${enemy_struck_down_flavour}
      
    *if (enemy_action = 2)
      ${stalemate_flavour}
    
    *if (enemy_action = 3)
      *set taunted true
      
      ${enemy_taunt_flavour}
  *selectable_if (taunted = false) # Taunt.
    *set enemy_taunted true
    
    ${taunt_flavour}
    
    *if (enemy_action = 1)
      *set health (health - enemy_damage)
      
      ${enemy_strike_flavour} ${enemy} deals you ${enemy_damage} damage.
      *if (tough)
        [n/][TOUGH] Half damage!
      *if ((jester) and (jester_taunt))
        [n/][JESTER] Minimum damage!
        *set jester_taunt false

    *if (enemy_action = 2)
      ${enemy_useless_bide_flavour}
    *if (enemy_action = 3)
      *set taunted true
      
      ${enemy_taunt_flavour}
  *selectable_if (taunted = false) # Study.
    *set studied true
    *rand study_random 1 100
    *if (study_random <= 5)
      *set study_action 1
    *if ((study_random > 5) and (study_random <= 65))
      *set study_action 2
    *if (study_random > 65)
      *set study_action 3
    
    ${study_flavour}
    
    Health: ${enemy_health}
    [n/]Damage:
    *if (enemy_power = 1)
      1-2
    *if (enemy_power = 2)
      1-4
    *if (enemy_power = 3)
      2-5
    *if (enemy_power = 4)
      4-7
    *if (enemy_power = 5)
      5-10
    [n/]Strike: ${strike}%
    [n/]Bide: ${bide}%
    [n/]Taunt: ${taunt}%
    
    Next round, @{enemy_gender he|she|they} will @{study_action strike|bide|taunt}.
    
    *if (record_keeper)
      [RECORD_KEEPER] While studying, you also bide!
      
      *if (enemy_action = 1)
        *set enemy_health (enemy_health - enemy_damage)
        
        ${enemy_countered_flavour}
        
        *if (enemy_health > 0)
          ${enemy_still_alive_flavour} @{enemy_gender He has|She has|They have} ${enemy_health} health remaining.
        *if (enemy_health <= 0)
          ${enemy_struck_down_flavour}
        
      *if (enemy_action = 2)
        ${stalemate_flavour}
      
      *if (enemy_action = 3)
        *set taunted true
        
        ${enemy_taunt_flavour}
    *if not(record_keeper)
      *if (enemy_action = 1)
        *set health (health - enemy_damage)
        
        ${enemy_strike_flavour} ${enemy} deals you ${enemy_damage} damage.
        *if (tough)
          [n/][TOUGH] Half damage!
        *if ((jester) and (jester_taunt))
          [n/][JESTER] Minimum damage!
          *set jester_taunt false
  
      *if (enemy_action = 2)
        ${enemy_useless_bide_flavour}
      *if (enemy_action = 3)
        *set taunted true
        
        ${enemy_taunt_flavour}
  *if (resolution) # [RESOLUTION] End the fight immediately. (${resolution_chance}%)
    *if (resolution_check <= resolution_chance)
      *set enemy_health 0
      
      ${resolution_flavour}
    *if (resolution_check > resolution_chance)
      *set resolution_chance +20
      
      You are unable to end the fight immediately. Your chances increase on subsequent attempts.
      
      *if (enemy_action = 1)
        *set health (health - enemy_damage)
        
        ${enemy_strike_flavour} ${enemy} deals you ${enemy_damage} damage.
        *if (tough)
          [n/][TOUGH] Half damage!
        *if ((jester) and (jester_taunt))
          [n/][JESTER] Minimum damage!
          *set jester_taunt false
      *if (enemy_action = 2)
        ${enemy_useless_bide_flavour}
        
      *if (enemy_action = 3)
        *set taunted true
        
        ${enemy_taunt_flavour}
  *if ((revision) and (revision_used = false)) # [REVISION] Heal yourself fully.
    *set revision_used true
    You heal yourself back up to full health.
    *set health health_max
    
    *if (enemy_action = 1)
      *set health (health - enemy_damage)
      
      ${enemy_strike_flavour} ${enemy} deals you ${enemy_damage} damage.
      *if (tough)
        [n/][TOUGH] Half damage!
      *if ((jester) and (jester_taunt))
        [n/][JESTER] Minimum damage!
        *set jester_taunt false
    
    *if (enemy_action = 2)
      ${enemy_useless_bide_flavour}
      
    *if (enemy_action = 3)
      *set taunted true
      
      ${enemy_taunt_flavour}

*label resolution

*if ((enemy_health > 0) and (health > 0))
  *goto next_turn
*elseif ((enemy_health <= 0) and (health <= 0))
  *set godzilla true
  *gosub_scene level_up
  
  ${pyrrhic_flavour}
  
  *return
*elseif ((enemy_health <= 0) and (health > 0))
  *set godzilla true
  
  ${win_flavour}
  
  *return
*else
  *set godzilla false
  *gosub_scene level_up
  
  ${lose_flavour}
  
  *return

Running through it manually, I get:

Perfect.

Putting it through randomtest on ten thousand iterations, I get:

image

Perfect.

However, putting it through quicktest, I get:

image

And that’s the problem. TypeError: this.temps.param is undefined

I think it might have something to do with how quicktest runs through every scene in startup.txt one by one, so it goes to fight.txt, sees the *params command, and freaks out because nobody has gosub_scene’d in to activate it.

Can someone with more technical knowledge than me confirm my suspicions? And is there any way for quicktest to test this scene without quicktest freaking out? Otherwise I’ll have to remove it from the scene list and then I won’t find all the countless errors I’ve made without going through the laborious process of manual testing.

1 Like

Your assessment is correct. The name of the error is a bit misleading, and you may get a different name, depending on how you construct your subroutine scenes. This happens because Quicktest does not actually “run” your code from the startup file. It goes to every file and tries to “run” the code with various variable permutations (even if they never come up in the game).

In your case, it’s trying to “cold start” your fight scene without any params (since it reached that file). In order to fix this issue, you have to add a “bug” command at the top of the file to ensure that the scene is never run without params (as in the qt scenario). Granted, this will still throw an error when using quicktest, but at least you have more control.

*bug Subroutine scene called without label. Make sure to specify the subroutine to be called by label
*label fight

*params enemy health damage

${enemy} has {health} health and deals you ${damage} damage!

*return
1 Like

I just put *finish at the top of fight.txt, and quicktest skips it, but that means that it doesn’t test it! Annoying.

1 Like

I have never played around with *params, but would something like this work?

Put the *params in an *if statement, that is true by default.
Above the label, set the condition for the *if to false.
If necessary, add ‘fake’ params above the label, by creating all the used params as temps.

I tried doing that when I had the same issue, but I can’t remember why I switched to this approach.

1 Like

I have a combat system in my own WIP. It’s not as complicated as yours but i wanted to provide it for variety’s sake, and so that you could compare and see if there was anything you could find. For refrence, mine works with both quick test and random test. Hope you figure it out!

*temp tutorial 10
*label tutorial
[b]Encounter! Level 2 Red Moti[/b]
*line_break
[b]You have ${health} health remaining[/b]
*line_break
[b]The Moti has ${tutorial} health remaining[/b]
*choice
    #Attack with your fists (combat)
        *set tutorial - combat
        A bit draconian, but it gets the job done. You give the Moti a hard punch in what you assume is its face.
        *page_break
        *goto t
    #Attack it with a flung projectile (power)
        *set tutorial - power
        Noticing a nearby projectile, you lift it with your telekinesis, and launch it towards the Moti's head. It hits, and the Moti lets out an audible, yet slimy cry of pain.
        *page_break
        *goto t
    #Coordinate with Mackenzie (syncronicity)
        *set tutorial - sync
        "Need my help?" ${Mackenzie} asks you, having to shout to be heard. You nod, and ${she} creates a lighting strike, striking the Moti on the head, and creating a visible burn mark.
        *page_break
        *goto t
*label t
*if (tutorial < 1)
    *goto tutorial_end
*else
    *temp dodge
    *rand dodge 1 2
    *if dodge = 1
        The Moti lifts up its huge, red arm, and slams it down. You manage to dodge the attack.
        *page_break
        *goto tutorial
    *if dodge = 2
        *set health -1
        The Moti lifts up its huge, red arm, and slams it down. You are scratched by the impact.
        *line_break
        - 1 HP
        *page_break
        *goto tutorial
*label tutorial_end
*set motis_killed +1
*set level +1
*set max_health +1

The combat system isn’t causing the issue lol, it’s the use of parameters. If you wanted to revise your combat system such that it would work with any enemy in the game without having to copy and paste anything, you’d face the same problem, I reckon.