예제 #1
0
	def __init__(self, damage, accuracy=0, speed=1, range=1.5, range_increments=1,
				magazine_size=0, calibre=None, skill="Brawl", sprite="unarmed",
				ranged=False, martial_art=None):
		# valid damage formats: "XdY+Z" (some parts can be omitted) or (X,Y,Z)
		# it used to accept more complex expressions but seriously let's not do this
		if isinstance(damage, str):
		#	damage_list = []
		#	for i in damage.split("+"):
		#		if "d" in i:
		#			if i.split("d")[0]:
		#				damage_list.append(tuple(map(int, i.split("d"))))
		#			else:
		#				damage_list.append((1,int(i.split("d")[1])))
		#		else:
		#			damage_list.append((int(i),1))
		#	self.damage = tuple(damage_list)
			x, y, z = 0, 0, 0
			for i in damage.split("+"):
				if "d" in i:
					if i.split("d")[0]:
						x, y = map(int, i.split("d"))
					else:
						x, y = 1, int(i.split("d")[1])
				else:
					z = int(i)
			self.damage = x, y, z
		else:
			self.damage = damage
		self.accuracy = AnnotatedValue(accuracy, "weapon accuracy")
		self.speed = speed
		self.range = AnnotatedValue(range, "weapon basic range")
		self.range_increments = AnnotatedValue(range_increments, "number of range increments")
		self.magazine_size = magazine_size
		self.calibre = calibre
		self.skill = skill
		self.sprite = sprite
		self.magazine = []
		self.ranged = ranged
		self.martial_art = martial_art
예제 #2
0
 def skillTotal(self, skill):
     return AnnotatedValue(
         self.skillBase(skill) + self.skills[skill] +
         self.skillModifier(skill), "{} total".format(skill))
예제 #3
0
 def attributeModifier(self, attribute):
     return AnnotatedValue(self.attributes[attribute] // 2 - 5,
                           "{} modifier".format(attribute))
예제 #4
0
 def wounds_skill_penalty(self):
     modifier = 0
     for wound in self.wounds:
         modifier += getattr(wound, "skill_penalty", 0)
     return AnnotatedValue(modifier, "wounds skill penalty")
예제 #5
0
 def wounds_pdm_penalty(self):
     modifier = 0
     for wound in self.wounds:
         modifier += getattr(wound, "pdm_penalty", 0)
     return AnnotatedValue(modifier, "wounds PDM penalty")
예제 #6
0
 def passive_defense_modifier(self):
     return (AnnotatedValue(2, "PDM base") -
             AnnotatedValue(self.skills["Dodge"], "Dodge ranks") -
             AnnotatedValue(self.attributeModifier("REF"), "REF modifier") +
             self.wounds_pdm_penalty)
예제 #7
0
 def initiative(self):
     return (AnnotatedValue(40, "initiative base") -
             AnnotatedValue(self.attributes["WIT"], "WIT") -
             AnnotatedValue(self.attributes["REF"], "REF"))
