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