summaryrefslogtreecommitdiffstats
path: root/Actors.py
blob: b713c30fe2c8e8acaa38883b6e83f26ce1b064ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
from datetime import datetime, timedelta
import threading

from Setup import *
from Particle import Dust
# from PhysicsBody import PhysicsBody
from Block import Block


class Actor:
    width, height = 50, 100
    colour = (76, 82, 92)
    speed = 0.6
    jump_strength = 1
    gravity = 0.2
    friction = 0.2
    coyote_time_amount = timedelta(milliseconds=100)  # seconds after walking off ground that it still counts
    jump_buffer = []
    variable_jump_time = timedelta(milliseconds=125)  # how long to hold jump so we go higher
    terminal_velocity = 10
    invincibility_time = 150


    def __init__(self, pos, collision_layer, collision_mask):
        self.position = pg.Vector2(pos)
        self.velocity = pg.Vector2(0, 0)

        self.on_ground = False

        collision_layer.add(self)  # the layer the actor is on for collisions
        self.collision_layer = collision_layer
        self.collision_mask = collision_mask  # the layer the actor detects collisions against

        self.health = 100
        self.dead = False

        self.areas = dict()

        self.movable = False

        self.jumping = False
        self.coyote_time = datetime.utcnow()
        self.hold_jump = datetime.utcnow()
        self.stun_time = 1000
        self.attacked = False
        self.invincibility_frames = 0

    def update(self, delta):
        if self.jumping:
            if self.on_ground:
                self.jumping = False
                Dust(pg.Vector2(self.get_collision_rect().midbottom) + pg.Vector2(-16,-15), 32, 0)

        self.stun_time -= delta
        self.stun_time = max(self.stun_time, 0)

        self.invincibility_frames -= delta
        self.invincibility_frames = max(self.invincibility_frames, 0)
        if self.invincibility_frames == 0 and self.on_ground:
            self.attacked = False

        for area in self.areas.values():
            area.update(delta, self.position)

        if self.is_dead():
            return

        # Apply friction so the enemy isn't walking on ice
        if self.on_ground:
            self.velocity.x *= self.friction

            # Apply gravity
        if self.jumping and self.velocity.y > 0:
            self.velocity.y += self.gravity * 2
        else:
            self.velocity.y += self.gravity
        self.velocity.x = max(min(self.velocity.x, self.terminal_velocity), -self.terminal_velocity)
        self.velocity.y = max(min(self.velocity.y, self.terminal_velocity), -self.terminal_velocity)

    def is_dead(self, reason=None):
        if self.health <= 0:
            self.dead = True

    def modify_health(self, amount, reason):
        self.health += amount
        if amount < 0:
            self.stun_time = -amount * 10
        self.is_dead(reason)

    def attack(self, enemy, weapon, direction):
        if not self.attacked and self.invincibility_frames == 0:
            self.push(direction, 2, -1)
            self.modify_health(weapon.damage, "enemy")
            self.attacked = True
            self.invincibility_frames = self.invincibility_time

    def follow_target(self, node, follow_range=100000, stop_dist=115):
        # if stop_dist is None:
        #     stop_dist = max(self.height, self.width) * 1.5

        target = node.position

        # So that actor doesn't come up and hug u lol
        distance_between = ((self.position+pg.Vector2(self.width,self.height)/2) - (target+pg.Vector2(node.width,node.height)/2)).length_squared()

        if abs(self.position.x - target.x) < stop_dist:
            self.velocity.x = 0
            return
        if distance_between > follow_range ** 2:
            return

        if target.x + node.width < self.position.x:
            self.move_left()
        elif target.x > self.position.x:
            self.move_right()
        if (not (target.y - node.height/2< self.position.y < target.y + node.height/2)) and target.y < self.position.y + self.height/2:
            self.jump()

    def jump(self):
        # print(self)
        if self.stun_time > 0:
            return
        if (self.on_ground and not self.jumping) or (datetime.utcnow() - self.hold_jump <= self.variable_jump_time):
            self.velocity.y = -self.jump_strength
            if not self.jumping:
                self.hold_jump = datetime.utcnow()
            self.jumping = True
        # elif not (datetime.utcnow() - self.hold_jump <= self.variable_jump_time):

    def dash_left(self):
        pass

    def dash_right(self):
        pass

    def move_left(self):
        if self.stun_time == 0:
            self.velocity.x = -self.speed

    def move_right(self):
        if self.stun_time == 0:
            self.velocity.x = self.speed

    def push(self, direction, strength=1, y=-1):
        self.velocity += pg.Vector2(direction * strength, y)

    def move_and_collide(self, pos, vel, delta):
        pos.x += vel.x * delta
        collision_rect = self.get_collision_rect(pos)
        for mask in self.collision_mask:
            for thing in mask:
                if thing == self:
                    continue
                if collision_rect.colliderect(thing.get_collision_rect()):
                    # print(self, self.position.x, self.velocity.x)
                    # if thing.movable:
                    #     if vel.x > 0:
                    #         thing.position.x = pos.x + self.width
                    #     elif vel.x < 0:
                    #         thing.position.x = pos.x - thing.width
                    if vel.x > 0:
                        pos.x = thing.position.x - self.width
                        vel.x = min(vel.x, 0)
                    elif vel.x < 0:
                        pos.x = thing.position.x + thing.width
                        vel.x = max(vel.x, 0)
                        # print(pos.x, vel.x)
                    collision_rect = self.get_collision_rect(pos)

        if datetime.utcnow() - self.coyote_time >= self.coyote_time_amount or self.jumping:
            self.on_ground = False
        pos.y += vel.y * delta
        collision_rect = self.get_collision_rect(pos)
        for mask in self.collision_mask:
            for thing in mask:
                if thing == self:
                    continue
                for area in self.areas.values():
                    if area.is_colliding(thing):
                        area.signal(thing)
                if collision_rect.colliderect(thing.get_collision_rect()):
                    if vel.y >= 0:
                        pos.y = thing.position.y - self.height
                        vel.y = min(vel.y, 0)
                        self.on_ground = True
                        self.coyote_time = datetime.utcnow()
                    elif vel.y < 0:
                        pos.y = thing.position.y + thing.height
                        vel.y = max(vel.y, 0)
                    collision_rect = self.get_collision_rect(pos)
        return pos, vel

    def get_collision_rect(self, pos=None):
        if pos is None:
            pos = self.position
        return pg.Rect(pos, (self.width, self.height))

    def draw(self, surf):
        pg.draw.rect(surf, self.colour, get_display_rect(self.get_collision_rect()), border_radius=8)
        # pg.draw.rect(surf, (0,0,0), self.get_collision_rect(), border_radius=8, width=2)

        # Uncomment for debugging area hitboxes
        for area in self.areas.values():
            area.draw(surf)