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