-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgame.py
More file actions
453 lines (413 loc) · 14 KB
/
game.py
File metadata and controls
453 lines (413 loc) · 14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
import platform
import os
import sys
import time
from random import randint, random
from stats import STAT_OPTIONS
from moves import MOVES
from elementalist import Elementalist
from fight import server_first, attack, is_strong, is_weak
from net import (
start_client_and_connect_to_server,
start_server_and_connect_to_client,
send, receive
)
# adapted from gist.github.com/ssbarnea/1316877
def supports_ansi():
stdout_is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
term_is_ansi = 'TERM' in os.environ and os.environ['TERM'] == 'ANSI'
if not stdout_is_a_tty and not term_is_ansi:
return False
if platform.system() == 'Windows' and not term_is_ansi:
return False
return True
def pick_stats():
n = 1
print('Next, choose which set of stats you want.\n')
for stat in STAT_OPTIONS:
print(
'Stats {}:\nHP: {} Attack: {} Speed: {}\n'.format(
n, stat['hp'], stat['attack'], stat['speed']
)
)
n += 1
while True:
stats = input('Enter 1-5: ')
if (
stats == '1' or stats == '2' or stats == '3' or
stats == '4' or stats == '5'
):
print('\n')
return stats
def pick_element():
print('\nElementalist! Choose your element!\n')
time.sleep(1)
print('Will your spirit burn like Fire?')
time.sleep(1)
print('Or be both tranquil and tempestuous as Water?')
time.sleep(1)
print('Are you as invisible and powerful as Wind?')
time.sleep(1)
print('Or as unyielding as the Earth itself?\n')
time.sleep(1)
element = input('Your element: ').lower()
while True:
if element in {'earth', 'fire', 'water', 'wind'}:
print('\n')
return element
element = input('Earth, Fire, Water, or Wind? ').lower()
def pick_moveset(element):
print('Very good. It is now time to pick your moves. You may choose three:\n')
for move in MOVES:
if element == MOVES[move]['type'] or MOVES[move]['type'] == 'normal':
print_move(move)
moveset = []
# choose three moves
for i in range(0, 3):
move_choice = input('Move #{}: '.format(i + 1)).lower()
# test if move exists in movelist
is_valid = False
while not is_valid:
if (
move_choice in MOVES and
move_choice not in moveset and
(MOVES[move_choice]['type'] == 'normal' or
MOVES[move_choice]['type'] == element)
):
moveset.append(move_choice)
is_valid = True
if not is_valid:
move_choice = input('Invalid move. Choose your move: ').lower()
print('\n')
return moveset
def print_move(move):
print(move.upper())
print(
'Type: {type}\tPower: {power}\tSpeed: {speed}\nInfo: {info}\n'.format(
**MOVES[move]
)
)
# what prints to your screen when you make an attack
def print_atk_results(results):
print('You used {}!'.format(results['move']))
if results['strong']:
print('It\'s super effective against your opponent!')
if results['weak']:
print(
'Unfortunately, your move\'s type is weak against this particular ' +
'opponent.'
)
if results['damage'] != 0:
print('You dealt {} damage!'.format(results['damage']))
if results['status'] == 'burned':
print('Your opponent was burned!')
if results['backlash'] != 0:
print(
'You were hurt in the fracas and lost {} HP.'.format(results['backlash'])
)
if results['atkboost'] != 0:
print('Your attack was boosted by {} points!'.format(results['atkboost']))
if results['spdboost'] != 0:
print('Your speed was boosted by {} points!'.format(results['spdboost']))
wait()
# what prints to your screen when you are attacked by the opponent
def print_def_results(results):
print('Your opponent used {}!'.format(results['move']))
if results['strong']:
print('The opponent launches a devastating attack!')
if results['weak']:
print(
'You easily shake off your opponent\'s ill-planned, ineffective attack.'
)
if results['damage'] != 0:
print('You lose {} HP!'.format(results['damage']))
if results['status'] == 'burned':
print('Ssssszzz! Agh! You were burned!')
if results['backlash'] != 0:
print(
'You\'re pleased to see that your opponent is at least injured in the ' +
'tussle, sustaining {} damage.'.format(results['backlash'])
)
if results['atkboost'] != 0:
print(
'Your opponent\'s attack is boosted by {} points!'.format(
results['atkboost']
)
)
if results['spdboost'] != 0:
print(
'Your opponent\'s speed is boosted by {} points!'.format(
results['spdboost']
)
)
wait()
def wait():
print('.')
time.sleep(1)
print('.')
time.sleep(1)
print('.')
time.sleep(1)
def knocked_out(your_hp, opponent_hp):
if your_hp > 0 and opponent_hp <= 0:
print(
'You smite your opponent upon the slopes of the great mountain.\n' +
'You have won. Well fought, elementalist.\n\n'
)
return True
elif your_hp <= 0 and opponent_hp > 0:
print(
'You fall to your knees, unable to hold yourself up any longer\nas your' +
' head swims and your vision darkens,\nand you fall slowly into the ' +
'abyss.\nYou have fallen.\n\n'
)
return True
elif your_hp <= 0 and opponent_hp <= 0:
print(
'In one final, mighty clash, you both throw yourselves at each other ' +
'with all of your remaining strength.\nYou are evenly matched until ' +
'the end.\nYou fall there, side by side, equals in both life and death.' +
'\n\n'
)
return True
else:
return False
def play_as_server(client):
if supports_ansi():
print('\x1b[3F\x1b[J', end='') # clear initial connection text
else:
print()
# CREATE CHARACTERS #
server_element = pick_element()
# given the number stat chosen, get the map for those stats
server_stat_num = int(pick_stats()) - 1
server_stats = STAT_OPTIONS[server_stat_num]
# pick moveset from that elementalist's movelist of possible moves
if server_element == 'earth':
server_moveset = pick_moveset('earth')
elif server_element == 'fire':
server_moveset = pick_moveset('fire')
elif server_element == 'water':
server_moveset = pick_moveset('water')
elif server_element == 'wind':
server_moveset = pick_moveset('wind')
server_elementalist = Elementalist(
server_element, server_stats['hp'], server_stats['attack'],
server_stats['speed'], server_moveset
)
client_info = receive(client)
# don't trust client: check info before instantiating elementalist
if client_info['element'] not in {'earth', 'fire', 'water', 'wind'}:
raise SystemExit(
'The client\'s element choice was invalid. Client may be tampering with' +
' the code. Exiting the program.'
)
# check that client chose one of the five stats options
if int(client_info['stats']) not in range (1, 6):
raise SystemExit(
'The client\'s stats choice was invalid. Client may be tampering with ' +
'the code. Exiting the program.'
)
# check that the moveset is valid
for choice in client_info['moveset']:
# check that choice is a valid type for that elementalist
valid_choice = False
if client_info['element'] == 'earth':
if MOVES[choice]['type'] == 'earth':
valid_choice = True
elif client_info['element'] == 'fire':
if MOVES[choice]['type'] == 'fire':
valid_choice = True
elif client_info['element'] == 'water':
if MOVES[choice]['type'] == 'water':
valid_choice = True
elif client_info['element'] == 'wind':
if MOVES[choice]['type'] == 'wind':
valid_choice = True
# normal type moves are valid for all elementalists
if MOVES[choice]['type'] == 'normal':
valid_choice = True
# if one of the moves was not in the proper movelist
if not valid_choice:
raise SystemExit('The client\'s move choice was invalid. Client may be ' +
'tampering with the code. Exiting the program.'
)
# finished checking client_info
# Given the number stat chosen, get the map for those stats
client_stat_num = int(client_info['stats']) - 1
client_stats = STAT_OPTIONS[client_stat_num]
client_elementalist = Elementalist(
client_info['element'], client_stats['hp'], client_stats['attack'],
client_stats['speed'], client_info['moveset']
)
# BATTLE STAGE #
wait()
print('Let the battle commence!')
wait()
while True:
hp_info = {
'server_hp': server_elementalist.hp,
'client_hp': client_elementalist.hp
}
send(client, hp_info)
print('Your HP: {} Opponent\'s HP: {}\n'.format(
server_elementalist.hp, client_elementalist.hp
)
)
# choose a move
print('Your moves:\n')
for move in server_elementalist.moveset:
print_move(move)
while True:
server_move = input('Choose your move: ').lower()
if server_move in server_elementalist.moveset:
break
# receive and validate client's move
client_move = receive(client)
if client_move not in client_elementalist.moveset:
raise SystemExit(
'The client\'s move choice was invalid. Client may be tampering with ' +
'the code. Exiting the program.'
)
wait() # call wait before printing results for nicer user experience
# evaluate speeds
if server_first(
server_elementalist, server_move, client_elementalist, client_move
):
server_second = False
# if server's first, server attacks client
server_atk_results = attack(
server_elementalist, server_move, client_elementalist
)
# let client know which results these are
server_atk_results['attacker'] = 'server'
# tell client the hp of each after the attack
server_atk_results['server_hp'] = server_elementalist.hp
server_atk_results['client_hp'] = client_elementalist.hp
send(client, server_atk_results)
print_atk_results(server_atk_results)
if knocked_out(server_elementalist.hp, client_elementalist.hp):
break
else:
server_second = True
# client attacks server
client_atk_results = attack(
client_elementalist, client_move, server_elementalist
)
client_atk_results['attacker'] = 'client'
client_atk_results['server_hp'] = server_elementalist.hp
client_atk_results['client_hp'] = client_elementalist.hp
send(client, client_atk_results)
print_def_results(client_atk_results)
if knocked_out(server_elementalist.hp, client_elementalist.hp):
break
# if server hadn't been first, server attacks client
if server_second:
server_atk_results = attack(
server_elementalist, server_move, client_elementalist
)
server_atk_results['attacker'] = 'server'
server_atk_results['server_hp'] = server_elementalist.hp
server_atk_results['client_hp'] = client_elementalist.hp
send(client, server_atk_results)
print_atk_results(server_atk_results)
if knocked_out(server_elementalist.hp, client_elementalist.hp):
break
# status conditions
if server_elementalist.status == 'burned':
print('You lose 25 HP from your burn.')
wait()
server_elementalist.hp -= 25
if client_elementalist.status == 'burned':
print('Your opponent loses 25 HP from burns.')
wait()
client_elementalist.hp -= 25
# send status condition info to client
status_info = {
'server_status': server_elementalist.status,
'client_status': client_elementalist.status,
'server_hp': server_elementalist.hp,
'client_hp': client_elementalist.hp
}
send(client, status_info)
# check that the status condition didn't knock one out
if knocked_out(server_elementalist.hp, client_elementalist.hp):
break
def play_as_client(server):
if supports_ansi():
print('\x1b[3F\x1b[J', end='') # clear initial connection text
else:
print()
# CREATE CHARACTER #
client_element = pick_element()
client_stats = pick_stats()
# pick moveset from that elementalist's movelist of possible moves
if client_element == 'earth':
client_moveset = pick_moveset('earth')
elif client_element == 'fire':
client_moveset = pick_moveset('fire')
elif client_element == 'water':
client_moveset = pick_moveset('water')
elif client_element == 'wind':
client_moveset = pick_moveset('wind')
client_info = {
'element': client_element,
'stats': client_stats,
'moveset': client_moveset
}
send(server, client_info)
# BATTLE STAGE #
wait()
print('Let the battle commence!')
wait()
while True:
hp_info = receive(server)
print('Your HP: {} Opponent\'s HP: {}\n'.format(
hp_info['client_hp'], hp_info['server_hp']
)
)
print('Your moves:\n')
for move in client_moveset:
print_move(move)
# push client for move until valid move entered
while True:
client_move = input('Choose your move: ').lower()
if client_move in client_moveset:
break
send(server, client_move)
wait() # wait before printing results for better user experience
# faster player's attack results
results = receive(server)
if results['attacker'] == 'server':
print_def_results(results)
else:
print_atk_results(results)
# check if game over
if knocked_out(results['client_hp'], results['server_hp']):
break
# slower player's attack results
results = receive(server)
if results['attacker'] == 'server':
print_def_results(results)
else:
print_atk_results(results)
# check if game over
if knocked_out(results['client_hp'], results['server_hp']):
break
# receive status info
status_info = receive(server)
if status_info['server_status'] == 'burned':
print('Your opponent loses 25 HP from burns.')
wait()
if status_info['client_status'] == 'burned':
print('You lose 25 HP from your burn.')
wait()
if knocked_out(status_info['client_hp'], status_info['server_hp']):
break
if __name__ == '__main__':
if 'start' in input('Would you like to start or join? ').lower():
with start_server_and_connect_to_client() as client:
play_as_server(client)
else:
with start_client_and_connect_to_server() as server:
play_as_client(server)