mirror of
https://forge.apps.education.fr/phroy/codetower.git
synced 2024-01-27 11:35:17 +01:00
355 lines
14 KiB
Python
355 lines
14 KiB
Python
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 <philippe.roy@ac-grenoble.fr>
|
||
# @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']<duration*(1/scene.objects['Terrain']['speed']):
|
||
# print("Temporization commands :",scene.objects['Terrain']['delay_cmd'])
|
||
time.sleep(0.001)
|
||
# pass
|
||
|
||
def ct_tempo_wave (duration):
|
||
scene.objects['Terrain']['delay_wave']=0
|
||
while scene.objects['Terrain']['delay_wave']<duration*(1/scene.objects['Terrain']['speed']):
|
||
# print("Temporization waves :",scene.objects['Terrain']['delay_wave'])
|
||
time.sleep(0.001)
|
||
# pass
|
||
|
||
# def ct_tempo_wave_trigger (duree):
|
||
# print ("delay wave ", scene.objects['Terrain']['delay_wave'])
|
||
|
||
###############################################################################
|
||
# Affichage
|
||
###############################################################################
|
||
|
||
# Texte du panel d'information
|
||
def ct_print (text):
|
||
# text_info (texte)
|
||
if text=="":
|
||
scene.objects['Text_info-1'].setVisible(False,False)
|
||
scene.objects['Text_info-2'].setVisible(False,False)
|
||
else:
|
||
lines_txt=texte.split("\n", 6)
|
||
for i in range (len(lines_txt),6):
|
||
lines_txt.append("")
|
||
scene.objects['Text_info-1'].setVisible(True,False)
|
||
scene.objects['Text_info-2'].setVisible(True,False)
|
||
scene.objects['Text_info-1']['Text']=lines_txt[0]+"\n"+lines_txt[1]+"\n"+lines_txt[2]
|
||
scene.objects['Text_info-2']['Text']=lines_txt[3]+"\n"+lines_txt[4]+"\n"+lines_txt[5]
|
||
|
||
# Texte de carte
|
||
def ct_map_text(text):
|
||
scene.objects['Map_text']['Text']=text
|
||
scene.objects['Map_text'].setVisible(True,False)
|