class HeroSprite(EntitySprite): """The hero sprite.""" Size = euclid.Vector2(300, 425) # Card size (original). SizeBase = Size // 2 # Coordinate base of children sprites. ImagePart = 0.10, 0.35, 0.76, 0.65 # Start x, start y, width, height ImageScale = 1.15 def __init__(self, user, hero, position=(0, 0), scale=1.0, **kwargs): self.user = user # The border that indicate the status of this sprite (inactive, active, highlighted). self.attack_label = None self.health_label = None self.armor_sprite = None self.armor_label = None self.frozen_sprite = None super().__init__(hero, position, scale, **kwargs) def _get_frozen_sprite(self): if self.frozen_sprite is None: self.frozen_sprite = Sprite( 'Frozen.png', pos(-0.00, 0.10, base=self.SizeBase), scale=1.0, opacity=200, ) return self.frozen_sprite def _build_components(self): border_rect = rect.Rect(0, 0, self.Size[0], self.Size[1]) border_rect.center = (0, 0) self.activated_border = Rect(border_rect, self.SelectedColor, width=4) self._create_status_border(border_rect, width=2, z=4) # Hero image (background and blank foreground). image = try_load_image('Hero-{}.png'.format(self.entity.id), image_part=self.ImagePart) if image is not None: self.add(Sprite(image, pos(0.05, 0.255, base=self.SizeBase), scale=self.ImageScale), z=0) self.add(Sprite('Hero.png', pos(0, 0, base=self.SizeBase), scale=1.0, opacity=255), z=1) self.add(hs_style_label(str(self.user.nickname), pos(0.0, -0.54, base=self.SizeBase), font_size=18, anchor_y='center'), z=2) self.add(hs_style_label(str(self.entity.name), pos(0.0, -0.65, base=self.SizeBase), font_size=12, anchor_y='center'), z=2) self.attack_sprite = Sprite('Atk.png', pos(-0.75, -0.34, base=self.SizeBase), scale=1.0) self.attack_sprite.visible = False self.add(self.attack_sprite, z=2) self.attack_label = hs_style_label('0', pos(-0.687, -0.34, base=self.SizeBase), font_size=46, anchor_y='center', color=Colors['white']) self.attack_label.visible = False self.add(self.attack_label, z=3) self.health_label = hs_style_label(str(self.entity.health), pos(0.73, -0.34, base=self.SizeBase), font_size=46, anchor_y='center', color=self._get_health_color()) self.add(self.health_label, z=2) self.armor_sprite = Sprite('Armor.png', pos(0.71, -0.00, base=self.SizeBase), scale=1.0) self.armor_sprite.visible = False self.add(self.armor_sprite, z=2) self.armor_label = hs_style_label('0', pos(0.71, 0.04, base=self.SizeBase), font_size=46, anchor_y='center', color=Colors['white']) self.armor_label.visible = False self.add(self.armor_label, z=3) def update_content(self, **kwargs): super().update_content(**kwargs) attack = self.entity.attack self.attack_label.element.text = str(attack) self.attack_label.visible = attack > 0 self.attack_sprite.visible = attack > 0 self.health_label.element.text = str(self.entity.health) self.health_label.element.color = self._get_health_color() armor = self.entity.armor if armor == 0: self.armor_label.visible = False self.armor_sprite.visible = False else: self.armor_label.element.text = str(armor) self.armor_label.visible = True self.armor_sprite.visible = True self._update_attr_sprite('frozen', self._get_frozen_sprite, z=3)
def __init__(self, x, y, img='img/shoot.png'): super(Shoot, self).__init__(img, x, y) self.speed = eu.Vector2(0, -400)
def __init__(self, x, y): score = random.choice(MysteryShip.SCORES) super(MysteryShip, self).__init__('img/alien4.png', x, y, score) self.speed = eu.Vector2(150, 0)
def __init__(self, x, y, color): super(Actor, self).__init__('ball.png', color=color) self.position = pos = eu.Vector2(x, y) self.cshape = cm.CircleShape(pos, self.width / 2)
def copy(self): return AARectShape(eu.Vector2(*self.center), self.rx, self.ry)
def __init__(self, x, y): super(PlayerShoot, self).__init__('img/laser.png', x, y) # players shoot up, aliens shoot down self.speed = eu.Vector2(0, 400) # store newly constructed instance in class variable PlayerShoot.INSTANCE = self
def get_action_menu_slots(num, x, y): # num should be 4 # return [euclid.Vector2(x - 133,y - 40*(slot - 1) + 10) for slot in range(num)] if num == 4: return [ euclid.Vector2(x - 104, y + 67), euclid.Vector2(x - 127, y + 22), euclid.Vector2(x - 127, y - 27), euclid.Vector2(x - 108, y - 72) ] elif num == 8: return [ euclid.Vector2(x - 104, y + 67), euclid.Vector2(x - 127, y + 22), euclid.Vector2(x - 127, y - 27), euclid.Vector2(x - 108, y - 72), euclid.Vector2(x - 88, y - 90), euclid.Vector2(x + 118, y - 78), euclid.Vector2(x + 148, y - 30), euclid.Vector2(x + 158, y + 8) ]
def __init__(self, image, center_x, center_y, radius): super(CollidableSprite, self).__init__(image) self.position = (center_x, center_y) self.cshape = cm.CircleShape(eu.Vector2(center_x, center_y), radius)
def switch(self): new_dir = eu.Vector2(self.y - self.planet.y, self.planet.x - self.x) self.direction = new_dir.normalized() * self.linearSpeed self.planet = None self.particles.gravity = eu.Point2(-self.direction.x, -self.direction.y)
def cshape( self ): #069會在每楨update為了檢測碰撞區塊而呼叫chsape時觸發,這樣就可以跟actions做連動了,本體跟碰撞區塊都會跟著一起移動 self._cshape.center = eu.Vector2(self.x, self.y) return self._cshape
def __init__(self, cx, cy, radius): self.cshape = cm.CircleShape(eu.Vector2(center_x, center_y), radius)
def __init__(self, pos, side): self.cshape = cm.AARectShape(eu.Vector2(*pos), side * 0.5, side * 0.5)
def on_mouse_motion(self, x, y, dx, dy): self.actor.target = eu.Vector2(x, y)
class WeaponSprite(EntitySprite): """The weapon sprite.""" Size = euclid.Vector2(120, 120) # Weapon size (original). SizeBase = Size // 2 # Coordinate base of children sprites. ImagePart = 0.22, 0.50, 0.58, 0.38 ImageScale = 0.5 def __init__(self, weapon, position=(0, 0), scale=1.0, **kwargs): self.border_sprite = None self.main_sprite = None self.sheathed_sprite = None self.attack_sprite, self.attack_label = None, None self.health_sprite, self.health_label = None, None super().__init__(weapon, position, scale, **kwargs) def _build_components(self): image = try_load_image(self.entity.get_image_name(), image_part=self.ImagePart) if image is not None: self.main_sprite = Sprite(image, pos(0.0, 0.0, base=self.SizeBase), scale=self.ImageScale) self.add(self.main_sprite, z=0) self.border_sprite = Sprite('Weapon.png', pos(0.0, 0.0, base=self.SizeBase), scale=0.60) self.add(self.border_sprite, z=1) self.attack_sprite = Sprite('WeaponAtk.png', pos(-0.58, -0.49, base=self.SizeBase), scale=0.30) self.add(self.attack_sprite, z=2) self.attack_label = hs_style_label('0', pos(-0.58, -0.41, base=self.SizeBase), font_size=24, anchor_y='center') self.add(self.attack_label, z=3) self.health_sprite = Sprite('WeaponHealth.png', pos(0.60, -0.44, base=self.SizeBase), scale=0.32) self.add(self.health_sprite, z=2) self.health_label = hs_style_label('0', pos(0.59, -0.40, base=self.SizeBase), font_size=24, anchor_y='center') self.add(self.health_label, z=3) self.sheathed_image = Sprite('WeaponSheathed.png', pos(0.0, 0.0, base=self.SizeBase), scale=0.60) self.add(self.sheathed_image, z=1) def update_status_border(self): # Weapons does not have status border. pass def update_content(self, **kwargs): super().update_content(**kwargs) self.attack_label.element.text = str(self.entity.attack) self.attack_label.element.color = self._get_attack_color() self.health_label.element.text = str(self.entity.health) self.health_label.element.color = self._get_health_color() if self.entity.sheathed: if self.main_sprite is not None: self.main_sprite.visible = False self.border_sprite.visible = False self.sheathed_image.visible = True else: if self.main_sprite is not None: self.main_sprite.visible = True self.border_sprite.visible = True self.sheathed_image.visible = False
def set_cshape(self): #self.cshape = cm.CircleShape(eu.Vector2(self.x, self.y), 15) self.cshape = cm.CircleShape(eu.Vector2(self.x, self.y), 5)
def cshape(self): self._cshape.center = eu.Vector2(self.x, self.y) return self._cshape
def fire(self, start_position): self.x = start_position[0] self.y = start_position[1] self.cshape = cm.CircleShape(eu.Vector2(self.x, self.y), 5) self.velocity = 0, self.speed
def planet(self, val): if val is not None: dx, dy = self.x - val.x, self.y - val.y self.angle = -math.atan2(dy, dx) self._distance = abs(eu.Vector2(dx, dy)) self._planet = val
def aabb_to_aa_rect(rect): half_width = abs(rect.left - rect.right) / 2 half_height = abs(rect.top - rect.bottom) / 2 return collision_model.AARectShape(euclid.Vector2(*rect.center), half_width, half_height)
def reflection_y(a): assert isinstance(a, eu.Vector2) return eu.Vector2(a.x, -a.y)
def __init__(self, bone): super(Skeleton, self).__init__() self.bone = bone self.matrix = euclid.Matrix3.new_identity() self.translation = euclid.Vector2(0, 0)
def generate_random_level(self): # hardcoded params: food_num = 5 food_scale = 1.0 # relative to player wall_num = 10 gate_scale = 1.5 # relative to player min_separation_rel = 3.0 # as fraction of player diameter # build ! width = self.width height = self.height rPlayer = self.rPlayer min_separation = min_separation_rel * rPlayer wall_scale_min = self.wall_scale_min wall_scale_max = self.wall_scale_max pics = self.pics z = 0 #add player cx, cy = (0.5*width, 0.5*height) self.player = Actor(cx, cy, rPlayer, 'player', pics['player']) self.collman.add(self.player) minSeparation = min_separation*2.*rPlayer # add gate rGate = gate_scale * rPlayer self.gate = Actor(cx, cy, rGate, 'gate', pics['wall'] ) self.gate.color = Actor.palette['wall'] cntTrys = 0 while cntTrys<100: cx = rGate+random.random()*(width-2.0*rGate) cy = rGate+random.random()*(height-2.0*rGate) self.gate.update_center(eu.Vector2(cx,cy)) if not self.collman.they_collide(self.player, self.gate): break cntTrys +=1 self.add(self.gate, z=z) z += 1 self.collman.add(self.gate) # add food rFood = food_scale*rPlayer self.cnt_food = 0 for i in range(food_num): food = Actor(cx, cy, rFood, 'food', pics['food'] ) cntTrys = 0 while cntTrys<100: cx = rFood+random.random()*(width-2.0*rFood) cy = rFood+random.random()*(height-2.0*rFood) food.update_center(eu.Vector2(cx, cy)) if self.collman.any_near(food, min_separation) is None: self.cnt_food +=1 self.add(food, z=z) z += 1 self.collman.add(food) break cntTrys +=1 # add walls for i in range(wall_num): s = random.random() r = rPlayer*( wall_scale_min*s + wall_scale_max*(1.0-s)) #lerp wall = Actor(cx, cy, r, 'wall', pics['wall'] ) cntTrys = 0 while cntTrys<100: cx = r+random.random()*(width-2.0*r) cy = r+random.random()*(height-2.0*r) wall.update_center(eu.Vector2(cx, cy)) if self.collman.any_near(wall, min_separation) is None: self.add(wall, z=z) z += 1 self.collman.add(wall) break cntTrys +=1 self.add(self.player, z=z) z += 1
def copy(self): return CircleShape(eu.Vector2(*self.center), self.r)
def update(self, dt): # if not playing dont update model if self.win_status != 'undecided': return # update collman self.collman.clear() for z, node in self.children: self.collman.add(node) #interactions player - others for other in self.collman.iter_colliding(self.player): typeball = other.btype if typeball == 'food': self.toRemove.add(other) self.cnt_food -= 1 if not self.cnt_food: self.open_gate() elif (typeball == 'wall' or typeball == 'gate' and self.cnt_food>0): self.level_losed() elif typeball == 'gate': self.level_conquered() # update player buttons = self.buttons ma = buttons['right'] - buttons['left'] if ma != 0: self.player.rotation += ma*dt*self.angular_velocity a = math.radians(self.player.rotation) self.impulse_dir = eu.Vector2(math.sin(a), math.cos(a)) newVel = self.player.vel mv = buttons['up'] if mv != 0: newVel += dt * mv * self.accel * self.impulse_dir nv = newVel.magnitude() if nv > self.topSpeed: newVel *= self.topSpeed/nv ppos=self.player.cshape.center newPos = ppos r = self.player.cshape.r while dt>1.e-6: newPos = ppos + dt*newVel consumed_dt = dt # what about screen boundaries ? if colision bounce if newPos.x<r: consumed_dt = (r-ppos.x)/newVel.x newPos = ppos+consumed_dt*newVel newVel = -reflection_y(newVel) if newPos.x>(self.width-r): consumed_dt = (self.width-r-ppos.x)/newVel.x newPos = ppos+consumed_dt*newVel newVel = -reflection_y(newVel) if newPos.y<r: consumed_dt = (r-ppos.y)/newVel.y newPos = ppos+consumed_dt*newVel newVel = reflection_y(newVel) if newPos.y>(self.height-r): consumed_dt = (self.height-r-ppos.y)/newVel.y newPos = ppos+consumed_dt*newVel newVel = reflection_y(newVel) dt -= consumed_dt self.player.vel = newVel self.player.update_center(newPos) # at end of frame do removes; as collman is fully regenerated each frame # theres no need to update it here. for node in self.toRemove: self.remove(node) self.toRemove.clear()
def __init__(self, x, y): self.columns = [AlienColumn(x + i * 60, y) for i in range(10)] self.speed = eu.Vector2(10, 0) self.direction = 1 self.elapsed = 0.0 self.period = 1.0
def __init__(self, bullet): center_x, center_y = bullet.position self.radius = bullet.damageRadius self.cshape = cm.CircleShape(eu.Vector2(center_x, center_y), bullet.damageRadius) self.bullet = bullet
def __init__(self, x, y): super(PlayerCannon, self).__init__('img/cannon.png', x, y) self.speed = eu.Vector2(200, 0)
def __init__(self, image): super(HeroShip, self).__init__(image) self.image = image self.position = (100, 100) self.velocity = (0, 0) self.cshape = cm.AARectShape(eu.Vector2(self.position), 32, 32)
def __init__(self, image, x, y): super(Actor, self).__init__(image) self.position = eu.Vector2(x, y) self.cshape = cm.AARectShape(self.position, self.width * 0.5, self.height * 0.5)
class MinionSprite(EntitySprite): ImagePart = 0.30, 0.51, 0.40, 0.38 # Start x, start y, width, height ImageScale = 0.9 ImageSize = euclid.Vector2(286, 395) # Card size (original). Size = euclid.Vector2( # Sprite size. int(ImageSize[0] * ImagePart[2] * ImageScale), int(ImageSize[1] * ImagePart[3] * ImageScale)) SizeBase = Size // 2 # Coordinate base of children sprites. def __init__(self, minion, position=(0, 0), scale=1.0, **kwargs): self.image_sprite = None self.atk_label = None self.health_label = None # Show enchantments of this minion. self.enchantments = [] # TODO: Windfury sprite, etc. self.divine_shield_sprite = None self.taunt_sprite = None self.frozen_sprite = None self.deathrattle_sprite = None self.trigger_sprite = None # TODO: Show the related card when mouse on it over N seconds. # TODO: (Need support of focus time in ``ActiveMixin``.) self.related_card = None super().__init__(minion, position, scale, **kwargs) def _get_ds_sprite(self): if self.divine_shield_sprite is None: self.divine_shield_sprite = Sprite('DivineShield.png', pos(0.0, 0.0, base=self.SizeBase), opacity=80) self.divine_shield_sprite.scale_x = self.ImageScale * self.ImagePart[ 2] * 1.1 self.divine_shield_sprite.scale_y = self.ImageScale * self.ImagePart[ 3] * 1.1 return self.divine_shield_sprite def _get_taunt_sprite(self): if self.taunt_sprite is None: self.taunt_sprite = Sprite( 'TauntMarker.png', pos(-0.05, 0.0, base=self.SizeBase), opacity=200, ) self.taunt_sprite.scale_x = self.ImageScale * self.ImagePart[ 2] * 5.472222 self.taunt_sprite.scale_y = self.ImageScale * self.ImagePart[ 3] * 6.140351 return self.taunt_sprite def _get_frozen_sprite(self): if self.frozen_sprite is None: self.frozen_sprite = Sprite( 'Frozen.png', pos(0.00, 0.00, base=self.SizeBase), scale=0.55, opacity=200, ) return self.frozen_sprite def _get_dr_sprite(self): if self.deathrattle_sprite is None: self.deathrattle_sprite = Sprite( 'Deathrattle.png', pos(0.00, -0.90, base=self.SizeBase), scale=0.7, ) return self.deathrattle_sprite def _stealth_opacity(self): return 50 if self.entity.stealth else 255 def _build_components(self): border_rect = rect.Rect(0, 0, self.Size[0], self.Size[1]) border_rect.center = (0, 0) self.activated_border = Rect(border_rect, self.SelectedColor, width=4) self._create_status_border(border_rect, z=3, width=2) self.status_border.visible = True # Get the part of card image. image = try_load_image(self.entity.get_image_name(), image_part=self.ImagePart, default='Minion-Skeleton.png') self.image_sprite = Sprite(image, pos(0.0, 0.0, base=self.SizeBase), scale=self.ImageScale, opacity=self._stealth_opacity()) self.add(self.image_sprite, z=2) if self.entity.type == Type.Minion: atk_sprite = Sprite('Atk.png', pos(-0.92, -0.81, base=self.SizeBase), scale=0.6) self.atk_label = hs_style_label(str(self.entity.attack), pos(-0.84, -0.8, base=self.SizeBase), anchor_y='center', font_size=32, color=self._get_attack_color()) health_sprite = Sprite('Health.png', pos(0.88, -0.81, base=self.SizeBase), scale=0.56) self.health_label = hs_style_label(str(self.entity.health), pos(0.86, -0.8, base=self.SizeBase), anchor_y='center', font_size=32, color=self._get_health_color()) self.add(atk_sprite, z=3) self.add(self.atk_label, z=4) self.add(health_sprite, z=3) self.add(self.health_label, z=4) else: # self.entity.type == Type.Permanent pass def update_status_border(self): if not self.in_control(): self.status_border.color = self.CommonColor else: action_status = self.entity.can_do_action() if action_status == self.entity.Inactive: color = self.CommonColor elif action_status == self.entity.Active: color = self.CanActionColor else: # action_status == self.entity.Highlighted color = self.HighlightColor self.status_border.color = color def update_content(self, **kwargs): super().update_content(**kwargs) if self.entity.type == Type.Minion: if self.image_sprite is not None: self.image_sprite.opacity = self._stealth_opacity() self.atk_label.element.text = str(self.entity.attack) self.atk_label.element.color = self._get_attack_color() self.health_label.element.text = str(self.entity.health) self.health_label.element.color = self._get_health_color() self._update_attr_sprite('divine_shield', self._get_ds_sprite, z=6) # TODO: Set taunt opacity to 50 if this minion is negated_taunt. self._update_attr_sprite('taunt', self._get_taunt_sprite, z=0) self._update_attr_sprite('frozen', self._get_frozen_sprite, z=5) # TODO: Only show one sprite at mid-bottom when more than one available (show which?). self._update_attr_sprite('dr_list', self._get_dr_sprite, z=5) else: # self.entity.type == Type.Permanent # Anything to do? pass