예제 #8
0
 def attack(self, target, attack_type, attack_count=0):
     attacker = self.combatants[self.current_combatant]
     if attacker == target:
         # trying to attack self, aborting
         return False
     if target not in self.combatants:
         # target not in combat, aborting
         return False
     if not self.can_attack and attack_count == 0:
         # not enough AP, aborting
         return False
     if attack_type not in self.available_attacks:
         # selected attack cannot be performed, aborting
         return False
     weapon = attacker.inventory.hands
     dist = AnnotatedValue(
         gridhelper.distance(attacker.coords, target.coords), "distance")
     # calculate effective PDM
     if dist >= 5:
         pdm = AnnotatedValue(0, "PDM ignored (too far)")
     elif target in self.surprised_combatants:
         pdm = AnnotatedValue(0, "PDM ignored (surprised)")
     else:
         # using PDM if closer than 5m
         pdm = target.rpg_stats.passive_defense_modifier
         # martial artists can in some circumstances add 2 to target's PDM
         if (attacker.martial_art_used and not target.martial_art_used
                 and target.rpg_stats.skillTotal("Dodge") < 12 and
                 target.rpg_stats.skillTotal(weapon[0].weapon_data.skill
                                             if weapon else "Brawl") < 14):
             pdm += AnnotatedValue(2, "martial arts PDM modifier")
     # attack roll
     # TODO: golden success/tumble effects
     if weapon:
         # weapon attack
         if dist > (weapon[0].weapon_data.total_range):
             # not in range, aborting
             return False
         if (weapon[0].weapon_data.magazine_size > 0) and (len(
                 weapon[0].weapon_data.magazine) == 0):
             # no ammo, aborting
             return False
         if len(weapon[0].weapon_data.magazine) > 0:
             # remove the bullet
             weapon[0].weapon_data.magazine.pop(0)
         # weapon skill check, or Rapid Fire skill check if ranged Full Attack
         skill_used = (
             "Rapid Shooting" if attack_type == "Full Attack"
             and attacker.rpg_stats.skills["Rapid Shooting"] <
             attacker.rpg_stats.skills[weapon[0].weapon_data.skill] else
             weapon[0].weapon_data.skill)
         # modifier = target's PDM + weapon accuracy - range penalty - multi attack penalty
         attack_mods = (
             pdm + weapon[0].weapon_data.accuracy -
             dist // weapon[0].weapon_data.range -
             AnnotatedValue(attack_count, "penalty for multiple attacks"))
         hit = attacker.rpg_stats.skillCheck(skill_used, attack_mods)
         # damage roll
         damage = weapon[0].weapon_data.damage_roll
         # STR bonus for balanced and heavy weapons; can't be higher than weapon's max damage
         if weapon[0].weapon_data.skill in ("Melee, Balanced",
                                            "Melee, Heavy"):
             # heavy STR bonus
             if (weapon[0].weapon_data.skill == "Melee, Heavy"
                     and attacker.rpg_stats.skills["Melee, Heavy"] >= 3
                     and (attacker.rpg_stats.skills["Melee, Heavy"] >= 6
                          or not self.moved)):
                 str_mod = max(attacker.rpg_stats.attributeModifier("STR"),
                               attacker.rpg_stats.attributes["STR"] - 10)
             # regular STR bonus
             else:
                 str_mod = attacker.rpg_stats.attributeModifier("STR")
             damage += min(str_mod, weapon[0].weapon_data.max_damage)
         if attacker.martial_art_used:
             # martial art skill check; additional damage on success
             # reuses the attack roll but checks against a different skill
             martial_art_hit = attacker.rpg_stats.skillCheck(
                 attacker.martial_art_used, attack_mods)
             martial_art_hit.results = hit.results
             if martial_art_hit.golden:
                 damage += AnnotatedValue(
                     3, annotation="martial arts golden success bonus")
             elif martial_art_hit.success:
                 damage += Roll(3, annotation="martial arts damage bonus")
         # determine hit location
         if weapon[0].weapon_data.ranged:
             location_roll = roll(20)
             if location_roll >= 19:
                 hit_location = "head"
             elif location_roll >= 13:
                 hit_location = "torso"
             #elif location_roll >= 10:
             #	hit_location = "right arm"
             elif location_roll >= 7:
                 hit_location = "arm"
             #elif location_roll >= 4:
             #	hit_location = "right leg"
             else:
                 hit_location = "leg"
         else:
             if attack_type == "High attack":
                 location_roll = roll(7)
                 if location_roll >= 7:
                     hit_location = "head"
                 elif location_roll >= 5:
                     hit_location = "torso"
                 #elif location_roll >= 3:
                 #	hit_location = "right arm"
                 else:
                     hit_location = "arm"
             else:
                 location_roll = roll(8)
                 if location_roll >= 7:
                     hit_location = "torso"
                 #elif location_roll >= 4:
                 #	hit_location = "right leg"
                 else:
                     hit_location = "leg"
     else:
         # unarmed attack
         if dist > 1.5:
             # not in range, aborting
             return False
         # Brawl skill check; modifier = target's PDM
         hit = attacker.rpg_stats.skillCheck("Brawl", pdm)
         # damage roll + unarmed STR bonus
         damage = (Roll(3, annotation="unarmed damage") +
                   attacker.rpg_stats.attributeModifier("STR"))
         if attacker.martial_art_used:
             # martial art skill check; additional damage on success
             # reuses the attack roll but checks against a different skill
             martial_art_hit = attacker.rpg_stats.skillCheck(
                 attacker.martial_art_used, pdm)
             martial_art_hit.results = hit.results
             if martial_art_hit.golden:
                 damage += AnnotatedValue(
                     4, annotation="martial arts golden success bonus")
             elif martial_art_hit.success:
                 damage += Roll(4, annotation="martial arts damage bonus")
         # determine hit location
         if attack_type == "High attack":
             location_roll = roll(7)
             if location_roll >= 7:
                 hit_location = "head"
             elif location_roll >= 5:
                 hit_location = "torso"
             #elif location_roll >= 3:
             #	hit_location = "right arm"
             else:
                 hit_location = "arm"
         else:
             location_roll = roll(8)
             if location_roll >= 7:
                 hit_location = "torso"
             #elif location_roll >= 4:
             #	hit_location = "right leg"
             else:
                 hit_location = "leg"
     # damage bonus from hit roll margin
     damage += AnnotatedValue(hit.margin,
                              "hit roll's margin of success") // 2
     # save hit results to be used later by onTargetHit
     self.target = target
     self.hit = hit
     self.damage = damage
     self.hit_location = hit_location
     # play the attack animation
     attacker.visual.attack(target.visual.instance.getLocation())
     # after attacking immediately end turn
     #self.endTurn()
     self.current_AP = 0
     self.application.view.clearTiles()
     # additional attacks
     if weapon:
         # ranged Full Attack
         if (attack_type == "Full attack" and not self.moved
                 and weapon[0].weapon_data.speed > attack_count + 1):
             self.multi_attack.append(
                 lambda: self.attack(target, attack_type, attack_count + 1))
         # Finesse weapon with skill >= 3
         elif (weapon[0].weapon_data.skill == "Melee, Finesse"
               and attack_count == 0
               and attacker.rpg_stats.skills["Melee, Finesse"] >= 3
               and (attacker.rpg_stats.skills["Melee, Finesse"] >= 6
                    or not self.moved)):
             self.multi_attack.append(
                 lambda: self.attack(target, attack_type, attack_count + 1))
     return True