This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# all weapons must accept power1 and power2 arguments even if the weapon doesn't actually support upgrades.
# All weapons must have a shoot() method to shoot the weapon.
# All weapons must have a genShots() method to generate all possible shots the wieldingunit in its current position with this weapon can take. It must yield arguments to the weapons shoot() method.
# Any weapons that deal damage should store the amount of damage as self.damage
# genShots() should generate a shot to take, but not test the entire state of the board to ensure it is valid. It makes more sense to have shoot() invalidate the shot when it discovers that it's invalid.
# That way if it is valid, we may have temporary variables ready to go for the shot and we avoided checking to see if it's valid twice.
# The weapon should raise NullWeaponShot when it detects an invalid shot. The board should NOT be changed before NullWeaponShot is raised.
# Any weapons that deal damage must store the amount of damage as self.damage
# Any weapons that deal self damage must store the amount of damage as self.selfdamage
# Any weapons that have limited range must store their range as self.range
# Weapons with limited uses must accept the argument usesremaining=int() in __init__(). Set the number of uses left as self.usesremaining
# self.game will be set by the unit that owns the weapon.
@@ -1603,20 +1607,18 @@ def remove(self, square):
# Generator base classes:
classWeapon_DirectionalGen_Base():
"The base class for weapons that need a direction to be shot."
"The base class for weapons that only need a direction to be shot, like projectiles."
defgenShots(self):
"""A shot generator that yields each direction unless the weapon wielder is on the edge and trying to shoot off the board.
For example, if you have a unit in the bottom left corner (1, 1), you can only shoot up and right, but not left or down."""
fordinDirection.gen():
ifself.game.board[self.wieldingunit.square].getRelSquare(d, 1): # false if it goes off the board
yieldd
yieldd
classWeapon_ArtilleryGen_Base():
"The generator for artillery weapons."
defgenShots(self, minimumdistance=2):
"""Generate every possible shot that the weapon wielder can take from their position with an artillery weapon. Yields a tuple of (direction, relativedistance).
minimumdistance is how near the wielder the weapon can shoot. Artillery weapons typically can't shoot the square next to them, but Hydraulic Legs can.
genShots() methods usually don't take arguments, only child objects should use this argument."""
genShots() methods usually don't take arguments, only child objects should use this argument.
This genShots can only return valid shots by nature, no need to validate them in weapons that use this."""
fordirectioninDirection.gen():
relativedistance=minimumdistance# artillery weapons can't shoot the tile next to them, they start at one tile past that.
whileTrue:
@@ -1647,6 +1649,13 @@ def genShots(self):
forrinrange(1, self.range+1):
yield (d, r)
classWeapon_MirrorGen_Base():
"A base class for weapons that shoot out of both sides of the wielder"
defgenShots(self):
"There are only 2 possible shots here since it shoots out of both sides at once. Being in a corner can't invalidate a shot."
yieldDirection.UP
yieldDirection.RIGHT
# Low-level shared weapon functionality:
classWeapon_hurtAndPush_Base():
"A base class for weapons that need to hurt and push a unit."
"""Travel from the weapon wielder's tile in direction, returning the square of the first unit we find.
If none is found, return False.
If none is found and edgeok is True, return the square on the edge of the board."""
targetsquare=self.game.board[self.wieldingunit.square].getRelSquare(direction, 1) # start the projectile at square in direction from the unit that used the weapon...
If none is found and edgeok is True, return the square on the edge of the board.
startrel is which relative tile to start on by default. Most weapons use 1, but the grappling hook can't grab a unit that's already next to it."""
targetsquare=self.game.board[self.wieldingunit.square].getRelSquare(direction, startrel) # start the projectile at square in direction from the unit that used the weapon...
ifnottargetsquare:
raiseNullWeaponShot# the first square we tried to get was off the board, this is an invalid shot. XXX TODO: implement more of this!
raiseNullWeaponShot# the first square we tried to get was off the board, this is an invalid shot.
except (KeyError, NullWeaponShot): # raised from game.board[False] trying to hurt and push off the board or the relative square being False inside of _hurtAndPush(), just ignore it and continue
pass
classWeapon_isMountain_Base():
"A base class that provides a method to test a unit for mountainness."
classWeapon_ClusterArtillery(Weapon_Artillery_Base, Weapon_hurtAndPushEnemy_Base):# TODO: change artilleryGen to yield squares instead of relative dirs and distance. We can avoid calculating the square twice
When building chain is not powered (power1), you cannot hurt buildings or chain through them with this at all.
It does not go through mountains or supervolcano either. It does go through rocks.
Cannot attack mines on the ground.
Reddit said you can attack a building if it's webbed, this is not true. Even if you attack the scorpion webbing the building, the building won't pass the attack through or take damage.
When you chain through units that are explosive, they explode in the reverse order in which they were shocked.
When you chain through units that are explosive, they explode in the reverse order in which they were shocked. # TODO: this is true and not implemented!
You can never chain through yourself when you shoot!"""
# False is included because getRelSquare will return False when you go off the board. We can use this in the branching logic to tell it that anything off the board has been visited.
# we also include the unit that shot the weapon since you can NEVER chain through yourself!
ifself.game.board[self.game.board[self.wieldingunit.square].getRelSquare(d, 1)].unit: # if there's a unit directly next to this one, we can't fire even just to get the shield.
continue
exceptKeyError: # board[False] means we went off the board
continue
else: # There was an available square without a unit
hotsquares= [] # a list of squares to damage. Build the list first so we can determine if this is an invalid shot
forrinrange(1, distance+1):
targetsquare=self._getRelSquare(direction, r)
ifnottargetsquare: # went off the board
try:
ifself.isMountain(self.game.board[targetsquare].unit) andr<self.range: # if the unit on the targetsquare is a mountain (which stops flamethrower from going through it) and there was range remaining...
raiseNullWeaponShot# bail since this shot was already taken with less range
exceptKeyError: # board[False]
raiseNullWeaponShot
hotsquares.append(targetsquare)
# Now we know this is a valid shot.
fortargetsquareinhotsquares:
try: # unit takes damage if it was already on fire
exceptAttributeError: # None.effects, there was no unit
pass
self.game.board[targetsquare].applyFire() # light it up
ifself.isMountain(self.game.board[targetsquare].unit) andr<self.range: # if the unit on the targetsquare is a mountain (which stops flamethrower from going through it) and there was range remaining...
raiseNullWeaponShot# bail since this shot was already taken with less range
self.game.board[targetsquare].push(direction) # and finally push the last tile
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters