3
\$\begingroup\$

This is my battle system that I wrote in Lua. Bear with me on this one, this is my first real program that does something besides write hello world. If there is anything that I could have done differently, drop an answer down there and I will take your advice for my next program. Huge shoutout to Jakuje for helping me make this, he refined my code for me and told me how to do things correctly.

math.randomseed( os.time() )
math.random(); math.random(); math.random()
local function battle() 
  n = math.random(10) + 1 -- Everybody's HP, enemy HP randomly generated number from 10 to 100
  enemyhp = 10*n
  exp = enemyhp*0.5
  g_herohp = 100
  print("You have encountered an enemy!")
  io.write("Your HP: ")
  io.write(g_herohp)
  io.write(" ")
  io.flush()
  io.write("Enemy HP: ")
  io.write(enemyhp)
  io.write(" ")
  io.flush()
end
local function attack()  -- Attacking the enemy or running away
  print("|Attack|Flee| ")
  input = io.read()
  if input == "attack" then
    attackdamage = math.random(51)
    if attackdamage == 51 then
      print("Critical Hit! 100 damage!")
      enemyhp = enemyhp - 100
    else
      enemyhp = enemyhp - attackdamage
      io.write("You dealt ")
      io.write(attackdamage)
      io.write(" damage!")
    end
  elseif input == "flee" then
    escapechance = math.random(2)
    if escapechance == 1 then
    escape = true
    end
  end
end
local function enemyattack() -- Enemy attacks you with a random amount of damage
  print(" Enemy is attacking!")
  eattackdamage = math.random(40)
  g_herohp = g_herohp - eattackdamage
  io.write("Enemy dealt ")
  io.write(eattackdamage)
  io.write(" damage!")
end
local function battleinprogress() -- If I used battle then it would reset everyones health.
  if g_herohp <= 0 then
    print(" You have died!")
    end
  io.write(" Your HP: ")
  io.write(g_herohp)
  io.write(" ")
  io.flush()
  io.write("Enemy HP: ")
  io.write(enemyhp)
  io.write(" ")
  io.flush()
  if enemyhp <= 0 then
    print("You won!")
  end
  if escape == true then
  print("You have run away!")
  end
end
battle()
repeat
attack()
enemyattack()
battleinprogress()
until g_herohp <= 0 or enemyhp <= 0 or escape == true
end
\$\endgroup\$
  • \$\begingroup\$ How are you actually running the game? \$\endgroup\$ – hjpotter92 Jun 29 '16 at 9:36
  • \$\begingroup\$ @hjpotter92 I use an IDE to run it. I'll be releasing an adventure expansion too, and once it's done I'll compile it. \$\endgroup\$ – Dropdatderp Jun 29 '16 at 13:11
  • \$\begingroup\$ No, what I meant was, where do you call these functions? How is the player object/table/variable defined? How many enemies can there be? etc. \$\endgroup\$ – hjpotter92 Jun 29 '16 at 13:43
  • \$\begingroup\$ @hjpotter92 Did I forget to add that in with the post? Once I have access to my drive again I will edit this post. \$\endgroup\$ – Dropdatderp Jun 29 '16 at 13:45
  • \$\begingroup\$ @hjpotter92 Although currently there is one enemy. I am implementing a level and item system so I will be updating this post a lot. \$\endgroup\$ – Dropdatderp Jun 29 '16 at 13:46
1
\$\begingroup\$

Since you are making calls to math.random a lot, keep a local copy in your code:

local random = math.random

Why are you switching between io.write and print statements? Use a string.format to format the data as you want:

print( ("You dealt %d damage!"):format(attackdamage) )

The above is merely a representation. It can be made a lot better (by putting templates as variables etc.)


n = math.random(10) + 1

The above will generate n in the range of 2 to 11. From the docs:

When called with two integers m and n, math.random returns a pseudo-random integer with uniform distribution in the range [m,n]. [...] The call math.random(n) is equivalent to math.random(1,n).

And, no need for keeping a temporary variable (here n):

enemyhp = 10 * random(10) -- better yet, use random(10, 100)

escapechance = math.random(2)
if escapechance == 1 then
escape = true
end

can simply become:

escape = random(2) == 1

Convert your input to lowercase first. Players might enter Attack or ATTACK too.

if input:lower() == "attack"

or

local input = io.read():lower()

Most important

You should encapsulate your player, enemies etc. inside objects. Lua supports a rough implementation of OOP design using tables. Read the Lua PiL chapter #16

\$\endgroup\$
0
\$\begingroup\$

I understand that you're new to programming, and some of this might be a bit hard to understand, but I'm hoping it will be useful anyway.

Formatting

It's a bit hard to visually get an overview of the code when it's so dense. You should add in some vertical space to make your code easier to read. For example, there should be one blank line between function definitions.

The indentation is a bit inconsistent. Try to always keep the "end" on the same level as the statement that starts the block (e.g. the "if"), and everything in between one level to the right.

Structure/algorithm

You're using global variables to keep track of HP, which is generally not a good idea. For a small program like this it's OK, but consider how hard it would be to keep track of their values if the program had been thousands of lines spread across multiple files. It's a good idea to get into the habit of avoiding globals. Usually, this is accomplished by passing the relevant data as arguments to the functions, and either returning the updated data, or having the functions update the data in place.

Another thing that would be problematic in a larger program is that the output logic is mixed with the game logic. This can make it harder to change the code without breaking something unrelated to the thing you wanted to change. A central, important idea in programming is to separate different concepts as much as possible, so that the individual parts are simpler and have fewer reasons to change.

Here's how your main loop could look like with these concepts applied:

-- returns a table containing the player's data
local player = create_player()
repeat
    local enemy = create_enemy()
    local input = get_player_input()
    -- implements the attack/flee logic and returns a table that represents the outcome, so that it can be printed later
    local player_outcome = perform_player_action(player, enemy, input)
    local enemy_outcome = perform_enemy_action(player, enemy)
    print_turn(player_outcome, enemy_outcome) -- all the output happens here
until player.hp <= 0 or not enemy.hp <= 0 or player_outcome.escaped
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.