I wrote a turn-based combat sequence with the enemy occasionally using attacks that happen over two turns but in some cases they happen instantly

I’m learning Python, my first project was a tiny text-based game, now I remade it using Pygame. When I got to implementing combat I ran into this problem and failed to solve it on my own. What’s supposed to happen is the enemy goes idle for a turn, meaning a hard-hitting attack is incoming, and then after the player takes their turn the attack is delivered on the enemy’s second turn. And in most cases it behaves as it should. But if the player uses a defensive action (parry or counterspell) when the enemy is not preapring an attack then the idle part of the attack is skipped and it goes straight to instant damage.

Here’s the piece of the code that handles the player and the enemy’s turns:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> if not player_turn:
if sblade.hp < sblade.hp_max*0.3:
if not action_generated:
action = random.randint(1, 2)
action_generated = True
if action == 1:
sblade.idle_index = 0
sblade.is_interrupting = False
sblade.is_preparing_attack = False
sblade.is_preparing_spell = False
sblade.is_idle = True
third_status_message = f"{sblade.name} looks scared and is taking a defensive posture."
sblade.is_parrying = True
player_turn = not player_turn
elif action == 2:
sblade.idle_index = 0
sblade.is_parrying = False
sblade.is_preparing_attack = False
sblade.is_preparing_spell = False
sblade.is_idle = True
third_status_message = f"{sblade.name} looks scared and is preparing a counterspell"
sblade.is_interrupting = True
player_turn = not player_turn
else:
if sblade.is_preparing_attack:
if not virtual_keypress:
# enemy uses the same attack functions as the player and they rely on variables being set to certain
# values by the player pressing Enter so there's a virtual keypress for the enemy
keys_locked = True
sblade.is_moving = True
sblade.is_moving_forward = True
virtual_keypress = True
channeled_attack(sblade, lavender, SBLADE_CHARGED_ATTACK2_LIST)
elif sblade.is_preparing_spell:
if not virtual_keypress:
keys_locked = True
sblade.is_moving = True
sblade.is_moving_forward = True
virtual_keypress = True
channeled_attack(sblade, lavender, SBLADE_CHARGED_SPELL2_LIST)
else:
if not action_generated:
action = random.randint(1, 4)
action_generated = True
if action == 1:
if not virtual_keypress:
keys_locked = True
sblade.is_parrying = False
sblade.is_interrupting = False
sblade.is_moving = True
sblade.is_moving_forward = True
virtual_keypress = True
attack(sblade, lavender, SBLADE_ATTACK_LIST)
elif action == 2:
if not virtual_keypress:
keys_locked = True
sblade.is_parrying = False
sblade.is_interrupting = False
sblade.is_casting = True
virtual_keypress = True
spell(sblade, lavender)
elif action == 3:
sblade.idle_index = 0
sblade.is_interrupting = False
sblade.is_parrying = False
sblade.is_preparing_attack = True
sblade.status_message = f"{sblade.name} is looking for an opening."
player_turn = not player_turn
elif action == 4:
sblade.idle_index = 0
sblade.is_parrying = False
sblade.is_interrupting = False
sblade.is_preparing_spell = True
sblade.status_message = f"{sblade.name} is looking focused."
player_turn = not player_turn
elif player_turn:
if not keys_locked:
arrow_movement(keys_pressed, arrow_rect)
if arrow_rect.y == pos1[1]:
if keys_pressed[pygame.K_RETURN] and not keys_locked:
main.confirm_sound.play()
keys_locked = True
lavender.is_parrying = False
lavender.is_interrupting = False
lavender.is_moving = True
lavender.is_moving_forward = True
attack(lavender, sblade, LAVENDER_ATTACK_LIST)
if arrow_rect.y == pos2[1]:
if keys_pressed[pygame.K_RETURN] and not keys_locked:
main.confirm_sound.play()
keys_locked = True
lavender.is_parrying = False
lavender.is_interrupting = False
lavender.is_casting = True
spell(lavender, sblade)
if arrow_rect.y == pos3[1]:
if keys_pressed[pygame.K_RETURN] and not keys_locked:
main.confirm_sound.play()
lavender.is_parrying = True
lavender.is_interrupting = False
lavender.status_message = "Lavender is preparing to parry."
action_generated = False
virtual_keypress = False
player_turn = not player_turn
if arrow_rect.y == pos4[1]:
if keys_pressed[pygame.K_RETURN] and not keys_locked:
main.confirm_sound.play()
lavender.is_interrupting = True
lavender.is_parrying = False
lavender.status_message = "Lavender is preparing to interrupt a spell."
action_generated = False
virtual_keypress = False
player_turn = not player_turn
</code>
<code> if not player_turn: if sblade.hp < sblade.hp_max*0.3: if not action_generated: action = random.randint(1, 2) action_generated = True if action == 1: sblade.idle_index = 0 sblade.is_interrupting = False sblade.is_preparing_attack = False sblade.is_preparing_spell = False sblade.is_idle = True third_status_message = f"{sblade.name} looks scared and is taking a defensive posture." sblade.is_parrying = True player_turn = not player_turn elif action == 2: sblade.idle_index = 0 sblade.is_parrying = False sblade.is_preparing_attack = False sblade.is_preparing_spell = False sblade.is_idle = True third_status_message = f"{sblade.name} looks scared and is preparing a counterspell" sblade.is_interrupting = True player_turn = not player_turn else: if sblade.is_preparing_attack: if not virtual_keypress: # enemy uses the same attack functions as the player and they rely on variables being set to certain # values by the player pressing Enter so there's a virtual keypress for the enemy keys_locked = True sblade.is_moving = True sblade.is_moving_forward = True virtual_keypress = True channeled_attack(sblade, lavender, SBLADE_CHARGED_ATTACK2_LIST) elif sblade.is_preparing_spell: if not virtual_keypress: keys_locked = True sblade.is_moving = True sblade.is_moving_forward = True virtual_keypress = True channeled_attack(sblade, lavender, SBLADE_CHARGED_SPELL2_LIST) else: if not action_generated: action = random.randint(1, 4) action_generated = True if action == 1: if not virtual_keypress: keys_locked = True sblade.is_parrying = False sblade.is_interrupting = False sblade.is_moving = True sblade.is_moving_forward = True virtual_keypress = True attack(sblade, lavender, SBLADE_ATTACK_LIST) elif action == 2: if not virtual_keypress: keys_locked = True sblade.is_parrying = False sblade.is_interrupting = False sblade.is_casting = True virtual_keypress = True spell(sblade, lavender) elif action == 3: sblade.idle_index = 0 sblade.is_interrupting = False sblade.is_parrying = False sblade.is_preparing_attack = True sblade.status_message = f"{sblade.name} is looking for an opening." player_turn = not player_turn elif action == 4: sblade.idle_index = 0 sblade.is_parrying = False sblade.is_interrupting = False sblade.is_preparing_spell = True sblade.status_message = f"{sblade.name} is looking focused." player_turn = not player_turn elif player_turn: if not keys_locked: arrow_movement(keys_pressed, arrow_rect) if arrow_rect.y == pos1[1]: if keys_pressed[pygame.K_RETURN] and not keys_locked: main.confirm_sound.play() keys_locked = True lavender.is_parrying = False lavender.is_interrupting = False lavender.is_moving = True lavender.is_moving_forward = True attack(lavender, sblade, LAVENDER_ATTACK_LIST) if arrow_rect.y == pos2[1]: if keys_pressed[pygame.K_RETURN] and not keys_locked: main.confirm_sound.play() keys_locked = True lavender.is_parrying = False lavender.is_interrupting = False lavender.is_casting = True spell(lavender, sblade) if arrow_rect.y == pos3[1]: if keys_pressed[pygame.K_RETURN] and not keys_locked: main.confirm_sound.play() lavender.is_parrying = True lavender.is_interrupting = False lavender.status_message = "Lavender is preparing to parry." action_generated = False virtual_keypress = False player_turn = not player_turn if arrow_rect.y == pos4[1]: if keys_pressed[pygame.K_RETURN] and not keys_locked: main.confirm_sound.play() lavender.is_interrupting = True lavender.is_parrying = False lavender.status_message = "Lavender is preparing to interrupt a spell." action_generated = False virtual_keypress = False player_turn = not player_turn </code>
    if not player_turn:
        if sblade.hp < sblade.hp_max*0.3:
            if not action_generated:
                action = random.randint(1, 2)
                action_generated = True
            if action == 1:
                sblade.idle_index = 0
                sblade.is_interrupting = False
                sblade.is_preparing_attack = False
                sblade.is_preparing_spell = False
                sblade.is_idle = True
                third_status_message = f"{sblade.name} looks scared and is taking a defensive posture."
                sblade.is_parrying = True
                player_turn = not player_turn
            elif action == 2:
                sblade.idle_index = 0
                sblade.is_parrying = False
                sblade.is_preparing_attack = False
                sblade.is_preparing_spell = False
                sblade.is_idle = True
                third_status_message = f"{sblade.name} looks scared and is preparing a counterspell"
                sblade.is_interrupting = True
                player_turn = not player_turn
        else:
            if sblade.is_preparing_attack:
                if not virtual_keypress:
                    # enemy uses the same attack functions as the player and they rely on variables being     set to certain
                    # values by the player pressing Enter so there's a virtual keypress for the enemy
                    keys_locked = True
                    sblade.is_moving = True
                    sblade.is_moving_forward = True
                    virtual_keypress = True
                channeled_attack(sblade, lavender, SBLADE_CHARGED_ATTACK2_LIST)
            elif sblade.is_preparing_spell:
                if not virtual_keypress:
                    keys_locked = True
                    sblade.is_moving = True
                    sblade.is_moving_forward = True
                    virtual_keypress = True
                channeled_attack(sblade, lavender, SBLADE_CHARGED_SPELL2_LIST)
            else:
                if not action_generated:
                    action = random.randint(1, 4)
                    action_generated = True
                if action == 1:
                    if not virtual_keypress:
                        keys_locked = True
                        sblade.is_parrying = False
                        sblade.is_interrupting = False
                        sblade.is_moving = True
                        sblade.is_moving_forward = True
                        virtual_keypress = True
                    attack(sblade, lavender, SBLADE_ATTACK_LIST)
                elif action == 2:
                    if not virtual_keypress:
                        keys_locked = True
                        sblade.is_parrying = False
                        sblade.is_interrupting = False
                        sblade.is_casting = True
                        virtual_keypress = True
                    spell(sblade, lavender)
                elif action == 3:
                    sblade.idle_index = 0
                    sblade.is_interrupting = False
                    sblade.is_parrying = False
                    sblade.is_preparing_attack = True
                    sblade.status_message = f"{sblade.name} is looking for an opening."
                    player_turn = not player_turn
                elif action == 4:
                    sblade.idle_index = 0
                    sblade.is_parrying = False
                    sblade.is_interrupting = False
                    sblade.is_preparing_spell = True
                    sblade.status_message = f"{sblade.name} is looking focused."
                    player_turn = not player_turn

    elif player_turn:
        if not keys_locked:
            arrow_movement(keys_pressed, arrow_rect)
        if arrow_rect.y == pos1[1]:
            if keys_pressed[pygame.K_RETURN] and not keys_locked:
                main.confirm_sound.play()
                keys_locked = True
                lavender.is_parrying = False
                lavender.is_interrupting = False
                lavender.is_moving = True
                lavender.is_moving_forward = True
            attack(lavender, sblade, LAVENDER_ATTACK_LIST)
        if arrow_rect.y == pos2[1]:
            if keys_pressed[pygame.K_RETURN] and not keys_locked:
                main.confirm_sound.play()
                keys_locked = True
                lavender.is_parrying = False
                lavender.is_interrupting = False
                lavender.is_casting = True
            spell(lavender, sblade)
        if arrow_rect.y == pos3[1]:
            if keys_pressed[pygame.K_RETURN] and not keys_locked:
                main.confirm_sound.play()
                lavender.is_parrying = True
                lavender.is_interrupting = False
                lavender.status_message = "Lavender is preparing to parry."
                action_generated = False
                virtual_keypress = False
                player_turn = not player_turn
        if arrow_rect.y == pos4[1]:
            if keys_pressed[pygame.K_RETURN] and not keys_locked:
                main.confirm_sound.play()
                lavender.is_interrupting = True
                lavender.is_parrying = False
                lavender.status_message = "Lavender is preparing to interrupt a spell."
                action_generated = False
                virtual_keypress = False
                player_turn = not player_turn

The non-channelled attacks still happen when the player uses a defesive action so, since they’re in the same action pool as initiations of the channelled attacks, I’m guessing that the first half of a channelled attack still happens but then for some reason instead of passing the turn to the player it goes back to the beginning and the second half gets triggered. And if the player isn’t using a defesive action then it passes the turn as it should.

I don’t even see why it would be like that and so where to start with looking for a solution. Maybe I’m just too fatigued at this point becasue so far I’ve been figuring stuff and my own without asking anyone and while successful with everything except this one thing it also made the whole process take a veeeeryyyy long time. And I also got some brain lag and decided to make the player’s defesive actions set keys_locked to True even though it’s only ever a condition for allowing player input but for some reason it actually solved the problem which just confused me even more. Did I just go blind and can’t see how it makes a difference? But that’s not a valid solution, as to enable player input for subsequent turns the enemy’s actions that set up channelled attacks would need to set keys_locked back to False and when I made them do that channelled attacks went back to happenning instantly if the player uses a defensive action.

New contributor

Nemoniah is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật