skoolkid / pyskool

A remake of Skool Daze and Back to Skool in Python and Pygame.
https://pyskool.ca
GNU General Public License v3.0
16 stars 4 forks source link

IndexError: list index out of range (Python 3.8.1, MacOS Monterey 12.6) #15

Open cesss opened 2 years ago

cesss commented 2 years ago

Any idea of what can be the reason for this problem? It happens to me whenever I start any of the games: The window appears, as well as the welcome music (with the only weird thing that the character sprites seem to have green paper and BRIGHT 1 in their cells), but then even before I press any key, the window disappears.

This is what I get in the Terminal:

./skool_daze.py 
pygame 2.1.2 (SDL 2.0.18, Python 3.8.1)
Hello from the pygame community. https://www.pygame.org/contribute.html
Using ini file at /Users/mike/games/pyskool/pyskool-master-2022-10-16/pyskool/data/pyskool.ini
Using game ini files in /Users/mike/.pyskool/ini/skool_daze
Using images in /Users/mike/.pyskool/images
Using sounds in /Users/mike/.pyskool/sounds
Using /Users/mike/.pyskool to save/load games
Reading /Users/mike/games/pyskool/pyskool-master-2022-10-16/pyskool/data/pyskool.ini
Reading /Users/mike/.pyskool/ini/skool_daze/command_lists.ini
Reading /Users/mike/.pyskool/ini/skool_daze/config.ini
Reading /Users/mike/.pyskool/ini/skool_daze/font.ini
Reading /Users/mike/.pyskool/ini/skool_daze/lessons.ini
Reading /Users/mike/.pyskool/ini/skool_daze/messages.ini
Reading /Users/mike/.pyskool/ini/skool_daze/skool.ini
Reading /Users/mike/.pyskool/ini/skool_daze/sprites.ini
Traceback (most recent call last):
  File "./skool_daze.py", line 24, in <module>
    main(os.path.abspath(os.path.dirname(__file__)))
  File "/Users/mike/games/pyskool/pyskool-master-2022-10-16/pyskool/run.py", line 238, in main
    game.play()
  File "/Users/mike/games/pyskool/pyskool-master-2022-10-16/pyskool/game.py", line 386, in play
    if self._main_loop():
  File "/Users/mike/games/pyskool/pyskool-master-2022-10-16/pyskool/game.py", line 461, in _main_loop
    self.scroll = self.skool.move_characters()
  File "/Users/mike/games/pyskool/pyskool-master-2022-10-16/pyskool/skool.py", line 272, in move_characters
    return self.cast.move()
  File "/Users/mike/games/pyskool/pyskool-master-2022-10-16/pyskool/cast.py", line 151, in move
    return self.eric.move()
  File "/Users/mike/games/pyskool/pyskool-master-2022-10-16/pyskool/eric.py", line 154, in move
    if self.keyboard.pressed(keys.LEFT):
  File "/Users/mike/games/pyskool/pyskool-master-2022-10-16/pyskool/input.py", line 96, in pressed
    return self.was_pressed(keys) or self.is_pressed(keys)
  File "/Users/mike/games/pyskool/pyskool-master-2022-10-16/pyskool/input.py", line 69, in is_pressed
    if self.pressed_keys[key]:
IndexError: list index out of range
cesss commented 2 years ago

By the way, you can take a look at the weird color of the sprites here (as I said, the window appears just to play the welcome music, then it disappears with the error above)

screen
cesss commented 2 years ago

I was able to do some debugging:

