import bge # Bibliothèque Blender Game Engine (UPBGE) import threading # Multithreading import trace import sys import time import math import mathutils import random # from ct import * # Bibliothèque CodeTower ############################################################################### # ct_lib.py # @title: User library # @project: CodeTower # @lang: fr,en # @authors: Philippe Roy # @copyright: Copyright (C) 2022 Philippe Roy # @license: GNU GPL # # Ce simulateur est un jeu du type tower defense où les tours sont à piloter par la programmation Python. # This game is a tower defense coding game. The towers are driven with Python code. # # Commandes déclenchées par les joueurs : ct_* # Commandes déclenchées par la scene 3D : scn_* # ############################################################################### scene = bge.logic.getCurrentScene() # Colors tower_purple = [0.202, 0.114, 0.521,1] tower_turquoise = [0.051, 0.270, 0.279,1] tower_magenta = [0.799, 0.005, 0.314,1] tower_orange = [0.799, 0.130, 0.063,1] tower_yellow = [0.799, 0.617, 0.021, 1] tower_green = [0.246, 0.687, 0.078, 1] tower_red = [0.799, 0.031, 0.038, 1] ############################################################################### # Méthode kill pour les tâches (threads) ############################################################################### class thread_with_trace(threading.Thread): def __init__(self, *args, **keywords): threading.Thread.__init__(self, *args, **keywords) self.killed = False def start(self): self.__run_backup = self.run self.run = self.__run threading.Thread.start(self) def __run(self): sys.settrace(self.globaltrace) self.__run_backup() self.run = self.__run_backup def globaltrace(self, frame, event, arg): if event == 'call': return self.localtrace else: return None def localtrace(self, frame, event, arg): if self.killed: if event == 'line': raise SystemExit() return self.localtrace def kill(self): self.killed = True ############################################################################### # Start et stop des tâches (threads) ############################################################################### def thread_start(threads, type_txt, fct): threads.append(thread_with_trace(target = fct)) threads[len(threads)-1].start() print ("Thread",type_txt, "#", len(threads)-1, "open.") def thread_stop(threads, type_txt): i=0 zombie_flag=False for t in threads: if not t.is_alive(): print ("Thread",type_txt, "#",i,"closed.") else: print ("Thread",type_txt, "#",i,"still open ...") t.kill() t.join() if not t.is_alive(): print ("Thread",type_txt, "#",i,"killed.") else: print ("Thread",type_txt, "#",i,"zombie...") zombie_flag=True i +=1 if zombie_flag==False: print ("All threads",type_txt, "are closed.") else: print ("There are zombies threads",type_txt, ".") ############################################################################### # Vagues (minions) ############################################################################### # Minion caracteristics : category (class), level, hp, speed, armor, bounty, lifes_damage minion_carac={ 'Knight-lv1' : ["Knight", 1 , 2.0, 1.0, 0.0, 5,1], 'Knight-lv2' : ["Knight", 2, 4.0, 1.0, 1.0, 20,1], 'Knight-lv3' : ["Knight", 3, 8.0, 1.0, 2.0, 80,1]} # Minion 3d object : body (male,female,old, ...), head (A,B,C,D), level minion_3d={ 'Knight-lv1' : [['Knight_m', 'Knight_f', 'OldKnight_m'],['A','B','C','D'],['common']], 'Knight-lv2' : [['Knight_m', 'Knight_f', 'OldKnight_m'],['A','B','C','D'],['uncommon']], 'Knight-lv3' : [['Knight_m', 'Knight_f', 'OldKnight_m'],['A','B','C','D'],['rare']]} # Création d'un minion def ct_minion(x,y,cat,level): category=cat+"-lv"+str(level) # Pause while scene.objects['Terrain']['run'] == False: time.sleep(0) # Object 3D body = random.choice(minion_3d[category][0])+"_"+random.choice(minion_3d[category][1])+"_"+random.choice(minion_3d[category][2]) minion= scene.addObject(body, scene.objects['Terrain']) minion.worldScale=mathutils.Vector((0.25,0.25,0.25)) minion.worldPosition=mathutils.Vector((x,y,0.1)) # minion.worldPosition=mathutils.Vector((x,y,0.3)) # minion.worldPosition=mathutils.Vector((x,y,0.37)) # old 2 scene.objects['Points']['minions']= scene.objects['Points']['minions']+1 # Caracteristics minion.components['Minion'].args['cat']=minion_carac[category][0] minion.components['Minion'].args['level']=minion_carac[category][1] minion.components['Minion'].args['hp']=minion_carac[category][2] minion.components['Minion'].args['speed']=minion_carac[category][3] minion.components['Minion'].args['armor']=minion_carac[category][4] minion.components['Minion'].args['bounty']=minion_carac[category][5] minion.components['Minion'].args['lifes_damage']=minion_carac[category][6] minion['hp'] =minion.components['Minion'].args['hp'] # Actuator Steering minion.actuators['Steering'].navmesh=scene.objects[scene.objects['Terrain']['navmesh']] minion.actuators['Steering'].target=scene.objects[scene.objects['Terrain']['endtile']] minion.actuators['Steering'].distance=0.5 minion.actuators['Steering'].velocity=minion.components['Minion'].args['speed']*scene.objects['Terrain']['speed'] # Destruction d'un minion def scn_minion_dead(cont): obj = cont.owner scene.objects['Points']['minions']= scene.objects['Points']['minions']-1 scene.objects['Points']['coins']= scene.objects['Points']['coins']+obj.components['Minion'].args['bounty'] obj.endObject() ############################################################################### # Tours ############################################################################### # Tower caracteristics : category (class), damage, speed, range tower_carac={ 'Base' : ["Base", 1.0 , 0.02, 3.0]} # carac_tower={ # 'Base' : ["Base", 1.0 , 0.02, 3.0]} # Création d'une tour def ct_build(x,y,tower_name="Tower",color=tower_purple, cat='Base'): # Vérification de la place if [x,y] in scene.objects['Terrain']['scene_tile_noncontruct'] or [x,y] in scene.objects['Terrain']['scene_tile_tower']: return False # Vérification du niveau scene.objects['Points']['level']= scene.objects['Points']['level'] + 1 if scene.objects['Points']['level'] > scene.objects['Points']['level_max'] : tour= scene.addObject("Tower_error", scene.objects['Terrain']) tour.worldPosition=mathutils.Vector((x,y,0.2)) tour.worldScale=mathutils.Vector((1,1,1)) scene.objects['Terrain']['scene_tile_tower'].append([x,y]) return False # Objet 3D tour= scene.addObject("Tower", scene.objects['Terrain']) tour.color = color tour.worldPosition=mathutils.Vector((x,y,0.2)) tour.worldScale=mathutils.Vector((1,1,1)) scene.objects['Terrain']['scene_tile_tower'].append([x,y]) # Caractéristiques (composant python et propriétés de l'objet 3D) tour.components['Tower'].args['cat']=tower_carac[cat][0] tour.components['Tower'].args['lvl']=1 tour.components['Tower'].args['tower_name']=tower_name tour.components['Tower'].args['damage']=tower_carac[cat][1] tour.components['Tower'].args['speed']=tower_carac[cat][2] tour.components['Tower'].args['range']=tower_carac[cat][3] tour['cat']=tower_carac[cat][0] tour['lvl']=1 tour['tower_name']=tower_name tour['damage']=tower_carac[cat][1] tour['speed']=tower_carac[cat][2] tour['range']=tower_carac[cat][3] # Capteur Near tour.sensors['Near'].distance=tour.components['Tower'].args['range'] tour.sensors['Near'].skippedTicks =round(1/(tour.components['Tower'].args['speed']*scene.objects['Terrain']['speed'])) print (tour.sensors['Near'].skippedTicks) # for obj_i in scene.objects: # if "type_tower" in obj_i.getPropertyNames(): # print (obj_i['tower_name']) return True # Supression d'une tour def ct_remove(x,y): for obj_i in scene.objects: # Supprimer les tours if "type_tower" in obj_i.getPropertyNames(): if x == obj_i.worldPosition.x and y == obj_i.worldPosition.y: obj_i.endObject() scene.objects['Points']['level']= scene.objects['Points']['level'] - 1 # Réaction d'une tour def scn_tower_near(cont): obj = cont.owner sensor = obj.sensors['Near'] # Tir # FIXME: deux tirs à la fois ? # FIXME: tir sur le plus avancé if len(sensor.hitObjectList)>0 and scene.objects['Terrain']['run']==True : target=sensor.hitObjectList[0] # target.actuators['Steering'].velocity=(target.components['Minion'].args['speed']*scene.objects['Terrain']['speed'])/2 # Reduction de la vitesse du minion # Bullet (3d object) (vitesse <=1) if scene.objects['Terrain']['speed']<=1: bullet= scene.addObject("Bullet", scene.objects['Terrain']) bullet.mass=0.001 # bullet.applyForce=((0,0,9.81),True) bullet.worldPosition=mathutils.Vector((obj.worldPosition.x,obj.worldPosition.y,1.5)) bullet.worldScale=[0.75,0.75,0.75] # bullet.worldScale=[0.5,0.5,0.5] bullet.worldLinearVelocity.x = (target.worldPosition.x-bullet.worldPosition.x)*bullet['velocity'] bullet.worldLinearVelocity.y= (target.worldPosition.y-bullet.worldPosition.y)*bullet['velocity'] bullet.worldLinearVelocity.z = (target.worldPosition.z+0.1-bullet.worldPosition.z)*bullet['velocity'] # Line (vitesse >=2) if scene.objects['Terrain']['speed']>=2: bge.render.drawLine([obj.worldPosition.x, obj.worldPosition.y, obj.worldPosition.z+0.1], [target.worldPosition.x, target.worldPosition.y, target.worldPosition.z], [0.8, 0.619, 0.021]) # Dégats target['hp'] = target['hp'] - obj.components['Tower'].args['damage'] if target['hp']<=0: target['dead']=True ############################################################################### # Carte ############################################################################### # Fin def ct_map_end(x,y): mapend= scene.addObject("Map_end", scene.objects['Terrain']) mapend.worldPosition=[x,y,0.2] mapend.worldScale=[0.25,0.25,0.25] # Minion arrivé à la fin def scn_map_end_near(cont): obj = cont.owner sensor = obj.sensors['Near'] # if len(sensor.hitObjectList)>0: # print ("Arrive à la fin", sensor.hitObjectList) for i in range (len(sensor.hitObjectList)) : sensor.hitObjectList[i].endObject() scene.objects['Points']['lifes']= scene.objects['Points']['lifes']-sensor.hitObjectList[0].components['Minion'].args['lifes_damage'] scene.objects['Points']['minions']= scene.objects['Points']['minions']-1 # Drapeau de fin def ct_map_endflag(x,y): endflag= scene.addObject("Map_endflag", scene.objects['Terrain']) endflag.worldPosition=[x,y,0.3] endflag.worldScale=[0.3,0.3,0.3] if round(x) == x : if round(y) == y : scene.objects['Terrain']['scene_tile_noncontruct'].append([x,y]) else: scene.objects['Terrain']['scene_tile_noncontruct'].append([x,math.floor(y)]) scene.objects['Terrain']['scene_tile_noncontruct'].append([x,math.ceil(y)]) else: if round(y) == y : scene.objects['Terrain']['scene_tile_noncontruct'].append([math.floor(x),y]) scene.objects['Terrain']['scene_tile_noncontruct'].append([math.ceil(x),y]) else: scene.objects['Terrain']['scene_tile_noncontruct'].append([math.floor(x),math.floor(y)]) scene.objects['Terrain']['scene_tile_noncontruct'].append([math.floor(x),math.ceil(y)]) scene.objects['Terrain']['scene_tile_noncontruct'].append([math.ceil(x),math.floor(y)]) scene.objects['Terrain']['scene_tile_noncontruct'].append([math.ceil(x),math.ceil(y)]) ############################################################################### # Temporisation ############################################################################### def ct_sleep (duration): time.sleep(duration*(1/scene.objects['Terrain']['speed'])) def ct_tempo (duration): scene.objects['Terrain']['delay_cmd']=0 while scene.objects['Terrain']['delay_cmd']