~fkmclane/armagetronad/armagetron.py

« back to all changes in this revision

Viewing changes to armagetron/armagetron.py

  • Committer: Foster McLane
  • Date: 2018-06-24 17:22:10 UTC
  • Revision ID: fkmclane@gmail.com-20180624172210-v0y56p209e0f1ufd
turn into Python package

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import sys
 
2
import time
 
3
 
 
4
 
 
5
poll_interval = 0.5
 
6
 
 
7
ladderlog = sys.stdin
 
8
server = sys.stdout
 
9
 
 
10
 
 
11
def add_handler(command, handler):
 
12
    if command not in commands:
 
13
        commands[command] = []
 
14
 
 
15
    commands[command].append(handler)
 
16
 
 
17
 
 
18
def remove_handler(command, handler):
 
19
    commands[command].remove(handler)
 
20
 
 
21
 
 
22
def set_chat_handler(command, handler):
 
23
    chat_commands[command] = handler
 
24
 
 
25
 
 
26
def remove_chat_handler(command):
 
27
    del chat_commands[command]
 
28
 
 
29
 
 
30
def send_command(command):
 
31
    server.write(command + '\n')
 
32
 
 
33
 
 
34
def say(message):
 
35
    send_command('SAY ' + message)
 
36
 
 
37
 
 
38
def console_message(message):
 
39
    send_command('CONSOLE_MESSAGE "' + message + '"')
 
40
 
 
41
 
 
42
def center_message(message):
 
43
    send_command('CENTER_MESSAGE "' + message + '"')
 
44
 
 
45
 
 
46
def send_message(player, message):
 
47
    if type(player) == Player:
 
48
        send_command('PLAYER_MESSAGE ' + player.name + ' "' + message + '"')
 
49
    else:
 
50
        send_command('PLAYER_MESSAGE ' + player + ' "' + message + '"')
 
51
 
 
52
 
 
53
def pause_round():
 
54
    send_command('WAIT_FOR_EXTERNAL_SCRIPT 1')
 
55
 
 
56
 
 
57
def continue_round():
 
58
    send_command('WAIT_FOR_EXTERNAL_SCRIPT 0')
 
59
 
 
60
 
 
61
def set_repository(address):
 
62
    send_command('RESOURCE_REPOSITORY_SERVER ' + address)
 
63
 
 
64
 
 
65
def set_map(resource):
 
66
    send_command('MAP_FILE ' + resource)
 
67
 
 
68
 
 
69
def include(config):
 
70
    send_command('INCLUDE ' + config)
 
71
 
 
72
 
 
73
def rinclude(config):
 
74
    send_command('RINCLUDE ' + config)
 
75
 
 
76
 
 
77
def reload():
 
78
    include('settings.cfg')
 
79
    include('server_info.cfg')
 
80
    include('settings_custom.cfg')
 
81
    send_command('START_NEW_MATCH')
 
82
 
 
83
 
 
84
def end_round():
 
85
    send_command('WIN_ZONE_MIN_LAST_DEATH 0')
 
86
    send_command('WIN_ZONE_MIN_ROUND_TIME 0')
 
87
 
 
88
 
 
89
def chat_command(command):
 
90
    if command[1] in chat_commands:
 
91
        chat_commands[command[1]](command[1:])
 
92
    else:
 
93
        send_message(command[2], 'Unknown chat command "' + command[1] + '".')
 
94
 
 
95
 
 
96
def init(command):
 
97
    send_command('LADDERLOG_WRITE_NUM_HUMANS 1')
 
98
    send_command('LADDERLOG_WRITE_POSITIONS 1')
 
99
    send_command('LADDERLOG_WRITE_INVALID_COMMAND 1')
 
100
    send_command('INTERCEPT_UNKNOWN_COMMANDS 1')
 
101
    send_command('WAIT_FOR_EXTERNAL_SCRIPT_TIMEOUT 10')
 
102
 
 
103
 
 
104
def run():
 
105
    while True:
 
106
        line = ladderlog.readline()
 
107
        if not line:
 
108
            time.sleep(poll_interval)
 
109
            continue
 
110
        if line.startswith('QUIT'):
 
111
            break
 
112
        command = line.split()
 
113
        if command and command[0] in commands:
 
114
            for handler in commands[command[0]]:
 
115
                handler(command)
 
116
 
 
117
 
 
118
# grid stuff
 
119
 
 
120
class Team:
 
121
    def __init__(self, name):
 
122
        self.name = name
 
123
        self.score = 0
 