The "list index out of range" happens because the SDL key values for the cursor keys and other special keys have a high value (like 1073741904 for example), and for some reason this is not friendly with your source code. I removed the offending keys, and now the game seems to run (but keys are not 100% fine: keeping pressed "o", "p", "q", "a" doesn't work as expected, only the keypress event works, so I get only one walking step per key press).

The problem with the bright green paper in sprites happens only when the sprite is walking left to right, while it's correct when it walks right to left. So, there seems to be something wrong in the code where you perform the symmetry.

cesss commented 2 years ago

Workaround for the bright green paper in "left to right" sprites: increase the scale, and decrease it back, and it's fixed. I have no idea why this works.

Now I need to find out why only keypress events work for "o", "p", "q", "a". I'm a C programmer, with almost no idea of Python, so I'm not sure if I'll succeed...

skoolkid commented 2 years ago

I'm not sure why you're having these problems. I've never tested Pyskool against Pygame 2, so I wonder if the problems are caused by Pygame 1/2 incompatibilities. I will try to set up an environment with Pygame 2 soon. In the meantime, good luck with your debugging.

cesss commented 2 years ago

It looks like the behaviour of pressed keys has been changed in the current Pygame versions. I even tried to call again self.pressed_keys = list(pygame.key.get_pressed()) at line 68 of input.py, so that the check of whether a key is pressed or not is performed with fresh values, but even if I do this, keeping a key pressed doesn't work... you need to press it many times for getting the keydown events...

skoolkid commented 2 years ago

Took a quick look and yes, this is a Pygame 1/2 incompatibility. The type of pygame.key.get_pressed() changed from a simple sequence to something called a ScancodeWrapper between versions 1 and 2. I'll investigate the proper fix for this in due course, but for now, there is a quick (and possibly improper) fix on the pygame2 branch, if you want to try that.

As for the weird green graphics problem, no idea I'm afraid.

cesss commented 2 years ago

It works, thanks!! Only thing, you have to delete line 70 in the file, as well, because ScancodeWrapper cannot be written into. It seems to work fine now! Thanks!!

Regarding the graphics glitch, scaling up and down fixes it for me. Now I have it in usable status 😃

cesss commented 2 years ago

Hi again, @skoolkid , sorry to bother you with this Python question, but as I said I'm a C/C++ programmer, and I'm having difficulty understanding how a Python class is accessing, with self, a variable that doesn't exist in the class.

For example, in ai.py, there's this code for defining the GoTo command class:

class GoTo(Command):
    """Command that makes a character go to a location.
    :param location_id: The ID of the location to go to (may be `None`).
    :type destination: :class:`~pyskool.location.Location`
    :param destination: The location to go to (required if `location_id`
                        is `None`).
    :param go_one_step: `True` if this command should terminate after
                        sending the character one step towards the
                        destination.
    """
    def __init__(self, location_id, destination=None, go_one_step=False):
        self.location_id = location_id
        self.destination = destination
        self.go_one_step = go_one_step
        self.done = False

    def execute(self):
        """Make a character take the next step towards his destination.
        :return: `self` if the character has already reached his destination,
                 `None` otherwise.
        """
        if self.destination is None:
            self.destination = self.character.resolve_location_id(self.location_id)
            if self.destination is None:
                debug.log("Cannot resolve location ID '%s' for %s" % (self.location_id, self.character.name))
                return
[...snipped...]

So, GoTo is a class which inherits from Command, but the thing is that self.character doesn't exist in the Command nor in the GoTo class. Where is self.character ???

I imagine some dynamic type (re)definition must be happening here at runtime, but I searched for it and couldn't find a place in which you make Commands inherit from another class that has a character...

skoolkid commented 2 years ago

It's been a few years since I last looked at this code, but IIRC, a Command object is assigned its character attribute when it's added to a command list. See the add_command method of CommandList in ai.py:

def add_command(self, command):
    """Add a command to the stack.

    :param command: The :class:`Command` to add.
    """
    command.character = self.character
    self.stack.append(command)

Python supports this kind of dynamic attribute assignment. There's no need for Command to inherit character from a superclass.

cesss commented 2 years ago

Oh, thanks a lot! I skipped the CommandList source code and went directly to the Command source because I thought CommandList was just a manager that didn't add any interesting stuff, but I was obviously wrong. Thanks!