Module models
[hide private]
[frames] | no frames]

Source Code for Module models

  1  import os 
  2  import re 
  3  import time 
  4  import thread 
  5  import random 
  6  from threading import Timer 
  7   
  8  from cocos import euclid 
  9  from cocos.actions import MoveTo, Delay 
 10  from cocos.actions.instant_actions import CallFunc, Hide, Show 
 11  from cocos.sprite import * 
 12  from pyglet.resource import media 
 13  from cocos.actions import FadeOut 
 14   
 15  import utils 
 16  import objects 
 17  from constants import * 
 18  from utils import aabb_to_aa_rect 
 19   
20 -class Packet(Sprite):
21 speed = 1 22
23 - def __init__(self, position):
24 parity = random.choice(["0","1"]) 25 super(Packet, self).__init__(os.path.join("images", "packet" + parity + ".png")) 26 self.position = position 27 self.onMap = False
28 29
30 -class Unit(Sprite):
31
32 - def __init__(self, image, curVertex, health=0, pid=0, buildTime=1):
33 super(Unit, self).__init__(image) 34 self.imageName = image 35 self.imageName = image.split("/")[2] 36 self.imageOutline = "images/outlines/" + self.imageName.split(".")[0] + "_outline.png" 37 38 self.initialHealth = health 39 40 self.pid = pid 41 42 tid = -1 # id of type of troop 43 44 self.maxHealth = health 45 46 self.buildTime = buildTime/TEST_SPEEDUP 47 48 self.playerIndicator = None 49 50 self.curVertex = curVertex 51 52 self.health = 1 53 54 self.uid = -1 55 56 self.slotIndex = -1 57 58 self.isSelected = False 59 60 self.menu = None 61 62 self.isDestroyed = False 63 64 self.isSelectable = True # if a unit is performaing certain actions (ping), it can't be selected 65 66 self.color = (255,255,255) 67 68 self.selectedColor = (200,200,200) 69 70 self.unselectedColor = self.color 71 72 self.selectedSprite = None 73 74 self.isRemoved = False
75 76
77 - def on_completion(self):
78 pass
79
80 - def on_destruction(self): # called when unit is destroyed
81 pass
82
83 - def set_is_selected(self, selectedVal, gameMap, cm, player):
84 # Sets the current selected value to the input param 'selectedVal' and 85 # scales image accordingly. 86 if selectedVal == True: 87 self.curVertex.highlight_adjacents(True) 88 else: 89 self.curVertex.highlight_adjacents(False) 90 91 if selectedVal == self.isSelected: 92 return 93 94 if selectedVal: 95 self.scale = UNIT_SCALE_SELECTED 96 self.unselectedColor = self.color 97 if self.playerIndicator: 98 self.playerIndicator.visible = False 99 self.color = self.selectedColor 100 if self.selectedSprite: 101 self.add(self.selectedSprite) 102 if not type(self) == CPU: 103 self.display_action_menu(gameMap, cm, player) 104 else: 105 self.scale = UNIT_SCALE_NORMAL 106 self.color = self.unselectedColor 107 if self.playerIndicator: 108 self.playerIndicator.visible = True 109 if self.selectedSprite: 110 self.remove(self.selectedSprite) 111 if not type(self) == CPU: 112 self.clear_action_menu(gameMap, cm, player) 113 self.isSelected = selectedVal
114
115 - def display_action_menu(self, gameMap, cm, player):
116 # Displays this unit's action menu (if it has one) 117 # I don't know why we need to reinitialize this each time, but we need 118 # to. 119 from game_layers import ActionMenu 120 if issubclass(type(self), Troop): 121 index = self.slotIndex # TODO, fix this for other positions 122 position = self.curVertex.actionMenuSlots[index] 123 elif issubclass(type(self), Building): 124 position = self.position 125 126 self.menu = ActionMenu(position[0], position[1], self.get_action_list(player), self) 127 if self.actionList != []: 128 gameMap.add(self.menu, z=4) 129 self.menu.add_action_buttons(gameMap, cm)
130
131 - def clear_action_menu(self, gameMap, cm, player):
132 # Hides this unit's action menu. 133 if self.get_action_list(player) != [] and self.menu != None: 134 try: 135 gameMap.remove(self.menu) 136 self.menu.remove_action_buttons(gameMap, cm) 137 except: 138 self.menu.remove_action_buttons(gameMap, cm)
139
140 - def get_action_list(self, player):
141 # Returns the up to date action list for this unit (based on player research) 142 self.actionList = player.unitActionLists[self.__class__.__name__] 143 return self.actionList
144
145 - def on_attack(self, power, attacker):
146 # attacker = the one attacking 147 # self is the target being attacked 148 149 c = objects.Objects.get_controller() 150 c.client_attack(self.pid,self.uid,power) 151 self.health -= power 152 self.color = (self.unselectedColor[0] * float(self.health) / self.maxHealth, self.unselectedColor[1] * 153 float(self.health) / self.maxHealth, self.unselectedColor[2] * float(self.health) / self.maxHealth) 154 155 if self.health <= 0: 156 # Destroyed the target unit. Remove it from our set of things to attack and destroy the unit. 157 if self in attacker.shouldAttack: 158 attacker.shouldAttack.remove(self) 159 self.shouldAttack = set() 160 if not self.isDestroyed: 161 self.isDestroyed = True 162 self.isSelectable = False 163 self.do(CallFunc(self.destroy_action))
164
165 - def client_on_attack(self,power):
166 self.health -= power 167 self.color = (self.unselectedColor[0] * float(self.health) / self.maxHealth, self.unselectedColor[1] * 168 float(self.health) / self.maxHealth, self.unselectedColor[2] * float(self.health) / self.maxHealth)
169
170 - def destroy_action(self):
171 self.on_destruction() 172 controller = objects.Objects.get_controller() 173 controller.remove_unit(self)
174 175 Unit.register_event_type("on_destruction") 176 177
178 -class Troop(Unit):
179
180 - def __init__(self, image, curVertex, health=0, pid=0, speed=0, power=0, attackRange=0, buildTime=1):
181 182 fullImagePath = os.path.join("images", "troops", image) 183 184 super(Troop, self).__init__(fullImagePath, curVertex, health, pid=pid, buildTime=buildTime) 185 186 187 self.initialHealth = health 188 189 self.playerIndicator = Sprite(os.path.join("images","troops", "player.png")) 190 self.playerIndicator.color = PLAYER_COLORS[self.pid] 191 self.playerIndicator.opacity = 0 192 self.add(self.playerIndicator,z=-1) 193 194 195 if not curVertex.add_troop(self): 196 self.slotIndex = -1 197 return 198 199 self.position = curVertex.troopSlots[self.slotIndex].position 200 self.cshape = aabb_to_aa_rect(self.get_AABB()) 201 self.cshape.center = self.position 202 203 self.power = power 204 205 # keep track of the destination while troop is moving 206 self.destVertex = None 207 208 self.speed = float(speed) * TEST_SPEEDUP 209 210 self.attackRange = attackRange 211 212 self.scale = UNIT_SCALE_NORMAL 213 214 # the target sets this flag to False once it's died 215 self.shouldAttack = set() # A set containing the enemies that this thing should attack (when empty we should stop attacking) 216 217 self.is_attacking = False 218 219 self.attackingPath = [] 220 221 self.packets = [] 222 223 self.targetVid = -1 224 225 self.sourceVid = -1 226 227 for i in range(4): 228 self.packets.append(Packet(self.position))
229 # a pool of packets to use when animating attacks 230
231 - def update_opacity(self,o):
232 self.opacity = o 233 self.playerIndicator.opacity = o
234
235 - def attack(self, target, m):
236 # Already gauranteed that self.pid and target.pid != 237 if self.is_attacking and type(self) != BufferOverflow: 238 # Ignore for BufferOverflow as it can attack >1 unit 239 # DEBUG 240 # print "already attacking" 241 return 242 self.shouldAttack.add(target) 243 if self.can_attack(target): 244 path = m.get_path(self.curVertex, target.curVertex, self.pid, self, "attack") 245 246 if path and len(path) <= self.attackRange + 1: 247 self.attackingPath = path 248 self.is_attacking = True 249 c = objects.Objects.get_controller() 250 c.client_attack_animation(self.pid,self.uid,target.pid,target.uid,self.attackingPath) 251 self.targetVid = target.curVertex.vid 252 self.sourceVid = self.curVertex.vid 253 self.schedule_interval(self.attack_action, 1, target, m) 254 else: 255 # Can't attack. Let the user know 256 utils.play_sound("error.wav") 257 return
258 259
260 - def client_attack(self,target,m): #only animate, no actual attack
261 self.is_attacking = True 262 self.schedule_interval(self.animate_packets,1,target,m)
263
264 - def can_attack(self, target):
265 # Determines if this unit can attack the given target # CHANGE THIS 'UNIT' TO BUILDING IF WE ONLY WANT DNS TO ATTACK BUILDINGS 266 if (issubclass(type(target),Unit) and type(self) != DNSPoison and type(self) != SQLInjection) or (type(self) == DNSPoison and issubclass(type(target),Unit)): 267 # Either we're a normal troop attacking any Unit, or we're a DNSPoison and can only attack Buildings. 268 269 return True 270 elif type(self) == SQLInjection and issubclass(type(target),Unit) and target.curVertex.building != None and (type(target.curVertex.building) == Server or type(target.curVertex.building) == Database): 271 # SQLInjection attacking an enemy unit in a vertex with a Server or Database. 272 return True 273 else: 274 return False
275
276 - def attack_action(self, dt, target, gameMap):
277 targetVid = target.curVertex.vid 278 sourceVid = self.curVertex.vid 279 c = objects.Objects.get_controller() 280 if len(self.shouldAttack) > 0: 281 if target in self.shouldAttack: 282 if sourceVid != self.sourceVid: 283 self.end_attack() 284 return 285 if self.targetVid != targetVid: # target moved 286 self.attackingPath = gameMap.get_path(self.curVertex, target.curVertex, self.pid, self, action="Attack") 287 288 if self.attackingPath == None or len(self.attackingPath) > self.attackRange: 289 self.end_attack() 290 return 291 c.client_attack_animation(self.pid,self.uid,target.pid,target.uid,self.attackingPath) 292 self.targetVid = targetVid 293 target.on_attack(self.power, self) 294 self.animate_packets(dt,target,gameMap) 295 else: 296 self.end_attack()
297 298
299 - def animate_packets(self,dt,target,gameMap):
300 #### packet logic ### 301 if self.is_attacking: 302 if self.packets: 303 p = self.packets.pop() 304 p.position = self.position 305 if not p.onMap: 306 p.onMap = True 307 gameMap.add(p, z=PACKET_Z) 308 action = Show() 309 action += MoveTo(gameMap.vertices[self.attackingPath[0]].position, 0.2) 310 for v in self.attackingPath[1:]: 311 vertex = gameMap.vertices[v] 312 action += MoveTo(vertex.position, float(1 / p.speed)) 313 action += MoveTo(target.position, 0.2) 314 action += Hide() 315 p.do(action) 316 self.packets.insert(0, p)
317 ### end of packet logic ### 318 319
320 - def end_attack(self,isClient=False):
321 for p in self.packets: 322 p.stop() 323 p.visible = False 324 self.shouldAttack = set() 325 self.is_attacking = False 326 c = objects.Objects.get_controller() 327 if not isClient: 328 self.unschedule(self.attack_action) 329 c.client_attack_animation(self.pid,self.uid,-1,-1,[]) 330 else: 331 self.unschedule(self.animate_packets)
332
333 - def add_to_map(self, m):
334 m.add(self, TROOP_Z)
335 336 Troop.register_event_type("troop_attack") 337 338
339 -class Building(Unit):
340
341 - def __init__(self, image, curVertex, health=0, pid=0, buildTime=1):
342 fullImagePath = os.path.join("images", "buildings", image) 343 super(Building, self).__init__(fullImagePath, curVertex, health, pid, buildTime=buildTime) 344 345 # flag to check if unit is busy doing something 346 self.isBusy = False 347 self.initialHealth = health 348 349 350 if not curVertex.add_building(self): 351 self.slotIndex = -1 352 return 353 354 if type(self) == CPU: 355 self.position = curVertex.position 356 else: 357 self.position = euclid.Vector2(curVertex.buildingSlot.position[0], curVertex.buildingSlot.position[1] + 10) 358 self.cshape = aabb_to_aa_rect(self.get_AABB()) 359 self.cshape.center = self.position
360
361 - def add_to_map(self, m):
362 m.add(self, BUILDING_Z)
363
364 - def update_opacity(self,o):
365 self.opacity = o
366
367 -class EncryptedTroop(Troop):
368 - def __init__(self, curVertex=None, position=None, health=24, pid=0, buildTime=0, speed=2):
369 370 super(EncryptedTroop, self).__init__("encrypted_troop.png", curVertex, health, pid, speed=speed, buildTime=buildTime) 371 self.originalType = None
372
373 -class Ping(Troop):
374
375 - def __init__(self, curVertex, health=24, pid=0, speed=1.0):
376 super(Ping, self).__init__('ping_gray.png', curVertex, health, pid, speed,buildTime=10) 377 self.selectedSprite = Sprite("images/troops/ping.png") 378 self.tid = "Ping"
379
380 - def ping(self, delete_method):
381 c = objects.Objects.get_controller() 382 if self.curVertex.borderVertices: 383 curVertex = self.curVertex 384 for asID in self.curVertex.borderVertices.keys(): 385 destVertexList = self.curVertex.borderVertices[asID] 386 for destVertex in destVertexList: 387 if destVertex.asID not in c.visibleASes: 388 self.isSelectable = False 389 self.isDestroyed = True 390 pingAction = MoveTo(self.curVertex.position, 0.6) 391 pingAction += MoveTo(destVertex.position, 2) 392 pingAction += MoveTo(self.curVertex.position, 2) 393 pingAction += CallFunc(c._show_as, asID) 394 # pingAction += CallFunc(utils.play_sound, "Visible.wav") 395 pingAction += CallFunc(delete_method, self) 396 pingAction += CallFunc(curVertex._update_visibility) 397 398 self.do(pingAction) 399 self.dispatch_event("Ping") 400 401 utils.play_sound("ping.wav") 402 403 return True # We successfully pinged. Return True. 404 405 return False
406 407 408 Ping.register_event_type("Ping") 409 410
411 -class DOS(Troop):
412
413 - def __init__(self, curVertex, health=32, pid=0, speed=0.5, power=2, attackRange=2):
414 super(DOS, self).__init__("dos_gray.png", curVertex, health, pid, speed, power, attackRange=attackRange,buildTime=11) 415 self.selectedSprite = Sprite("images/troops/dos.png") 416 self.tid = "DOS"
417 418
419 -class BufferOverflow(Troop):
420
421 - def __init__(self, curVertex, health=48, pid=0, speed=0.25, power=2, attackRange=2):
422 super(BufferOverflow, self).__init__("buffer_overflow_gray.png", curVertex, health, pid, speed,power, attackRange=attackRange,buildTime=25) 423 self.selectedSprite = Sprite("images/troops/buffer_overflow.png") 424 self.tid = "BufferOverflow"
425 426
427 -class SQLInjection(Troop):
428
429 - def __init__(self, curVertex, health=60, pid=0, speed=0.4, power=10, attackRange=2):
430 super(SQLInjection, self).__init__("sql_injection_gray.png", curVertex, health, pid, speed,power, attackRange=attackRange,buildTime=18) 431 self.selectedSprite = Sprite("images/troops/sql_injection.png") 432 self.tid = "SQLInjection"
433 434
435 -class PingOfDeath(Troop):
436
437 - def __init__(self, curVertex, health=96, pid=0, speed=0.3, power=2, attackRange=2):
438 super(PingOfDeath, self).__init__("ping_of_death_gray.png", curVertex, health, pid, speed,power, attackRange=attackRange,buildTime=20) 439 self.selectedSprite = Sprite("images/troops/ping_of_death.png") 440 self.tid = "PingOfDeath"
441
442 - def attack(self, target, gameMap):
443 from maps import Vertex 444 if type(target) == Vertex or ((issubclass(type(target), Troop) or type(target) == Firewall) and target.pid != self.pid): 445 # Clicked a vertex or an enemy troop or an enemy Firewall 446 if type(target) != Vertex: 447 target = target.curVertex 448 path = gameMap.get_path(self.curVertex, target, self.pid, self, "attack") 449 if path and len(path) <= self.attackRange + 1: 450 self.attackingPath = path 451 self.is_attacking = True 452 453 # Move the Ping Of Death to the dest vertex 454 pingOfDeathAction = Delay(0) 455 456 for vertexID in path: 457 vertex = gameMap.vertices[vertexID] 458 pingOfDeathAction += MoveTo(vertex.position, self.speed) 459 460 # Execute Ping Of Death! 461 pingOfDeathAction += CallFunc(self.POD, target) 462 463 controller = objects.Objects.get_controller() 464 pingOfDeathAction += CallFunc(controller.remove_unit, self) 465 self.do(pingOfDeathAction) 466 return True 467 return False
468 469
470 - def POD(self, target):
471 hitEnemyTroop = False 472 473 if target.building != None and type(target.building) == Firewall and target.building.pid != self.pid: 474 # DESTROY FIREWALL 475 target.building.destroy_action() 476 hitEnemyTroop = True 477 self.isDestroyed = True 478 479 s = objects.Objects.get_controller() 480 for slot in target.troopSlots.values(): 481 troop = slot.troop 482 if troop.pid != self.pid: 483 # Halves the health of this troop 484 troop.on_attack((troop.health + 1) / 2,self) 485 self.isDestroyed = True 486 hitEnemyTroop = True 487 if self.pid == s.pid: 488 if hitEnemyTroop: 489 utils.play_sound("ping_of_death.wav") 490 else: 491 utils.play_sound("ping_of_death_fail.wav")
492
493 -class DNSPoison(Troop):
494
495 - def __init__(self, curVertex, health=42, pid=0, speed=0.1, power=7, attackRange=2):
496 super(DNSPoison, self).__init__("dns_poison_gray.png", curVertex, health, pid, speed,power, attackRange=attackRange,buildTime=28) 497 self.selectedSprite = Sprite("images/troops/dns_poison.png") 498 self.tid = "DNSPoison"
499 500 501
502 -class Installer(Troop):
503
504 - def __init__(self, curVertex, health=32, pid=0, speed=0.5):
505 super(Installer, self).__init__("constructor0.png", curVertex, health, pid, speed,buildTime=6) 506 self.tid = "Installer"
507 508
509 -class APTGet(Troop):
510
511 - def __init__(self, curVertex, health=48, pid=0, speed=0.5):
512 super(APTGet, self).__init__("constructor1.png", curVertex, health, pid, speed,buildTime=6) 513 self.tid = "APTGet"
514 515
516 -class NMap(Troop):
517
518 - def __init__(self, curVertex, health=100, pid=0, speed=0.15):
519 super(NMap, self).__init__("nmap.png", curVertex, health, pid, speed,buildTime=8) 520 self.tid = "NMap"
521
522 -class Handshake(Troop):
523
524 - def __init__(self, curVertex, health=20, pid=0, speed=0.15):
525 super(Handshake, self).__init__("handshake_gray.png", curVertex, health, pid, speed,buildTime=8) 526 self.selectedSprite = Sprite("images/troops/handshake.png") 527 self.tid = "Handshake"
528
529 - def shake(self, other):
530 # Adds an edge between this Handshakes vertex and the vertex that the destShake troop is in. 531 # returns edges to draw 532 c = objects.Objects.get_controller() 533 source = c.map.vertices[str(self.curVertex.vid)] 534 dest = c.map.vertices[str(other.curVertex.vid)] 535 source_position = c.map.vertexPositions[source.vid] 536 dest_position = c.map.vertexPositions[dest.vid] 537 538 # checking whether a valid edge can be made 539 if dest == source: 540 print "Can't make an edge to the same vertex" 541 # error sound 542 else: 543 # if destShake's curvertex is in your own AS make the edges 544 from maps import Edge, ASEdge 545 if source.asID != dest.asID: # create asEdge 546 source.borderVertices[dest.asID].append(dest) 547 dest.borderVertices[source.asID].append(source) 548 new_edge = ASEdge(source_position, dest_position, source, dest, AS_EDGE_COLOR, visible=True) 549 else: # create normal edge 550 # DEBUG 551 # print "Internal Edge Making" 552 new_edge = Edge(source_position, dest_position, source, dest, EDGE_COLOR, visible=True) 553 554 # Storing edge in approriate places and updating adjaceny vertex list, and adding it to the map 555 source.adjacentVertices.append(dest) 556 dest.adjacentVertices.append(source) 557 558 source.edges.append(new_edge) 559 dest.edges.append(new_edge) 560 561 new_edge.visible = True 562 563 c.map.edges.append(new_edge) 564 c.map.add(new_edge, z=EDGE_Z) 565 566 c.remove_unit(other) 567 c.remove_unit(self)
568 569
570 -class Spoof(Troop):
571
572 - def __init__(self, curVertex, health=50, pid=0, speed=0.5):
573 super(Spoof, self).__init__("spoof_gray.png", curVertex, health, pid, speed,buildTime=5) 574 self.selectedSprite = Sprite("images/troops/spoof.png") 575 self.tid = "Spoof"
576
577 -class SpoofedBuilding(Building):
578
579 - def __init__(self, curVertex, health=50, pid=0):
580 581 image = random.choice(["firewall.png", "research_factory.png", "algorithm_factory.png", "sinkhole.png", "server.png", "rsa.png", 582 "database.png"]) # Picks a random image 583 584 self.buildingType = { 585 "firewall.png":Firewall, 586 "research_factory.png":SoftwareUpdater, 587 "algorithm_factory.png":AlgorithmFactory, 588 "sinkhole.png":Sinkhole, 589 "server.png":Server, 590 "rsa.png":RSA, 591 "database.png":Database 592 }[image] 593 super(SpoofedBuilding, self).__init__(image, curVertex, health, pid, buildTime=7) 594 self.tid = "SpoofedBuilding"
595
596 -class Firewall(Building):
597
598 - def __init__(self, curVertex, health=190, pid=0):
599 super(Firewall, self).__init__("firewall.png", curVertex, health, pid,buildTime=27) 600 self.tid = "Firewall"
601
602 -class SoftwareUpdater(Building):
603 - def __init__(self, curVertex, health=130, pid=0):
604 super(SoftwareUpdater, self).__init__("research_factory.png", curVertex, health, pid,buildTime=12) 605 self.tid = "SoftwareUpdater"
606
607 -class AlgorithmFactory(Building):
608 - def __init__(self, curVertex, health=130, pid=0):
609 super(AlgorithmFactory, self).__init__("algorithm_factory.png", curVertex, health, pid,buildTime=12) 610 self.tid = "AlgorithmFactory"
611
612 -class Sinkhole(Building):
613 - def __init__(self, curVertex, health=150, pid=0):
614 super(Sinkhole, self).__init__("sinkhole.png", curVertex, health, pid,buildTime=13) 615 self.tid = "Sinkhole" 616 self.shouldAttack = set()
617
618 -class CPU(Building):
619 - def __init__(self, initCore, health=90, pid=0):
620 super(CPU, self).__init__("cpu.png", initCore, health, pid,buildTime=40) 621 self.action = None 622 self.transTimer = None 623 self.tid = "CPU"
624
625 -class Server(Building):
626 - def __init__(self, curVertex, health=220, pid=0):
627 super(Server, self).__init__("server.png", curVertex, health, pid,buildTime=0) 628 self.tid = "Server"
629 630
631 -class RSA(Building):
632 - def __init__(self, curVertex, player=None, health=100, pid=0,buildTime=9):
633 super(RSA, self).__init__("rsa.png", curVertex, health, pid) 634 # DEPRECATED, use self.pid instead 635 self.player = player # Store owner so we can auto encrypt. 636 self.tid = "RSA"
637
638 - def on_completion(self):
639 c = objects.Objects.get_controller() 640 self.curVertex.color = (0, 0, 0) 641 c.map.batch_remove(self.curVertex) 642 c.map.batch_add(self.curVertex, z=RSA_Z)
643
644 - def on_destruction(self):
645 c = objects.Objects.get_controller() 646 self.curVertex.color = (255, 255, 255) 647 c.map.batch_remove(self.curVertex) 648 c.map.batch_add(self.curVertex, z=VERTEX_Z)
649 650
651 -class Database(Building):
652 - def __init__(self, curVertex, health=150, pid=0,buildTime=30):
653 super(Database, self).__init__("database.png", curVertex, health, pid) 654 self.tid = "Database"
655
656 - def on_completion(self):
657 c = objects.Objects.get_controller() 658 self.curVertex.numOfSlots = 8 659 self.curVertex.draw_empty_slot_sprites(c.map)
660
661 - def on_destruction(self):
662 c = objects.Objects.get_controller() 663 self.curVertex.numOfSlots = 4 664 self.curVertex.draw_empty_slot_sprites(c.map)
665 666 667 # constant for units, has to be here to avoid circular reference 668 ALL_UNITS = { 669 "Ping": Ping, 670 "PingOfDeath": PingOfDeath, 671 "Installer": Installer, 672 "APTGet": APTGet, 673 "SQL": SQLInjection, 674 "DOS": DOS, 675 "DNSPoison": DNSPoison, 676 "Firewall": Firewall, 677 "Server": Server, 678 "AlgorithmFactory": AlgorithmFactory, 679 "SoftwareUpdater": SoftwareUpdater, 680 "RSA": RSA, 681 "Database": Database 682 } 683