124
        self.players = {}
 
125
        self.positions = []
 
126
 
 
127
 
 
128
class Player:
 
129
    def __init__(self, name, screenname, ip):
 
130
        self.name = name
 
131
        self.screenname = screenname
 
132
        self.ip = ip
 
133
        self.score = 0
 
134
        self.alive = False
 
135
 
 
136
    def send_message(self, message):
 
137
        send_message(self, message)
 
138
 
 
139
    def kill(self):
 
140
        send_command('KILL ' + self.name)
 
141
 
 
142
    def kick(self, reason=None):
 
143
        command = ['KICK', self.name]
 
144
        if reason:
 
145
            command.append(reason)
 
146
 
 
147
        send_command(' '.join(command))
 
148
 
 
149
    def ban(self, time=None, reason=None):
 
150
        command = ['BAN', self.name]
 
151
        if time:
 
152
            command.append(time)
 
153
        if reason:
 
154
            command.append(reason)
 
155
 
 
156
        send_command(' '.join(command))
 
157
 
 
158
    def ban_ip(self, time, reason=None):
 
159
        command = ['BAN_IP', self.ip]
 
160
        if time:
 
161
            command.append(time)
 
162
            if reason:
 
163
                command.append(reason)
 
164
 
 
165
        send_command(' '.join(command))
 
166
 
 
167
    def declare_winner(self):
 
168
        send_command('DECLARE_ROUND_WINNER ' + self.name)
 
169
 
 
170
    def teleport(self, x, y, xdir, ydir):
 
171
        send_command('TELEPORT_PLAYER ' + self.name + ' ' + x + ' ' + y + ' ' + xdir + ' ' + ydir)
 
172
 
 
173
    def respawn(self, x, y, xdir, ydir):
 
174
        if not self.alive:
 
175
            send_command('RESPAWN_PLAYER ' + self.name + ' 1 ' + x + ' ' + y + ' ' + xdir + ' ' + ydir)
 
176
 
 
177
 
 
178
class Zone:
 
179
    def __init__(self, name, type, x, y, size, growth=0, xdir=0, ydir=0, interactive=None, r=None, g=None, b=None, target_size=None, rubber=None, player=None, owner=None, command=None):
 
180
        self.name = name
 
181
        self.type = type
 
182
        self.x = x
 
183
        self.y = y
 
184
        self.size = size
 
185
        self.growth = growth
 
186
        self.xdir = xdir
 
187
        self.ydir = ydir
 
188
 
 
189
        if interactive:
 
190
            self.interactive = interactive
 
191
 
 
192
        if r and g and b:
 
193
            self.r = r
 
194
            self.g = g
 
195
            self.b = b
 
196
 
 
197
        if target_size:
 
198
            self.target_size = target_size
 
199
 
 
200
        if type == 'rubber':
 
201
            self.rubber = rubber
 
202
 
 
203
        if type == 'zombie':
 
204
            self.player = player
 
205
 
 
206
        if type == 'zombieOwner':
 
207
            self.player = player
 
208
            self.owner = owner
 
209
 
 
210
        if type == 'target' and command is not None:
 
211
            self.set_command(command)
 
212
 
 
213
    def __del__(self):
 
214
        self.change_size(0)
 
215
 
 
216
    def change_color(self, r, g, b):
 
217
        self.r = r
 
218
        self.g = g
 
219
        self.b = b
 
220
        send_command('SET_ZONE_COLOR ' + self.name + ' ' + r + ' ' + g + ' ' + b)
 
221
 
 
222
    def change_expansion(self, growth):
 
223
        self.growth = growth
 
224
        send_command('SET_ZONE_EXPANSION ' + self.name + ' ' + growth)
 
225
 
 
226
    def change_position(self, x, y):
 
227
        self.x = x
 
228
        self.y = y
 
229
        send_command('SET_ZONE_POSITION ' + self.name + ' ' + x + ' ' + y)
 
230
 
 
231
    def change_size(self, size, growth=None):
 
232
        self.size = size
 
233
 
 
234
        command = ['SET_ZONE_RADIUS', self.name, size]
 
235
        if growth:
 
236
            command.append(growth)
 
237
 
 
238
        send_command(' '.join(command))
 
239
 
 
240
    def change_speed(self, xdir, ydir):
 
241
        self.xdir = xdir
 
242
        self.ydir = ydir
 
243
        send_command('SET_ZONE_SPEED ' + self.name + ' ' + xdir + ' ' + ydir)
 
