-
Notifications
You must be signed in to change notification settings - Fork 0
/
carte.py
710 lines (654 loc) · 44.6 KB
/
carte.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
# -*-coding:utf-8 -*
import pygame, math, narro.directions, narro.tmxreader, numpy, os, sys
from pygame.locals import *
from narro import *
import pygame.surfarray as surfarray
from collections import OrderedDict
from .tile import *
from .constantes import *
from .observateur import *
from .zonePensee import *
if SESSION_DEBUG:
import pdb
class Carte(Observateur):
"""Classe représentant une carte au niveau des données"""
def __init__(self, nomCarte, jeu):
"""Initialise la carte à exécuter <carte> à partir des données issues de son fichier SQMAP.
Cette méthode se charge surtout du transfert du format de carte .narromap à celui du Narro Engine (purement mémoriel)."""
Observateur.__init__(self)
self._carteTiled = tmxreader.TileMapParser().parse_decode(os.path.join(DOSSIER_RESSOURCES, nomCarte + ".tmx"))
self._nom, self._description = self._carteTiled.properties.get("nom", nomCarte), self._carteTiled.properties.get("description", "")
self._musique = self._carteTiled.properties.get("musique", "")
self._longueur, self._largeur = self._carteTiled.width, self._carteTiled.height
self._tmxLayers, self._scrollingBackground = [], False
for layer in self._carteTiled.layers:
if type(layer) == narro.tmxreader.TileLayer:
self._tmxLayers.append(layer)
elif type(layer) == narro.tmxreader.ImageLayer:
self._scrollingBackground = layer.source
self._nombreCouches, self._hauteurTile = len(self._tmxLayers), self._carteTiled.tilewidth
self._scrollingX, self._scrollingY = 0,0
self._jeu, self._toutAChanger = jeu, True
self._dicoSurfaces, self._tiles, self._tileRect, self._tilesEtendus, self._blocsRef, self._pnj, i = dict(), list(), Rect(0, 0, 32, 32), dict(), list(), dict(), 0
self._subTilesEtendus, self._subTilesBlittes, self._ecran = dict(), list(), Rect(0, 0, self._longueur*32, self._largeur*32)
self._scrollingPossible, self._etapeScrolling = False, 0
self._surfaceZonePensee, self._positionZonePensee, self._besoinAffichageZonePensee, self._faceActuelle = None, None, False, False
self._emplacementScrollingX, self._emplacementScrollingY = int(int(FENETRE["longueurFenetre"]/2) / 32)*32, int(int(FENETRE["largeurFenetre"]/2)/32)*32
self._ecranVisible = Rect(0, 0, FENETRE["longueurFenetre"], FENETRE["largeurFenetre"])
self._positionsDepart, self._polices = dict(), dict()
self._ajouterPolice(NOM_FICHIER_POLICE_PAR_DEFAUT, TAILLE_POLICE_SPLASH_SCREEN)
self._fenetre, self._blitFrame = self._jeu.fenetre, False
self._transformationsGlobales, self._transformationsParties, self._parametresTransformations = list(), list(), dict()
self._idParametres, self._donneesParametres, self._messagesCollision = dict(), dict(), list()
if self._scrollingBackground is not False:
self._bgScrollingX, self._bgScrollingY = 0,0
self._bgEcranVisible = Rect(0, 0, FENETRE["longueurFenetre"], FENETRE["largeurFenetre"])
self._ajouterSurface(False, self._scrollingBackground, (0,0,0), mobile=False)
self._bgLongueur, self._bgLargeur = self._dicoSurfaces[self._scrollingBackground]["Source"].get_width(), self._dicoSurfaces[self._scrollingBackground]["Source"].get_height()
self._bgRatioX, self._bgRatioY = self._bgLongueur / (self._longueur*self._hauteurTile), self._bgLargeur / (self._largeur*self._hauteurTile)
self._bgRatioXWait, self._bgRatioYWait = 0,0
self._dicoGid = dict()
for tileset in self._carteTiled.tile_sets:
for image in tileset.images:
self._ajouterSurface(False, image.source, False, tileset=tileset, mobile=False)
#Création des tiles vides, le parcours de blit a lieu ensuite
self._tilesLayers = []
i, x, y = 0, 0, 0
while i < self._nombreCouches:
x = 0
self._tilesLayers.append(pygame.Surface((self._longueur * 32, self._largeur * 32), flags=SRCALPHA))
self._pnj[i] = dict()
while x < self._longueur:
y = 0
self._tiles.append(list())
while y < self._largeur:
self._tiles[x].append(Tile(self._nombreCouches))
y += 1
x += 1
i += 1
###
i, x, y = 0, 0, 0
while i < self._nombreCouches:
y = 0
while y < self._largeur:
x = 0
while x < self._longueur:
gid = self._tmxLayers[i].content2D[x][y]
if gid != 0: #Bloc plein
if gid not in self._tilesEtendus.keys(): #Tile plein standard
self._tiles[x][y].bloc.append(Bloc(infos=self._dicoGid[gid]))
surfaceTileset, positionSource = self._dicoSurfaces[self._dicoGid[gid][0]]["Source"], self._dicoGid[gid][2]
self._tilesLayers[i].blit(surfaceTileset, (x * self._hauteurTile, y * self._hauteurTile), area=positionSource)
else: #Tile étendu formé de sous-tiles
self._tiles[x][y].bloc.append(Bloc(vide=True)) #On met un tile vide en la position de réf du tile étendu ; ce sera complété avec un sous-tile étendu
if i == 0:
self._tilesLayers[i].fill((0,0,0), (x * self._hauteurTile, y * self._hauteurTile, self._hauteurTile, self._hauteurTile))
####
positionSource = self._dicoGid[gid][2]
longueurTileset, largeurTileset = int(positionSource[2]/self._hauteurTile), int(positionSource[3]/self._hauteurTile)
xMin, yMin = x, y - largeurTileset + 1
xMax, yMax, xActuel, yActuel, i2 = xMin + longueurTileset, yMin + largeurTileset, xMin, yMin, 0
(praticabilites, couches) = self._tilesEtendus[gid]
couches = [i if c == -1 else int(c) for c in couches] #-1 est la valeur par défaut pour dire la couche du tile étendu
while yActuel < yMax:
xActuel = xMin
while xActuel < xMax:
self._subTilesEtendus.setdefault((xActuel, yActuel, couches[i2]), [])
positionSourceSubTile = positionSource[0] + ((xActuel-xMin)*self._hauteurTile), positionSource[1] + ((yActuel-yMin)*self._hauteurTile), self._hauteurTile, self._hauteurTile
self._subTilesEtendus[(xActuel, yActuel, couches[i2])].append((couches[i2], praticabilites[i2], positionSourceSubTile, self._dicoGid[gid][0]))
if couches[i2] <= i:
self._completerAvecTileEtendu(xActuel,yActuel,couches[i2])
xActuel, i2 = xActuel + 1, i2 + 1
yActuel += 1
else: #Bloc vide
self._tiles[x][y].bloc.append(Bloc(vide=True))
if i == 0 and self._scrollingBackground is False: #Sur la couche 0, il faut mettre du noir pour les blocs vides
self._tilesLayers[i].fill((0,0,0), (x * self._hauteurTile, y * self._hauteurTile, self._hauteurTile, self._hauteurTile))
if i == self._nombreCouches - 1:
self._tiles[x][y].recalculerPraticabilites()
x += 1
y += 1
i += 1
del self._dicoGid
def _completerAvecTileEtendu(self, x, y, c, initialisation=True):
for tileEtendu in self._subTilesEtendus[x,y,c]:
if initialisation:
self._tiles[x][y].ajouterTileEtendu(*tileEtendu)
surfaceTileset, positionSource = self._dicoSurfaces[tileEtendu[3]]["Source"], tileEtendu[2]
self._tilesLayers[c].blit(surfaceTileset, (x * self._hauteurTile, y * self._hauteurTile), area=positionSource)
self._subTilesBlittes.append((x,y,c))
def _completerDicoGids(self, nomTileset, tileset, longueur, largeur):
"""Lors du chargement d'un tileset dans _ajouterSurface (quand une carte est créée), cette fonction se charge de faire correspondre à chaque tile du tileset les infos
qui lui correspondent. 1 Tile dans le tileset = 1 GID = 1 position source, 1 praticabilité, 1 nom de tileset"""
gid, idTileset, x, y, tileWidth, tileHeight = int(tileset.firstgid), 0, 0, 0, int(tileset.tilewidth), int(tileset.tileheight)
tilesEtendus = tileset.properties.get("tileEtendu", False)
while y < largeur:
x = 0
while x < longueur:
if len(tileset.tiles) > 0:
praticabilite = tileset.tiles[idTileset].properties.get("Praticabilite", False) == "True"
else: #Tileset importé d'ailleurs, les praticabilités n'ont pas été indiquées
praticabilite = True
self._dicoGid[gid] = nomTileset, praticabilite, (x, y, tileWidth, tileHeight)
if tilesEtendus: #On récupère les praticabilités et couches des sous-tiles
if "Praticabilites" in tileset.tiles[idTileset].properties.keys():
praticabilitesTile =tileset.tiles[idTileset].properties["Praticabilites"].split(",")
praticabilitesTile = [praticabiliteTile == "True" for praticabiliteTile in praticabilitesTile]
else:
praticabilitesTile = [praticabilite] * ( int(tileWidth/self._hauteurTile) * int(tileHeight/self._hauteurTile) )
if "Couches" in tileset.tiles[idTileset].properties.keys():
couchesTile = tileset.tiles[idTileset].properties["Couches"].split(",")
else:
couchesTile = [-1] * ( int(tileWidth/self._hauteurTile) * int(tileHeight/self._hauteurTile) )
self._tilesEtendus[gid] = praticabilitesTile, couchesTile
gid, idTileset, x = gid + 1, idTileset + 1, x + tileWidth #increments
y += tileHeight
def _ajouterSurface(self, positionSource, cheminVersTileset, couleurTransparente, tileset=False, mobile=True):
"""Ajoute la surface correspondant à un bloc dans le dico de surfaces, si elle n'y est pas déjà.
Pour les tilesets, on ajoute la surface entière seulement. Pour les mobiles, on enregistre aussi la partie du tileset qui nous intéresse.
Pour les tilesets, on complète le dico de GIDs (lors de la création de la carte)."""
nomTileset = os.path.basename(cheminVersTileset)
if nomTileset not in self._dicoSurfaces:
self._dicoSurfaces[nomTileset] = dict()
try:
self._dicoSurfaces[nomTileset]["Source"] = pygame.image.load(os.path.join(DOSSIER_RESSOURCES,nomTileset))
if tileset is not False:
self._completerDicoGids(nomTileset, tileset, self._dicoSurfaces[nomTileset]["Source"].get_width(), self._dicoSurfaces[nomTileset]["Source"].get_height())
except pygame.error as erreur:
print( MESSAGE_ERREUR_CHARGEMENT_TILESET.format(nomTileset), str(erreur) )
if mobile is True and positionSource not in self._dicoSurfaces[nomTileset].keys(): #On ne conserve les sous-surfaces que des mobiles
self._dicoSurfaces[nomTileset][positionSource] = pygame.Surface((positionSource[2],positionSource[3]), flags=SRCALPHA).convert_alpha()
self._dicoSurfaces[nomTileset][positionSource].blit(self._dicoSurfaces[nomTileset]["Source"], (0,0), area=positionSource)
elif mobile is False and positionSource is not False: #pour changerBloc : on retourne la sous-surface pour la blitter sur les tiles layers
return self._dicoSurfaces[nomTileset]["Source"].subsurface(positionSource)
def changerBloc(self, x, y, c, nomTileset, positionSource, couleurTransparente, praticabilite, vide=False, permanente=False, nomModif=None):
if self.tileExistant(x,y) is True and c < self.nombreCouches:
bloc, jeu = self._tiles[x][y].bloc[c], self._jeu
if vide is False:
bloc = Bloc(nomTileset=nomTileset, positionSource=positionSource, couleurTransparente=couleurTransparente, praticabilite=praticabilite)
self._tiles[x][y].bloc[c] = bloc
surfaceBloc = self._ajouterSurface(positionSource, nomTileset, couleurTransparente, tileset=False, mobile=False)
self._tilesLayers[c].fill((0,0,0,0), rect=Rect(x*self._hauteurTile, y*self._hauteurTile, self._hauteurTile, self._hauteurTile))
self._tilesLayers[c].blit(surfaceBloc, (x*self._hauteurTile, y*self._hauteurTile) )
else:
bloc, praticabilite = Bloc(jeu, vide=True), True
self._tiles[x][y].bloc[c] = bloc
absi, ordo, i, a = x*self._hauteurTile, y*self._hauteurTile, 0, 0
couleurEntierementTransparente = Color(0,0,0,0)
while i < self._hauteurTile: #On rend transparent les pixels du tile désormais vide
a = 0
while a < self._hauteurTile:
self._tilesLayers[c].set_at((absi+i, ordo+a), couleurEntierementTransparente)
a += 1
i += 1
if permanente:
self._jeu.ajouterModificationCarte(self._nom, nomModif, x, y, c, nomTileset, positionSource, couleurTransparente, praticabilite, vide=vide)
self._tiles[x][y].modifierPraticabilite(c, praticabilite)
if (x,y,c) in self._tilesEtendus.keys():
self._completerAvecTileEtendu(x, y, c, initialisation=False)
self.mettreToutAChanger()
def tileExistant(self,x,y):
"""Retourne True si le tile de coordonnées <x>,<y> existe"""
return x >= 0 and x < self._longueur and y >= 0 and y < self._largeur
def tilePraticable(self, x, y, c, eau=False):
if x < len(self._tiles):
if y < len(self._tiles[x]):
if c < len(self._tiles[x][y].praticabilite):
praticabilite = self._tiles[x][y].praticabilite[c]
if eau is False or praticabilite is True:
return praticabilite
else: #PNJ marin et pas de praticabilité terrestre : on vérifié la praticabilité marine
return False if self._tiles[x][y].praticabiliteEau == False else self._tiles[x][y].praticabiliteEau[c]
else:
return False
else:
return False
else:
return False
def _determinerPresenceSurTiles(self, x, y, longueur, largeur):
abscisses, ordonnees, x, y, longueur, largeur, i = [], [], (x / 32), (y/32), int(longueur/32), int(largeur/32), 0
abscisses = list(range(math.floor(x), math.ceil(x) + longueur))
ordonnees = list(range(math.floor(y), math.ceil(y) + largeur))
listeTilesPresence = [(absa, ordo) for absa in abscisses for ordo in ordonnees]
return listeTilesPresence
def coordonneesAuTileSuivant(self, direction, x, y):
"""Retourne les deux coordonnées au tile suivant en fonction de la direction."""
xReponse, yReponse = int(x/32), int(y/32)
if direction is "Gauche" or direction is "Droite":
xReponse = int( directions.ajusterCoordonneesLorsDeplacement(x, direction) / 32)
elif direction is "Haut" or direction is "Bas":
yReponse = int( directions.ajusterCoordonneesLorsDeplacement(y, direction) / 32)
return (xReponse, yReponse)
def _traiterPositionRelative(self, positionCarte, positionRelative):
positionFinale = positionCarte.copy()
positionFinale.width, positionFinale.height = positionCarte.width, positionCarte.height
return positionFinale.move(positionRelative.left, positionRelative.top)
def _prevenirCollision(self, pnjCogne, pnjCogneur, positionCogneur):
self._messagesCollision.append((pnjCogne, pnjCogneur, positionCogneur))
def deplacementPossible(self, positionCarte, c, nomPNJ, verifPrecise=False, positionCollision=False, positionVisible=False, ecranVisible=False, exclusionCollision=[], collisionEffective=False, axeTiles=True, eau=False):
"""Indique si un déplacement en <x><y><c> est possible. Retourne un 2-tuple avec :
* <True> si un PNJ peut être positionné en <x><y><c>, sinon <False>. Si <xOld>,<yOld> sont fournis, ne prend pas en compte le PNJ à cette position pour les collisions.
* La praticabilité est vérifiée via des Rect quand <verifPrecise> vaut <True>.
* Vérifie que l'objet est visible à l'écran quand <ecranVisible> vaut <True>.
* Ne prend pas en compte les collisions avec tout PNJ dont le nom est présent dans la liste <exclusionCollision>.
* Prend en compte la praticabilité spécifique des tiles d'eaux si <eau> vaut <True>
* Le tile qui vient d'être quitté."""
deplacementPossible = True
if positionCollision is False:
positionCollision = positionCarte
else:
positionCollision = self._traiterPositionRelative(positionCarte, positionCollision)
if not positionVisible:
positionVisible = positionCarte
else:
positionVisible = self._traiterPositionRelative(positionCarte, positionVisible)
if self._ecran.contains(positionCarte) == 0: #Si la position d'arrivée existe dans la carte
deplacementPossible = False
if ecranVisible is True and not (self._ecranVisible.contains(positionVisible) or self._ecranVisible.colliderect(positionVisible)):
deplacementPossible = False
pnjsEnCollision = [pnj for pnj in self._pnj[c].values() if pnj.nomPNJ != nomPNJ and pnj.nomPNJ not in exclusionCollision and (pnj.positionCollision.colliderect(positionCollision) == 1 and (not axeTiles or (pnj.positionCarteSuivante == positionCarte or pnj.positionCarteSuivante == False)))]
if len(pnjsEnCollision) > 0:
deplacementPossible = False
if collisionEffective:
for pnj in pnjsEnCollision:
self._prevenirCollision(pnj.nomPNJ, nomPNJ, positionCarte)
if deplacementPossible:
for (x,y) in self._determinerPresenceSurTiles(positionCarte.left, positionCarte.top, positionCarte.width, positionCarte.height):
if self.tilePraticable(x, y, c, eau=eau) is False: #Si le tile est impraticable
self._tileRect.left, self._tileRect.top = x*32, y*32
if (verifPrecise and positionCollision.colliderect(self._tileRect)) or (not verifPrecise):
deplacementPossible = False
return deplacementPossible
def supprimerPNJ(self, nomPNJ, couche):
"""Supprime un PNJ à l'écran."""
if nomPNJ in self._pnj[couche].keys():
del self._pnj[couche][nomPNJ]
self._toutAChanger = True
def poserPNJ(self, positionCarte, c, positionSource, nomTileset, couleurTransparente, nomPNJ, positionCarteSuivante=False, positionCollision=False, positionVisible=False):
"""Ordonne l'affichage à l'écran d'un PNJ à une nouvelle position et l'effacement du PNJ à sa position précedente"""
hauteurTile = self._hauteurTile
x,y = float(positionCarte.left), float(positionCarte.top)
if nomPNJ not in self._pnj[c].keys():
self._pnj[c][nomPNJ] = Bloc(self._jeu, pnj=True, nomPNJ=nomPNJ, nomTileset=nomTileset, positionCarte=positionCarte, positionCarteSuivante=positionCarteSuivante, positionSource=positionSource, positionCollision=positionCollision, positionVisible=positionVisible)
pnj = self._pnj[c][nomPNJ]
if not positionCollision:
positionCollision = pnj.positionCollision
if not positionVisible:
positionVisible = pnj.positionVisible
if pnj.positionCollision != positionCollision:
pnj.positionCollision = positionCollision
if pnj.positionVisible != positionVisible:
pnj.positionVisible = positionVisible
if pnj.positionSource != positionSource:
pnj.positionSource = positionSource
if pnj.nomTileset != nomTileset:
pnj.nomTileset = nomTileset
if pnj.couleurTransparente != couleurTransparente:
pnj.couleurTransparente = couleurTransparente
if pnj.positionCarte != positionCarte:
pnj.positionCarte = positionCarte
if pnj.positionCarteSuivante != positionCarteSuivante:
pnj.positionCarteSuivante = positionCarteSuivante
self._toutAChanger = True
self._ajouterSurface( (positionSource.left, positionSource.top, positionSource.width, positionSource.height), nomTileset, couleurTransparente)
def mettreToutAChanger(self):
self._toutAChanger = True
def _coordonneeScrollingPossible(self, coor, abs=False):
"""Retourne <True> si <coor> est dans un emplacement où le scrolling est possible.
Paramètre : quand <abs> vaut <True>, il s'agit non pas d'une ordonnée, mais d'une abscisse."""
if abs is False: #Ordonnée
return coor == self._emplacementScrollingY
else: #Abscisse
return coor == self._emplacementScrollingX
def verifierScrollingPossible(self, x, y, direction):
"""Vérifie si le scrolling est possible pour faciliter le traitement dans gererScrolling"""
self._scrollingPossible, scrollingDirection, xOriginal, yOriginal = False, True, x, y
if direction == "Bas" and int(self._scrollingY / 32) + int(FENETRE["largeurFenetre"]/32) >= self._largeur:
scrollingDirection = False
if direction == "Haut" and self._scrollingY == 0:
scrollingDirection = False
if direction == "Droite" and int(self._scrollingX / 32) + int(FENETRE["longueurFenetre"]/32) >= self._longueur:
scrollingDirection = False
if direction == "Gauche" and self._scrollingX == 0:
scrollingDirection = False
if scrollingDirection is True:
x, y = x - self._scrollingX, y - self._scrollingY
scrollingPossibleX = self._coordonneeScrollingPossible(x, abs=True)
scrollingPossibleY = self._coordonneeScrollingPossible(y, abs=False)
if (direction == "Haut" or direction == "Bas") and scrollingPossibleY is True:
self._scrollingPossible, self._directionScrolling = True, direction
elif (direction == "Gauche" or direction == "Droite") and scrollingPossibleX is True:
self._scrollingPossible, self._directionScrolling = True, direction
if self._scrollingBackground is not False:
self._verifierScrollingPossibleBackground(xOriginal,yOriginal,direction)
def _verifierScrollingPossibleBackground(self, x, y, direction):
"""Vérifie si le scrolling du background est possible pour faciliter le traitement dans gererScrolling"""
self._bgScrollingPossible, scrollingDirection = False, True
if direction == "Bas" and self._bgScrollingY + FENETRE["largeurFenetre"] >= self._bgLargeur:
scrollingDirection = False
if direction == "Haut" and self._bgScrollingY <= 0:
scrollingDirection = False
if direction == "Droite" and self._bgScrollingX + FENETRE["longueurFenetre"] >= self._bgLongueur:
scrollingDirection = False
if direction == "Gauche" and self._bgScrollingX <= 0:
scrollingDirection = False
if scrollingDirection is True:
x, y = x - self._scrollingX, y - self._scrollingY
scrollingPossibleX = self._coordonneeScrollingPossible(x, abs=True)
scrollingPossibleY = self._coordonneeScrollingPossible(y, abs=False)
if (direction == "Haut" or direction == "Bas") and scrollingPossibleY is True:
self._bgScrollingPossible, self._bgDirectionScrolling = True, direction
elif (direction == "Gauche" or direction == "Droite") and scrollingPossibleX is True:
self._bgScrollingPossible, self._bgDirectionScrolling = True, direction
def gererScrolling(self, changement, direction):
"""Gère le scrolling"""
if (direction == "Droite" or direction == "Gauche") and self._scrollingPossible is True:
self._scrollingX += changement
self.mettreToutAChanger()
self._ecranVisible.move_ip(changement, 0)
elif (direction == "Bas" or direction == "Haut") and self._scrollingPossible is True:
self._scrollingY += changement
self.mettreToutAChanger()
self._ecranVisible.move_ip(0, changement)
if self._scrollingBackground is not False:
if (direction == "Droite" or direction == "Gauche") and self._bgScrollingPossible is True:
changement = self._bgRatioX if changement > 0 else (self._bgRatioX * -1)
if self._bgRatioX < 1.0:
self._bgRatioXWait += changement
else:
self._bgRatioXWait = changement
if self._bgRatioXWait >= 1.0 or self._bgRatioXWait <= -1.0:
self._bgScrollingX += self._bgRatioXWait
self._bgEcranVisible.move_ip(self._bgRatioXWait, 0)
self._bgRatioXWait = 0
self.mettreToutAChanger()
return True
elif (direction == "Bas" or direction == "Haut") and self._bgScrollingPossible is True:
changement = self._bgRatioY if changement > 0 else (self._bgRatioY * -1)
if self._bgRatioY < 1.0:
self._bgRatioYWait += changement
else:
self._bgRatioYWait = changement
if self._bgRatioYWait >= 1.0 or self._bgRatioYWait <= -1.0:
self._bgScrollingY += self._bgRatioYWait
self._bgEcranVisible.move_ip(0, self._bgRatioYWait)
self._bgRatioYWait = 0
self.mettreToutAChanger()
return True
def initialiserScrolling(self, x, y):
"""Après la création de la carte, initialise le scrolling à la position du joueur si nécessaire.
<x> est l'abscisse du joueur, <y> son ordonnée. Ces coordonnées sont données en pixels."""
self._ecranVisible.top, self._ecranVisible.left = 0, 0
scrollingAInitialiserX, scrollingAInitialiserY, x, y = True, True, x, y
if FENETRE["largeurFenetre"] >= self._largeur * 32: #Carte petite
scrollingAInitialiserY = False
if FENETRE["longueurFenetre"] >= self._longueur * 32:
scrollingAInitialiserX = False
if x < self._emplacementScrollingX: #On est dans une partie de la carte où le scrolling est inutile
scrollingAInitialiserX = False
if y < self._emplacementScrollingY:
scrollingAInitialiserY = False
if scrollingAInitialiserX is True:
self._scrollingX = x - self._emplacementScrollingX #A chaque instant, on a x - scrollingX = emplacementScrollingX, d'où cette relation
if int(FENETRE["longueurFenetre"]/32) + int(self._scrollingX/32) >= self._longueur: #Quand on est aux bords de la carte
self._scrollingX = (self._longueur*32) - FENETRE["longueurFenetre"]
if scrollingAInitialiserY is True:
self._scrollingY = y - self._emplacementScrollingY
if int(FENETRE["largeurFenetre"]/32) + int(self._scrollingY/32) >= self._largeur:
self._scrollingY = (self._largeur*32) - FENETRE["largeurFenetre"]
self._ecranVisible = Rect(0, 0, FENETRE["longueurFenetre"], FENETRE["largeurFenetre"])
self._ecranVisible.move_ip(self._scrollingX, self._scrollingY)
def initialiserScrollingBackground(self, x, y):
if self._scrollingBackground is False:
return
if x >= self._emplacementScrollingX:
self._bgScrollingX = x - self._emplacementScrollingX
if FENETRE["longueurFenetre"] + self._bgScrollingX >= self._bgLongueur: #Quand on est aux bords de la carte
self._bgScrollingX = self._bgLongueur - FENETRE["longueurFenetre"]
if y >= self._emplacementScrollingY:
self._bgScrollingY = y - self._emplacementScrollingY
if FENETRE["largeurFenetre"] + self._bgScrollingY >= self._bgLargeur: #Quand on est aux bords de la carte
self._bgScrollingY = self._bgLargeur - FENETRE["largeurFenetre"]
self._bgEcranVisible = Rect(0, 0, FENETRE["longueurFenetre"], FENETRE["largeurFenetre"])
self._bgEcranVisible.move_ip(self._bgScrollingX, self._bgScrollingY)
def obsOnNouvelleObservation(self, instance, nomAttribut, info):
if isinstance(instance, ZonePensee) is True and nomAttribut == "_surface":
if info is not None:
self._surfaceZonePensee, self._besoinAffichageZonePensee = info.copy(), True
elif isinstance(instance, ZonePensee) is True and nomAttribut == "_positionSurface":
if info is not None:
self._positionZonePensee = list(info)
elif isinstance(instance, ZonePensee) is True and nomAttribut == "_faceActuelle":
self._faceActuelle = info.copy() if isinstance(info, pygame.Surface) else False
def _transformerPartie(self, surface, nomPnj, positionVisible, positionCarte, **p):
"""Applique une transformation individuellement à chaque <surface> (mobile) lors de sa pose."""
for nomTransformation in self._transformationsParties:
p = self._parametresTransformations[nomTransformation]
if nomTransformation == "AlphaFixe":
"""pixels = surfarray.pixels_alpha(surface)
positionsNulles = numpy.where(pixels == 0)
pixels[:,:] = p["alpha"]
pixels[positionsNulles] = 0"""
surface.set_alpha(None)
surface.set_alpha(p["alpha"])
elif nomTransformation == "Action Joueur" and nomPnj == "Joueur":
centre = positionCarte.move(-self._scrollingX, -self._scrollingY).center
pygame.draw.circle(self._fenetre, (255,255,255), centre, p["rayon"], 1)
elif "Rouge" in nomTransformation and nomPnj == p["nom"]:
pixels = surfarray.pixels3d(surface) #On exclut la zone de pensée
pixels[:,:,1:] = 0
def _ajouterPolice(self, fichierPolice, taillePolice):
if fichierPolice not in self._polices.keys():
self._polices[fichierPolice] = dict()
if taillePolice not in self._polices[fichierPolice].keys():
self._polices[fichierPolice][taillePolice] = pygame.font.Font(os.path.join(DOSSIER_RESSOURCES, fichierPolice), taillePolice)
def _appliquerTransformationGlobale(self, nomTransformation, **p):
"""Applique la transformation globale <nomTransformation> avec le dico de paramètres <p>."""
if nomTransformation == "Alpha":
surface = self._fenetre.copy()
self._fenetre.fill((0,0,0))
surface.set_alpha(None)
surface.set_alpha(0)
surface.set_alpha(p["alpha"])
self._fenetre.blit(surface, (0,0))
if nomTransformation == "Rouge":
pixels = surfarray.pixels3d(self._fenetre)[:FENETRE["longueurFenetre"], :FENETRE["largeurFenetre"]] #On exclut la zone de pensée
pixels[:,:,1:] = 0
elif nomTransformation == "Noir" or nomTransformation == "NoirTransition":
pixels = surfarray.pixels3d(self._fenetre)[:FENETRE["longueurFenetre"],:FENETRE["largeurFenetre"]]
pixels /= p["coef"]
if p["coef"] >= 12:
pixels[:] = (0,0,0)
elif nomTransformation == "NoirTotal":
pixels = surfarray.pixels3d(self._fenetre)[:FENETRE["longueurFenetre"],:FENETRE["largeurFenetre"]]
pixels[:] = (0,0,0)
elif nomTransformation == "Glow":
nomPNJ, c = p["nomPNJ"], p["couche"]
if nomPNJ in self._pnj[c].keys():
x, y = self._pnj[c][nomPNJ].positionCarte.left - self._scrollingX, self._pnj[c][nomPNJ].positionCarte.top - self._scrollingY
x1, y1, x2, y2 = x-32 if x-32 >= 0 else 0, y-32 if y-32 >= 0 else 0, x+64 if x+64 <= FENETRE["longueurFenetre"] else FENETRE["longueurFenetre"], y+64 if y+64 <= FENETRE["largeurFenetre"] else FENETRE["largeurFenetre"]
#On limite les coordonnées du carré autour du cercle aux limites de la fenêtre
cerclePossible = False
if x1 < x2 and y1 < y2: #On évite les problèmes quand le cercle est en dehors de l'écran à l'arrière...
xCentre, yCentre, (xPixels, yPixels), pixels, cerclePossible = x+16, y+16, numpy.mgrid[x1:x2, y1:y2], surfarray.pixels3d(self._fenetre)[x1:x2,y1:y2], True
if cerclePossible and 0 not in pixels.shape: #...comme à l'avant
distancesCarrees = (xPixels - xCentre) ** 2 + (yPixels - yCentre) ** 2
pixels[distancesCarrees <= 32 ** 2] *= 5
elif nomTransformation == "Fog":
idParam = id(self._parametresTransformations[nomTransformation])
fogAlpha = p.get("alpha", 150)
if self._idParametres.get(nomTransformation, False) == idParam or (self._idParametres.get(nomTransformation, False) != idParam and p.get("minorChange", False) is True):
fog, tempsPrecedent = self._donneesParametres[nomTransformation]["fog"], self._donneesParametres[nomTransformation]["tempsPrecedent"]
fogScrollX = self._donneesParametres[nomTransformation]["fogScrollX"]
else:
fog = pygame.image.load(os.path.join(DOSSIER_RESSOURCES, "fog.png"))
surfarray.pixels_alpha(fog)[:] = fogAlpha
self._donneesParametres[nomTransformation] = {"fog":fog, "tempsPrecedent":pygame.time.get_ticks(), "fogScrollX":0}
self._idParametres[nomTransformation], tempsPrecedent = idParam, self._donneesParametres[nomTransformation]["tempsPrecedent"]
fogScrollX = self._donneesParametres[nomTransformation]["fogScrollX"]
if p.get("minorChange", False) is True:
surfarray.pixels_alpha(fog)[:] = fogAlpha
tempsActuel = pygame.time.get_ticks()
avancee = ((tempsActuel - tempsPrecedent) / 1000) * VITESSE_DEFILEMENT_FOG
if avancee >= 1.0:
self._donneesParametres[nomTransformation]["tempsPrecedent"] = tempsActuel
fogScrollX += avancee
self._donneesParametres[nomTransformation]["fogScrollX"] = fogScrollX
#We calculate where the junction between two scroll images will be on screen
xScrollRelatif = (((self._scrollingX+fogScrollX) / FENETRE["longueurFenetre"]) - int((self._scrollingX+fogScrollX) / FENETRE["longueurFenetre"])) * FENETRE["longueurFenetre"]
yScrollRelatif = ((self._scrollingY / FENETRE["largeurFenetre"]) - int(self._scrollingY / FENETRE["largeurFenetre"])) * FENETRE["largeurFenetre"]
fogPositions = dict()
#Four images on screen
if xScrollRelatif > 0 and yScrollRelatif > 0:
fogPositions[0] = Rect(xScrollRelatif, yScrollRelatif, FENETRE["longueurFenetre"]-xScrollRelatif, FENETRE["largeurFenetre"]-yScrollRelatif)
fogPositions[1] = Rect(0, yScrollRelatif, xScrollRelatif, FENETRE["largeurFenetre"]-yScrollRelatif)
fogPositions[2] = Rect(xScrollRelatif, 0, FENETRE["longueurFenetre"]-xScrollRelatif, yScrollRelatif)
fogPositions[3] = Rect(0, 0, xScrollRelatif, yScrollRelatif)
elif xScrollRelatif > 0:
fogPositions[0] = Rect(xScrollRelatif, 0, FENETRE["longueurFenetre"]-xScrollRelatif, FENETRE["largeurFenetre"])
fogPositions[1] = Rect(0, 0, xScrollRelatif, FENETRE["largeurFenetre"])
elif yScrollRelatif > 0:
fogPositions[0] = Rect(0, yScrollRelatif, FENETRE["longueurFenetre"], FENETRE["largeurFenetre"]-yScrollRelatif)
fogPositions[2] = Rect(0, 0, FENETRE["longueurFenetre"], yScrollRelatif)
else:
fogPositions[0] = Rect(0,0, FENETRE["longueurFenetre"], FENETRE["largeurFenetre"])
i = 0
while i < 4:
coord = (0,0) if i == 0 else (FENETRE["longueurFenetre"]-xScrollRelatif,0) if i == 1 else (0, FENETRE["largeurFenetre"]-yScrollRelatif) if i == 2 else (FENETRE["longueurFenetre"]-xScrollRelatif, FENETRE["largeurFenetre"]-yScrollRelatif)
self._fenetre.blit(fog.subsurface(fogPositions[i]), coord)
i += 1
while i not in fogPositions.keys() and i < 4:
i += 1
elif nomTransformation == "RemplirNoir":
self._fenetre.fill((0,0,0), rect=(0,0,FENETRE["longueurFenetre"],FENETRE["largeurFenetre"]))
elif "SplashText" in nomTransformation:
idParam = id(p)
if self._idParametres.get(nomTransformation, False) == idParam:
surfaceTexte = self._donneesParametres[nomTransformation]
else:
self._idParametres[nomTransformation] = id(p)
couleurFond, police, taille = p.get("couleurFond", None), p.get("police", NOM_FICHIER_POLICE_PAR_DEFAUT), p.get("taille", TAILLE_POLICE_SPLASH_SCREEN)
self._ajouterPolice(police, taille)
surfaceTexte = self._polices[police][taille].render(p["texte"], p["antialias"], p["couleurTexte"], couleurFond)
self._donneesParametres[nomTransformation] = surfaceTexte
if p.get("position", False) is not False:
position = p["position"]
self._fenetre.blit(surfaceTexte, position)
elif self._ecranVisible.colliderect(self._pnj[p["couche"]][p["nomPNJ"]].positionCarte) or self._jeu.carteActuelle._ecranVisible.contains(self._pnj[p["couche"]][p["nomPNJ"]].positionCarte):
position = self._pnj[p["couche"]][p["nomPNJ"]].positionCarte.move(-16, -surfaceTexte.get_height())
position.move_ip(-self._scrollingX, -self._scrollingY)
self._fenetre.blit(surfaceTexte, position)
elif nomTransformation == "Nuit":
self._fenetre.fill((0,0,0), rect=(0,0,FENETRE["longueurFenetre"],FENETRE["largeurFenetre"]))
c = 0
while c < self._nombreCouches:
for nomPnj in self._pnj[c]:
self._afficherBlocPnj(c, nomPnj)
c += 1
def _transformerSurfaceGlobalement(self, affichageComplet=False):
"""A chaque frame, regarde s'il y a des transformations globales à appliquer, et les exécute lorsque c'est le cas.
<affichageComplet> doit valoir <True> si la fonction doit mettre à jour l'écran entier elle-même (car personne ne le fait après).
Retourne <True> quand la fonction s'est occupée de la mise à jour de l'écran (car on le lui a demandé ET qu'il y avait des transfos à traiter)."""
if len(self._transformationsGlobales) > 0: #S'il y a des transformations à opérer
longueurFenetre, largeurFenetre = FENETRE["longueurFenetre"], FENETRE["largeurFenetre"]
for nomTransformation in self._transformationsGlobales:
self._appliquerTransformationGlobale(nomTransformation, **self._parametresTransformations[nomTransformation]) #On applique la transfo
def _afficherZonePensee(self, affichageComplet=False):
"""S'il y a quelque chose à afficher, réaffiche la zone de pensée.
<affichageComplet> est un booléen qui vaut <True> lorsque pygame.display.flip est appelée à la suite de l'appel de la fonction."""
positionZoneEntiere = (0, FENETRE["largeurFenetre"], FENETRE["longueurFenetre"], FENETRE["largeurFenetreReelle"] - FENETRE["largeurFenetre"])
self._fenetre.fill(COULEUR_FOND_ZONE_PENSEE,rect=positionZoneEntiere)
if self._surfaceZonePensee is not None:
self._fenetre.blit(self._surfaceZonePensee, self._positionZonePensee)
if self._faceActuelle is not False:
self._fenetre.blit(self._faceActuelle, positionZoneEntiere)
if affichageComplet is False:
pygame.display.update(positionZoneEntiere)
self._besoinAffichageZonePensee = False
def _afficherBlocPnj(self, c, nomPnj):
"""Affiche un PNJ sur un bloc"""
pnj = self._pnj[c][nomPnj]
positionVisible, positionCarte = pnj.positionVisible, pnj.positionCarte
if self._ecranVisible.contains(positionCarte) or self._ecranVisible.colliderect(positionCarte):
positionCollage = positionVisible.move(-self._scrollingX, -self._scrollingY)
if len(self._transformationsParties) > 0:
surfaceCollage = self._dicoSurfaces[pnj.nomTileset][(pnj.positionSource.left, pnj.positionSource.top, pnj.positionSource.width, pnj.positionSource.height)].copy()
self._transformerPartie(surfaceCollage, nomPnj, positionVisible, positionCarte)
else:
surfaceCollage = self._dicoSurfaces[pnj.nomTileset][(pnj.positionSource.left, pnj.positionSource.top, pnj.positionSource.width, pnj.positionSource.height)]
self._fenetre.blit(surfaceCollage, positionCollage)
def afficher(self):
"""Cette méthode gère l'affichage de la carte"""
self._blitFrame = False
if self._toutAChanger is True:
coucheActuelle = 0
self._fenetre.fill((0,0,0))
while coucheActuelle < self._nombreCouches:
if coucheActuelle == 0 and self._scrollingBackground is not False:
self._fenetre.blit(self._dicoSurfaces[self._scrollingBackground]["Source"], (0,0), area=self._bgEcranVisible)
couche = self._tilesLayers[coucheActuelle]
if len(self._transformationsParties) > 0:
self._transformerPartie(couche, False, (0,0), (0,0))
self._fenetre.blit(couche, (0,0), area=self._ecranVisible)
nomsPnjs = sorted(self._pnj[coucheActuelle], key=lambda nomPNJ: self._pnj[coucheActuelle][nomPNJ].positionCarte.top)
#Tri des PNJs selon leur ordonnée (de manière croissante) : on affiche ceux en haut de l'écran avant ceux en bas, pour avoir une superposition
for nomPnj in nomsPnjs:
self._afficherBlocPnj(coucheActuelle, nomPnj)
coucheActuelle += 1
self._afficherZonePensee(affichageComplet=True)
self._transformerSurfaceGlobalement()
self._blitFrame = True
if self._blitFrame is True:
if LIMITER_FPS:
self._jeu.horlogeFps.tick(NOMBRE_MAX_DE_FPS)
else:
self._jeu.horlogeFps.tick()
pygame.display.flip()
def _getNombreCouches(self):
"""Retourne le nombre de couches défini sur la carte"""
return self._nombreCouches
def _getHauteurTile(self):
"""Retourne la hauteur d'un tile sur la carte"""
return self._hauteurTile
def _getNom(self):
return self._nom
def _getLongueur(self):
return self._longueur
def _getLargeur(self):
return self._largeur
def _getTransformationsGlobales(self):
return self._transformationsGlobales
def _setTransformationsGlobales(self, val):
self._transformationsGlobales = val
def _getTransformationsParties(self):
return self._transformationsParties
def _setTransformationsParties(self, val):
self._transformationsParties = val
def _getParametresTransformations(self):
return self._parametresTransformations
def _setIdParametres(self, val):
self._idParametres = val
def _getIdParametres(self):
return self._idParametres
def _getTiles(self):
return self._tiles
def _getMessagesCollision(self):
return self._messagesCollision
def _setMessagesCollision(self, nouveauxMessages):
self._messagesCollision = nouveauxMessages
def _getPnj(self):
return self._pnj
def _setPnj(self, nouveauxPnj):
self._pnj = nouveauxPnj
nombreCouches = property(_getNombreCouches)
hauteurTile = property(_getHauteurTile)
nom = property(_getNom)
longueur = property(_getLongueur)
largeur = property(_getLargeur)
tiles = property(_getTiles)
pnj = property(_getPnj, _setPnj)
idParametres = property(_getIdParametres, _setIdParametres)
transformationsGlobales = property(_getTransformationsGlobales, _setTransformationsParties)
transformationsParties = property(_getTransformationsParties, _setTransformationsParties)
parametresTransformations = property(_getParametresTransformations)
messagesCollision = property(_getMessagesCollision, _setMessagesCollision)