[45] | 1 | #!/usr/bin/env python |
---|
| 2 | # -*- coding: utf-8 -*- |
---|
[71] | 3 | # |
---|
| 4 | # Copyright (C) 2010 by Atzm WATANABE <atzm@atzm.org> |
---|
| 5 | # |
---|
| 6 | # This program is free software; you can redistribute it and/or modify it |
---|
| 7 | # under the terms of the GNU General Public License (version 2) as |
---|
| 8 | # published by the Free Software Foundation. It is distributed in the |
---|
| 9 | # hope that it will be useful, but WITHOUT ANY WARRANTY; without even the |
---|
| 10 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
---|
| 11 | # PURPOSE. See the GNU General Public License for more details. |
---|
| 12 | # |
---|
[47] | 13 | # $Id$ |
---|
[71] | 14 | # |
---|
[45] | 15 | |
---|
| 16 | import os |
---|
| 17 | import sys |
---|
| 18 | import random |
---|
| 19 | import pycodebattler |
---|
| 20 | |
---|
| 21 | |
---|
| 22 | class DrawGame(Exception): |
---|
| 23 | pass |
---|
| 24 | |
---|
| 25 | |
---|
| 26 | class BattleProto: |
---|
| 27 | HANDLER_COMMAND = 0 |
---|
| 28 | |
---|
| 29 | def __init__(self, warriors, max_turn=sys.maxint - 1): |
---|
| 30 | self._warriors = warriors |
---|
| 31 | self._max_turn = max_turn |
---|
| 32 | self._handlers = {} |
---|
| 33 | |
---|
| 34 | self.register_handler(self.HANDLER_COMMAND, |
---|
| 35 | self._handle_command_random) |
---|
| 36 | |
---|
| 37 | def winner(self): |
---|
| 38 | living = [w for w in self._warriors if not w.is_dead()] |
---|
| 39 | if not living: |
---|
| 40 | raise DrawGame() |
---|
| 41 | if len(living) == 1: |
---|
| 42 | return living[0] |
---|
| 43 | return None |
---|
| 44 | |
---|
| 45 | def loosers(self): |
---|
| 46 | return [w for w in self._warriors if w.is_dead()] |
---|
| 47 | |
---|
| 48 | def battle(self): |
---|
| 49 | for i in xrange(1, self._max_turn + 1): |
---|
| 50 | if self.winner(): |
---|
| 51 | break |
---|
| 52 | yield self.turn() |
---|
| 53 | else: |
---|
| 54 | raise DrawGame() |
---|
| 55 | |
---|
| 56 | def turn(self): |
---|
| 57 | for attacker in self._sorted_by_agility(): |
---|
| 58 | if attacker in self.loosers(): |
---|
| 59 | continue |
---|
| 60 | |
---|
| 61 | targets = [w for w in self._warriors |
---|
| 62 | if w not in self.loosers() + [attacker]] |
---|
| 63 | |
---|
| 64 | yield self._call_handler(self.HANDLER_COMMAND, attacker, targets) |
---|
| 65 | |
---|
| 66 | if self.winner(): |
---|
| 67 | break |
---|
| 68 | |
---|
| 69 | def register_handler(self, id_, handler, *ext_args): |
---|
| 70 | self._handlers[id_] = (handler, ext_args) |
---|
| 71 | |
---|
| 72 | def _call_handler(self, id_, *args): |
---|
| 73 | handler, ext_args = self._handlers[id_] |
---|
| 74 | return handler(self, *(args + ext_args)) |
---|
| 75 | |
---|
| 76 | def _sorted_by_agility(self): |
---|
| 77 | def cmp_agility(w1, w2): |
---|
| 78 | agi1 = w1.agility() |
---|
| 79 | agi2 = w2.agility() |
---|
| 80 | agi1 += random.randint(0 - agi1 / 8, agi1 / 8) |
---|
| 81 | agi2 += random.randint(0 - agi2 / 8, agi2 / 8) |
---|
| 82 | return agi2 - agi1 |
---|
| 83 | return sorted(self._warriors, cmp=cmp_agility) |
---|
| 84 | |
---|
| 85 | @staticmethod |
---|
| 86 | def _handle_command_random(self, attacker, targets): |
---|
| 87 | target = random.choice(targets) |
---|
| 88 | |
---|
| 89 | try: |
---|
| 90 | skname = random.choice(attacker.skill_list()) |
---|
| 91 | except IndexError: |
---|
| 92 | skname = None |
---|
| 93 | |
---|
| 94 | if attacker.can_invoke(skname) and random.randint(1, 10) > 8: |
---|
| 95 | sk = attacker.skill(skname) |
---|
| 96 | tt = sk.selectable(attacker, self._warriors) |
---|
| 97 | |
---|
[50] | 98 | if sk.skilltype() is pycodebattler.skill.SuicideAttackType: |
---|
[45] | 99 | damages = attacker.invoke(skname, tt, self._warriors) |
---|
| 100 | return attacker, skname, damages |
---|
| 101 | |
---|
| 102 | if tt: |
---|
| 103 | r = min([len(tt), sk.range(attacker)]) |
---|
| 104 | t = random.sample(tt, random.randint(1, r)) |
---|
| 105 | damages = attacker.invoke(skname, t, self._warriors) |
---|
| 106 | return attacker, skname, damages |
---|
| 107 | |
---|
| 108 | dmg, miss, crit = attacker.attack_to(target) |
---|
| 109 | return attacker, None, [(target, dmg, miss, crit)] |
---|
| 110 | |
---|
| 111 | |
---|
| 112 | def _main_entry(): |
---|
| 113 | from optparse import OptionParser |
---|
| 114 | |
---|
| 115 | parser = OptionParser(usage='%prog [options] file1 file2 ...') |
---|
| 116 | parser.add_option('-C', action='store_true', help='no color output') |
---|
| 117 | parser.add_option('-W', action='store_true', help='no wait per each turn') |
---|
| 118 | options, args = parser.parse_args() |
---|
| 119 | |
---|
| 120 | if len(args) <= 1: |
---|
| 121 | parser.print_help() |
---|
| 122 | return |
---|
| 123 | |
---|
| 124 | pycodebattler.util.set_color_enable(not options.C) |
---|
| 125 | |
---|
| 126 | skills = [pycodebattler.skill.Skill(*sk) for sk in [ |
---|
| 127 | ['Chakra', 3, pycodebattler.skill.HealType, 1], |
---|
| 128 | ['Resurrection', 2, pycodebattler.skill.ResurrectionType, 1], |
---|
| 129 | ['Psychokinesis', 13, pycodebattler.skill.SingleAttackType, 2], |
---|
| 130 | ['Flame Radiation', 8, pycodebattler.skill.RangeAttackType, 1], |
---|
| 131 | ['Meltdown', 7, pycodebattler.skill.MultiAttackType, 1], |
---|
| 132 | ['Big Bang', 23, pycodebattler.skill.SuicideAttackType, 1], |
---|
| 133 | ]] |
---|
| 134 | players = [ |
---|
| 135 | pycodebattler.warrior.Warrior( |
---|
| 136 | pycodebattler.util.path2name(f), |
---|
| 137 | open(f), |
---|
| 138 | random.sample(skills, random.randint(1, len(skills))) |
---|
| 139 | ) |
---|
| 140 | for f in args |
---|
| 141 | ] |
---|
| 142 | proto = BattleProto(players) |
---|
| 143 | |
---|
| 144 | def print_warriors(warriors, all=False): |
---|
| 145 | print('Warriors:') |
---|
| 146 | print(pycodebattler.warrior.pformat(players)) |
---|
| 147 | |
---|
| 148 | if not all: |
---|
| 149 | return |
---|
| 150 | |
---|
| 151 | for w in warriors: |
---|
| 152 | print("%s's skill:" % w) |
---|
| 153 | print(pycodebattler.skill.pformat( |
---|
| 154 | [w.skill(n) for n in w.skill_list()])) |
---|
| 155 | |
---|
| 156 | def print_match_result(attacker, skname, damages): |
---|
| 157 | if skname: |
---|
| 158 | print("-> %s invokes %s!!!" % |
---|
| 159 | (attacker, pycodebattler.util.cyan(skname))) |
---|
| 160 | else: |
---|
| 161 | print("-> %s's attack!" % attacker) |
---|
| 162 | |
---|
| 163 | if not damages: |
---|
| 164 | print(' but no effect...\n') |
---|
| 165 | return |
---|
| 166 | |
---|
| 167 | for victim, damage, miss, critical in damages: |
---|
| 168 | if critical: |
---|
| 169 | print(' %s!!!' % pycodebattler.util.red('CRITICAL')) |
---|
| 170 | if miss: |
---|
| 171 | print(' ...but %s!! %s got no damage!' % |
---|
| 172 | (pycodebattler.util.green('MISS'), victim)) |
---|
| 173 | continue |
---|
| 174 | |
---|
| 175 | if damage < 0: |
---|
| 176 | print(' %s is %s cured!' % |
---|
| 177 | (victim, pycodebattler.util.green(0 - damage))) |
---|
| 178 | else: |
---|
| 179 | print(' %s got %s damage!' % |
---|
| 180 | (victim, pycodebattler.util.yellow(damage))) |
---|
| 181 | |
---|
| 182 | if victim.is_dead(): |
---|
| 183 | print(pycodebattler.util.red(' %s is DEAD...' % victim)) |
---|
| 184 | |
---|
| 185 | print('') |
---|
| 186 | |
---|
| 187 | try: |
---|
| 188 | print_warriors(players, True) |
---|
| 189 | |
---|
| 190 | if not options.W: |
---|
| 191 | raw_input('> Hit enter key... ') |
---|
| 192 | print('') |
---|
| 193 | |
---|
| 194 | num_turn = 1 |
---|
| 195 | for turn in proto.battle(): |
---|
| 196 | print(pycodebattler.util.green( |
---|
| 197 | '============= turn %d =============' % num_turn)) |
---|
| 198 | |
---|
| 199 | for attacker, skname, damages in turn: |
---|
| 200 | print_match_result(attacker, skname, damages) |
---|
| 201 | |
---|
| 202 | print_warriors(players) |
---|
| 203 | num_turn += 1 |
---|
| 204 | |
---|
| 205 | if not options.W: |
---|
| 206 | raw_input('> Hit enter key... ') |
---|
| 207 | print('') |
---|
| 208 | |
---|
| 209 | print('') |
---|
| 210 | print(pycodebattler.util.green( |
---|
| 211 | '>>> %s is WINNER!! <<<' % proto.winner())) |
---|
| 212 | |
---|
| 213 | except (KeyboardInterrupt, EOFError, DrawGame): |
---|
| 214 | print('\n') |
---|
| 215 | print(pycodebattler.util.green('>>> Draw Game! <<<')) |
---|
| 216 | |
---|
| 217 | print('') |
---|
| 218 | print_warriors(players) |
---|
| 219 | |
---|
| 220 | |
---|
| 221 | if __name__ == '__main__': |
---|
| 222 | _main_entry() |
---|