daily pastebin goal
39%
SHARE
TWEET

kraken.py

a guest Jun 4th, 2012 90 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. from collections import deque
  2. from math import cos, sin, sqrt, ceil, pi
  3. from random import randrange, uniform, choice
  4. from operator import itemgetter, attrgetter
  5. from itertools import product
  6.  
  7. from twisted.internet import reactor
  8. from twisted.internet.task import LoopingCall
  9. from pyspades.server import Territory
  10. from pyspades.server import orientation_data, move_object, grenade_packet
  11. from pyspades.server import block_action, set_color, position_data
  12. from pyspades.world import Grenade
  13. from pyspades.common import Vertex3, Quaternion, make_color, coordinates
  14. from pyspades.collision import vector_collision, distance_3d_vector
  15. from pyspades.constants import *
  16. from commands import name, add, get_player, admin
  17.  
  18. ALLOW_KRAKEN_COMMAND = False
  19.  
  20. USE_DAYCYCLE = True
  21. RESPAWN_TIME = 15
  22. FALLING_BLOCK_COLOR = 0x606060
  23. FALLING_BLOCK_DAMAGE = 100
  24. FALLING_BLOCK_Z = 0
  25. REGEN_ONSET = 2.0
  26. REGEN_FREQUENCY = 0.15
  27. REGEN_AMOUNT = 3
  28. WATER_DAMAGE = 25
  29. GRAB_DAMAGE = 40
  30. EYE_PAIN_TIME = 8.0
  31. KRAKEN_BLACK = 0x000000
  32. KRAKEN_BLOOD = make_color(120, 255, 120)
  33. KRAKEN_EYE_SMALL = [
  34.     ( 0, 0, -1, 0xC00000),
  35.     ( 0, 0, -2, 0x400000),
  36.     ( 0, 0, -3, 0xC00000),
  37.     (-1, 0, -1, 0xFF0000),
  38.     (-1, 0, -2, 0xC00000),
  39.     (-1, 0, -3, 0x800000),
  40.     ( 1, 0, -1, 0xFF0000),
  41.     ( 1, 0, -2, 0xC00000),
  42.     ( 1, 0, -3, 0xFFFFFF)]
  43. KRAKEN_EYE_SMALL_CLOSED = [
  44.     (-1, 0, -1, 0x000000),
  45.     (-1, 0, -2, 0x000000),
  46.     (-1, 0, -3, 0x000000),
  47.     ( 1, 0, -1, 0x000000),
  48.     ( 1, 0, -2, 0x000000),
  49.     ( 1, 0, -3, 0x000000),
  50.     ( 0, 0, -1, 0x000000),
  51.     ( 0, 0, -2, 0x000000),
  52.     ( 0, 0, -3, 0x000000)]
  53.  
  54. def conv_color(val):
  55.     return ((val >> 16) & 255, (val >> 8) & 255, val & 255)
  56.  
  57. def cube(s):
  58.     s0, s1 = -s / 2 + 1, s / 2 + 1
  59.     return product(xrange(s0, s1), repeat = 3)
  60.  
  61. def prism(x, y, z, w, d, h):
  62.     return product(xrange(x, x + w), xrange(y, y + d), xrange(z, z + h))
  63.  
  64. def plane(r):
  65.     r0, r1 = -r / 2 + 1, r / 2 + 1
  66.     return product(xrange(r0, r1), repeat = 2)
  67.  
  68. def disc(rr, x = 0, y = 0, min_rr = None):
  69.     for u, v in plane(rr):
  70.         d = u * u + v * v
  71.         if d > rr or (min_rr and d < min_rr):
  72.             continue
  73.         yield x + u, y + v
  74.  
  75. def sphere(r, x = 0, y = 0, z = 0, min_r = None):
  76.     rr = r * r
  77.     min_rr = min_r and min_r * min_r
  78.     for w, v, u in cube(r):
  79.         d = u * u + v * v + w * w
  80.         if d > rr or (min_r and d < min_rr):
  81.             continue
  82.         yield x + u, y + v, z + w
  83.  
  84. def aabb(x, y, z, i, j, k, w, d, h):
  85.     return not (x < i or x > i + w or y < j or y > j + d or z < k or z > k + h)
  86.  
  87. def aabb_centered(x, y, z, i, j, k, s):
  88.     return not (x < i - s or x > i + s or y < j - s or y > j + s or
  89.         z < k - s or z > k + s)
  90.  
  91. def randrangerect(x1, y1, x2, y2):
  92.     return randrange(x1, x2), randrange(y1, y2)
  93.  
  94. def fall_eta(height):
  95.     return 2.0 * (height / 64.0) ** 0.75
  96.  
  97. def is_valid_enemy(player):
  98.     return not (player.world_object is None or player.world_object.dead or
  99.         player.grabbed_by or player.trapped or player.regenerating or player.god)
  100.  
  101. class Animated:
  102.     blocks_per_cycle = 3
  103.     build_interval = 0.01
  104.     build_queue = None
  105.     build_loop = None
  106.     blocks = None
  107.    
  108.     def __init__(self, protocol):
  109.         self.protocol = protocol
  110.         self.build_queue = deque()
  111.         self.build_loop = LoopingCall(self.build_cycle)
  112.         self.build_loop.start(self.build_interval)
  113.         self.blocks = set()
  114.    
  115.     def build_cycle(self):
  116.         if not self.build_queue:
  117.             return        
  118.         blocks_left = self.blocks_per_cycle
  119.         last_color = None
  120.         while self.build_queue and blocks_left > 0:
  121.             x, y, z, color = self.build_queue.popleft()
  122.             if color != last_color:
  123.                 self.protocol.set_block_color(color)
  124.                 last_color = color
  125.             if self.protocol.build_block(x, y, z, color):
  126.                 blocks_left -= 1
  127.  
  128. class Tentacle(Animated):
  129.     dead = False
  130.     dying = False
  131.     on_death = None
  132.     on_removed = None
  133.     parent = None
  134.     protocol = None
  135.     origin = None
  136.     up = None
  137.     orientation = None
  138.     start_orientation = None
  139.     target_orientation = None
  140.     lerp_t = None
  141.     facing = None
  142.     sections = None
  143.     radius = 2
  144.     spread = radius / 2.0
  145.     follow = None
  146.     follow_interval = 1.3
  147.     follow_timer = follow_interval
  148.     initial_growth_interval = 0.2
  149.     growth_interval = initial_growth_interval
  150.     growth_timer = growth_interval
  151.     blocks_destroyed = None
  152.     last_block_destroyed = None
  153.     growing = True
  154.     withdraw = False
  155.     grabbed_player = None
  156.     max_hp = None
  157.    
  158.     def __init__(self, protocol, parent, (x, y, z)):
  159.         Animated.__init__(self, protocol)
  160.         self.parent = parent
  161.         self.origin = Vertex3(x, y, z)
  162.         self.up = Vertex3(0.0, 0.0, -1.0)
  163.         self.orientation = Quaternion()
  164.         self.facing = self.orientation.transform_vector(self.up)
  165.         self.sections = []
  166.         self.blocks_destroyed = []
  167.         self.parent.tentacles.append(self)
  168.         self.find_target()
  169.    
  170.     def find_target(self):
  171.         best = None
  172.         best_dist = None
  173.         best_followed = None
  174.         for player in self.protocol.players.values():
  175.             if not is_valid_enemy(player):
  176.                 continue
  177.             dist = distance_3d_vector(player.world_object.position, self.origin)
  178.             followed = self.parent.is_enemy_targeted(player)
  179.             if not best or dist < best_dist or best_followed and not followed:
  180.                 best, best_dist, best_followed = player, dist, followed
  181.         self.follow = best
  182.    
  183.     def think(self, dt):
  184.         tip = self.sections and self.sections[-1][0] or self.origin
  185.        
  186.         self.follow_timer -= dt
  187.         if self.follow and not is_valid_enemy(self.follow):
  188.             self.find_target()
  189.             if not self.follow:
  190.                 self.growth_timer = 0.66
  191.                 self.growing = False
  192.                 self.withdraw = True
  193.         elif self.follow and self.follow_timer <= 0.0:
  194.             self.follow_timer = self.follow_interval
  195.             follow_pos = self.follow.world_object.position
  196.             direction = follow_pos - tip
  197.             q = self.facing.get_rotation_to(direction)
  198.             self.start_orientation = Quaternion(*self.orientation.get())
  199.             self.target_orientation = q * self.orientation
  200.             self.lerp_t = 0.0
  201.         if self.target_orientation and self.lerp_t <= 1.0:
  202.             self.orientation = self.start_orientation.slerp(
  203.                 self.target_orientation, self.lerp_t)
  204.             self.lerp_t += 0.02
  205.         self.facing = self.orientation.transform_vector(self.up)
  206.         self.facing.normalize()
  207.        
  208.         self.growth_timer -= dt
  209.         if self.growth_timer <= 0.0:
  210.             self.growth_timer = self.growth_interval
  211.             if self.growing and self.follow:
  212.                 tip = self.grow(tip.copy())
  213.             elif self.withdraw:
  214.                 if self.sections:
  215.                     pos, blocks = self.sections.pop()
  216.                     tip = pos
  217.                     for uvw in blocks:
  218.                         if not self.parent.is_location_inside(uvw, skip = self):
  219.                             self.protocol.remove_block(*uvw)
  220.                         self.blocks.discard(uvw)
  221.                 else:
  222.                     for uvw in self.blocks:
  223.                         if not self.parent.is_location_inside(uvw, skip = self):
  224.                             self.protocol.remove_block(*uvw)
  225.                     self.dead = True
  226.                     if self.on_removed:
  227.                         self.on_removed(self)
  228.        
  229.         player = self.grabbed_player
  230.         if player:
  231.             if self.dead or not player.world_object or player.world_object.dead:
  232.                 player.grabbed_by = None
  233.                 self.grabbed_player = None
  234.             else:
  235.                 player.set_location((tip.x, tip.y, tip.z - 1.0))
  236.                 if tip.z >= 63:
  237.                     player.got_water_damage = True
  238.                     player.kill(type = FALL_KILL)
  239.                     player.got_water_damage = False
  240.    
  241.     def on_block_destroy(self, x, y, z, mode):
  242.         if mode == SPADE_DESTROY and (x, y, z) in self.blocks:
  243.             return False
  244.    
  245.     def on_block_removed(self, x, y, z):
  246.         xyz = (x, y, z)
  247.         if xyz not in self.blocks:
  248.             return
  249.         self.blocks.discard(xyz)
  250.         total_damage = 0.0
  251.         for u, v, w in self.blocks_destroyed:
  252.             xu, yv, zw = x - u, y - v, z - w
  253.             d = sqrt(xu*xu + yv*yv + zw*zw)
  254.             total_damage += d >= 1.0 and 1.0 / d or 1.0
  255.             if total_damage > self.max_hp:
  256.                 self.fracture(x, y, z)
  257.                 self.last_block_destroyed = None
  258.                 self.die()
  259.                 if self.on_death:
  260.                     self.on_death(self)
  261.                 return
  262.         if self.last_block_destroyed:
  263.             self.protocol.set_block_color(KRAKEN_BLOOD)
  264.             u, v, w = self.last_block_destroyed
  265.             self.protocol.build_block(u, v, w, KRAKEN_BLOOD)
  266.             self.blocks.add(self.last_block_destroyed)
  267.         self.last_block_destroyed = xyz
  268.         self.blocks_destroyed.append(xyz)
  269.    
  270.     def die(self):
  271.         self.follow = None
  272.         self.target_orientation = None
  273.         if self.grabbed_player:
  274.             self.grabbed_player.grabbed_by = None
  275.         self.grabbed_player = None
  276.         self.growth_timer = 0.66
  277.         speedup = 2.0 + max(len(self.sections) / 140.0, 1.0)
  278.         self.growth_interval = self.initial_growth_interval / speedup
  279.         self.growing = False
  280.         self.withdraw = True
  281.         self.dying = True
  282.    
  283.     def fracture(self, x, y, z):
  284.         protocol = self.protocol
  285.         radius = self.radius
  286.         for uvw in sphere(int(radius * 1.5), x, y, z):
  287.             if not self.parent.is_location_inside(uvw, skip = self):
  288.                 protocol.remove_block(*uvw)
  289.             self.blocks.discard(uvw)
  290.         to_remove = []
  291.         breakpoint = False
  292.         while self.sections:
  293.             pos, blocks = self.sections.pop()
  294.             for uvw in blocks:
  295.                 if not self.parent.is_location_inside(uvw, skip = self):
  296.                     if breakpoint:
  297.                         protocol.remove_block(*uvw)
  298.                     else:
  299.                         to_remove.append(uvw)
  300.                 self.blocks.discard(uvw)
  301.             if breakpoint:
  302.                 break
  303.             i, j, k = pos.get()
  304.             breakpoint = aabb_centered(x, y, z, i, j, k, radius)
  305.         if self.sections:
  306.             self.sections.pop()
  307.         for u, v, w in to_remove:
  308.             protocol.remove_block(u, v, w)
  309.    
  310.     def grow(self, tip):
  311.         if self.sections:
  312.             tip += self.facing * self.spread
  313.         map = self.protocol.map
  314.         radius = self.radius
  315.         ix, iy, iz = int(tip.x), int(tip.y), int(tip.z)
  316.         blocks = []
  317.         destroyed = 0
  318.         for x, y, z in sphere(radius, ix, iy, iz):
  319.             if (x < 0 or x >= 512 or y < 0 or y >= 512 or
  320.                 z < 0 or z >= 63):
  321.                 continue
  322.             xyz = (x, y, z)
  323.             if xyz not in self.blocks:
  324.                 if not map.get_solid(x, y, z):
  325.                     blocks.append(xyz)
  326.                     self.blocks.add(xyz)
  327.                     self.build_queue.append(xyz + (KRAKEN_BLACK,))
  328.                 elif not self.parent.is_location_inside(xyz, skip = self):
  329.                     destroyed += 1
  330.         if destroyed >= radius:
  331.             for x, y, z in sphere(radius + 2, ix, iy, iz, min_r = radius):
  332.                 if self.parent.is_location_inside((x, y, z)):
  333.                     continue
  334.                 self.protocol.remove_block(x, y, z)
  335.             self.protocol.create_explosion_effect(tip)
  336.         for player in self.protocol.players.values():
  337.             if not is_valid_enemy(player):
  338.                 continue
  339.             pos = player.world_object.position
  340.             if vector_collision(pos, tip, radius * 0.75):
  341.                 self.follow = None
  342.                 self.target_orientation = None
  343.                 self.growth_timer = 0.4
  344.                 self.growing = False
  345.                 self.withdraw = True
  346.                 self.grabbed_player = player
  347.                 player.grabbed_by = self
  348.                 player.set_location((tip.x, tip.y, tip.z - 1.0))
  349.                 player.hit(GRAB_DAMAGE)
  350.                 break
  351.         self.sections.append((tip, blocks))
  352.         return tip
  353.  
  354. class Eye():
  355.     parent = None
  356.     protocol = None
  357.     dead = False
  358.     blocks = None
  359.     origin_x = None
  360.     pos = None
  361.     base = None
  362.     hits = None
  363.     look_interval_min = 0.8
  364.     look_interval_max = 2.5
  365.     look_timer = look_interval_max
  366.     on_hit = None
  367.     create_call = None
  368.    
  369.     def __init__(self, parent, base, ox, oy, oz, hits = 3):
  370.         self.parent = parent
  371.         self.protocol = parent.protocol
  372.         self.blocks = set()
  373.         self.pos = parent.origin.copy().translate(ox, oy, oz)
  374.         self.origin_x = self.pos.x
  375.         self.base = base[:]
  376.         self.hits = hits
  377.         parent.eyes.append(self)
  378.    
  379.     def think(self, dt):
  380.         if not self.blocks:
  381.             return
  382.         self.look_timer -= dt
  383.         if self.look_timer <= 0.0:
  384.             self.look_timer = uniform(self.look_interval_min,
  385.                 self.look_interval_max)
  386.             old_x = self.pos.x
  387.             self.pos.x = max(self.origin_x - 1, min(self.origin_x + 1,
  388.                 self.pos.x + choice([-1, 1])))
  389.             if old_x != self.pos.x:
  390.                 old_blocks = self.blocks
  391.                 self.blocks = set()
  392.                 self.create_instant()
  393.                 old_blocks -= self.blocks
  394.                 self.protocol.set_block_color(KRAKEN_BLACK)
  395.                 for x, y, z in old_blocks:
  396.                     self.protocol.build_block(x, y, z, KRAKEN_BLACK,
  397.                         force = True)
  398.    
  399.     def create(self, block_queue = None, close = False):
  400.         if block_queue is None:
  401.             block_queue = deque(self.base)
  402.         last_color = None
  403.         x, y, z = self.pos.get()
  404.         x_d = None
  405.         while block_queue:
  406.             u, v, w, color = block_queue[0]
  407.             if x_d is None:
  408.                 x_d = abs(u)
  409.             elif abs(u) != x_d:
  410.                 break
  411.             if color != last_color:
  412.                 self.protocol.set_block_color(color)
  413.                 last_color = color
  414.             u, v, w = x + u, y + v, z + w
  415.             uvw = (u, v, w)
  416.             self.protocol.build_block(u, v, w, color, force = True)
  417.             if not close:
  418.                 self.parent.head.discard(uvw)
  419.                 self.blocks.add(uvw)
  420.             block_queue.popleft()
  421.         if block_queue:
  422.             self.create_call = reactor.callLater(0.25, self.create, block_queue)
  423.    
  424.     def create_instant(self, block_list = None):
  425.         if block_list is None:
  426.             block_list = self.base
  427.         last_color = None
  428.         x, y, z = self.pos.get()
  429.         block_list = sorted(block_list, key = itemgetter(3))
  430.         for u, v, w, color in block_list:
  431.             if color != last_color:
  432.                 self.protocol.set_block_color(color)
  433.                 last_color = color
  434.             u, v, w = x + u, y + v, z + w
  435.             uvw = (u, v, w)
  436.             self.protocol.build_block(u, v, w, color, force = True)
  437.             self.parent.head.discard(uvw)
  438.             self.blocks.add(uvw)
  439.    
  440.     def on_block_removed(self, x, y, z):
  441.         xyz = (x, y, z)
  442.         if self.dead or xyz not in self.blocks:
  443.             return
  444.         protocol = self.protocol
  445.         protocol.create_explosion_effect(Vertex3(x, y, z))
  446.         self.parent.build_queue.append((x, y, z, KRAKEN_BLOOD))
  447.         self.hits -= 1
  448.         if self.hits > 0:
  449.             self.pain()
  450.             uvw = (x - self.pos.x, y - self.pos.y, z - self.pos.z)
  451.             i = [uvwc[:-1] for uvwc in self.base].index(uvw)
  452.             self.base[i] = uvw + (KRAKEN_BLOOD,)
  453.         else:
  454.             self.close()
  455.             self.dead = True
  456.         if self.on_hit:
  457.             self.on_hit(self)
  458.    
  459.     def close(self):
  460.         self.parent.head.update(self.blocks)
  461.         self.blocks.clear()
  462.         if self.create_call and self.create_call.active():
  463.             self.create_call.cancel()
  464.         reactor.callLater(0.5, self.create, deque(KRAKEN_EYE_SMALL_CLOSED),
  465.             close = True)
  466.    
  467.     def pain(self):
  468.         self.close()
  469.         reactor.callLater(EYE_PAIN_TIME, self.create)
  470.         self.look_timer = EYE_PAIN_TIME + self.look_interval_min
  471.  
  472. class Kraken(Animated):
  473.     dead = False
  474.     origin = None
  475.     tentacles = None
  476.     head = None
  477.     eyes = None
  478.     max_hp = 10.0
  479.     hp = max_hp
  480.     size = 7
  481.     on_last_tentacle_death = None
  482.     on_death = None
  483.     on_removed = None
  484.     finally_call = None
  485.     phase = 0
  486.    
  487.     def __init__(self, protocol, (x, y, z)):
  488.         Animated.__init__(self, protocol)
  489.         self.origin = Vertex3(x, y, z)
  490.         self.head = set()
  491.         self.eyes = []
  492.         self.tentacles = []
  493.    
  494.     def is_location_inside(self, location, skip = None):
  495.         if location in self.head:
  496.             return True
  497.         for eye in self.eyes:
  498.             if location in eye.blocks:
  499.                 return True
  500.         for t in self.tentacles:
  501.             if t is not skip and location in t.blocks:
  502.                 return True
  503.         return False
  504.    
  505.     def is_enemy_targeted(self, player):
  506.         for t in self.tentacles:
  507.             if t.follow is player:
  508.                 return True
  509.         return False
  510.    
  511.     def on_block_destroy(self, x, y, z, mode):
  512.         for t in self.tentacles:
  513.             if t.on_block_destroy(x, y, z, mode) == False:
  514.                 return False
  515.    
  516.     def on_block_removed(self, x, y, z):
  517.         eye_died = False
  518.         for eye in self.eyes:
  519.             eye.on_block_removed(x, y, z)
  520.             eye_died = eye_died or eye.dead
  521.         if eye_died:
  522.             self.eyes = [eye for eye in self.eyes if not eye.dead]
  523.             if not self.eyes:
  524.                 self.die()
  525.        
  526.         for t in self.tentacles:
  527.             t.on_block_removed(x, y, z)
  528.    
  529.     def die(self):
  530.         protocol = self.protocol
  531.         def remove(this, remover, blocks):
  532.             if blocks:
  533.                 remover(*blocks.pop())
  534.                 reactor.callLater(0.01, this, this, remover, blocks)
  535.             elif self.on_removed:
  536.                 self.on_removed(self)
  537.         def explode(this, effect, blocks, left):
  538.             x = self.origin.x + uniform(-5.0, 5.0)
  539.             y = self.origin.y + self.size + 1.0
  540.             z = self.origin.z + uniform(-15.0, 0.0)
  541.             effect(Vertex3(x, y, z))
  542.             if not blocks or left <= 0:
  543.                 return
  544.             delay = uniform(0.3, 0.8)
  545.             left -= 1
  546.             reactor.callLater(delay, this, this, effect, blocks, left)
  547.         remove(remove, protocol.remove_block, self.head)
  548.         explode(explode, protocol.create_explosion_effect, self.head, 10)
  549.        
  550.         self.dead = True
  551.         for t in self.tentacles:
  552.             t.die()        
  553.         if self.on_death:
  554.             self.on_death(self)
  555.    
  556.     def think(self, dt):
  557.         for eye in self.eyes:
  558.             eye.think(dt)
  559.        
  560.         rebuild_list = False
  561.         for t in self.tentacles:
  562.             t.think(dt)
  563.             rebuild_list = rebuild_list or t.dead
  564.         if rebuild_list:
  565.             self.tentacles = [t for t in self.tentacles if not t.dead]
  566.             if not self.tentacles and self.on_last_tentacle_death:
  567.                 self.on_last_tentacle_death(self)
  568.    
  569.     def hit(self, value, rate):
  570.         hp_bar = self.protocol.hp_bar
  571.         if not hp_bar.shown:
  572.             hp_bar.progress = 1.0 - self.hp / self.max_hp
  573.             hp_bar.show()
  574.         self.hp = max(self.hp - value, 0)
  575.         previous_rate = hp_bar.rate
  576.         hp_bar.get_progress(True)
  577.         hp_bar.rate = rate
  578.         hp_bar.update_rate()
  579.         hp_bar.send_progress()
  580.         target_progress = 1.0 - self.hp / self.max_hp
  581.         delay = (target_progress - hp_bar.progress) / hp_bar.rate_value
  582.         hp_call = hp_bar.hp_call
  583.         if hp_call and hp_call.active():
  584.             if previous_rate == 0:
  585.                 hp_call.cancel()
  586.             else:
  587.                 hp_call.reset(delay)
  588.                 return
  589.         hp_bar.hp_call = reactor.callLater(delay, hp_bar.stop)
  590.    
  591.     def create_head(self, head_list, height = None):
  592.         height = height or len(head_list)
  593.         x, y, z = self.origin.get()
  594.         for d in head_list[-height:]:
  595.             for u, v in d:
  596.                 xyzc = (x + u, y + v, z, KRAKEN_BLACK)
  597.                 self.build_queue.append(xyzc)
  598.                 self.head.add(xyzc[:-1])
  599.             z -= 1
  600.         if height < len(head_list):
  601.             delay = 0.6
  602.             reactor.callLater(delay, self.create_head, head_list, height + 6)
  603.  
  604. force_boss = False
  605.  
  606. @admin
  607. def kraken(connection, value = None):
  608.     global force_boss
  609.     protocol = connection.protocol
  610.     if protocol.game_mode != TC_MODE:
  611.         return 'Unfortunately, the game mode is required to be TC. Change it then restart'
  612.     if not protocol.boss_ready:
  613.         force_boss = True
  614.         return 'The next map will be kraken-ready. Change maps then try again'
  615.     if protocol.boss:
  616.         return "There is already a kraken! Why can't I hold all these krakens?"
  617.     try:
  618.         x, y = coordinates(value)
  619.     except (ValueError):
  620.         return 'Need coordinates where to spawn the kraken, e.g /kraken E3'
  621.     start_kraken(protocol, max(x, 64), max(y, 64))
  622.  
  623. if ALLOW_KRAKEN_COMMAND:
  624.     add(kraken)
  625.  
  626. def start_kraken(protocol, x, y, hardcore = False, finally_call = None):
  627.     y += 32
  628.     boss = Kraken(protocol, (x, y - 12, 63))
  629.     protocol.boss = boss
  630.     if USE_DAYCYCLE and protocol.daycycle_loop.running:
  631.         protocol.daycycle_loop.stop()
  632.    
  633.     arena = getattr(protocol.map_info.info, 'arena', None)
  634.     if arena:
  635.         arena_center = (int((arena[2] - arena[0]) / 2.0 + arena[0]),
  636.             int((arena[3] - arena[1]) / 2.0 + arena[1]))
  637.         arena_radius = min(arena[2] - arena[0], arena[3] - arena[1]) / 2.0
  638.    
  639.     def randring():
  640.         min_r, max_r = 12.0, 32.0
  641.         r = uniform(min_r, max_r)
  642.         a = uniform(0.0, pi)
  643.         return x + cos(a) * r, y + sin(a) * r, 63
  644.    
  645.     def randring_arena():
  646.         if not arena:
  647.             return randring()
  648.         r = uniform(arena_radius, arena_radius * 1.2)
  649.         a = uniform(0.0, 2*pi)
  650.         x, y = arena_center
  651.         return x + cos(a) * r, y + sin(a) * r, 63
  652.    
  653.     def minor_hit(caller = None):
  654.         boss.hit(1.0, 1)
  655.         caller.on_removed = None
  656.    
  657.     def major_hit(caller = None):
  658.         boss.hit(3.0, 1)
  659.    
  660.     def major_hit_and_progress(caller = None):
  661.         caller.on_hit = major_hit
  662.         major_hit()
  663.         progress()
  664.    
  665.     def major_hit_and_pain(caller = None):
  666.         major_hit()
  667.         boss_alive = False
  668.         for eye in caller.parent.eyes:
  669.             if eye is not caller and not eye.dead:
  670.                 eye.pain()
  671.                 boss_alive = True
  672.         if boss_alive and caller.dead:
  673.             falling_blocks_start()
  674.    
  675.     def respawn_tentacle(caller = None):
  676.         if boss and not boss.dead:
  677.             reactor.callLater(5.0, spawn_tentacles, 1, True)
  678.    
  679.     def spawn_tentacles(amount, respawn = False, fast = False, arena = False,
  680.         no_hit = False):
  681.         if not hardcore:
  682.             toughness = max(3.0, min(10.0, len(protocol.players) * 0.5))
  683.         else:
  684.             toughness = max(5.0, min(13.0, len(protocol.players) * 0.85))
  685.         if boss and not boss.dead:
  686.             for i in xrange(amount):
  687.                 origin = randring_arena() if arena else randring()
  688.                 t = Tentacle(protocol, boss, origin)
  689.                 t.max_hp = toughness
  690.                 t.growth_timer = uniform(i * 1.0, i * 1.2)
  691.                 if hardcore:
  692.                     t.initial_growth_interval *= 0.8
  693.                 if fast:
  694.                     t.initial_growth_interval *= 0.5
  695.                 else:
  696.                     t.follow_timer = 2.0
  697.                 t.growth_interval = t.initial_growth_interval
  698.                 if respawn:
  699.                     t.on_removed = respawn_tentacle
  700.                 elif not no_hit:
  701.                     t.on_death = minor_hit
  702.                     t.on_removed = minor_hit
  703.    
  704.     def falling_blocks_cycle():
  705.         alive_players = filter(is_valid_enemy, protocol.players.values())
  706.         if not alive_players:
  707.             return
  708.         player = choice(alive_players)
  709.         x, y, z = player.world_object.position.get()
  710.         protocol.create_falling_block(int(x), int(y), randrange(2, 4), 2)
  711.    
  712.     def falling_blocks_start():
  713.         for i in range(20):
  714.             reactor.callLater(i * 0.4, falling_blocks_cycle)
  715.    
  716.     def squid_head():
  717.         h = []
  718.         for i in xrange(37, 5, -2):
  719.             h.append(list(disc(i, min_rr = i - 15)))
  720.         return h
  721.    
  722.     def squid_head_large():
  723.         h = []
  724.         for i in xrange(42, 3, -2):
  725.             ii = int(i ** 1.3)
  726.             h.append(list(disc(ii, y = int(sqrt(i)), min_rr = i + 10)))
  727.         return h
  728.    
  729.     def regenerate_players():
  730.         for player in protocol.players.values():
  731.             player.trapped = False
  732.             player.last_hit = reactor.seconds()
  733.             player.regenerating = True
  734.             if not player.world_object.dead:
  735.                 player.regen_loop.start(REGEN_FREQUENCY)
  736.             else:
  737.                 player.spawn(player.world_object.position.get())
  738.    
  739.     def round_end(caller = None):
  740.         regenerate_players()
  741.         reactor.callLater(8.0, progress)
  742.    
  743.     def round_end_delay(caller = None):
  744.         reactor.callLater(10.0, round_end)
  745.    
  746.     def round_start(caller = None):
  747.         for player in protocol.players.values():
  748.             player.regenerating = False
  749.    
  750.     def progress_delay(caller = None):
  751.         reactor.callLater(6.0, progress)
  752.    
  753.     def victory(caller = None):
  754.         regenerate_players()
  755.         if USE_DAYCYCLE:
  756.             protocol.current_time = 23.30
  757.             protocol.update_day_color()
  758.    
  759.     def cleanup(caller = None):
  760.         round_start()
  761.         protocol.boss = None
  762.         if USE_DAYCYCLE and protocol.daycycle_loop.running:
  763.             protocol.daycycle_loop.stop()
  764.         if caller.finally_call:
  765.             caller.finally_call(caller)
  766.    
  767.     def red_sky():
  768.         if USE_DAYCYCLE:
  769.             protocol.day_colors = [
  770.                 ( 0.00, (0.5527, 0.24, 0.94), False),
  771.                 ( 0.10, (0.0,    0.05, 0.05), True),
  772.                 ( 0.20, (0.0,    1.00, 0.34), False),
  773.                 (23.30, (0.0,    1.00, 0.34), False),
  774.                 (23.50, (0.5527, 0.24, 0.94), False)]
  775.             protocol.current_time = 0.00
  776.             protocol.target_color_index = 0
  777.             protocol.update_day_color()
  778.             if not protocol.daycycle_loop.running:
  779.                 protocol.daycycle_loop.start(protocol.day_update_frequency)
  780.    
  781.     progress = None
  782.    
  783.     def progress_normal(caller = None):
  784.         boss.phase += 1
  785.         round_start()
  786.        
  787.         if boss.phase == 1:
  788.             boss.on_last_tentacle_death = progress_delay
  789.             spawn_tentacles(2)
  790.         elif boss.phase == 2:
  791.             boss.on_last_tentacle_death = round_end
  792.             spawn_tentacles(4)
  793.         elif boss.phase == 3:
  794.             boss.on_last_tentacle_death = round_end
  795.             spawn_tentacles(3, fast = True)
  796.         elif boss.phase == 4:
  797.             boss.on_last_tentacle_death = None
  798.             boss.on_death = round_end_delay
  799.             boss.size = 7
  800.             boss.create_head(squid_head())
  801.             eye = Eye(boss, KRAKEN_EYE_SMALL, 0, 5, -1, hits = 5)
  802.             eye.on_hit = major_hit_and_progress
  803.             reactor.callLater(7.0, eye.create)
  804.         elif boss.phase == 5:
  805.             spawn_tentacles(3, respawn = True)
  806.             spawn_tentacles(2, arena = True, no_hit = True)
  807.         elif boss.phase == 6:
  808.             protocol.send_chat('LOOK UP!', global_message = None)
  809.             falling_blocks_start()
  810.             reactor.callLater(15.0, round_end)
  811.         elif boss.phase == 7:
  812.             boss.dead = False
  813.             boss.on_last_tentacle_death = round_end
  814.             spawn_tentacles(4, fast = True, arena = True)
  815.         elif boss.phase == 8:
  816.             red_sky()
  817.             boss.on_last_tentacle_death = None
  818.             boss.on_death = victory
  819.             boss.on_removed = cleanup
  820.             boss.finally_call = finally_call
  821.             boss.origin.y -= 24
  822.             boss.size = 16
  823.             boss.create_head(squid_head_large())
  824.             eye = Eye(boss, KRAKEN_EYE_SMALL, 0, 16, -2, hits = 4)
  825.             eye.on_hit = major_hit_and_pain
  826.             reactor.callLater(16.0, eye.create)
  827.             eye = Eye(boss, KRAKEN_EYE_SMALL, 0, 14, -6, hits = 4)
  828.             eye.on_hit = major_hit_and_pain
  829.             reactor.callLater(16.0, eye.create)
  830.             reactor.callLater(18.0, spawn_tentacles, 5, respawn = True)
  831.    
  832.     def progress_hardcore(caller = None):
  833.         boss.phase += 1
  834.         round_start()
  835.        
  836.         if boss.phase == 1:
  837.             boss.on_last_tentacle_death = progress_delay
  838.             spawn_tentacles(3)
  839.             falling_blocks_start()
  840.         elif boss.phase == 2:
  841.             boss.on_last_tentacle_death = round_end
  842.             spawn_tentacles(4, fast = True)
  843.         elif boss.phase == 3:
  844.             boss.on_last_tentacle_death = None
  845.             boss.on_death = round_end_delay
  846.             boss.size = 7
  847.             boss.create_head(squid_head())
  848.             eye = Eye(boss, KRAKEN_EYE_SMALL, 0, 5, -1, hits = 8)
  849.             eye.look_interval_min *= 0.8
  850.             eye.look_interval_max *= 0.6
  851.             eye.on_hit = major_hit_and_progress
  852.             reactor.callLater(7.0, eye.create)
  853.         elif boss.phase == 4:
  854.             spawn_tentacles(3, respawn = True)
  855.             spawn_tentacles(3, arena = True, no_hit = True)
  856.         elif boss.phase == 5:
  857.             boss.dead = False
  858.             boss.on_last_tentacle_death = round_end
  859.             spawn_tentacles(5, fast = True, arena = True)
  860.         elif boss.phase == 6:
  861.             red_sky()
  862.             boss.on_last_tentacle_death = None
  863.             boss.on_death = victory
  864.             boss.on_removed = cleanup
  865.             boss.finally_call = finally_call
  866.             boss.origin.y -= 24
  867.             boss.size = 16
  868.             boss.create_head(squid_head_large())
  869.             eye = Eye(boss, KRAKEN_EYE_SMALL, 0, 16, -2, hits = 6)
  870.             eye.look_interval_min *= 0.8
  871.             eye.look_interval_max *= 0.6
  872.             eye.on_hit = major_hit_and_pain
  873.             reactor.callLater(16.0, eye.create)
  874.             eye = Eye(boss, KRAKEN_EYE_SMALL, 0, 14, -6, hits = 6)
  875.             eye.look_interval_min *= 0.8
  876.             eye.look_interval_max *= 0.6
  877.             eye.on_hit = major_hit_and_pain
  878.             reactor.callLater(16.0, eye.create)
  879.             reactor.callLater(18.0, spawn_tentacles, 5, respawn = True)
  880.             reactor.callLater(14.0, falling_blocks_start)
  881.    
  882.     boss.blocks_per_cycle = 2
  883.     boss.build_interval = 0.01
  884.     if not hardcore:
  885.         progress = progress_normal
  886.         boss.hp = boss.max_hp = 2.0 + 4.0 + 3.0 + 5*3.0 + 4.0 + (4 + 4)*3.0
  887.     else:
  888.         progress = progress_hardcore
  889.         boss.hp = boss.max_hp = 3.0 + 4.0 + 8*3.0 + 5.0 + (6 + 6)*3.0
  890.     progress()
  891.     return boss
  892.  
  893. class BossTerritory(Territory):
  894.     shown = False
  895.     hp_call = None
  896.    
  897.     def add_player(self, player):
  898.         return
  899.    
  900.     def remove_player(self, player):
  901.         return
  902.    
  903.     def update_rate(self):
  904.         self.rate_value = self.rate * TC_CAPTURE_RATE
  905.         self.capturing_team = (self.rate_value < 0 and
  906.             self.protocol.blue_team or self.protocol.green_team)
  907.         self.start = reactor.seconds()
  908.    
  909.     def show(self):
  910.         self.shown = True
  911.         for player in self.protocol.players.values():
  912.             self.update_for_player(player)
  913.    
  914.     def hide(self):
  915.         self.shown = False
  916.         self.update()
  917.    
  918.     def stop(self):
  919.         self.rate = 0
  920.         self.get_progress(True)
  921.         self.update_rate()
  922.         self.send_progress()
  923.         self.hp_call = reactor.callLater(3.0, self.hide)
  924.    
  925.     def update_for_player(self, connection, orientation = None):
  926.         x, y, z = orientation or connection.world_object.orientation.get()
  927.         v = Vertex3(x, y, 0.0)
  928.         v.normalize()
  929.         v *= -10.0
  930.         v += connection.world_object.position
  931.         move_object.object_type = self.id
  932.         move_object.state = self.team and self.team.id or NEUTRAL_TEAM
  933.         move_object.x = v.x
  934.         move_object.y = v.y
  935.         move_object.z = v.z
  936.         connection.send_contained(move_object)
  937.  
  938. def apply_script(protocol, connection, config):
  939.     class BossProtocol(protocol):
  940.         game_mode = TC_MODE
  941.        
  942.         boss = None
  943.         boss_ready = False
  944.         hp_bar = None
  945.        
  946.         def start_kraken(self, x, y, hardcore = False, finally_call = None):
  947.             return start_kraken(self, x, y, hardcore, finally_call)
  948.        
  949.         def is_indestructable(self, x, y, z):
  950.             if self.boss:
  951.                 if self.boss.head and (x, y, z) in self.boss.head:
  952.                     return True
  953.             return protocol.is_indestructable(self, x, y, z)
  954.        
  955.         def on_world_update(self):
  956.             if self.boss:
  957.                 self.boss.think(UPDATE_FREQUENCY)
  958.             protocol.on_world_update(self)
  959.        
  960.         def on_map_change(self, map):
  961.             self.boss = None
  962.             self.boss_ready = False
  963.             self.hp_bar = None
  964.             protocol.on_map_change(self, map)
  965.        
  966.         def get_cp_entities(self):
  967.             global force_boss
  968.             if force_boss or getattr(self.map_info.info, 'boss', False):
  969.                 if (USE_DAYCYCLE and self.daycycle_loop and
  970.                     self.daycycle_loop.running):
  971.                     self.daycycle_loop.stop()
  972.                 force_boss = False
  973.                 self.boss_ready = True
  974.                 self.hp_bar = BossTerritory(0, self, 0.0, 0.0, 0.0)
  975.                 self.hp_bar.team = self.green_team
  976.                 return [self.hp_bar]
  977.             return protocol.get_cp_entities(self)
  978.        
  979.         def create_explosion_effect(self, position):
  980.             self.world.create_object(Grenade, 0.0, position, None,
  981.                 Vertex3(), None)
  982.             grenade_packet.value = 0.0
  983.             grenade_packet.player_id = 32
  984.             grenade_packet.position = position.get()
  985.             grenade_packet.velocity = (0.0, 0.0, 0.0)
  986.             self.send_contained(grenade_packet)
  987.        
  988.         def falling_block_collide(self, x, y, z, size):
  989.             if not self.map.get_solid(x, y, z):
  990.                 new_z = self.map.get_z(x, y)
  991.                 if new_z > z:
  992.                     remaining = fall_eta(new_z - z)
  993.                     reactor.callLater(remaining, self.falling_block_collide,
  994.                         x, y, new_z, size)
  995.                     return
  996.             for player in self.players.values():
  997.                 i, j, k = player.world_object.position.get()
  998.                 s = size + 3.0
  999.                 if aabb(i, j, k, x - 1.5, y - 1.5, z - 5.0, s, s, 6.0):
  1000.                     player.hit(FALLING_BLOCK_DAMAGE, type = FALL_KILL)
  1001.             half_size = int(ceil(size / 2.0))
  1002.             ox, oy = x - half_size, y - half_size
  1003.             for u, v, w in prism(ox, oy, z - 1, size, size, 3):
  1004.                 self.remove_block(u, v, w, user = True)
  1005.             self.create_explosion_effect(Vertex3(x, y, z))
  1006.        
  1007.         def create_falling_block(self, x, y, size, height):
  1008.             self.set_block_color(FALLING_BLOCK_COLOR)
  1009.             half_size = int(ceil(size / 2.0))
  1010.             ox, oy = x - half_size, y - half_size
  1011.             for u, v, w in prism(ox, oy, FALLING_BLOCK_Z, size, size, height):
  1012.                 self.build_block(u, v, w, FALLING_BLOCK_COLOR)
  1013.             self.remove_block(ox, oy, FALLING_BLOCK_Z)
  1014.            
  1015.             z = self.map.get_z(x, y)
  1016.             eta = fall_eta(z - FALLING_BLOCK_Z)
  1017.             reactor.callLater(eta, self.falling_block_collide, x, y, z, size)
  1018.        
  1019.         def set_block_color(self, color):
  1020.             set_color.value = color
  1021.             set_color.player_id = 32
  1022.             self.send_contained(set_color, save = True)
  1023.        
  1024.         def remove_block(self, x, y, z, user = False):
  1025.             if z >= 63:
  1026.                 return False
  1027.             if not self.map.remove_point(x, y, z):
  1028.                 return False
  1029.             block_action.value = DESTROY_BLOCK
  1030.             block_action.player_id = 32
  1031.             block_action.x = x
  1032.             block_action.y = y
  1033.             block_action.z = z
  1034.             self.send_contained(block_action, save = True)
  1035.             return True
  1036.        
  1037.         def build_block(self, x, y, z, color, force = False):
  1038.             if force:
  1039.                 self.remove_block(x, y, z)
  1040.             if not self.map.get_solid(x, y, z):
  1041.                 self.map.set_point(x, y, z, conv_color(color))
  1042.                 block_action.value = BUILD_BLOCK
  1043.                 block_action.player_id = 32
  1044.                 block_action.x = x
  1045.                 block_action.y = y
  1046.                 block_action.z = z
  1047.                 self.send_contained(block_action, save = True)
  1048.                 return True
  1049.             return False
  1050.    
  1051.     class BossConnection(connection):
  1052.         regenerating = False
  1053.         trapped = False
  1054.         got_water_damage = False
  1055.         grabbed_by = None
  1056.         last_hit = None
  1057.         regen_loop = None
  1058.        
  1059.         def __init__(self, *arg, **kw):
  1060.             connection.__init__(self, *arg, **kw)
  1061.             self.regen_loop = LoopingCall(self.regen_cycle)
  1062.        
  1063.         def regen_cycle(self):
  1064.             if (not self.regenerating or self.god or
  1065.                 self.world_object is None or self.world_object.dead):
  1066.                 self.regen_loop.stop()
  1067.                 return
  1068.             last_hit = self.last_hit
  1069.             if last_hit and reactor.seconds() - last_hit < REGEN_ONSET:
  1070.                 return
  1071.             if self.hp < 100 - REGEN_AMOUNT:
  1072.                 self.set_hp(self.hp + REGEN_AMOUNT, type = FALL_KILL)
  1073.             else:
  1074.                 self.refill()
  1075.                 self.regen_loop.stop()
  1076.        
  1077.         def get_spawn_location(self):
  1078.             if self.protocol.boss and self.world_object and not self.trapped:
  1079.                 return self.world_object.position.get()
  1080.             return connection.get_spawn_location(self)
  1081.        
  1082.         def get_respawn_time(self):
  1083.             if self.protocol.boss:
  1084.                 return 2 if self.trapped else RESPAWN_TIME
  1085.             return connection.get_respawn_time(self)
  1086.        
  1087.         def on_spawn(self, pos):
  1088.             if self.trapped:
  1089.                 self.send_chat('You were eaten by a giant squid :( Pray your '
  1090.                     'friends can get you out of this one.')
  1091.                 self.set_location(self.protocol.boss.origin.get())
  1092.             return connection.on_spawn(self, pos)
  1093.        
  1094.         def on_reset(self):
  1095.             self.regenerating = False
  1096.             self.trapped = False
  1097.             self.got_water_damage = False
  1098.             self.grabbed_by = None
  1099.             self.last_hit = None
  1100.             if self.regen_loop and self.regen_loop.running:
  1101.                 self.regen_loop.stop()
  1102.             connection.on_reset(self)
  1103.        
  1104.         def on_disconnect(self):
  1105.             if self.regen_loop and self.regen_loop.running:
  1106.                 self.regen_loop.stop()
  1107.             self.regen_loop = None
  1108.             connection.on_disconnect(self)
  1109.        
  1110.         def on_kill(self, killer, type, grenade):
  1111.             if self.protocol.boss:
  1112.                 if self.grabbed_by:
  1113.                     self.grabbed_by.grabbed_player = None
  1114.                 self.grabbed_by = None
  1115.                 if (self.trapped or self.got_water_damage and
  1116.                     self.protocol.boss and not self.protocol.boss.dead and
  1117.                     self.protocol.boss.head):
  1118.                     self.trapped = True
  1119.                 else:
  1120.                     self.send_chat('You died! Yell at your friends to walk '
  1121.                         'over you to revive you.')
  1122.             connection.on_kill(self, killer, type, grenade)
  1123.        
  1124.         def on_weapon_set(self, value):
  1125.             if self.protocol.boss and self.regenerating:
  1126.                 self.weapon = value
  1127.                 self.set_weapon(self.weapon, no_kill = True)
  1128.                 self.spawn(self.world_object.position.get())
  1129.                 return False
  1130.             return connection.on_weapon_set(self, value)
  1131.        
  1132.         def on_orientation_update(self, x, y, z):
  1133.             if self.protocol.hp_bar and self.protocol.hp_bar.shown:
  1134.                 self.protocol.hp_bar.update_for_player(self, (x, y, z))
  1135.             connection.on_orientation_update(self, x, y, z)
  1136.        
  1137.         def on_position_update(self):
  1138.             if not self.protocol.boss_ready:
  1139.                 connection.on_position_update(self)
  1140.                 return
  1141.             if is_valid_enemy(self) and self.world_object.position.z >= 61:
  1142.                 self.got_water_damage = True
  1143.                 self.hit(WATER_DAMAGE)
  1144.                 self.got_water_damage = False
  1145.             if (not self.world_object.dead and not self.grabbed_by
  1146.                 and not self.trapped):
  1147.                 for player in self.protocol.players.values():
  1148.                     if player is not self and player.world_object.dead:
  1149.                         pos = player.world_object.position
  1150.                         if vector_collision(self.world_object.position, pos):
  1151.                             player.spawn(pos.get())
  1152.             if self.protocol.hp_bar and self.protocol.hp_bar.shown:
  1153.                 self.protocol.hp_bar.update_for_player(self)
  1154.             connection.on_position_update(self)
  1155.        
  1156.         def on_block_build_attempt(self, x, y, z):
  1157.             if self.trapped:
  1158.                 return False
  1159.             return connection.on_block_build(self, x, y, z)
  1160.        
  1161.         def on_block_destroy(self, x, y, z, mode):
  1162.             if self.trapped:
  1163.                 return False
  1164.             if self.protocol.boss:
  1165.                 if self.protocol.boss.on_block_destroy(x, y, z, mode) == False:
  1166.                     return False
  1167.             return connection.on_block_destroy(self, x, y, z, mode)
  1168.        
  1169.         def on_block_removed(self, x, y, z):
  1170.             if self.protocol.boss:
  1171.                 self.protocol.boss.on_block_removed(x, y, z)
  1172.             connection.on_block_removed(self, x, y, z)
  1173.        
  1174.         def on_hit(self, hit_amount, player, type, grenade):
  1175.             self.last_hit = reactor.seconds()
  1176.             if self.regenerating and not self.regen_loop.running:
  1177.                 self.regen_loop.start(REGEN_FREQUENCY)
  1178.             if self.protocol.boss_ready:
  1179.                 if self is player and self.hp:
  1180.                     if hit_amount >= self.hp:
  1181.                         return self.hp - 1
  1182.             return connection.on_hit(self, hit_amount, player, type, grenade)
  1183.        
  1184.         def on_fall(self, damage):
  1185.             if self.grabbed_by or self.regenerating:
  1186.                 return False
  1187.             self.last_hit = reactor.seconds()
  1188.             if self.regenerating and not self.regen_loop.running:
  1189.                 self.regen_loop.start(REGEN_FREQUENCY)
  1190.             return connection.on_fall(self, damage)
  1191.    
  1192.     return BossProtocol, BossConnection
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top