0

I'm writing an angry-birds-like game, and I want that when the player types 'g', and then 'o', and then 'd' ('god', for 'god mode'), a target will apear on the spot that the bird will currently (given the degrees and initial velocity) land on (if you'd fire it). - god mode is available 3 times in a game.

So I tried class, and storing each two-last chars typed: [I use tkinter and Image, ImageTk from PIL]

class GodMode:

times_left = 3   # Default

def __init__(self, master, image_file):
    self.master = master
    self.target_image = Image.open(image_file) 
    self.target = ImageTk.PhotoImage(self.target_image.resize((RED_CROSS_WIDTH, RED_CROSS_HEIGHT), Image.ANTIALIAS))

def display(self, bird):
    self.times_left -= 1
    self.x = physics.distance_traveled(bird)
    self.y = TARGET_Y_LOCATION
    self.master.create_image(self.x, self.y, image = self.target)

def typing_god(self, keystroke, bird):
    '''This function is first called when "g" is typed, from another 
    function (who is binded in the "main" function)
    '''
    if keystroke is None:
        self.last_char = "g"
    else:
        if keystroke.char == "d" or keystroke.char == "D":
            if self.last_char == "o" and self.char_before_last == "g":
                if self.times_left == 3:
                    self.god_mode_start(bird)
                elif 0 < self.times_left:
                    self.god_mode_alert(bird)
                else:
                    help_over()
                    return
        else:
            self.char_before_last = self.last_char
            self.last_char = keystroke.char
    self.master.bind('<Key>', lambda event: typing_god(event, bird))

def god_mode_start(self):
    start_input = messagebox.askyesno(title = "GOD Mode", message = "The god mode is only optional 3 times. Are you sure?")
    if not start_input:
        return
    self.display(bird)

def god_mode_alert(self, bird):
    start = messagebox.showinfinfo(title = "GOD Mode", message = GOD_MODE_FORMAT % self.gode_mode_left)
    if not start:
        return
    self.display(bird)

@staticmethod
def help_over():
    messagebox.showinfo(title = "GOD Mode not available", message = "You have used all your help.")

...but that didn't work, I can't tell why.

Thanks a lot for any help!

David

David
  • 45
  • 6
  • But what is the error exactly? – mic4ael Aug 24 '15 at 13:48
  • No error, it simply doesn't work. A target does not apear in the place the bird would have land if you'd fired it. – David Aug 24 '15 at 13:50
  • 1
    `self.master.bind('', lambda event: typing_god(event, bird))` that looks suspicious - are you trying to call `typing_god` from within `GodMode` class? If so, then you should change it to `self.typing_god` – mic4ael Aug 24 '15 at 13:53
  • You're right. I changed it, but it still didn't work. – David Aug 24 '15 at 13:56
  • For extra challenge, write a solution that also works for cheat codes "XYZZY" and "idspispopd", and doesn't use a variable named `char_before_char_before_char_before_char_before_char_before_char_before_char_before_char_before_char_before_last`. – Kevin Aug 24 '15 at 14:00

3 Answers3

0

It looks like self.master.bind('<Key>', lambda event: typing_god(event, bird)) may never be getting called, have you added a print around this statement to check?

You may want to instead put it in the constructor, and use self.typing_god:

def __init__(self, master, image_file):
    self.master = master
    self.target_image = Image.open(image_file) 
    self.target = ImageTk.PhotoImage(self.target_image.resize((RED_CROSS_WIDTH, RED_CROSS_HEIGHT), Image.ANTIALIAS))
    # Add a debug statement to check that this method is getting called
    print "Binding key events to self.typing_god"
    # Bind the key down event in the constructor
    self.master.bind('<Key>', lambda event: self.typing_god(event, bird))
Julia Schwarz
  • 2,610
  • 1
  • 19
  • 25
0

You can string events together when doing a bind. So, for example, you can bind on the three character sequence "god" (eg: "<g><o><d>")

def god_mode(event):
    print("god mode is activated")
some_widget.bind("<g><o><d>", god_mode)

Assuming that some_widget has keyboard focus, when you press the keys "g", "o" and "d" in that order, the message will print.

This is a good opportunity to use virtual events. You can define your own event and bind to it instead:

some_widget.event_add("<<GodMode>>", "<g><o><d>")
some_widget.bind("<<GodMode>>", god_mode)
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
-1

I would use a deque for that. You can set the maxlen = 3. And you can append to it when you get a new keystroke. Checking if you have god in the deque is also easy:

In [1]: from collections import deque
In [2]: d = deque(maxlen=3)
In [3]: d.append('g') # new keystroke
In [4]: d.append('o') # new keystroke
In [5]: d.append('d') # new keystroke
In [6]: d
Out[6]: deque(['g', 'o', 'd'], maxlen=3)
In [7]: ''.join(d) == 'god'  # check if we already in the god mode
Out[7]: True
In [8]: d.append('x') # new keystroke
In [9]: d
Out[9]: deque(['o', 'd', 'x'], maxlen=3)
In [10]: ''.join(d) == 'god' # check if we already in the god mode
Out[10]: False
Sait
  • 19,045
  • 18
  • 72
  • 99