codetower/ct_lib.py

870 lines
36 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 # Blender Game Engine (UPBGE)
import aud # Sounds
import threading # Multithreading
import trace
import sys
import time
import math
import mathutils
import random
###############################################################################
# ct_lib.py
# @title: User library
# @project: CodeTower
# @lang: fr,en
# @authors: Philippe Roy <phroy@phroy.org>
# @copyright: Copyright (C) 2022 Philippe Roy
# @license: GNU GPL
#
# This game is a tower defense coding game. The towers are driven by Python code.
# Ce simulateur est un jeu du type tower defense où les tours sont à piloter par la programmation Python.
#
# Commands trigged by button : cmd_*
# Commands trigged by 3D scene objects : scn_*
# Commands trigged by user (student or map designer) : ct_*
# 3D scene manipulation : manip_*
#
###############################################################################
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)
tower_blue = (0.127, 0.456, 1.000, 1)
tower_black = (0, 0, 0, 1)
color_text = (0, 0, 0, 1) # Noir
color_text_red = (0.799, 0.031, 0.038, 1)
color_text_orange = (0.799, 0.176, 0.054, 1)
color_text_yellow = (0.799, 0.617, 0.021, 1)
ray_yellow = (0.799, 0.617, 0.021, 1) # [0.8, 0.619, 0.021])
ray_blue = (0.127, 0.456, 1.000, 1)
ray_black = (0, 0, 0, 1)
color_kaykit_black = (0.019, 0.032, 0.037, 1)
# Sounds
audiodev = aud.Device()
snd_build = aud.Sound('asset/sounds/build.wav')
sndbuff_build = aud.Sound.cache(snd_build)
snd_archer = aud.Sound('asset/sounds/archer.wav')
sndbuff_archer = aud.Sound.cache(snd_archer)
snd_mage = aud.Sound('asset/sounds/mage.wav')
sndbuff_mage = aud.Sound.cache(snd_mage)
snd_life = aud.Sound('asset/sounds/life.ogg')
sndbuff_life = aud.Sound.cache(snd_life)
threads_waves=[]
threads_cmd=[]
# UPBGE constants
JUST_ACTIVATED = bge.logic.KX_INPUT_JUST_ACTIVATED
JUST_RELEASED = bge.logic.KX_INPUT_JUST_RELEASED
ACTIVATE = bge.logic.KX_INPUT_ACTIVE
# JUST_DEACTIVATED = bge.logic.KX_SENSOR_JUST_DEACTIVATED
###############################################################################
# Données générales
###############################################################################
def ct_level_current():
return scene.objects['Points']['level']
def ct_level():
return scene.objects['Points']['level_max']
###############################################################################
# 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.")
return True
else:
print ("There are zombies threads",type_txt, ".")
return False
def thread_waves_start(fct):
thread_start(threads_waves, "waves", fct)
def thread_waves_stop():
thread_stop(threads_waves, "waves")
def thread_cmd_start(fct):
thread_start(threads_cmd, "commands", fct)
def thread_cmd_stop():
thread_stop(threads_cmd, "commands")
###############################################################################
# Sounds
###############################################################################
# FIXME : Sound crash in Windows (very strange : blender, UPBGE, python ?), no music for Bill
def sound_play (sound):
if scene.objects['Commands']['sound'] and sys.platform!="win32":
audiodev.play(sound)
###############################################################################
# Waves (minions)
###############################################################################
##
# Création d'un minion
#
# Minion caracteristics : category (class), level, hp, speed, armor, bounty, lifes_damage
# Minion 3d body : body (male,female,old, ...), variante (A,B,C,D, ...), level
##
def ct_minion_create(x,y,cat,level):
category=cat+"-lv"+str(level)
minion_3d= scene.objects['Terrain']['minion_3d']
body = random.choice(minion_3d[category][0])+"_"+random.choice(minion_3d[category][1])+"_"+random.choice(minion_3d[category][2])
return (ct_minion_create_details(x,y,cat,level,body))
# Création d'un minion détaillée
def ct_minion_create_details(x,y,cat,level,body="Knight_m_A_common"):
category=cat+"-lv"+str(level)
# Pause
while scene.objects['Terrain']['run'] == False:
time.sleep(0.01)
# Synchronisation des threads : attente de la création d'une tour
while scene.objects['Terrain']['thread_cmd_lock'] == True:
# print ("ct_minion : thread_cmd_lock =True")
time.sleep(0.01)
# Blocage des autres threads pendant l'apparition du minion
scene.objects['Terrain']['thread_cmd_lock'] = True
# Object 3D
minion= scene.addObject(body, scene.objects['Terrain'])
minion.worldScale=[0.25,0.25,0.25]
minion.worldPosition=[x,y,0.1]
minion.suspendPhysics (True)
minion.setVisible(False)
scene.objects['Terrain']['idm']=scene.objects['Terrain']['idm']+1
minion['id']=scene.objects['Terrain']['idm']
minion.name="wm("+str(minion['id'])+")" # Wave minion (wm), identifier minion (idm)
scene.objects['Points']['minions'] +=1
scene.objects['Points']['minions_run'] +=1
# Gestion de la distance et des minions zombis
minion['dist']=0.0
minion['dist_old']=0.0
minion['dist_last_x']=minion.worldPosition.x
minion['dist_last_y']=minion.worldPosition.y
minion['dist_new']=True
# Caracteristics
minion_carac= scene.objects['Terrain']['minion_carac']
minion['cat']=minion_carac[category][0]
minion['level']=minion_carac[category][1]
minion['hp']=minion_carac[category][2]
minion['speed']=minion_carac[category][3]
minion['speed_base']=minion_carac[category][3]
minion['armor']=minion_carac[category][4]
minion['bounty']=minion_carac[category][5]
minion['lifes_damage']=minion_carac[category][6]
minion['buff']=[]
minion['resist']=[]
# 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=2
# minion.actuators['Steering'].distance=0.5
minion.actuators['Steering'].velocity=minion['speed_base']*scene.objects['Terrain']['speed']
# Déblocage des autres threads après l'apparition du minion
scene.objects['Terrain']['thread_cmd_lock'] = False
return minion.name
##
# Activation du minion (steering)
##
def ct_minion_go(minion_name):
minion=scene.objects[minion_name]
minion.restorePhysics()
minion.setVisible(True)
##
# Destruction d'un minion
##
def scn_minion_dead(cont):
obj = cont.owner
scene.objects['Points']['minions'] -=1
scene.objects['Points']['minions_run'] -=1
scene.objects['Points']['kills'] +=1
scene.objects['Points']['coins']= scene.objects['Points']['coins']+obj['bounty']
obj.setVisible(False)
obj.suspendPhysics (True)
# obj.endObject()
###############################################################################
# Spells / Casts
###############################################################################
##
# Buff/debuff Minion
##
def scn_minion_affect(cont):
if scene.objects['Terrain']['run'] == False: # Pause
return
obj = cont.owner
# print (obj.name, obj['buff'])
slow_state=False
# Distance parcourue
obj['dist']=obj['dist']+ math.sqrt((obj.worldPosition.x-obj['dist_last_x'])**2+(obj.worldPosition.y-obj['dist_last_y'])**2)
obj['dist_last_x']=obj.worldPosition.x
obj['dist_last_y']=obj.worldPosition.y
# Lod
# print(obj.currentLodLevel)
# Etats actif
for debuff_i in obj['buff']:
if debuff_i[1] <= 0:
obj['buff'].remove(debuff_i)
continue
if debuff_i[0] == "slow":
slow_state=True
debuff_i[1] -= scene.objects['Terrain']['speed']
# Effets
if slow_state:
obj.actuators['Steering'].velocity =(obj['speed_base']*scene.objects['Terrain']['speed'])/3
# obj.actuators['Steering'].velocity =(obj['speed_base']*scene.objects['Terrain']['speed'])/2
else:
obj.actuators['Steering'].velocity = obj['speed_base']*scene.objects['Terrain']['speed']
###############################################################################
# Towers
###############################################################################
##
# Création d'une tour
#
# Tower caracteristics : category (class), damage, speed, range
##
def ct_build(x,y, cat='Archer tower', tower_name="Tower", color=tower_purple, building="square-A"):
tower_minion_3d= scene.objects['Terrain']['tower_minion_3d']
if cat=='Archer tower': # Archer
category="Archer-lv1"
if cat=='Mage tower': # Mage
category="Mage-lv1"
body = random.choice(tower_minion_3d[category][0])+"_"+random.choice(tower_minion_3d[category][1])+"_"+random.choice(tower_minion_3d[category][2])
return (ct_build_details(x,y, cat, tower_name, color, building, body))
##
# Création d'une tour détaillée
##
def ct_build_details(x,y, cat='Archer tower', tower_name="Tower", color=tower_purple, building="square-A", body="Archer_m_A_common"):
# 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=[x,y,0.2]
tour.worldScale=[1,1,1]
scene.objects['Terrain']['scene_tile_tower'].append([x,y])
return False
# Blocage des autres threads pendant la construction
scene.objects['Terrain']['thread_cmd_lock'] = True
# Objets 3D
time.sleep(0.02)
tour= scene.addObject('Tower-'+building, scene.objects['Terrain'])
time.sleep(0.02)
tour.color = color
tour.worldPosition=[x,y,0.2]
tour.worldScale=[1,1,1]
tour.name="tower("+str(x)+','+str(y)+")"
scene.objects['Terrain']['scene_tile_tower'].append([x,y])
tower_minion= scene.addObject(body, scene.objects['Terrain'])
tower_minion['type_towerminion']=False
del tower_minion['type_minion']
tower_minion.name="tm("+str(x)+','+str(y)+")" # Tower minion (tm)
tower_minion.worldPosition=[x,y,1]
tower_minion.worldScale=[0.25,0.25,0.25]
# Draw3d
if cat=="Archer tower":
for i in range (3):
ct_add_tower_bullet(x,y,i, "Arrow")
if cat=="Mage tower":
ct_add_tower_cast(x,y)
# Sounds
sound_play(sndbuff_build)
# Caracteristics
tower_carac= scene.objects['Terrain']['tower_carac']
tour['cat']=tower_carac[cat][0]
tour['tower_name']=tower_name
tour['xp']=0
tour['lvl_current']=1
tour['lvl']=1
tour['damage']=tower_carac[cat][1]
tour['speed']=tower_carac[cat][2]
tour['range']=tower_carac[cat][3]
tour['techno']=[]
tour['cast']="slow"
# tour['cast_duration']=2
tour['cast_duration']=3
tour['target']=[]
tour['target_past']=[]
# Capteur Near
tour.sensors['Near'].distance=tour['range']*2.5 # Range : 1 point = 2,5
# tour.sensors['Near'].skippedTicks =round(1/(tour['speed']*scene.objects['Terrain']['speed']))
tour.sensors['Near'].skippedTicks =round(tour['speed']*50*scene.objects['Terrain']['speed']) # Speed : 1 point = 50 tics
# Déblocage des autres threads après la construction
scene.objects['Terrain']['thread_cmd_lock'] = False
# print (scene.objects)
return True
##
# Suppression d'une tour
##
def ct_remove(x,y):
for obj_i in scene.objects:
if "type_tower" in obj_i.getPropertyNames():
if x == obj_i.worldPosition.x and y == obj_i.worldPosition.y:
scene.objects["tm("+str(round(obj_i.worldPosition.x))+','+str(round(obj_i.worldPosition.y))+")"].endObject()
obj_i.endObject()
scene.objects['Points']['level']= scene.objects['Points']['level'] - 1
##
# Création d'un projectile
##
def ct_add_tower_bullet(x, y, num, cat="Ball"):
if cat=="Ball":
bullet= scene.addObject("Bullet", scene.objects['Terrain'])
if cat=="Arrow":
bullet= scene.addObject("Arrow", scene.objects['Terrain'])
bullet.name="tower("+str(x)+','+str(y)+")-bullet"+str(num)
bullet.worldPosition=[x,y,1.5]
bullet.worldScale=[0.75,0.75,0.75]
bullet.suspendPhysics (True)
bullet.setVisible(False)
bullet['activated']=False
##
# Création des sorts
##
def ct_add_tower_cast(x, y):
cast= scene.addObject("Cast-slow", scene.objects['Terrain'])
cast.name="tower("+str(x)+','+str(y)+")-cast"
cast.worldPosition=[x,y,1.5]
cast.worldScale=[0.75,0.75,0.75]
cast.suspendPhysics (True)
cast.setVisible(False)
cast['activated']=False
##
# Réaction d'une tour
##
def scn_tower_near(cont):
obj = cont.owner
sensor = obj.sensors['Near']
# Tir
if sensor.positive and len(sensor.hitObjectList)>0 and scene.objects['Terrain']['run']==True :
# Tir sur le plus avancé basé sur les distances parcourues
target=sensor.hitObjectList[0]
target_dist = target['dist']
for obj_i in sensor.hitObjectList:
if obj_i['dist']> target_dist:
target=obj_i
target_dist = target['dist']
# Tir sur le plus avancé basé sur l'ordre de passage
# target=sensor.hitObjectList[0]
# target_id = target['navPosition']
# for obj_i in sensor.hitObjectList:
# if obj_i['navPosition']< target_id:
# target=obj_i
# target_id = target['navPosition']
# Tir sur le plus avancé basé sur les distances par rapport à la tour -> ne marche pas
# target=sensor.hitObjectList[0]
# if len(sensor.hitObjectList)>1:
# target_eloignement = False
# target_distance_eloignement = 0
# target_distance_approche = 100
# print ("detection:",sensor.hitObjectList)
# for obj_i in sensor.hitObjectList:
# for obj_j in obj['target_past']:
# if obj_j[0]==obj_i.name:
# print ("name :", obj_j[0], "distance :", obj.getDistanceTo(obj_i), "distance old :", obj_j[1], "ecart :", obj.getDistanceTo(obj_i) - obj_j[1])
# # Éloignement
# if obj.getDistanceTo(obj_i) - obj_j[1] > 0: # Ecart de distance
# target_eloignement = True
# if obj.getDistanceTo(obj_i) > target_distance_eloignement:
# target=obj_i
# target_distance_eloignement = obj.getDistanceTo(obj_i)
# # Approche
# else:
# if target_eloignement == False:
# if obj.getDistanceTo(obj_i) < target_distance_approche:
# target=obj_i
# target_distance_approche = obj.getDistanceTo(obj_i)
# if target_eloignement == True:
# print ("Eloignement : target:", target.name, "distance :", obj.getDistanceTo(target))
# print ("")
# else:
# print ("Approche : target:", target.name, "distance :", obj.getDistanceTo(target))
# print ("")
# obj['target_past']=[]
# for obj_i in sensor.hitObjectList:
# obj['target_past'].append([obj_i.name, obj.getDistanceTo(obj_i)])
# Orientation du tower minion
towerminion="tm("+str(round(obj.worldPosition.x))+','+str(round(obj.worldPosition.y))+")"
angle =math.atan((target.worldPosition.y-obj.worldPosition.y)/(target.worldPosition.x-obj.worldPosition.x))
if target.worldPosition.x>obj.worldPosition.x:
angle2=math.pi/2+angle-scene.objects[towerminion].worldOrientation.to_euler().z
angle3=angle
else:
angle2=math.pi+math.pi/2+angle-scene.objects[towerminion].worldOrientation.to_euler().z
angle3=math.pi+angle
scene.objects[towerminion].applyRotation((0, 0, angle2), False)
# Sounds
if obj['cat']=="Archer tower":
sound_play(sndbuff_archer)
if obj['cat']=="Mage tower":
sound_play(sndbuff_mage)
# Ligne (drawLine) (vitesse rapide)
if scene.objects['Terrain']['speed']<4: # Pas d'animation à 10 -> plantage
# Archer (tir de flêche)
if obj['cat']=="Archer tower":
if target.name in scene.objects:
for i in range (3):
bullet = scene.objects[obj.name+"-bullet"+str(i)]
if bullet['activated']==False:
bullet.worldPosition=[obj.worldPosition.x, obj.worldPosition.y, obj.worldPosition.z+0.8]
bullet['activated']=True
bullet.setVisible(True)
scene.objects['Terrain']['draw3d_process']=True
scene.objects['Terrain']['draw3d_list'].append([20, "arrow", obj.name, bullet.name, target.name, "normal", 20])
break
if i ==3 :
print ("Plus de bullet de disponible pour la tour : "+obj.name)
# Cast zone
if obj['cat']=="Mage tower": # Mage (cast)
cast = scene.objects[obj.name+"-cast"]
cast.worldPosition=[obj.worldPosition.x, obj.worldPosition.y, obj.worldPosition.z+0.8]
cast.worldScale=[0.01,0.01,0.01]
cast.setVisible(True)
scene.objects['Terrain']['draw3d_process']=True
scene.objects['Terrain']['draw3d_list'].append([60, "cast", obj.name, cast.name, "slow", 60])
# Rayon
# FIXME : ne marche plus (zoom et pan)
# if obj['cat']=="Test":
# if target.name in scene.objects:
# scene.objects['Terrain']['draw2d_process']=True
# scene.objects['Terrain']['draw2d_list'].append([5, "ray", [obj.worldPosition.x, obj.worldPosition.y, obj.worldPosition.z+0.8], target.name, angle3, ray_yellow,5]) # Suivi du minion
# Dégats : pas d'animation à 10
if scene.objects['Terrain']['speed'] >= 4:
target['hp'] = target['hp'] - obj['damage']
if target['hp']<=0:
target['dead']=True
# Cast (buff and debuff)
if obj['cat']=="Mage tower":
for target_i in sensor.hitObjectList:
target_i['buff'].append([obj['cast'], obj['cast_duration']])
###############################################################################
# Carte
###############################################################################
##
# Texte de carte
##
def ct_map_text_wave(wave):
scene.objects['Points-Map-text']['Text']=("Wave " + str(wave))
scene.objects['Points-Map-text'].setVisible(True,False)
scene.objects['Points-Map-text'].color = color_text_yellow
scene.objects['Points-Map-text']['timer']=0
scene.objects['Points-Map-text']['anim']=True
##
# Texte de carte
##
def ct_map_text(text):
scene.objects['Points-Map-text']['Text']=text
scene.objects['Points-Map-text'].setVisible(True,False)
##
# Fin
##
def ct_map_end(x,y):
scene.objects['Map_end'].worldPosition=[x,y,0.2]
scene.objects['Map_end'].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 sensor.positive :
for obj_i in sensor.hitObjectList :
sound_play(sndbuff_life)
if scene.objects['Points']['lifes']>0:
scene.objects['Points']['lifes']= scene.objects['Points']['lifes']-obj_i['lifes_damage']
scene.objects['Points']['minions_run'] -=1
for obj_i in sensor.hitObjectList :
obj_i.setVisible(False)
obj_i.suspendPhysics (True)
# obj_i.endObject()
##
# 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['Info-1-text'].setVisible(False,False)
scene.objects['Info-2-text'].setVisible(False,False)
else:
lines_txt=text.split("\n", 6)
for i in range (len(lines_txt),6):
lines_txt.append("")
scene.objects['Info-1-text'].setVisible(True,False)
scene.objects['Info-2-text'].setVisible(True,False)
scene.objects['Info-1-text']['Text']=lines_txt[0]+"\n"+lines_txt[1]+"\n"+lines_txt[2]
scene.objects['Info-2-text']['Text']=lines_txt[3]+"\n"+lines_txt[4]+"\n"+lines_txt[5]
###############################################################################
# Dessin 3d
###############################################################################
def scn_draw3d(cont):
obj = cont.owner
if obj.sensors['Draw3d'].positive==False:
return
if len(scene.objects['Terrain']['draw3d_list'])==0:
scene.objects['Terrain']['draw3d_process']=False
return
# Dépilage des draws à executer
for draw_cmd in scene.objects['Terrain']['draw3d_list']:
# Archer (tir de flêche)
# scene.objects['Terrain']['draw3d_list'].append([20, "arrow", obj.name, bullet.name, target.name, "normal", 20])
if draw_cmd[1]=="arrow":
if draw_cmd[4] in scene.objects:
tower= scene.objects[draw_cmd[2]]
bullet = scene.objects[draw_cmd[3]]
target = scene.objects[draw_cmd[4]]
x0 = tower.worldPosition.x
y0 = tower.worldPosition.y
# z0 = tower.worldPosition.z+0.8 # ajustement +0.8
z0 = tower.worldPosition.z+1 # ajustement +1
x1 = target.worldPosition.x
y1 = target.worldPosition.y
z1 = target.worldPosition.z+0.5 # ajustement +0.5
if x1>x0:
angle_z =math.atan((y1-y0)/(x1-x0))+math.pi/2
else:
angle_z =math.pi+math.atan((y1-y0)/(x1-x0))+math.pi/2
angle_y =math.atan((z1-z0)/(math.sqrt((x1-x0)**2+(y1-y0)**2)))
step_x=(x1-x0)/draw_cmd[6]
step_y=(y1-y0)/draw_cmd[6]
step_z=(z1-z0)/draw_cmd[6]
step = draw_cmd[6]-draw_cmd[0]
bullet.worldPosition=[x0+step_x*step, y0+step_y*step, z0+step_z*step]
bullet.worldOrientation=[0, angle_y, angle_z]
draw_cmd[0] = draw_cmd[0]-scene.objects['Terrain']['speed']
# Dégats
if draw_cmd[0]<=0:
bullet['activated']=False
bullet.setVisible(False)
target['hp'] = target['hp'] - tower['damage']
if target['hp']<=0: # Mort
target['dead']=True
# Mage (cast)
# scene.objects['Terrain']['draw3d_list'].append([60, "cast", obj.name, cast.name, "slow", 60])
if draw_cmd[1]=="cast":
cast = scene.objects[draw_cmd[3]]
step = draw_cmd[5]-draw_cmd[0]
cast.worldScale=[0.05*step,0.05*step,0.05*step]
# cast.worldScale=[0.75*step,0.75*step,0.75*step]
draw_cmd[0] = draw_cmd[0]-scene.objects['Terrain']['speed']
# Fin
if draw_cmd[0]<=0:
cast.setVisible(False)
# Suppression des draws finis
i=0
for draw_cmd in scene.objects['Terrain']['draw3d_list']:
if draw_cmd[0]<=0:
scene.objects['Terrain']['draw3d_list'].pop(i)
else:
i=i+1
if len(scene.objects['Terrain']['draw3d_list'])==0:
scene.objects['Terrain']['draw3d_process']=False
###############################################################################
# Dessin 2d (bge.render.drawLine)
###############################################################################
def circle (center, radius, color):
ang = 0.0
# ang_step = 0.1
ang_step = 0.2
while ang< 2 * math.pi:
x0 = center[0]+float(radius*math.cos(ang))
y0 = center[1]+float(radius*math.sin(ang))
x1 = center[0]+float(radius*math.cos(ang+ang_step))
y1 = center[1]+float(radius*math.sin(ang+ang_step))
bge.render.drawLine([x0,y0,center[2]],[x1,y1,center[2]],color)
ang += ang_step
##
# Affiche les draws 2d en cours
#
# Type de draw 2d:
# arrow : [5, "arrow", [obj.worldPosition.x, obj.worldPosition.y, obj.worldPosition.z+0.8],target.name, angle3, ray_yellow,5]
# cast : [30, "cast", [obj.worldPosition.x, obj.worldPosition.y, obj.worldPosition.z+0.8], ray_blue,30]
# ray : [5, "ray", [obj.worldPosition.x, obj.worldPosition.y, obj.worldPosition.z+0.8],[target.worldPosition.x, target.worldPosition.y, target.worldPosition.z], angle3, ray_yellow,5]
##
def scn_draw2d(cont):
obj = cont.owner
if obj.sensors['Draw2d'].positive==False:
return
if len(scene.objects['Terrain']['draw2d_list'])==0:
scene.objects['Terrain']['draw2d_process']=False
return
# Dépilage des draws à executer
for draw_cmd in scene.objects['Terrain']['draw2d_list']:
# Archer (tir de flêche)
if draw_cmd[1]=="arrow":
if draw_cmd[3] in scene.objects:
# x0 = draw_cmd[2][0]+0.25*(math.cos(draw_cmd[4]))
# y0 = draw_cmd[2][1]+0.25*(math.sin(draw_cmd[4]))
x0 = draw_cmd[2][0]
y0 = draw_cmd[2][1]
z0 = draw_cmd[2][2]
x1 = scene.objects[draw_cmd[3]].worldPosition.x
y1 = scene.objects[draw_cmd[3]].worldPosition.y
z1 = scene.objects[draw_cmd[3]].worldPosition.z-0.1 # ajustement -0.1
distance = math.sqrt((x1-x0)**2+(y1-y0)**2+(z1-z0)**2)
distance_xy = math.sqrt((x1-x0)**2+(y1-y0)**2)
distance_z = z1-z0
angle_z =math.atan((z1-z0)/(distance_xy))
angle_xy =math.atan((y1-y0)/(x1-x0))
step=distance_xy/(2+draw_cmd[6])
step_z=distance_z/(2+draw_cmd[6])
if x1>x0:
angle2=angle_xy
else:
angle2=math.pi+angle_xy
x2=x0+(((6-draw_cmd[0])*step)*(math.cos(angle2)))
y2=y0+(((6-draw_cmd[0])*step)*(math.sin(angle2)))
z2=z0-(((6-draw_cmd[0])*step_z)*(math.sin(angle_z)))
x3=x0+(((6-draw_cmd[0])*step+step)*(math.cos(angle2)))
y3=y0+(((6-draw_cmd[0])*step+step)*(math.sin(angle2)))
z3=z0-(((6-draw_cmd[0])*step_z+step_z)*(math.sin(angle_z)))
bge.render.drawLine([x2,y2, z2], [x3,y3,z3], draw_cmd[5])
draw_cmd[0] = draw_cmd[0]-scene.objects['Terrain']['speed']
# if scene.objects['Terrain']['speed']<1:
# draw_cmd[0] = draw_cmd[0]-scene.objects['Terrain']['speed']
# else:
# draw_cmd[0] = draw_cmd[0]-1
# bge.render.drawLine([draw_cmd[2][0]+((6-draw_cmd[0])*0.25)*(math.cos(draw_cmd[4])), draw_cmd[2][1]+((6-draw_cmd[0])*0.25)*(math.sin(draw_cmd[4])),draw_cmd[2][2]],
# [draw_cmd[2][0]+((6-draw_cmd[0])*0.25+0.25)*(math.cos(draw_cmd[4])), draw_cmd[2][1]+((6-draw_cmd[0])*0.25+0.25)*(math.sin(draw_cmd[4])),draw_cmd[2][2]],
# draw_cmd[5])
# Mage (cast)
# FIXME : Problème
if draw_cmd[1]=="cast": # Mage (cast)
circle(draw_cmd[2], 3.1-draw_cmd[0]*0.1, draw_cmd[3])
circle(draw_cmd[2], 3-draw_cmd[0]*0.1, draw_cmd[3])
circle(draw_cmd[2], 2.9-draw_cmd[0]*0.1, draw_cmd[3])
draw_cmd[0] = draw_cmd[0]-scene.objects['Terrain']['speed']
# if scene.objects['Terrain']['speed']<=2:
# draw_cmd[0] = draw_cmd[0]-scene.objects['Terrain']['speed']
# if scene.objects['Terrain']['speed']==4:
# draw_cmd[0] = draw_cmd[0]-draw_cmd[4]/2
# circle(draw_cmd[2], 3, draw_cmd[3]) # simple
# radius=[3,3,2.5,2.5,2,2,1.5,1.5,1,1,1] # basé sur un tableau
# circle(draw_cmd[2], radius[draw_cmd[0]], draw_cmd[3])
# Rayon
if draw_cmd[1]=="ray":
if draw_cmd[3] in scene.objects:
x0 = draw_cmd[2][0]+0.25*(math.cos(draw_cmd[4]))
y0 = draw_cmd[2][1]+0.25*(math.sin(draw_cmd[4]))
x1 = scene.objects[draw_cmd[3]].worldPosition.x
y1 = scene.objects[draw_cmd[3]].worldPosition.y
z1 = scene.objects[draw_cmd[3]].worldPosition.z
bge.render.drawLine([x0,y0, draw_cmd[2][2]], [x1,y1,z1], draw_cmd[5]) # suivi minion
# bge.render.drawLine([draw_cmd[2][0]+0.25*(math.cos(draw_cmd[4])), draw_cmd[2][1]+0.25*(math.sin(draw_cmd[4])), draw_cmd[2][2]], draw_cmd[3], draw_cmd[5]) # décalage minion
# bge.render.drawLine(draw_cmd[2], draw_cmd[3], draw_cmd[5]) # simple
draw_cmd[0] = draw_cmd[0]-scene.objects['Terrain']['speed']
# Suppression des draws finis
i=0
for draw_cmd in scene.objects['Terrain']['draw2d_list']:
if draw_cmd[0]<=0:
scene.objects['Terrain']['draw2d_list'].pop(i)
else:
i=i+1
if len(scene.objects['Terrain']['draw2d_list'])==0:
scene.objects['Terrain']['draw2d_process']=False