244
 
 
245
    def set_command(self, command):
 
246
        if self.type == 'target':
 
247
            send_command('SET_TARGET_COMMAND ' + self.name + ' ' + command)
 
248
 
 
249
 
 
250
class Grid:
 
251
    def __init__(self):
 
252
        self.reset(None)
 
253
 
 
254
    def get_team(self, name):
 
255
        if name in self.teams:
 
256
            return self.teams[name]
 
257
 
 
258
    def get_player(self, name):
 
259
        if name in self.players:
 
260
            return self.players[name]
 
261
 
 
262
    def get_zone(self, name):
 
263
        if name in self.zones:
 
264
            return self.zones[name]
 
265
 
 
266
    def create_zone(self, name, type, x, y, size, growth=0, xdir=0, ydir=0, interactive=None, r=None, g=None, b=None, target_size=None, rubber=None, player=None, owner=None, target_command=None):
 
267
        command = ['SPAWN_ZONE', 'n', name, type]
 
268
 
 
269
        if type == 'zombie':
 
270
            if player is None:
 
271
                return None
 
272
            command.append(player)
 
273
 
 
274
        if type == 'zombieOwner':
 
275
            if player is None or owner is None:
 
276
                return None
 
277
            command.append(player)
 
278
            command.append(owner)
 
279
 
 
280
        command.extend([x, y, size, growth, xdir, ydir])
 
281
 
 
282
        if type == 'rubber':
 
283
            if rubber is None:
 
284
                return None
 
285
            command.append(rubber)
 
286
 
 
287
        if interactive:
 
288
            command.append(interactive)
 
289
 
 
290
            if r and g and b:
 
291
                command.extend([r, g, b])
 
292
 
 
293
                if target_size:
 
294
                    command.append(target_size)
 
295
 
 
296
        send_command(' '.join(command))
 
297
 
 
298
        zone = Zone(name, type, x, y, size, growth, xdir, ydir, interactive, r, g, b, target_size, rubber, player, owner, target_command)
 
299
        self.zones[name] = zone
 
300
 
 
301
        return zone
 
302
 
 
303
    # ladderlog commands
 
304
 
 
305
    def new_round(self, command):
 
306
        self.round += 1
 
307
        self.zones = {}
 
308
        for player in self.players.values():
 
309
            player.alive = True
 
310
 
 
311
    def new_match(self, command):
 
312
        self.round = 1
 
313
        for team in self.teams.values():
 
314
            team.score = 0
 
315
        for player in self.players.values():
 
316
            player.score = 0
 
317
 
 
318
    def round_score(self, command):
 
319
        if command[2] in self.players:
 
320
            self.players[command[2]].score += int(command[1])
 
321
 
 
322
    def round_score_team(self, command):
 
323
        if command[2] in self.teams:
 
324
            self.teams[command[2]].score += int(command[1])
 
325
 
 
326
    def team_created(self, command):
 
327
        self.teams[command[1]] = Team(command[1])
 
328
 
 
329
    def team_destroyed(self, command):
 
330
        if command[1] in self.teams:
 
331
            del self.teams[command[1]]
 
332
 
 
333
    def team_renamed(self, command):
 
334
        if command[1] in self.teams:
 
335
            self.teams[command[2]] = self.teams.pop(command[1])
 
336
            self.teams[command[2]].name = command[2]
 
337
 
 
338
    def team_player_added(self, command):
 
339
        if command[1] in self.teams and command[2] in self.players:
 
340
            self.teams[command[1]].players[command[2]] = self.players[command[2]]
 
341
 
 
342
    def team_player_removed(self, command):
 
343
        if command[1] in self.teams and command[2] in self.teams[command[1]].players:
 
344
            del self.teams[command[1]].players[command[2]]
 
345
 
 
346
    def player_entered(self, command):
 
347
        self.players[command[1]] = Player(command[1], command[3], command[2])
 
348
 
 
349
    def player_left(self, command):
 
350
        if command[1] in self.players:
 
351
            del self.players[command[1]]
 
352
 
 
353
    def player_renamed(self, command):
 
354
        if command[1] in self.players:
 
355
            if not command[2] in self.players:
 
356
                self.players[command[2]] = self.players.pop(command[1])
 
357
                self.players[command[2]].name = command[2]
 
358
            self.players[command[2]].screenname = command[5]
 
359
 
 
360
    def num_humans(self, command):
 
361
        self.num_players = command[1]
 
362
 
 
363
    def positions(self, command):
 
364
        if command[1] in self.teams:
 
