"""
Green blow up blue!
by thepolm3
idea by 10se1ucgo
"""

from random import randint
from twisted.internet.reactor import callLater
from pyspades.server import block_action, grenade_packet
from pyspades.constants import *
from pyspades.common import Vertex3
from pyspades.world import Grenade

#defaults
HIDE_COORDS=(0,0,63)

def apply_script(protocol,connection,config):
    class SabotageConnection(connection):

        def on_spawn_location(self, pos):
            if self.team==self.protocol.green_team:
                return self.protocol.getcoord(0,True)
            else:
                return self.protocol.getcoord(1,True)

        def on_flag_take(self):
            if self.protocol.win:
                return connection.on_flag_take(self)
            if self.team==self.protocol.green_team:
                if self.protocol.bomb_active:
                    return False
                else:
                    self.send_chat("You have the bomb! Take it to the enemies base")

            else:
                if self.protocol.bomb_active:
                    if not self.protocol.diffuser:
                        self.protocol.diffuse_bomb(self)
                else:
                    return False
            return connection.on_flag_take(self)

        def capture_flag(self):
            if self.protocol.win:
                return connection.capture_flag(self)
            if self.team==self.protocol.green_team:
                if not self.protocol.bomb_active:
                    self.protocol.activate_bomb(self)
            return False

        def respawn(self):
            if self.protocol.hardcore and not self.protocol.win:
                return False
            else:
                return connection.respawn(self)

        def on_kill(self,by,weapon,grenade):
            if self==self.protocol.diffuser:
                self.protocol.diffuser=None
            alive=False
            for player in self.team.get_players():
                if not player==self and not player.world_object.dead:
                    alive=True
            if not alive:
                if by and by!=self:
                    self.protocol.win=True
                    by.take_flag()
                    by.capture_flag()
                    self.protocol.Bombreset()
                    self.protocol.win=False
                else:
                    self.protocol.Bombreset()
            return connection.on_kill(self,by,weapon,grenade)

        def on_team_switch(self):
            if self==self.protocol.bomber:
                self.protocol.bomber=None
            return connection.on_team_switch(self)

        def on_refill(self):
            return False

    class SabotageProtocol(protocol):
        bomb_active=False
        bomber=None
        diffuser=None
        win=False
        game_mode=CTF_MODE
        greenbase=[(125,225),(175,275)]
        bluebase=[(325,225),(375,275)]
        bombbase=[(225,125),(275,175)]
        bombsite=[(225,325),(275,375)]
        bombsize=10
        diffusetime=10
        bombtime=45
        hardcore=False
        bomb_time=bombtime

        def on_map_change(self, map):
            extensions = self.map_info.extensions
            if extensions.has_key('greenbase'):
                self.greenbase = extensions['greenbase']
            if extensions.has_key('bluebase'):
                self.bluebase = extensions['bluebase']
            if extensions.has_key('bombbase'):
                self.bombbase = extensions['bombbase']
            if extensions.has_key('bombsite'):
                self.bombsite = extensions['bombsite']
            if extensions.has_key('bombsize'):
                self.bombsize = extensions['bombsize']
            if extensions.has_key('diffusetime'):
                self.diffusetime = extensions['diffusetime']
            if extensions.has_key('hardcore'):
                self.hardcore = extensions['hardcore']
            if extensions.has_key('bombtime'):
                self.bombtime = extensions['bombtime']
                self.bomb_time=self.bombtime
            return protocol.on_map_change(self, map)

        def diffuse_bomb(self,connection):
            self.diffuser=connection
            self.send_chat("The bomb is being diffused by %s" %(connection.name))
            callLater(self.diffusetime,self.bomb_diffused)

        def bomb_diffused(self):
            if self.diffuser:
                self.send_chat("The bomb was diffused by %s" %(self.diffuser.name))
                if self.diffuser:
                    if self.diffuser.team==self.blue_team:
                        self.win=True
                        self.diffuser.capture_flag()
                        self.win=False
                else:
                    self.getpoint(True)
                self.bomb_active=False
                self.Bombreset()
    
        def activate_bomb(self,connection):
            self.bomber=connection
            self.bomb_time=self.bombtime
            self.send_chat("%s has activated the bomb! It will blow in %d seconds!" %(connection.name,self.bomb_time))
            self.bomb_active=True
            blueflag=self.blue_team.flag
            greenflag=self.green_team.flag
            base=self.green_team.base
            pos=connection.world_object.position
            x,y,z=pos.x,pos.y,pos.z
            base.set(*HIDE_COORDS)
            blueflag.player.drop_flag()
            blueflag.set(*HIDE_COORDS)
            greenflag.set(x,y,self.map.get_z(x,y))
            base.update()
            greenflag.update()
            blueflag.update()
            a=callLater(10,self.bomb_alert)

        def bomb_alert(self):
            if self.bomb_active:
                self.bomb_time-=10
                self.send_chat("The bomb will go off in %d seconds!" %(self.bomb_time))
                if self.bomb_time<=10:
                    if self.bomb_active:
                        a=callLater(self.bomb_time,self.bomb_splode)
                else:
                    a=callLater(10,self.bomb_alert)

        def bomb_splode(self):
            self.diffuser=None
            self.send_chat("BOOM!")
            self.win=True
            if self.bomber:
                if self.bomber.team==self.green_team:
                    self.bomber.take_flag()
                    self.bomber.capture_flag()
            else:
                self.getpoint(False)
            self.win=False
            greenflag=self.green_team.flag
            if greenflag.player:
                greenflag.player.kill()
            x,y,z=int(greenflag.x),int(greenflag.y),int(greenflag.z)
            self.create_explosion_effect(Vertex3(x,y,z))
            greenflag.set(*HIDE_COORDS)
            greenflag.update()
            
            for xa in range(x-self.bombsize,x+self.bombsize):
                for ya in range(y-self.bombsize,y+self.bombsize):
                    for za in range(z-self.bombsize,z+self.bombsize):
                        if ((xa-x)**2+(ya-y)**2+(za-z)**2)<=self.bombsize**2:
                            self.destroy_block(xa,ya,za)

            for key in self.players:
                player=self.players[key]
                if player.world_object:
                    xb,yb,zb=player.world_object.position.get()
                    if x-self.bombsize<xb<x+self.bombsize and y-self.bombsize<yb<y+self.bombsize and z-self.bombsize<zb<z+self.bombsize:
                        player.hit(100/((z-zb)+(y-yb)+(x-xb))/3)
            callLater(3,self.Bombreset)

        def create_explosion_effect(self, position):
            self.world.create_object(Grenade, 0.0, position, None, Vertex3(), None)
            grenade_packet.value = 0.0
            grenade_packet.player_id = 32
            grenade_packet.position = position.get()
            grenade_packet.velocity = (0.0, 0.0, 0.0)
            self.send_contained(grenade_packet)

        def destroy_block(self,x,y,z):
            if x<0 or y<0 or z<0 or x>511 or y>511 or z>62:
                pass
            else:
                block_action.x, block_action.y, block_action.z=x,y,z
                self.map.remove_point(x,y,z)
                block_action.value = DESTROY_BLOCK
                self.send_contained(block_action)

        def Bombreset(self):
            base=self.green_team.base
            base.set(*self.getcoord(3))
            base.update()
            greenflag=self.green_team.flag
            blueflag=self.blue_team.flag
            if greenflag.player:
                greenflag.player.drop_flag()
            if blueflag.player:
                blueflag.player.drop_flag()
            blueflag.set(*self.getcoord(2))
            greenflag.set(*HIDE_COORDS)
            greenflag.update()
            blueflag.update()
            self.bomb_active=False
            self.diffuser=None
            self.bomber=None
            self.win=True
            for i in self.players:
                self.players[i].respawn()
            self.win=False

        def getpoint(self,blue=False):
            team=[self.green_team,self.blue_team][blue]
            if (self.max_score not in (0, None) and 
            team.score + 1 >= self.max_score):
                self.reset_game(self)
                protocol.on_game_end(self)
            else:
                team.score+=1

        def getcoord(self,sid=0,spawn=False):
            if sid==0:
                x=randint(self.bluebase[0][0],self.bluebase[1][0])
                y=randint(self.bluebase[0][1],self.bluebase[1][1])
                if len(self.bluebase[0])>2:
                    z=randint(self.bluebase[0][2],self.bluebase[1][2])
                else:
                    z=self.map.get_z(x,y)
            elif sid==1:
                x=randint(self.greenbase[0][0],self.greenbase[1][0])
                y=randint(self.greenbase[0][1],self.greenbase[1][1])
                if len(self.greenbase[0])>2:
                    z=randint(self.greenbase[0][2],self.greenbase[1][2])
                else:
                    z=self.map.get_z(x,y)
            elif sid==2:
                x=randint(self.bombbase[0][0],self.bombbase[1][0])
                y=randint(self.bombbase[0][1],self.bombbase[1][1])
                if len(self.bombbase[0])>2:
                    z=randint(self.bombase[0][2],self.bombbase[1][2])
                else:
                    z=self.map.get_z(x,y)
            elif sid==3:
                x=randint(self.bombsite[0][0],self.bombsite[1][0])
                y=randint(self.bombsite[0][1],self.bombsite[1][1])
                if len(self.bombsite[0])>2:
                    z=randint(self.bombsite[0][2],self.bombsite[1][2])
                else:
                    z=self.map.get_z(x,y)
            if spawn:
                return x+0.5,y+0.5,z-2
            return x,y,z

        def on_flag_spawn(self,x,y,z,flag,entity_id):
            if entity_id==0:
                return self.getcoord(2)
            else:
                return HIDE_COORDS

        def on_base_spawn(self,x,y,z,flag,entity_id):
            if entity_id==3:
                return self.getcoord(3)
            else:
                return HIDE_COORDS

    return SabotageProtocol,SabotageConnection

