2011-04-29 10:26:37 +02:00
|
|
|
/*
|
|
|
|
* This software is licensed under the terms of the MIT-License
|
2017-02-11 04:52:08 +01:00
|
|
|
* See COPYING for further information.
|
2011-04-29 10:26:37 +02:00
|
|
|
* ---
|
2018-01-04 18:14:31 +01:00
|
|
|
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
|
|
|
|
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
|
2011-04-29 10:26:37 +02:00
|
|
|
*/
|
|
|
|
|
2017-11-25 20:45:11 +01:00
|
|
|
#include "taisei.h"
|
|
|
|
|
2011-04-29 10:26:37 +02:00
|
|
|
#include "item.h"
|
|
|
|
#include "global.h"
|
|
|
|
#include "list.h"
|
2017-12-13 20:05:12 +01:00
|
|
|
#include "stageobjects.h"
|
|
|
|
#include "objectpool_util.h"
|
2011-04-29 10:26:37 +02:00
|
|
|
|
2018-02-06 07:19:25 +01:00
|
|
|
static Sprite* item_sprite(ItemType type) {
|
2017-10-31 14:47:20 +01:00
|
|
|
static const char *const map[] = {
|
2018-02-06 07:19:25 +01:00
|
|
|
[Power] = "item/power",
|
|
|
|
[Point] = "item/point",
|
|
|
|
[Life] = "item/life",
|
|
|
|
[Bomb] = "item/bomb",
|
|
|
|
[LifeFrag] = "item/lifefrag",
|
|
|
|
[BombFrag] = "item/bombfrag",
|
|
|
|
[BPoint] = "item/bullet_point",
|
2017-10-31 14:47:20 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// int cast to silence a WTF warning
|
|
|
|
assert((int)type < sizeof(map)/sizeof(char*));
|
2018-02-06 07:19:25 +01:00
|
|
|
return get_sprite(map[type]);
|
2017-10-31 14:47:20 +01:00
|
|
|
}
|
|
|
|
|
2018-04-13 21:13:48 +02:00
|
|
|
static void ent_draw_item(EntityInterface *ent) {
|
|
|
|
Item *i = ENT_CAST(ent, Item);
|
|
|
|
|
|
|
|
Color c = rgba(1, 1, 1,
|
|
|
|
i->type == BPoint && !i->auto_collect
|
|
|
|
? clamp(2.0 - (global.frames - i->birthtime) / 60.0, 0.1, 1.0)
|
|
|
|
: 1.0
|
|
|
|
);
|
|
|
|
|
|
|
|
r_draw_sprite(&(SpriteParams) {
|
|
|
|
.sprite_ptr = item_sprite(i->type),
|
|
|
|
.pos = { creal(i->pos), cimag(i->pos) },
|
|
|
|
.color = c,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-10-31 14:47:20 +01:00
|
|
|
Item* create_item(complex pos, complex v, ItemType type) {
|
|
|
|
if((creal(pos) < 0 || creal(pos) > VIEWPORT_W)) {
|
|
|
|
// we need this because we clamp the item position to the viewport boundary during motion
|
|
|
|
// e.g. enemies that die offscreen shouldn't spawn any items inside the viewport
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-02-06 07:19:25 +01:00
|
|
|
// type = 1 + floor(Life * frand());
|
|
|
|
|
2017-12-24 07:16:25 +01:00
|
|
|
Item *i = (Item*)objpool_acquire(stage_object_pools.items);
|
2018-05-02 06:46:48 +02:00
|
|
|
list_append(&global.items, i);
|
2017-12-24 07:16:25 +01:00
|
|
|
|
2011-04-29 10:26:37 +02:00
|
|
|
i->pos = pos;
|
|
|
|
i->pos0 = pos;
|
|
|
|
i->v = v;
|
|
|
|
i->birthtime = global.frames;
|
|
|
|
i->auto_collect = 0;
|
|
|
|
i->type = type;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2018-04-13 21:13:48 +02:00
|
|
|
i->ent.draw_layer = LAYER_ITEM | i->type;
|
|
|
|
i->ent.draw_func = ent_draw_item;
|
|
|
|
ent_register(&i->ent, ENT_ITEM);
|
|
|
|
|
2011-05-13 19:03:02 +02:00
|
|
|
return i;
|
2011-04-29 10:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void delete_item(Item *item) {
|
2018-04-13 21:13:48 +02:00
|
|
|
ent_unregister(&item->ent);
|
2017-12-24 07:16:25 +01:00
|
|
|
objpool_release(stage_object_pools.items, (ObjectInterface*)list_unlink(&global.items, item));
|
2011-04-29 10:26:37 +02:00
|
|
|
}
|
|
|
|
|
2017-10-31 14:47:20 +01:00
|
|
|
Item* create_bpoint(complex pos) {
|
|
|
|
Item *i = create_item(pos, 0, BPoint);
|
|
|
|
|
|
|
|
if(i) {
|
2018-05-02 06:46:48 +02:00
|
|
|
PARTICLE(
|
|
|
|
.sprite = "flare",
|
|
|
|
.pos = pos, .timeout = 30,
|
|
|
|
.draw_rule = Fade
|
|
|
|
);
|
2017-10-31 14:47:20 +01:00
|
|
|
i->auto_collect = 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2012-08-10 22:08:51 +02:00
|
|
|
void delete_items(void) {
|
2017-12-13 20:05:12 +01:00
|
|
|
objpool_release_list(stage_object_pools.items, (List**)&global.items);
|
2011-04-29 10:26:37 +02:00
|
|
|
}
|
|
|
|
|
2018-05-16 19:55:03 +02:00
|
|
|
complex move_item(Item *i) {
|
2011-04-29 10:26:37 +02:00
|
|
|
int t = global.frames - i->birthtime;
|
2017-02-23 11:39:31 +01:00
|
|
|
complex lim = 0 + 2.0*I;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2018-05-16 19:55:03 +02:00
|
|
|
complex oldpos = i->pos;
|
|
|
|
|
2017-10-31 14:47:20 +01:00
|
|
|
if(i->auto_collect) {
|
2011-05-13 19:03:02 +02:00
|
|
|
i->pos -= (7+i->auto_collect)*cexp(I*carg(i->pos - global.plr.pos));
|
2017-10-31 14:47:20 +01:00
|
|
|
} else {
|
|
|
|
complex oldpos = i->pos;
|
2011-04-29 10:26:37 +02:00
|
|
|
i->pos = i->pos0 + log(t/5.0 + 1)*5*(i->v + lim) + lim*t;
|
2017-10-31 14:47:20 +01:00
|
|
|
|
|
|
|
complex v = i->pos - oldpos;
|
2018-02-06 07:19:25 +01:00
|
|
|
double half = item_sprite(i->type)->w/2.0;
|
2017-10-31 14:47:20 +01:00
|
|
|
bool over = false;
|
|
|
|
|
|
|
|
if((over = creal(i->pos) > VIEWPORT_W-half) || creal(i->pos) < half) {
|
|
|
|
complex normal = over ? -1 : 1;
|
|
|
|
v -= 2 * normal * (creal(normal)*creal(v));
|
|
|
|
v = 1.5*creal(v) - I*fabs(cimag(v));
|
|
|
|
|
|
|
|
i->pos = clamp(creal(i->pos), half, VIEWPORT_W-half) + I*cimag(i->pos);
|
|
|
|
i->v = v;
|
|
|
|
i->pos0 = i->pos;
|
|
|
|
i->birthtime = global.frames;
|
|
|
|
}
|
|
|
|
}
|
2018-05-16 19:55:03 +02:00
|
|
|
|
|
|
|
return i->pos - oldpos;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool item_out_of_bounds(Item *item) {
|
|
|
|
double margin = max(item_sprite(item->type)->w, item_sprite(item->type)->h);
|
|
|
|
|
|
|
|
return (
|
|
|
|
creal(item->pos) < -margin ||
|
|
|
|
creal(item->pos) > VIEWPORT_W + margin ||
|
|
|
|
cimag(item->pos) > VIEWPORT_H + margin
|
|
|
|
);
|
2011-04-29 10:26:37 +02:00
|
|
|
}
|
|
|
|
|
2012-08-10 22:08:51 +02:00
|
|
|
void process_items(void) {
|
2011-04-29 10:26:37 +02:00
|
|
|
Item *item = global.items, *del = NULL;
|
2018-05-02 08:08:32 +02:00
|
|
|
float r = player_property(&global.plr, PLR_PROP_COLLECT_RADIUS);
|
2018-05-16 19:55:03 +02:00
|
|
|
bool plr_alive = global.plr.deathtime <= global.frames && global.plr.deathtime == -1;
|
|
|
|
bool plr_bombing = global.frames - global.plr.recovery < 0;;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2011-04-29 10:26:37 +02:00
|
|
|
while(item != NULL) {
|
2017-03-21 11:09:32 +01:00
|
|
|
if((item->type == Power && global.plr.power >= PLR_MAX_POWER) ||
|
2017-02-10 22:26:46 +01:00
|
|
|
// just in case we ever have some weird spell that spawns those...
|
2018-05-16 19:55:03 +02:00
|
|
|
(global.stage->type == STAGE_SPELL && (item->type == Life || item->type == Bomb))
|
2017-02-10 22:26:46 +01:00
|
|
|
) {
|
2017-02-06 00:15:18 +01:00
|
|
|
item->type = Point;
|
|
|
|
}
|
|
|
|
|
2018-05-16 19:55:03 +02:00
|
|
|
if(plr_alive) {
|
|
|
|
if(
|
|
|
|
(cabs(global.plr.pos - item->pos) < r) ||
|
|
|
|
(cimag(global.plr.pos) < player_property(&global.plr, PLR_PROP_POC)) ||
|
|
|
|
plr_bombing
|
|
|
|
) {
|
2017-10-31 14:47:20 +01:00
|
|
|
item->auto_collect = 1;
|
|
|
|
}
|
2018-05-16 19:55:03 +02:00
|
|
|
} else if(item->auto_collect) {
|
|
|
|
item->auto_collect = 0;
|
|
|
|
item->pos0 = item->pos;
|
|
|
|
item->birthtime = global.frames;
|
|
|
|
item->v = -10*I + 5*nfrand();
|
2017-10-31 14:47:20 +01:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2018-05-16 19:55:03 +02:00
|
|
|
complex deltapos = move_item(item);
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2018-05-02 08:08:32 +02:00
|
|
|
int v = collision_item(item);
|
2011-04-29 10:26:37 +02:00
|
|
|
if(v == 1) {
|
|
|
|
switch(item->type) {
|
|
|
|
case Power:
|
2017-10-24 04:57:14 +02:00
|
|
|
player_set_power(&global.plr, global.plr.power + POWER_VALUE);
|
2017-03-24 20:42:56 +01:00
|
|
|
play_sound("item_generic");
|
2011-04-29 10:26:37 +02:00
|
|
|
break;
|
|
|
|
case Point:
|
2017-03-25 19:18:24 +01:00
|
|
|
player_add_points(&global.plr, 100);
|
2017-03-24 20:42:56 +01:00
|
|
|
play_sound("item_generic");
|
2011-04-29 10:26:37 +02:00
|
|
|
break;
|
2011-05-13 19:03:02 +02:00
|
|
|
case BPoint:
|
2017-03-25 19:18:24 +01:00
|
|
|
player_add_points(&global.plr, 1);
|
2017-03-24 20:42:56 +01:00
|
|
|
play_sound("item_generic");
|
2011-05-13 19:03:02 +02:00
|
|
|
break;
|
2011-04-29 10:26:37 +02:00
|
|
|
case Life:
|
2017-03-21 11:09:32 +01:00
|
|
|
player_add_lives(&global.plr, 1);
|
2011-04-29 10:26:37 +02:00
|
|
|
break;
|
|
|
|
case Bomb:
|
2017-03-21 11:09:32 +01:00
|
|
|
player_add_bombs(&global.plr, 1);
|
2011-04-29 10:26:37 +02:00
|
|
|
break;
|
2017-03-24 20:42:56 +01:00
|
|
|
case LifeFrag:
|
|
|
|
player_add_life_fragments(&global.plr, 1);
|
|
|
|
break;
|
|
|
|
case BombFrag:
|
|
|
|
player_add_bomb_fragments(&global.plr, 1);
|
|
|
|
break;
|
2017-03-21 11:09:32 +01:00
|
|
|
}
|
2011-04-29 10:26:37 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2018-05-16 19:55:03 +02:00
|
|
|
if(v == 1 || (cimag(deltapos) > 0 && item_out_of_bounds(item))) {
|
2011-04-29 10:26:37 +02:00
|
|
|
del = item;
|
|
|
|
item = item->next;
|
|
|
|
delete_item(del);
|
|
|
|
} else {
|
|
|
|
item = item->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int collision_item(Item *i) {
|
2011-06-28 19:23:02 +02:00
|
|
|
if(cabs(global.plr.pos - i->pos) < 10)
|
2011-04-29 10:26:37 +02:00
|
|
|
return 1;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2011-04-29 10:26:37 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2011-06-27 13:36:35 +02:00
|
|
|
|
2017-03-19 04:09:41 +01:00
|
|
|
void spawn_item(complex pos, ItemType type) {
|
2012-08-04 03:25:43 +02:00
|
|
|
tsrand_fill(2);
|
2018-05-16 20:08:21 +02:00
|
|
|
create_item(pos, (12 + 6 * afrand(0)) * (cexp(I*(3*M_PI/2 + anfrand(1)*M_PI/11))) - 3*I, type);
|
2011-06-28 19:23:02 +02:00
|
|
|
}
|
|
|
|
|
2017-03-25 07:46:20 +01:00
|
|
|
void spawn_items(complex pos, ...) {
|
2017-03-24 21:07:19 +01:00
|
|
|
va_list args;
|
2017-03-25 07:46:20 +01:00
|
|
|
va_start(args, pos);
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2017-03-24 21:07:19 +01:00
|
|
|
ItemType type;
|
2017-11-11 18:32:22 +01:00
|
|
|
|
|
|
|
while((type = va_arg(args, ItemType))) {
|
2017-03-24 21:07:19 +01:00
|
|
|
int num = va_arg(args, int);
|
|
|
|
for(int i = 0; i < num; ++i) {
|
|
|
|
spawn_item(pos, type);
|
|
|
|
}
|
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2017-03-24 21:07:19 +01:00
|
|
|
va_end(args);
|
2012-07-27 19:11:45 +02:00
|
|
|
}
|
2017-03-11 04:41:57 +01:00
|
|
|
|
|
|
|
void items_preload(void) {
|
2018-02-06 07:19:25 +01:00
|
|
|
preload_resources(RES_SPRITE, RESF_PERMANENT,
|
|
|
|
"item/power",
|
|
|
|
"item/point",
|
|
|
|
"item/life",
|
|
|
|
"item/bomb",
|
|
|
|
"item/lifefrag",
|
|
|
|
"item/bombfrag",
|
|
|
|
"item/bullet_point",
|
2017-03-11 04:41:57 +01:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
preload_resources(RES_SFX, RESF_OPTIONAL,
|
|
|
|
"item_generic",
|
|
|
|
NULL);
|
|
|
|
}
|