codetower/ct_lib.py

361 lines
14 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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],
'Orc-lv1' : ["Orc", 1 , 2.0, 1.0, 0.0, 5,1],
'Orc-lv2' : ["Orc", 2, 4.0, 1.0, 1.0, 20,1],
'Orc-lv3' : ["Orc", 3, 8.0, 1.0, 2.0, 80,1]}
# Minion 3d object : body (male,female,old, ...), variante (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']],
'Orc-lv1' : [['Orc'],['A','B','C','D','E','F'],['common']],
'Orc-lv2' : [['Orc'],['A','B','C','D','E','F'],['uncommon']],
'Orc-lv3' : [['Orc'],['A','B','C','D','E','F'],['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, tower_3d="square-A",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-'+tower_3d, 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)