Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

preface

In this episode we’ll take you a step further with our Tower mini-game, which will focus on completing the events that can be triggered when you come into contact with other, more complex map elements.

Without further ado, let’s begin happily

The development tools

Python version: 3.7.4

Related modules:

Pygame module;

And some modules that come with Python.

Environment set up

Install Python and add it to the environment variables. PIP installs the required related modules.

Introduction of the principle

Last time, we implemented some simple events that trigger when warriors come in contact with map elements, like the one below:

Obviously, this rendering is not perfect. For example, the time display on the left panel and the current layer display are not added. Here we can write a few lines of code to add them:

# -- Left panel bar
font = pygame.font.Font(self.cfg.FONTPATH_CN, 20)
font_renders = [
    self.hero.font.render(str(self.map_level_pointer), True, (255.255.255)),
    font.render('Game time:' + str(pygame.time.get_ticks() // 60000) + 'points' + str(pygame.time.get_ticks() // 1000 % 60) + 's'.True, (255.255.255)),
]
rects = [fr.get_rect() for fr in font_renders]
rects[0].topleft = (150.530)
rects[1].topleft = (75.630)
for fr, rect in zip(font_renders, rects):
    screen.blit(fr, rect)
Copy the code

After adding this section of code, it looks like this:

And then, as we said last time, in the original game, when the warrior and the fairy collide, there’s a dialog box that goes something like this:

How do you do that? First, you can be sure that the dialog box is made up of four parts: the rectangle, the background color filled in the rectangle, the character icon in the upper left corner, and the text content. Their implementation ideas should be:

Rectangles: Call PyGame to draw rectangles. Pygame.draw.rect; Background fill: Import the black tile from the background image to fill the dialog box. The character icon in the upper left corner: "PyGame.image.load" is imported and drawn to the corresponding position. Text: mainly call Pygame.font.Font implementation.Copy the code

Specifically, our code implementation is as follows:

"The Fairy and the Warrior speak."
def showconversationheroandfairy(self, screen, scenes) :
    Dialog box pointer
    conversation_pointer = 0
    # Define all conversations
    conversations = [
        ['... '], 
        ['You're awake! '], 
        ['... '.'Who are you? Where am I? '],
        ['I'm the fairy here, and you just got killed.'.'The little monster is knocked out.'],
        ['... '.'Sword, sword, where is my sword? '],
        ['They took your sword. I only had time.'.'To get you out.'],
        ['And the princess? I have come to save the princess.'],
        ['The princess is still in there. You can't go in there like that.'.'Past the little monster inside.'],
        ['What about me? I promised the king I would.'."Saved the princess. What am I supposed to do now?".'What should I do? '],
        ['Don't worry, I'll lend you my power, you.'.'and you can beat the little monsters. But you '.'You have to find something for me first. Find it.'.'Meet me here again.'],
        ['Looking for something? What are you looking for? '],
        ['It's a cross with a red one in the middle.'.'precious stones.],
        ['Is that any good? '],
        ['I was the guardian of this tower, but not long ago,'.'There's demons from the north. They've taken over.'.'This tower, and it seals my magic in here.'.'Inside the cross, if you can carry it out.'.'Come, and my power will be slowly restored,'.'Then I can lend you my strength.'."Save the princess. Remember, only with my magic.".'Before you open the door on the twenty-first floor.'],
        ['... '.'Okay, I'll try.'],
        ['I've just been looking. Your sword is in three.'."Building, your shield is on the fifth floor, and the cross.".The rack was placed on the seventh floor. To get to the seventh floor, you have to '.'Take your sword and shield first. And the one in the tower..'On other floors, there are hundreds of others.'.'The sword and the treasure of the year, if you get them,'.'Great for you to deal with the monsters in here.'.'Help.'],
        ['But how do I get in? '],
        ['I have three keys here. You take them first..'There are more of them in the tower, you.'.'Be sure to use it wisely. Brave go, brave man! ']]# main loop
    clock = pygame.time.Clock()
    while True:
        screen.fill((0.0.0))
        screen.blit(self.background_images['gamebg'], (0.0))
        self.map_parser.draw(screen)
        for scene in scenes:
            screen.blit(scene[0], scene[1])
        self.hero.draw(screen)
        # -- Key detection
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    conversation_pointer += 1
                    if conversation_pointer >= len(conversations): return
        # -- Draw dialog box
        conversation = conversations[conversation_pointer]
        font = pygame.font.Font(self.cfg.FONTPATH_CN, 20)
        # - warrior
        if conversation_pointer % 2= =0:
            left, top, width, height = 510.430.7.2
            pygame.draw.rect(screen, (199.97.20), (left - 4, top - 4, self.cfg.BLOCKSIZE * width + 8, self.cfg.BLOCKSIZE * height + 8), 7)
            id_image = self.hero.images['down']
        # - fairy
        else:
            left, top, width, height = 300.250.7.2
            if len(conversation) > 3: height = 3
            if len(conversation) > 5: height = 4
            if len(conversation) > 7: height = 5
            pygame.draw.rect(screen, (199.97.20), (left - 4, top - 4, self.cfg.BLOCKSIZE * width + 8, self.cfg.BLOCKSIZE * height + 8), 7)
            id_image = pygame.image.load(self.cfg.MAPELEMENTSPATHS['24'] [0])
        Color # -
        filepath = self.cfg.MAPELEMENTSPATHS['0'] [0]
        for col in range(width):
            for row in range(height):
                image = pygame.image.load(filepath)
                image = pygame.transform.scale(image, (self.cfg.BLOCKSIZE, self.cfg.BLOCKSIZE))
                screen.blit(image, (left + col * self.cfg.BLOCKSIZE, top + row * self.cfg.BLOCKSIZE))
        # ---- icon in upper left corner
        screen.blit(id_image, (left + 10, top + 10))
        Text in the # ---- dialog box
        for idx, text in enumerate(conversation):
            font_render = font.render(text, True, (255.255.255))
            rect = font_render.get_rect()
            rect.left, rect.top = left + self.cfg.BLOCKSIZE + 20, top + 10 + idx * 30
            screen.blit(font_render, rect)
        # - refresh
        pygame.display.flip()
        clock.tick(self.cfg.FPS)
Copy the code

Specific renderings are as follows:

The next thing we need to achieve is the monster fighting scene. According to the original game, when a warrior touches a monster on the map, if the warrior can defeat the monster, the battle mode will automatically trigger:

To do this, we first need to define the health, attack and defense values of all monsters on the map. Specifically, the code is defined as follows:

# All monster stats on the map: name, health, damage, defense
self.monsters_dict = {
    '40': (The Green-headed Monster.50.20.1),
    'and': (The Red-headed Monster.70.15.2),
    The '42': ('Little Bat'.100.20.5),
    '43': (The Green-headed Monster.200.35.10),
    '44': (Skull Man.110.25.5),
    '45': (Skeleton Soldiers.150.40.20),
    '46': ('Beast Man'.300.75.45),
    '47': ('Junior Guard'.450.150.90),
    ', 48 ': ('Big Bat'.150.65.30),
    '49': ('Red Bat'.550.160.90),
    '50': (The Knight in White.1300.300.150),
    '51': ('blame the king'.700.250.125),
    '52': ('The Red Wizard'.500.400.260),
    '53': ('The Red Lord'.15000.1000.1000),
    '54': (Golden Armor guard.850.350.200),
    '55': (Captain Gold.900.750.650),
    '56': (Captain Skull.400.90.50),
    '57': ('Psychic'.1500.830.730),
    '58': (Spirit Warrior.1200.980.900),
    '59': (Lord of the Underworld.30000.1700.1500),
    '60': ('Sackcloth Wizard'.250.120.70),
    '61': (Destiny Warrior.2000.680.590),
    '62': (Captain Destiny.2500.900.850),
    '63': ('Junior Mage'.125.50.25),
    '64': ('Senior Mage'.100.200.110),
    '65': (Stone Freak.500.115.65),
    '66': ('Beast Warrior'.900.450.330),
    '67': ('Swordsman with two hands'.1200.620.520),
    '68': ('Destiny Guard'.1250.500.400),
    '69': ('Senior Guard'.1500.560.460),
    '70': (Shadow Warrior.3100.1150.1050),
    '188': ('blood shadow'.99999.5000.4000),
    '198': ('the magic dragon'.99999.9999.5000),}Copy the code

Among them, the keys of the dictionary and the first phase of the classic childhood memories | from scratch with you rolled a found little game ah (1) defined in the map file number corresponding to the map element of meaning is the same. Next, in order to achieve the same functionality as the original, we need to write a function to determine whether the warrior’s current state can defeat the monster:

Determine if the warrior can beat the monster.
def winmonster(self, monster) :
    # If the damage is lower than the monster's defense, monster:
    if self.attack_power <= monster[3] :return False
    # If defense is higher than monster damage
    if self.defense_power >= monster[2] :return True
    How much health do we take when we hit a monster
    diff_our = self.attack_power - monster[3]
    # How much health does a monster take from us
    diff_monster = monster[2] - self.defense_power
    # Calculate who can win
    if round(monster[1] / diff_our) <= round(self.life_value / diff_monster):
        return True
    return False
Copy the code

If so, the battle scene is triggered. To be specific, we have already made the basic panel of the battle. We just need to draw the corresponding monsters, warriors and their respective states each time. The principle is similar to the following:

The main code implementation is as follows:

"Battle screen"
def battle(self, monster, monster_image, map_parser, screen) :
    monster = list(monster).copy()
    How much health do we take when we hit a monster
    diff_our = self.attack_power - monster[3]
    # How much health does a monster take from us
    diff_monster = monster[2] - self.defense_power
    Update the frequency of the battle panel
    update_count, update_interval, update_hero = 0.10.False
    # main loop
    clock = pygame.time.Clock()
    font = pygame.font.Font(self.cfg.FONTPATH_CN, 40)
    while True:
        screen.fill((0.0.0))
        screen.blit(self.background_images['gamebg'], (0.0))
        map_parser.draw(screen)
        for scene in self.cur_scenes:
            screen.blit(scene[0], scene[1])
        self.draw(screen)
        # -- Key detection
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
        Update the Combat panel
        update_count += 1
        if update_count > update_interval:
            update_count = 0
            if update_hero:
                self.life_value = self.life_value - (monster[2] - self.defense_power)
            else:
                monster[1] = max(monster[1] - (self.attack_power - monster[3]), 0)
            update_hero = not update_hero
            if monster[1] < =0: return
        screen.blit(self.background_images['battlebg'], (20.40))
        screen.blit(monster_image, (90.140))
        font_renders = [
            font.render(str(monster[1]), True, (255.255.255)),
            font.render(str(monster[2]), True, (255.255.255)),
            font.render(str(monster[3]), True, (255.255.255)),
            font.render(str(self.life_value), True, (255.255.255)),
            font.render(str(self.attack_power), True, (255.255.255)),
            font.render(str(self.defense_power), True, (255.255.255)),
        ]
        rects = [fr.get_rect() for fr in font_renders]
        for idx in range(3):
            rects[idx].top, rects[idx].left = 78 + idx * 95.320
        for idx in range(3.6):
            rects[idx].top, rects[idx].right = 78 + (idx - 3) * 95.655
        for fr, rect in zip(font_renders, rects):
            screen.blit(fr, rect)
        # - refresh
        pygame.display.flip()
        clock.tick(self.cfg.FPS)
Copy the code

The principle is actually very simple, our warriors first launched an attack, then the monster launched an attack, and so on, during this process, the battle panel updates the status of the current warriors and monsters in real time. The final result is shown below:

This issue complete source code details personal home page profile access ~

This is the end of the article, thank you for watching Python29 small games, next article to share the magic Tower small games.

To thank you readers, I’d like to share some of my recent programming favorites to give back to each and every one of you in the hope that they can help you.