365
            self.teams[command[1]].positions = []
 
366
            for i in range(2, len(command)):
 
367
                self.teams[command[1]].positions.append(self.getPlayer(command[i]))
 
368
 
 
369
    def zone_spawned(self, command):
 
370
        if command[2]:
 
371
            self.zones[command[2]] = Zone(command[2], None, command[3], command[4], None)
 
372
        else:
 
373
            self.zones[command[1]] = Zone(command[1], None, command[3], command[4], None)
 
374
 
 
375
    def zone_collapsed(self, command):
 
376
        if command[1] in self.zones:
 
377
            del self.zones[command[1]]
 
378
        elif command[2] in self.zones:
 
379
            del self.zones[command[2]]
 
380
 
 
381
    def death_frag(self, command):
 
382
        if command[1] in self.players:
 
383
            self.players[command[1]].alive = False
 
384
 
 
385
    def death_suicide(self, command):
 
386
        if command[1] in self.players:
 
387
            self.players[command[1]].alive = False
 
388
 
 
389
    def death_teamkill(self, command):
 
390
        if command[1] in self.players:
 
391
            self.players[command[1]].alive = False
 
392
 
 
393
    def death_basezone_conquered(self, command):
 
394
        if command[1] in self.players:
 
395
            self.players[command[1]].alive = False
 
396
 
 
397
    def death_deathzone(self, command):
 
398
        if command[1] in self.players:
 
399
            self.players[command[1]].alive = False
 
400
 
 
401
    def death_rubberzone(self, command):
 
402
        if command[1] in self.players:
 
403
            self.players[command[1]].alive = False
 
404
 
 
405
    def death_shot_frag(self, command):
 
406
        if command[1] in self.players:
 
407
            self.players[command[1]].alive = False
 
408
 
 
409
    def death_shot_suicide(self, command):
 
410
        if command[1] in self.players:
 
411
            self.players[command[1]].alive = False
 
412
 
 
413
    def death_shot_teamkill(self, command):
 
414
        if command[1] in self.players:
 
415
            self.players[command[1]].alive = False
 
416
 
 
417
    def reset(self, command):
 
418
        self.round = 0
 
419
        self.teams = {}
 
420
        self.num_players = 0
 
421
        self.players = {}
 
422
        self.zones = {}
 
423
 
 
424
 
 
425
grid = Grid()
 
426
 
 
427
commands = {
 
428
    'NEW_ROUND': [grid.new_round],
 
429
    'NEW_MATCH': [grid.new_match],
 
430
    'ROUND_SCORE': [grid.round_score],
 
431
    'ROUND_SCORE_TEAM': [grid.round_score_team],
 
432
    'TEAM_CREATED': [grid.team_created],
 
433
    'TEAM_DESTROYED': [grid.team_destroyed],
 
434
    'TEAM_RENAMED': [grid.team_renamed],
 
435
    'TEAM_PLAYER_ADDED': [grid.team_player_added],
 
436
    'TEAM_PLAYER_REMOVED': [grid.team_player_removed],
 
437
    'PLAYER_ENTERED': [grid.player_entered],
 
438
    'PLAYER_LEFT': [grid.player_left],
 
439
    'PLAYER_RENAMED': [grid.player_renamed],
 
440
    'NUM_HUMANS': [grid.num_humans],
 
441
    'POSITIONS': [grid.positions],
 
442
    'ZONE_SPAWNED': [grid.zone_spawned],
 
443
    'ZONE_COLLAPSED': [grid.zone_collapsed],
 
444
    'DEATH_FRAG': [grid.death_frag],
 
445
    'DEATH_SUICIDE': [grid.death_suicide],
 
446
    'DEATH_TEAMKILL': [grid.death_teamkill],
 
447
    'DEATH_BASEZONE_CONQUERED': [grid.death_basezone_conquered],
 
448
    'DEATH_DEATHZONE': [grid.death_deathzone],
 
449
    'DEATH_RUBBERZONE': [grid.death_rubberzone],
 
450
    'DEATH_SHOT_FRAG': [grid.death_shot_frag],
 
451
    'DEATH_SHOT_SUICIDE': [grid.death_shot_suicide],
 
452
    'DEATH_SHOT_TEAMKILL': [grid.death_shot_teamkill],
 
453
    'GAME_END': [grid.reset],
 
454
    'ENCODING': [init],
 
455
    'INVALID_COMMAND': [chat_command]
 
456
}
 
457
 
 
458
chat_commands = {}