]> git.mxchange.org Git - quix0rs-blobwars.git/blob - src/items.cpp
Don't link pak tool with SDL.
[quix0rs-blobwars.git] / src / items.cpp
1 /*
2 Copyright (C) 2004-2011 Parallel Realities
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "items.h"
22
23 void addItem(int itemType, const char *name, int x, int y, const char *spriteName, int health, int value, int flags, bool randomMovement)
24 {
25         Entity *item = new Entity();
26
27         item->id = itemType;
28         item->setName(name);
29         item->place(x, y);
30         item->setSprites(graphics.getSprite(spriteName, true), graphics.getSprite(spriteName, true), graphics.getSprite(spriteName, true));
31         item->health = health;
32         item->value = value;
33         item->flags = ENT_INANIMATE + ENT_BOUNCES + ENT_COLLECTABLE;
34
35         // raise items taller than the enemy
36         int x1 = x >> BRICKSHIFT;
37         int x2 = (x + item->width - 1) >> BRICKSHIFT;
38         int y2 = (y + item->height - 1) >> BRICKSHIFT;
39         if ((map.isSolid(x1, y2)) || (map.isSolid(x2, y2)))
40         {
41                 item->y = (y2 * BRICKSIZE) - item->height;
42         }
43
44         if (randomMovement)
45         {
46                 item->setRandomVelocity();
47         }
48
49         item->health += Math::prand() % 120;
50
51         Math::addBit(&item->flags, flags);
52         
53         if (item->id == ITEM_MISC_INVISIBLE)
54         {
55                 if ((gameData.completedWorld) || (game.skill == 3))
56                 {
57                         item->id = ITEM_MISC_NOSHOW;
58                 }
59         }
60
61         map.addItem(item);
62 }
63
64 void dropBossItems(int x, int y)
65 {
66         if ((Math::prand() % 5) > 0)
67         {
68                 return;
69         }
70         
71         int r = Math::rrand(ITEM_PISTOL, ITEM_DOUBLECHERRY);
72         
73         if (player.environment == ENV_WATER)
74         {
75                 r = Math::rrand(ITEM_CHERRY, ITEM_DOUBLECHERRY);
76         }
77         
78         if ((Math::prand() % 10) == 0)
79         {
80                 r = ITEM_TRIPLECHERRY;
81         }
82         
83         addItem(defItem[r].id, defItem[r].name, x, y, defItem[r].sprite[0]->name, 240, defItem[r].value, ENT_DYING, true);
84 }
85
86 void dropRandomItems(int x, int y)
87 {
88         int mapX = x >> BRICKSHIFT;
89         int mapY = y >> BRICKSHIFT;
90         
91         if (map.isSolid(mapX, mapY))
92         {
93                 return;
94         }
95         
96         if (map.isBossMission)
97         {
98                 dropBossItems(x, y);
99                 return;
100         }
101         
102         int amount = Math::rrand(1, 5);
103         int r = Math::rrand(ITEM_POINTS, ITEM_POINTS7);
104
105         int cherryChance = 10 + (10 * game.skill);
106
107         for (int i = 0 ; i < amount ; i++)
108         {
109                 if ((Math::prand() % 8) == 0)
110                 {
111                         r = Math::rrand(ITEM_PISTOL, ITEM_SPREAD);
112                 }
113
114                 if ((Math::prand() % 13) == 0)
115                 {
116                         switch (Math::prand() % cherryChance)
117                         {
118                                 case 0:
119                                         r = ITEM_TRIPLECHERRY;
120                                         break;
121                                 case 1:
122                                 case 2:
123                                 case 3:
124                                 case 4:
125                                 case 5:
126                                         r = ITEM_DOUBLECHERRY;
127                                         break;
128                                 default:
129                                         r = ITEM_CHERRY;
130                                         break;
131                         }
132                 }
133
134                 addItem(defItem[r].id, defItem[r].name, x, y, defItem[r].sprite[0]->name, 240, defItem[r].value, ENT_DYING, true);
135
136                 r = Math::rrand(ITEM_POINTS, ITEM_POINTS7);
137         }
138 }
139
140 void dropHelperItems(int x, int y)
141 {
142         int amount = Math::rrand(1, 5);
143         int r;
144
145         for (int i = 0 ; i < amount ; i++)
146         {
147                 r = Math::rrand(ITEM_PISTOL, ITEM_TRIPLECHERRY);
148
149                 addItem(defItem[r].id, defItem[r].name, x, y, defItem[r].sprite[0]->name, 240, defItem[r].value, ENT_DYING, true);
150         }
151 }
152
153 void stealCrystal()
154 {
155         Entity *item = (Entity*)map.itemList.getHead();
156         
157         Objective *objective = (Objective*)map.objectiveList.getHead();
158
159         while (objective->next != NULL)
160         {
161                 objective = (Objective*)objective->next;
162                 objective->completed = true;
163                 objective->currentValue = objective->targetValue;
164         }
165
166         while (item->next != NULL)
167         {
168                 item = (Entity*)item->next;
169
170                 if (strcmp(item->name, "Reality Crystal") == 0)
171                 {
172                         item->dx = 0;
173                         item->dy = 0;
174                         Math::addBit(&item->flags, ENT_TELEPORTING);
175                         addTeleportParticles(item->x + (item->width / 2), item->y + (item->height / 2), 50, SND_TELEPORT3);
176                         return;
177                 }
178         }
179 }
180
181 /*
182 We have to do this to avoid items being permanently lost.
183 To ensure we drop them in a safe place we put them in the player's
184 last check point position...
185 */
186 void dropCarriedItems()
187 {
188         Entity *item = (Entity*)map.itemList.getHead();
189
190         while (item->next != NULL)
191         {
192                 item = (Entity*)item->next;
193
194                 if (item->owner != &player)
195                         continue;
196
197                 Math::removeBit(&item->flags, ENT_DYING);
198                 
199                 item->owner = item;
200                 item->health = 240;
201                 item->dx = 0;
202                 item->dy = -2;
203                 item->x = game.checkPointX + Math::rrand(0, 6);
204                 item->y = game.checkPointY;
205                 item->flags = ENT_INANIMATE + ENT_BOUNCES + ENT_NOCOLLISIONS;
206         }
207 }
208
209 void pickUpItem(Entity *item)
210 {
211         char string[100];
212
213         if (item->flags & ENT_DYING)
214         {
215                 game.totalBonusesCollected++;
216         }
217         else if (item->id >= ITEM_MISC)
218         {
219                 game.currentMissionItemsCollected++;
220                 map.foundItems++;
221         }
222
223         item->health = 10;
224         item->flags = ENT_WEIGHTLESS + ENT_DYING + ENT_NOCOLLISIONS;
225         item->dx = 0;
226         item->dy = -5;
227
228         switch (item->id)
229         {
230                 case ITEM_PISTOL:
231                 case ITEM_MACHINEGUN:
232                 case ITEM_LASER:
233                 case ITEM_GRENADES:
234                 case ITEM_SPREAD:
235                         player.currentWeapon = &weapon[item->id];
236                         game.currentWeapon = item->id;
237                         audio.playSound(SND_GETWEAPON, CH_ITEM);
238                         break;
239                 case ITEM_POINTS:
240                 case ITEM_POINTS2:
241                 case ITEM_POINTS3:
242                 case ITEM_POINTS4:
243                 case ITEM_POINTS5:
244                 case ITEM_POINTS6:
245                 case ITEM_POINTS7:
246                         addPlayerScore(item->value);
247                         audio.playSound(SND_ITEM, CH_ITEM);
248                         break;
249                 case ITEM_CHERRY:
250                 case ITEM_DOUBLECHERRY:
251                 case ITEM_TRIPLECHERRY:
252                         Math::limitInt(&(player.health += item->value), 0, MAX_HEALTH);
253                         audio.playSound(SND_GULP + (Math::prand() % 2), CH_ITEM);
254                         break;
255                 case ITEM_MISC:
256                         item->owner = &player;
257                 case ITEM_MISC_NOSHOW:
258                         audio.playSound(SND_ITEM, CH_ITEM);
259                         break;
260         }
261
262         if ((item->id < ITEM_POINTS) || (item->id > ITEM_POINTS7))
263         {
264                 /*
265                         oh yeah, right... because "Picked up a Ancient Cog" is really good English, isn't it? It's almost
266                         as bad as Medal of Honor: Frontline where it said "Picked up 3 Stick Grenade(s)"... Would it really
267                         have taken that much effort to pass the item number to a function that worked out some basic grammar
268                         for how many items had been picked up??! Yeah... and EA expect us to pay �45 for that! Probably the
269                         worst bit about that game was that it was just fecking crap anyway!
270                 */
271                 switch (item->name[0])
272                 {
273                         case 'A':
274                         case 'a':
275                         case 'I':
276                         case 'i':
277                         case 'O':
278                         case 'o':
279                         case 'U':
280                         case 'u':
281                                 snprintf(string, sizeof string, _("Picked up an %s"), item->name);
282                                 break;
283                         default:
284                                 snprintf(string, sizeof string, _("Picked up a %s"), item->name);
285                                 break;
286                 }
287
288                 if (!map.isBossMission)
289                         engine.setInfoMessage(string, 0, INFO_NORMAL);
290
291                 checkObjectives(item->name, true);
292                 
293                 if (strcmp(item->name, "LRTS") == 0)
294                 {
295                         presentPlayerMedal("LRTS_PART");
296                 }
297         }
298 }
299
300 bool carryingItem(const char *name)
301 {
302         Entity *item = (Entity*)map.itemList.getHead();
303
304         while (item->next != NULL)
305         {
306                 item = (Entity*)item->next;
307
308                 if (item->owner != &player)
309                         continue;
310
311                 if (strcmp(item->name, name) == 0)
312                 {
313                         item->owner = item;
314                         item->health = -999;
315                         return true;
316                 }
317         }
318
319         return false;
320 }
321
322 void showCarriedItems()
323 {
324         int x = 0;
325         int y = 210;
326         int itemCount = 0;
327
328         Entity *item = (Entity*)map.itemList.getHead();
329
330         while (item->next != NULL)
331         {
332                 item = (Entity*)item->next;
333
334                 if (item->owner != &player)
335                         continue;
336
337                 x += (item->width + 8);
338                 itemCount++;
339         }
340
341         x = ((640 - x) / 2);
342
343         item = (Entity*)map.itemList.getHead();
344
345         while (item->next != NULL)
346         {
347                 item = (Entity*)item->next;
348
349                 if (item->owner != &player)
350                         continue;
351
352                 graphics.blit(item->getFaceImage(), x, y, graphics.screen, false);
353
354                 x += (item->width + 8);
355         }
356
357         if (itemCount == 0)
358                 graphics.drawString(_("Not carrying anything"), 320, 210, TXT_CENTERED, graphics.screen);
359 }
360
361 void doItems()
362 {
363         Entity *item = (Entity*)map.itemList.getHead();
364         Entity *previous = item;
365
366         int x, y;
367
368         while (item->next != NULL)
369         {
370                 previous = item;
371                 
372                 item = (Entity*)item->next;
373                 
374                 if (item->id == ITEM_MISC_INVISIBLE)
375                 {
376                         continue;
377                 }
378
379                 x = (int)(item->x - engine.playerPosX);
380                 y = (int)(item->y - engine.playerPosY);
381
382                 item->think();
383
384                 if (item->flags & ENT_TELEPORTING)
385                 {
386                         moveEntity(item);
387                 }
388                 else if ((abs(x) <= 800) && (abs(y) <= 600) && (item->owner == item))
389                 {
390                         // Gravity
391                         if (!(item->flags & ENT_WEIGHTLESS))
392                                 item->applyGravity();
393                                 
394                         if (!map.isIceLevel)
395                                 item->dx *= 0.98;
396
397                         moveEntity(item);
398
399                         if ((item->health >= 60) || ((engine.getFrameLoop() % 3) == 0))
400                         {
401                                 graphics.blit(item->getFaceImage(), x, y, graphics.screen, false);
402                         }
403
404                         item->animate();
405
406                         if ((player.health > 0) && (!(player.flags & ENT_TELEPORTING)))
407                         {
408                                 if (Collision::collision(&player, item))
409                                 {
410                                         if (item->flags & ENT_COLLECTABLE)
411                                         {
412                                                 pickUpItem(item);
413                                         }
414                                 }
415                         }
416                 }
417
418                 if ((item->health <= 0) && (item->owner != &player))
419                 {
420                         map.itemList.remove(previous, item);
421                         item = previous;
422                 }
423         }
424 }
425
426 void loadDefItems()
427 {
428         if (!engine.loadData("data/defItems"))
429                 graphics.showErrorAndExit("Couldn't load item definitions file (%s)", "data/defItems");
430
431         char *token = strtok((char*)engine.dataBuffer, "\n");
432
433         int id;
434         char name[50];
435         char sprite[100];
436         int value;
437
438         while (true)
439         {
440                 if (strcmp(token, "@EOF@") == 0)
441                         break;
442
443                 sscanf(token, "%d %*c %[^\"] %*c %s %d", &id, name, sprite, &value);
444
445                 defItem[id].id = id;
446                 defItem[id].setName(name);
447                 defItem[id].setSprites(graphics.getSprite(sprite, true), graphics.getSprite(sprite, true), graphics.getSprite(sprite, true));
448                 defItem[id].value = value;
449
450                 token = strtok(NULL, "\n");
451         }
452 }