def applyDamage(state: GameState, action: Attack, hitScore: int, score: int, success: int, target: Figure, weapon: Weapon) -> None: """Applies the damage of a weapon to the target, if succeeded.""" target.hp -= success * weapon.damage target.hit = True if target.hp <= 0: logger.debug( f'{action}: ({success} {score}/{hitScore}): KILL! ({target.hp}/{target.hp_max})' ) target.killed = True # kill all transported units for idx in target.transporting: f = state.getFigureByIndex(target.team, idx) f.killed = True f.hp = 0 logger.debug(f'{action}: {f} killed while transporting') else: logger.debug( f'{action}: ({success} {score}/{hitScore}): HIT! ({target.hp}/{target.hp_max})' ) # disable a random weapon weapons = [x for x in target.weapons if not weapon.disabled] to_disable = np.random.choice(weapons, weapon.damage * success, replace=False) for x in to_disable: target.weapons[x].disable()
def step(self, board: GameBoard, state: GameState, action: Action, forceHit: bool = False) -> Outcome: """Update the given state with the given action in a irreversible way.""" team: str = action.team # team performing action comment: str = '' logger.debug(f'{team} step with {action}') state.lastAction = action if isinstance(action, Wait): logger.debug(f'{action}: {comment}') return Outcome(comment=comment) if isinstance(action, Pass): if isinstance(action, PassFigure): f: Figure = state.getFigure(action) # who performs the action f.activated = True f.passed = True if isinstance(action, PassTeam) and not isinstance(action, Response): for f in state.getFigures(team): f.activated = True f.passed = True logger.debug(f'{action}: {comment}') return Outcome(comment=comment) if isinstance(action, Move): f: Figure = state.getFigure(action) # who performs the action f.activated = True f.moved = True f.stat = stat('IN_MOTION') if isinstance(action, MoveLoadInto): # figure moves inside transporter t = state.getTransporter(action) t.transportLoad(f) comment = f'(capacity: {len(t.transporting)}/{t.transport_capacity})' elif f.transported_by > -1: # figure leaves transporter t = state.getFigureByIndex(team, f.transported_by) t.transportUnload(f) comment = f'(capacity: {len(t.transporting)}/{t.transport_capacity})' state.moveFigure(f, f.position, action.destination) for transported in f.transporting: t = state.getFigureByIndex(team, transported) t.stat = stat('LOADED') state.moveFigure(t, t.position, action.destination) logger.debug(f'{action}: {comment}') return Outcome(comment=comment) if isinstance(action, AttackGround): f: Figure = state.getFigure(action) # who performs the action x: Cube = action.ground w: Weapon = state.getWeapon(action) f.stat = stat('NO_EFFECT') f.activated = True f.attacked = True w.shoot() if w.smoke: cloud = [ x + Cube(0, -1, 1), x + Cube(1, -1, 0), x + Cube(1, 0, -1), x + Cube(0, 1, -1), x + Cube(-1, 1, 0), x + Cube(-1, 0, 1), ] cloud = [(c.distance(f.position), c) for c in cloud] cloud = sorted(cloud, key=lambda y: -y[0]) state.addSmoke([c[1] for c in cloud[1:3]] + [x]) comment = f'smoke at {x}' logger.debug(f'{action}: {comment}') return Outcome(comment=comment) if isinstance(action, Attack): # Respond *is* an attack action f: Figure = state.getFigure(action) # who performs the action t: Figure = state.getTarget(action) # target # g: Figure = action.guard # who has line-of-sight on target w: Weapon = state.getWeapon(action) # los: list = action.los # line-of-sight on target of guard lof: list = action.lof # line-of-fire on target of figure # consume ammunition f.stat = stat('NO_EFFECT') w.shoot() if forceHit: score = [0] * w.dices else: score = np.random.choice(range(1, 21), size=w.dices) # attack/response if isinstance(action, Response): ATK = w.atk_response INT = f.int_def # can respond only once in a turn f.responded = True else: ATK = w.atk_normal INT = f.int_atk f.activated = True f.attacked = True # anti-tank rule if state.hasSmoke(lof): DEF = t.defense['smoke'] elif w.antitank and t.kind == 'vehicle': DEF = t.defense['antitank'] else: DEF = t.defense['basic'] TER = board.getProtectionLevel(t.position) STAT = f.stat.value + f.bonus END = f.endurance hitScore = hitScoreCalculator(ATK, TER, DEF, STAT, END, INT) success = len([x for x in score if x <= hitScore]) # target status changes for the _next_ hit t.stat = stat('UNDER_FIRE') # target can now respond to the fire t.attacked_by = f.index if success > 0: self.applyDamage(state, action, hitScore, score, success, t, w) comment = f'success=({success} {score}/{hitScore}) target=({t.hp}/{t.hp_max})' if t.hp <= 0: comment += ' KILLED!' elif w.curved: # missing with curved weapons v = np.random.choice(range(1, 21), size=1) hitLocation = MISS_MATRIX[team](v) missed = state.getFiguresByPos(t.team, hitLocation) missed = [m for m in missed if not m.killed] comment = f'({success} {score}/{hitScore}): shell missed and hit {hitLocation}: {len(missed)} hit' for m in missed: self.applyDamage(state, action, hitScore, score, 1, m, w) else: logger.debug(f'({success} {score}/{hitScore}): MISS!') logger.debug(f'{action}: {comment}') return Outcome( comment=comment, score=score, hitScore=hitScore, ATK=ATK, TER=TER, DEF=DEF, STAT=STAT, END=END, INT=INT, success=success > 0, hits=success, )