1

I'm new to programming and am stuck with this problem in a small RPG game I'm making. It's mostly text based but uses tkinter for a simple GUI.

During battles, I have a for loop that goes through all creatures that are participating in the battle, and executes their action one by one with small sleeps between each action. My problem is that when the user presses keys or clicks buttons, the commands are buffered and executed after the for loop completes. Is there an easy way to ignore the user commands while this loop is going? I've tried unbinding keys and disabling buttons, then re-enabling them when the loop is complete, but it still doesn't work. I've also read about flushing the user input but I can't figure it out. Since I'm a rookie, I think I'm missing some basic concept here.

Here's my code:

def battle_round(self, command):
    ''' (Battle, command) -> NoneType
    A round of battle where every creature gets an action.
    '''
    # Pause player input with mode variable.

    self.player.mode = 'wait'

    # Keep track of number of rounds.
    self.round_number += 1

    # Get initiative order for all mobs, party members, and player.
    ordered_creatures = self.initiative()

    # Get commands from each mob.
    for mob in self.mobs:
        mob.command = self.determine_mob_command()

    # Check battle speed option.
    delay = 1

    # Begin actions for all creatures.
    for creature in ordered_creatures:
        # Delay between actions. Write a space between lines.
        self.text_window.update_idletasks()
        self.write(self.text_window, '\n')
        time.sleep(delay)
        # Player action.
        if type(creature) == Player:
            if command == 'standard_attack':
                self.standard_player_attack()
            if command == 'retreat':
                self.retreat()

        if type(creature) == PartyMember:
            pass

        # MOB action.  
        if type(creature) == MOB:
            if creature.command == 'standard_attack':
                self.standard_mob_attack(creature)

    self.start_next_round()

I'm using Python 3.2.3 with Tk 8.5 and IDLE 3.2.3. My OS is Windows 7.

Thanks in advance!

Edit: Thanks for the replies so far guys. I may be in over my head here since I didn't even know what threading was until just now, and I'm not sure how I would go about reading and ignoring user input. As far as the code for the user input goes, I have a lot of it. I'll copy and paste some here:

def attack():
    if player.in_battle == True:
        if player.mode != 'wait':
            player.current_battle.battle_round('standard_attack')

def retreat():
    if player.in_battle == True:
        if player.mode != 'wait':
            player.current_battle.battle_round('retreat')

# Set up battle buttons.
attack_button = Button(battle_button_frame, text='(A)ttack', command=attack,
                     width=10,)
attack_button.grid(column=1, columnspan=1, row=2, padx=5, pady=5)

retreat_button = Button(battle_button_frame, text='(R)etreat', command=retreat,
                     width=10,)
retreat_button.grid(column=2, columnspan=1, row=2, padx=5, pady=5)


battle_button_list = [attack_button, retreat_button]

These buttons, for example are to have the user either attack the selected monster, or attempt to run away from the battle.

I also have several key bindings:

# Bind Keys
root.bind('<Escape>', func=keyboard_cancel)

root.bind('<Control-Key-m>', func=keyboard_move_mode)
root.bind('<Control-Key-M>', func=keyboard_move_mode)
root.bind('<Control-Key-l>', func=keyboard_look_mode)
root.bind('<Control-Key-L>', func=keyboard_look_mode)
root.bind('<Control-Key-t>', func=keyboard_talk_mode)
root.bind('<Control-Key-T>', func=keyboard_talk_mode)

root.bind('<space>', func=keyboard_begin_battle)

root.bind('<Left>', func=arrow_key_select_left)
root.bind('<Right>', func=arrow_key_select_right)

My problem remains that when the for loop with the sleeps is going, if the user presses a button or uses a key binding, it will get executed as soon as the battle round is over. (About 7 seconds if there are 6 monsters and the player.) I am a complete beginner at this so I apologize for not being clear with my post and for my code being a horrible mess.

Milkus
  • 11
  • 3
  • What about using threading for that loop? – solusipse Aug 03 '13 at 22:36
  • 1
    Your problem is about handling user input, but you pasted the code portion about something else, as far as I can tell. As far as I can guess, "flushing" user input by reading it and ignoring the results is correct. Maybe you have a bug in there, but we don't see the corresponding code. – Armin Rigo Aug 04 '13 at 07:55
  • You could avoid the problem alltogether by simply not using `time.sleep` and instead some calculations using `time.time` in the event loop. – Benproductions1 Apr 15 '14 at 02:13
  • I believe `widget['state'] = 'disabled'` is enough to make a widget unresponsive. Restore it with `'normal'`. – Terry Jan Reedy Nov 06 '14 at 04:21

0 Answers0