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
| unit Unit1;
interface
uses
// Gestion des fenêtres et messages Windows
Winapi.Windows, Winapi.Messages,
// Classes et routines système
System.SysUtils, System.Variants, System.Classes,
// JSON, fichiers et mathématiques
System.JSON, System.IOUtils, System.Math,
// Composants réseau pour HTTP et URL
System.Net.URLClient, System.Net.HttpClient,
// Dictionary pour la mise en cache
System.Generics.Collections,
// Composants VCL
Vcl.Graphics, Vcl.Controls,
Vcl.Forms, Vcl.Dialogs,
Vcl.ExtCtrls, Vcl.StdCtrls,
Vcl.Imaging.pngimage;
type
TForm1 = class(TForm)
ListPokemon: TListBox;
Card: TImage;
procedure FormCreate(Sender: TObject);
procedure ListPokemonClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
// Tableau JSON contenant tous les Pokémon
FPokemonArray: TJSONArray;
// Cache en mémoire des images téléchargées
FImageCache: TDictionary<string, TPngImage>;
procedure LoadJSONFromURL(const AURL: string);
procedure CreateCard(Index: Integer);
function DownloadIm(const URL: string): TPngImage;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ -------------------------------------------------------------------------------
Initialisation de la form : création du cache et chargement du JSON
------------------------------------------------------------------------------- }
procedure TForm1.FormCreate(Sender: TObject);
begin
// Création du dictionnaire pour stocker TPngImage par URL
FImageCache := TDictionary<string, TPngImage>.Create;
// Chargement du fichier JSON avec les données des Pokémon
try
LoadJSONFromURL('https://tyradex.vercel.app/api/v1/pokemon');
except
on E: Exception do
ShowMessage('Erreur de chargement des données : ' + E.Message);
end;
end;
{ -------------------------------------------------------------------------------
Libération des ressources : images du cache et tableau JSON
------------------------------------------------------------------------------- }
procedure TForm1.FormDestroy(Sender: TObject);
var
Img: TPngImage;
begin
// Parcours et libération de chaque image en cache
for Img in FImageCache.Values do
Img.Free;
// Libération du dictionnaire
FImageCache.Free;
// Libération du JSON
FPokemonArray.Free;
inherited;
end;
{ -------------------------------------------------------------------------------
Sélection d’un Pokémon : création et affichage de sa carte
------------------------------------------------------------------------------- }
procedure TForm1.ListPokemonClick(Sender: TObject);
begin
if ListPokemon.ItemIndex >= 0 then
CreateCard(ListPokemon.ItemIndex +1);
end;
{ -------------------------------------------------------------------------------
Lecture du JSON, parsing et remplissage de ListPokemon
------------------------------------------------------------------------------- }
procedure TForm1.LoadJSONFromURL(const AURL: string);
var
HttpClient: THTTPClient;
Response: IHTTPResponse;
JSONString: string;
JSONValue: TJSONValue;
I: Integer;
NameObj: TJSONObject;
begin
HttpClient := THTTPClient.Create;
try
// Télécharger le JSON
Response := HttpClient.Get(AURL);
if Response.StatusCode <> 200 then
raise Exception.CreateFmt('Impossible de récupérer le JSON (HTTP %d)', [Response.StatusCode]);
// Récupérer la réponse comme texte UTF-8
JSONString := Response.ContentAsString(TEncoding.UTF8);
// Parser le JSON
JSONValue := TJSONObject.ParseJSONValue(JSONString);
if not Assigned(JSONValue) then
raise Exception.Create('Impossible de parser le JSON');
// Vérifier que c’est bien un tableau racine
if JSONValue is TJSONArray then
FPokemonArray := TJSONArray(JSONValue)
else
begin
JSONValue.Free;
raise Exception.Create('Le JSON doit être un tableau racine');
end;
// Remplir la liste des noms
ListPokemon.Items.BeginUpdate;
try
ListPokemon.Clear;
for I := 0 to FPokemonArray.Count - 1 do
begin
NameObj := (FPokemonArray.Items[I] as TJSONObject).GetValue<TJSONObject>('name');
if NameObj.GetValue<string>('fr') = 'MissingNo.' then
Continue;
ListPokemon.Items.Add(NameObj.GetValue<string>('fr'));
end;
finally
ListPokemon.Items.EndUpdate;
end;
finally
HttpClient.Free;
end;
end;
{ -------------------------------------------------------------------------------
Construction graphique de la carte du Pokémon sélectionné
------------------------------------------------------------------------------- }
procedure TForm1.CreateCard(Index: Integer);
var
Pokemon, Sprites, Stats: TJSONObject;
Nom, Categorie, SpriteURL, TypesStr, StatsText: string;
TypesArray: TJSONArray;
I, yPos, xPos, xOrigin: Integer;
PSprite: TPngImage;
SurfaceW, SurfaceH: Integer;
TargetW, TargetH: Integer;
DestRect: TRect;
Rgn: HRGN;
CornerW, CornerH, Margin: Integer;
RegionLeft, RegionTop, RegionRight, RegionBottom: Integer;
StatsY: Integer;
begin
// Récupération de l’objet JSON du Pokémon sélectionné
Pokemon := FPokemonArray.Items[Index] as TJSONObject;
Nom := Pokemon.GetValue<TJSONObject>('name').GetValue<string>('fr');
Categorie := Pokemon.GetValue<string>('category');
// Chargement du sprite (URL et cache)
Sprites := Pokemon.GetValue<TJSONObject>('sprites');
SpriteURL := Sprites.GetValue<string>('regular');
PSprite := nil;
if SpriteURL <> '' then
PSprite := DownloadIm(SpriteURL);
// Concaténation des types en une seule chaîne
TypesStr := '';
TypesArray := Pokemon.GetValue<TJSONArray>('types');
for I := 0 to TypesArray.Count - 1 do
TypesStr := TypesStr + TypesArray.Items[I].GetValue<string>('name') + ' ';
TypesStr := TypesStr.Trim;
// Préparation du bitmap de dessin
Card.Picture.Bitmap.SetSize(Card.Width, Card.Height);
SurfaceW := Card.Picture.Bitmap.Width;
SurfaceH := Card.Picture.Bitmap.Height;
// Paramètres de mise en forme
Margin := 10; // marge extérieure
CornerW := 20; // rayon horizontal coins arrondis
CornerH := 20; // rayon vertical coins arrondis
RegionLeft := Margin;
RegionTop := Margin;
RegionRight := SurfaceW - Margin;
RegionBottom := SurfaceH - Margin;
xOrigin := RegionLeft + 10; // décalage X pour Texte/Sprite
yPos := RegionTop + 10; // décalage Y initial pour le texte
with Card.Picture.Bitmap.Canvas do
begin
// Dessin du fond et contour arrondi
Rgn := CreateRoundRectRgn(RegionLeft, RegionTop, RegionRight, RegionBottom, CornerW, CornerH);
try
Brush.Color := clWhite;
FillRgn(Handle, Rgn, Brush.Handle);
Pen.Color := clBlack;
Pen.Width := 3;
RoundRect(RegionLeft, RegionTop, RegionRight, RegionBottom, CornerW, CornerH);
finally
DeleteObject(Rgn);
end;
// Affichage du nom en gras
Font.Size := 16;
Font.Style := [fsBold];
TextOut(xOrigin, yPos, Nom);
Inc(yPos, TextHeight(Nom) + 5);
// Affichage de la catégorie
Font.Size := 10;
Font.Style := [];
TextOut(xOrigin, yPos, Categorie);
Inc(yPos, TextHeight(Categorie) + 5);
// Affichage des types
TextOut(xOrigin, yPos, 'Type(s) : ' + TypesStr);
Inc(yPos, TextHeight('Type(s) : ' + TypesStr) + 10);
// Sprite centré et redimensionné à 50% de la région intérieure
if Assigned(PSprite) then
begin
TargetW := (RegionRight - RegionLeft) div 2;
TargetH := (RegionBottom - RegionTop) div 2;
xPos := RegionLeft + ((RegionRight - RegionLeft) - TargetW) div 2;
DestRect := Rect(xPos, yPos, xPos + TargetW, yPos + TargetH);
SetStretchBltMode(Handle, HALFTONE);
SetBrushOrgEx(Handle, 0, 0, nil);
StretchDraw(DestRect, PSprite);
end;
// Préparation et affichage des statistiques en bas de la carte
Stats := Pokemon.GetValue<TJSONObject>('stats');
StatsText := Format('PV: %d ATK: %d DEF: %d VIT: %d',
[Stats.GetValue<Integer>('hp'), Stats.GetValue<Integer>('atk'),
Stats.GetValue<Integer>('def'), Stats.GetValue<Integer>('vit')]);
Font.Size := 10;
Font.Style := [];
// Calcul de la position Y pour coller le texte à la marge inférieure
StatsY := RegionBottom - Margin - TextHeight(StatsText);
TextOut(xOrigin, StatsY, StatsText);
end;
end;
{ -------------------------------------------------------------------------------
Téléchargement ou récupération en cache d’un sprite PNG par URL
------------------------------------------------------------------------------- }
function TForm1.DownloadIm(const URL: string): TPngImage;
var
Http: THTTPClient;
Response: IHTTPResponse;
Stream: TMemoryStream;
begin
// Retourner l’image du cache si elle existe
if FImageCache.TryGetValue(URL, Result) then
Exit;
// Sinon télécharger l’image
Http := THTTPClient.Create;
Stream := TMemoryStream.Create;
try
Response := Http.Get(URL, Stream);
if Response.StatusCode <> 200 then
raise Exception.CreateFmt('Erreur HTTP %d : %s', [Response.StatusCode, Response.StatusText]);
// Chargement en mémoire de l’image PNG
Stream.Position := 0;
Result := TPngImage.Create;
Result.LoadFromStream(Stream);
// Stockage dans le cache pour usages futurs
FImageCache.Add(URL, Result);
finally
Http.Free;
Stream.Free;
end;
end;
end. |
Partager