2011-05-08 13:48:25 +02:00
|
|
|
/*
|
2019-08-03 19:43:48 +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-05-08 13:48:25 +02:00
|
|
|
* ---
|
2019-01-23 21:10:43 +01:00
|
|
|
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
2019-07-03 20:00:56 +02:00
|
|
|
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
2011-05-08 13:48:25 +02:00
|
|
|
*/
|
|
|
|
|
2017-11-25 20:45:11 +01:00
|
|
|
#include "taisei.h"
|
|
|
|
|
2011-05-08 13:48:25 +02:00
|
|
|
#include "dialog.h"
|
|
|
|
#include "global.h"
|
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
Dialog *dialog_create(void) {
|
2019-07-03 19:50:43 +02:00
|
|
|
Dialog *d = calloc(1, sizeof(Dialog));
|
2019-07-08 02:47:50 +02:00
|
|
|
d->page_time = global.frames;
|
|
|
|
d->birthtime = global.frames;
|
|
|
|
return d;
|
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
void dialog_set_base(Dialog *d, DialogSide side, const char *sprite) {
|
|
|
|
d->spr_base[side] = sprite ? get_sprite(sprite) : NULL;
|
|
|
|
d->valid_composites &= ~(1 << side);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dialog_set_base_p(Dialog *d, DialogSide side, Sprite *sprite) {
|
|
|
|
d->spr_base[side] = sprite;
|
|
|
|
d->valid_composites &= ~(1 << side);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dialog_set_face(Dialog *d, DialogSide side, const char *sprite) {
|
|
|
|
d->spr_face[side] = sprite ? get_sprite(sprite) : NULL;
|
|
|
|
d->valid_composites &= ~(1 << side);
|
2019-07-08 02:47:50 +02:00
|
|
|
}
|
2018-02-06 07:19:25 +01:00
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
void dialog_set_face_p(Dialog *d, DialogSide side, Sprite *sprite) {
|
|
|
|
d->spr_face[side] = sprite;
|
|
|
|
d->valid_composites &= ~(1 << side);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dialog_set_char(Dialog *d, DialogSide side, const char *char_name, const char *char_face, const char *char_variant) {
|
|
|
|
size_t name_len = strlen(char_name);
|
|
|
|
size_t face_len = strlen(char_face);
|
|
|
|
size_t variant_len;
|
|
|
|
|
|
|
|
size_t lenfull_base = sizeof("dialog/") + name_len - 1;
|
|
|
|
size_t lenfull_face = lenfull_base + sizeof("_face_") + face_len - 1;
|
|
|
|
|
|
|
|
if(char_variant) {
|
|
|
|
variant_len = strlen(char_variant);
|
|
|
|
lenfull_base += sizeof("_variant_") + variant_len - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buf[imax(lenfull_base, lenfull_face) + 1];
|
|
|
|
char *dst = buf;
|
|
|
|
char *variant_dst;
|
|
|
|
dst = memcpy(dst, "dialog/", sizeof("dialog/") - 1);
|
|
|
|
dst = memcpy(dst + sizeof("dialog/") - 1, char_name, name_len + 1);
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
if(char_variant) {
|
|
|
|
variant_dst = dst + name_len;
|
2019-07-08 02:47:50 +02:00
|
|
|
} else {
|
2019-08-22 21:43:34 +02:00
|
|
|
d->spr_base[side] = get_sprite(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
dst = memcpy(dst + name_len, "_face_", sizeof("_face_") - 1);
|
|
|
|
dst = memcpy(dst + sizeof("_face_") - 1, char_face, face_len + 1);
|
|
|
|
d->spr_face[side] = get_sprite(buf);
|
|
|
|
|
|
|
|
if(!char_variant) {
|
|
|
|
return;
|
2019-07-08 02:47:50 +02:00
|
|
|
}
|
2019-08-22 21:43:34 +02:00
|
|
|
|
|
|
|
dst = memcpy(variant_dst, "_variant_", sizeof("_variant_") - 1);
|
|
|
|
dst = memcpy(dst + sizeof("_variant_") - 1, char_variant, variant_len + 1);
|
|
|
|
d->spr_base[side] = get_sprite(buf);
|
|
|
|
|
|
|
|
d->valid_composites &= ~(1 << side);
|
2011-05-08 13:48:25 +02:00
|
|
|
}
|
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
static int message_index(Dialog *d, int offset) {
|
|
|
|
int idx = d->pos + offset;
|
|
|
|
|
|
|
|
if(idx >= d->count) {
|
|
|
|
idx = d->count - 1;
|
|
|
|
}
|
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
while(
|
|
|
|
idx >= 0 &&
|
|
|
|
d->actions[idx].type != DIALOG_MSG_LEFT &&
|
|
|
|
d->actions[idx].type != DIALOG_MSG_RIGHT
|
|
|
|
) {
|
2019-07-03 19:50:43 +02:00
|
|
|
--idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
DialogAction *dialog_add_action(Dialog *d, const DialogAction *action) {
|
2019-07-08 02:47:50 +02:00
|
|
|
d->actions = realloc(d->actions, (++d->count)*sizeof(DialogAction));
|
2019-08-22 21:43:34 +02:00
|
|
|
d->actions[d->count - 1] = *action;
|
|
|
|
return d->actions + d->count - 1;
|
2011-05-08 13:48:25 +02:00
|
|
|
}
|
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
static void update_composite(Dialog *d, DialogSide side) {
|
|
|
|
if(d->valid_composites & (1 << side)) {
|
|
|
|
return;
|
2019-07-03 19:50:43 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
Sprite *composite = d->spr_composite + side;
|
|
|
|
Sprite *spr_base = d->spr_base[side];
|
|
|
|
Sprite *spr_face = d->spr_face[side];
|
|
|
|
|
|
|
|
if(composite->tex != NULL) {
|
|
|
|
r_texture_destroy(composite->tex);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(spr_base != NULL) {
|
|
|
|
assert(spr_face != NULL);
|
|
|
|
render_character_portrait(spr_base, spr_face, composite);
|
|
|
|
} else {
|
|
|
|
composite->tex = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
d->valid_composites |= (1 << side);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void update_composites(Dialog *d) {
|
|
|
|
update_composite(d, DIALOG_LEFT);
|
|
|
|
update_composite(d, DIALOG_RIGHT);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dialog_destroy(Dialog *d) {
|
|
|
|
memset(&d->spr_base, 0, sizeof(d->spr_base));
|
|
|
|
memset(&d->spr_face, 0, sizeof(d->spr_face));
|
|
|
|
d->valid_composites = 0;
|
|
|
|
update_composites(d);
|
2019-07-08 02:47:50 +02:00
|
|
|
free(d->actions);
|
2011-05-08 13:48:25 +02:00
|
|
|
free(d);
|
|
|
|
}
|
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
void dialog_draw(Dialog *dialog) {
|
2019-07-03 19:50:43 +02:00
|
|
|
if(dialog == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
float o = dialog->opacity;
|
|
|
|
|
|
|
|
if(o == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
update_composites(dialog);
|
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
r_state_push();
|
|
|
|
r_state_push();
|
|
|
|
r_shader("sprite_default");
|
|
|
|
|
|
|
|
r_mat_push();
|
|
|
|
r_mat_translate(VIEWPORT_X, 0, 0);
|
|
|
|
|
|
|
|
const double dialog_width = VIEWPORT_W * 1.2;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_push();
|
2019-07-03 19:50:43 +02:00
|
|
|
r_mat_translate(dialog_width/2.0, 64, 0);
|
|
|
|
|
|
|
|
int cur_idx = message_index(dialog, 0);
|
|
|
|
int pre_idx = message_index(dialog, -1);
|
|
|
|
|
|
|
|
assume(cur_idx >= 0);
|
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
int cur_side = dialog->actions[cur_idx].type;
|
|
|
|
int pre_side = pre_idx >= 0 ? dialog->actions[pre_idx].type : 2;
|
2019-07-03 19:50:43 +02:00
|
|
|
|
|
|
|
Color clr = { 0 };
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
const float page_time = 10;
|
|
|
|
float page_alpha = min(global.frames - (dialog->page_time), page_time) / page_time;
|
|
|
|
|
|
|
|
const float page_text_time = 60;
|
|
|
|
float page_text_alpha = min(global.frames - dialog->page_time, page_text_time) / page_text_time;
|
|
|
|
|
|
|
|
int loop_start = 1;
|
|
|
|
int loop_incr = 1;
|
|
|
|
|
|
|
|
if(cur_side == 0) {
|
|
|
|
loop_start = 1;
|
|
|
|
loop_incr = -1;
|
|
|
|
} else {
|
|
|
|
loop_start = 0;
|
|
|
|
loop_incr = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int i = loop_start; i < 2 && i >= 0; i += loop_incr) {
|
2019-08-22 21:43:34 +02:00
|
|
|
Sprite *portrait = dialog->spr_composite + i;
|
2019-07-08 02:47:50 +02:00
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
if(portrait->tex == NULL) {
|
2019-07-08 02:47:50 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
float portrait_w = sprite_padded_width(portrait);
|
|
|
|
float portrait_h = sprite_padded_height(portrait);
|
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_push();
|
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
if(i == DIALOG_MSG_LEFT) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_cull(CULL_FRONT);
|
|
|
|
r_mat_scale(-1, 1, 1);
|
|
|
|
} else {
|
|
|
|
r_cull(CULL_BACK);
|
2011-05-08 13:48:25 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
if(o < 1) {
|
|
|
|
r_mat_translate(120 * (1 - o), 0, 0);
|
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
float dir = (1 - 2 * (i == cur_side));
|
|
|
|
float ofs = 10 * dir;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
if(page_alpha < 10 && ((i != pre_side && i == cur_side) || (i == pre_side && i != cur_side))) {
|
|
|
|
r_mat_translate(ofs * page_alpha, ofs * page_alpha, 0);
|
2019-08-22 21:43:34 +02:00
|
|
|
float brightness = min(1.0 - 0.5 * page_alpha * dir, 1);
|
2019-07-03 19:50:43 +02:00
|
|
|
clr.r = clr.g = clr.b = brightness;
|
|
|
|
clr.a = 1;
|
2011-05-08 13:48:25 +02:00
|
|
|
} else {
|
2019-07-03 19:50:43 +02:00
|
|
|
r_mat_translate(ofs, ofs, 0);
|
2019-08-22 21:43:34 +02:00
|
|
|
clr = *RGB(1 - (dir > 0) * 0.5, 1 - (dir > 0) * 0.5, 1 - (dir > 0) * 0.5);
|
2011-05-08 13:48:25 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
color_mul_scalar(&clr, o);
|
2019-07-08 02:47:50 +02:00
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
r_draw_sprite(&(SpriteParams) {
|
|
|
|
.blend = BLEND_PREMUL_ALPHA,
|
|
|
|
.color = &clr,
|
|
|
|
.pos.x = (dialog_width - portrait_w) / 2 + 32,
|
|
|
|
.pos.y = VIEWPORT_H - portrait_h / 2,
|
|
|
|
.sprite_ptr = portrait,
|
|
|
|
});
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
r_mat_pop();
|
2011-05-08 13:48:25 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_pop();
|
2019-07-03 19:50:43 +02:00
|
|
|
r_state_pop();
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
o *= smooth(clamp((global.frames - dialog->birthtime - 10) / 30.0, 0, 1));
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
FloatRect dialog_bg_rect = {
|
|
|
|
.extent = { VIEWPORT_W-40, 110 },
|
|
|
|
.offset = { VIEWPORT_W/2, VIEWPORT_H-55 },
|
|
|
|
};
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
r_mat_push();
|
|
|
|
if(o < 1) {
|
|
|
|
r_mat_translate(0, 100 * (1 - o), 0);
|
|
|
|
}
|
|
|
|
r_color4(0, 0, 0, 0.8 * o);
|
|
|
|
r_mat_push();
|
|
|
|
r_mat_translate(dialog_bg_rect.x, dialog_bg_rect.y, 0);
|
|
|
|
r_mat_scale(dialog_bg_rect.w, dialog_bg_rect.h, 1);
|
2018-04-12 16:08:48 +02:00
|
|
|
r_shader_standard_notex();
|
|
|
|
r_draw_quad();
|
|
|
|
r_mat_pop();
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
Font *font = get_font("standard");
|
2017-10-23 12:10:40 +02:00
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
r_mat_mode(MM_TEXTURE);
|
|
|
|
r_mat_push();
|
|
|
|
// r_mat_scale(2, 0.2, 0);
|
|
|
|
// r_mat_translate(0, -global.frames/page_text_time, 0);
|
|
|
|
r_mat_mode(MM_MODELVIEW);
|
|
|
|
|
|
|
|
dialog_bg_rect.w = VIEWPORT_W * 0.86;
|
|
|
|
dialog_bg_rect.x -= dialog_bg_rect.w * 0.5;
|
|
|
|
dialog_bg_rect.y -= dialog_bg_rect.h * 0.5;
|
|
|
|
// dialog_bg_rect.h = dialog_bg_rect.w;
|
|
|
|
|
|
|
|
if(pre_idx >= 0 && page_text_alpha < 1) {
|
2019-07-08 02:47:50 +02:00
|
|
|
if(pre_side == DIALOG_MSG_RIGHT) {
|
2019-07-03 19:50:43 +02:00
|
|
|
clr = *RGB(0.6, 0.6, 1.0);
|
|
|
|
} else {
|
|
|
|
clr = *RGB(1.0, 1.0, 1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
color_mul_scalar(&clr, o);
|
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
text_draw_wrapped(dialog->actions[pre_idx].data, VIEWPORT_W * 0.86, &(TextParams) {
|
2019-07-03 19:50:43 +02:00
|
|
|
.shader = "text_dialog",
|
|
|
|
.aux_textures = { get_tex("cell_noise") },
|
|
|
|
.shader_params = &(ShaderCustomParams) {{ o * (1.0 - (0.2 + 0.8 * page_text_alpha)), 1 }},
|
|
|
|
.color = &clr,
|
|
|
|
.pos = { VIEWPORT_W/2, VIEWPORT_H-110 + font_get_lineskip(font) },
|
|
|
|
.align = ALIGN_CENTER,
|
|
|
|
.font_ptr = font,
|
|
|
|
.overlay_projection = &dialog_bg_rect,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
if(cur_side == DIALOG_MSG_RIGHT) {
|
2019-07-03 19:50:43 +02:00
|
|
|
clr = *RGB(0.6, 0.6, 1.0);
|
|
|
|
} else {
|
|
|
|
clr = *RGB(1.0, 1.0, 1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
color_mul_scalar(&clr, o);
|
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
text_draw_wrapped(dialog->actions[cur_idx].data, VIEWPORT_W * 0.86, &(TextParams) {
|
2019-07-03 19:50:43 +02:00
|
|
|
.shader = "text_dialog",
|
|
|
|
.aux_textures = { get_tex("cell_noise") },
|
|
|
|
.shader_params = &(ShaderCustomParams) {{ o * page_text_alpha, 0 }},
|
|
|
|
.color = &clr,
|
|
|
|
.pos = { VIEWPORT_W/2, VIEWPORT_H-110 + font_get_lineskip(font) },
|
|
|
|
.align = ALIGN_CENTER,
|
|
|
|
.font_ptr = font,
|
|
|
|
.overlay_projection = &dialog_bg_rect,
|
2018-06-29 23:36:51 +02:00
|
|
|
});
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
r_mat_mode(MM_TEXTURE);
|
|
|
|
r_mat_pop();
|
|
|
|
r_mat_mode(MM_MODELVIEW);
|
2018-04-12 16:08:48 +02:00
|
|
|
|
|
|
|
r_mat_pop();
|
2019-07-03 19:50:43 +02:00
|
|
|
r_mat_pop();
|
|
|
|
r_state_pop();
|
2011-05-08 13:48:25 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
bool dialog_page(Dialog **pdialog) {
|
|
|
|
Dialog *d = *pdialog;
|
|
|
|
|
|
|
|
if(!d || d->pos >= d->count) {
|
Give projectiles EVENT_BIRTH and call them with t==0 more consistently
The only case of t==0 being skipped is if the projectile was somehow
created after the projectile processing loop for this frame has been
finished. Currently that is only possible if a particle spawns a
non-particle projectile, which, ideally, should never happen. The same
problem exists with other types of entities. For example, if you have a
funny projectile rule that spawns an Enemy, that Enemy will have the 0th
frame skipped. One way to fix this is to maintain a list of all entities
in the game and process them all in a single loop, where newly spawned
entities would be appended to the tail of the list. This is also
probably the only good way to fix this, too.
Handling of all projectile events has been made mandatory to facilitate
easier debugging of subtle and/or hard to track bugs. If t<0, then the
projectile rule MUST return ACTION_ACK, signifying that it acknowledged
the event and handled it appropriately. Otherwise, it SHOULD return
ACTION_NONE or ACTION_DESTROY. In a perfect world, those just wouldn't
be conflated in the same function with update logic.
I've also cleaned up the stage_logic routine a bit. Moved most of the
dialog handling into dialog.c and gave higher priority to boss
processing. The later is currently necessary to let boss-spawned
projectiles and particles to get called with t==0.
2018-05-16 01:38:47 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
int to = d->actions[d->pos].timeout;
|
2017-11-12 18:16:15 +01:00
|
|
|
|
|
|
|
if(to && to > global.frames) {
|
2019-08-22 21:43:34 +02:00
|
|
|
assert(d->actions[d->pos].type == DIALOG_MSG_LEFT || d->actions[d->pos].type == DIALOG_MSG_RIGHT);
|
2017-11-12 18:16:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
DialogAction *a = d->actions + d->pos;
|
|
|
|
d->pos++;
|
|
|
|
d->page_time = global.frames;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
if(d->pos >= d->count) {
|
2017-11-12 18:16:15 +01:00
|
|
|
// XXX: maybe this can be handled elsewhere?
|
2012-08-05 21:35:49 +02:00
|
|
|
if(!global.boss)
|
|
|
|
global.timer++;
|
2019-08-22 21:43:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
switch(a->type) {
|
|
|
|
case DIALOG_SET_BGM:
|
|
|
|
stage_start_bgm(a->data);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DIALOG_SET_FACE_RIGHT:
|
|
|
|
case DIALOG_SET_FACE_LEFT: {
|
|
|
|
DialogSide side = (
|
|
|
|
a->type == DIALOG_SET_FACE_RIGHT
|
|
|
|
? DIALOG_RIGHT
|
|
|
|
: DIALOG_LEFT
|
|
|
|
);
|
|
|
|
|
|
|
|
dialog_set_face(d, side, a->data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2011-05-08 13:48:25 +02:00
|
|
|
}
|
2017-11-12 18:16:15 +01:00
|
|
|
|
|
|
|
return true;
|
2012-08-05 21:35:49 +02:00
|
|
|
}
|
Give projectiles EVENT_BIRTH and call them with t==0 more consistently
The only case of t==0 being skipped is if the projectile was somehow
created after the projectile processing loop for this frame has been
finished. Currently that is only possible if a particle spawns a
non-particle projectile, which, ideally, should never happen. The same
problem exists with other types of entities. For example, if you have a
funny projectile rule that spawns an Enemy, that Enemy will have the 0th
frame skipped. One way to fix this is to maintain a list of all entities
in the game and process them all in a single loop, where newly spawned
entities would be appended to the tail of the list. This is also
probably the only good way to fix this, too.
Handling of all projectile events has been made mandatory to facilitate
easier debugging of subtle and/or hard to track bugs. If t<0, then the
projectile rule MUST return ACTION_ACK, signifying that it acknowledged
the event and handled it appropriately. Otherwise, it SHOULD return
ACTION_NONE or ACTION_DESTROY. In a perfect world, those just wouldn't
be conflated in the same function with update logic.
I've also cleaned up the stage_logic routine a bit. Moved most of the
dialog handling into dialog.c and gave higher priority to boss
processing. The later is currently necessary to let boss-spawned
projectiles and particles to get called with t==0.
2018-05-16 01:38:47 +02:00
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
void dialog_update(Dialog **d) {
|
Give projectiles EVENT_BIRTH and call them with t==0 more consistently
The only case of t==0 being skipped is if the projectile was somehow
created after the projectile processing loop for this frame has been
finished. Currently that is only possible if a particle spawns a
non-particle projectile, which, ideally, should never happen. The same
problem exists with other types of entities. For example, if you have a
funny projectile rule that spawns an Enemy, that Enemy will have the 0th
frame skipped. One way to fix this is to maintain a list of all entities
in the game and process them all in a single loop, where newly spawned
entities would be appended to the tail of the list. This is also
probably the only good way to fix this, too.
Handling of all projectile events has been made mandatory to facilitate
easier debugging of subtle and/or hard to track bugs. If t<0, then the
projectile rule MUST return ACTION_ACK, signifying that it acknowledged
the event and handled it appropriately. Otherwise, it SHOULD return
ACTION_NONE or ACTION_DESTROY. In a perfect world, those just wouldn't
be conflated in the same function with update logic.
I've also cleaned up the stage_logic routine a bit. Moved most of the
dialog handling into dialog.c and gave higher priority to boss
processing. The later is currently necessary to let boss-spawned
projectiles and particles to get called with t==0.
2018-05-16 01:38:47 +02:00
|
|
|
if(!*d) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
if(dialog_is_active(*d)) {
|
2019-08-22 21:43:34 +02:00
|
|
|
while((*d)->pos < (*d)->count) {
|
|
|
|
if(
|
|
|
|
(*d)->actions[(*d)->pos].type != DIALOG_MSG_LEFT &&
|
|
|
|
(*d)->actions[(*d)->pos].type != DIALOG_MSG_RIGHT
|
|
|
|
) {
|
|
|
|
dialog_page(d);
|
|
|
|
} else {
|
|
|
|
int to = (*d)->actions[(*d)->pos].timeout;
|
|
|
|
|
|
|
|
if(
|
|
|
|
(to && to >= global.frames) ||
|
|
|
|
((global.plr.inputflags & INFLAG_SKIP) && global.frames - (*d)->page_time > 3)
|
|
|
|
) {
|
|
|
|
dialog_page(d);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2019-07-03 19:50:43 +02:00
|
|
|
}
|
Give projectiles EVENT_BIRTH and call them with t==0 more consistently
The only case of t==0 being skipped is if the projectile was somehow
created after the projectile processing loop for this frame has been
finished. Currently that is only possible if a particle spawns a
non-particle projectile, which, ideally, should never happen. The same
problem exists with other types of entities. For example, if you have a
funny projectile rule that spawns an Enemy, that Enemy will have the 0th
frame skipped. One way to fix this is to maintain a list of all entities
in the game and process them all in a single loop, where newly spawned
entities would be appended to the tail of the list. This is also
probably the only good way to fix this, too.
Handling of all projectile events has been made mandatory to facilitate
easier debugging of subtle and/or hard to track bugs. If t<0, then the
projectile rule MUST return ACTION_ACK, signifying that it acknowledged
the event and handled it appropriately. Otherwise, it SHOULD return
ACTION_NONE or ACTION_DESTROY. In a perfect world, those just wouldn't
be conflated in the same function with update logic.
I've also cleaned up the stage_logic routine a bit. Moved most of the
dialog handling into dialog.c and gave higher priority to boss
processing. The later is currently necessary to let boss-spawned
projectiles and particles to get called with t==0.
2018-05-16 01:38:47 +02:00
|
|
|
}
|
2019-07-03 19:50:43 +02:00
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
// important to check this again; the dialog_page call may have ended the dialog
|
2019-07-03 19:50:43 +02:00
|
|
|
|
|
|
|
if(dialog_is_active(*d)) {
|
|
|
|
fapproach_asymptotic_p(&(*d)->opacity, 1, 0.05, 1e-3);
|
|
|
|
} else {
|
|
|
|
fapproach_asymptotic_p(&(*d)->opacity, 0, 0.1, 1e-3);
|
|
|
|
if((*d)->opacity == 0) {
|
2019-07-08 02:47:50 +02:00
|
|
|
dialog_destroy(*d);
|
2019-07-03 19:50:43 +02:00
|
|
|
*d = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dialog_is_active(Dialog *d) {
|
|
|
|
return d && (d->pos < d->count);
|
Give projectiles EVENT_BIRTH and call them with t==0 more consistently
The only case of t==0 being skipped is if the projectile was somehow
created after the projectile processing loop for this frame has been
finished. Currently that is only possible if a particle spawns a
non-particle projectile, which, ideally, should never happen. The same
problem exists with other types of entities. For example, if you have a
funny projectile rule that spawns an Enemy, that Enemy will have the 0th
frame skipped. One way to fix this is to maintain a list of all entities
in the game and process them all in a single loop, where newly spawned
entities would be appended to the tail of the list. This is also
probably the only good way to fix this, too.
Handling of all projectile events has been made mandatory to facilitate
easier debugging of subtle and/or hard to track bugs. If t<0, then the
projectile rule MUST return ACTION_ACK, signifying that it acknowledged
the event and handled it appropriately. Otherwise, it SHOULD return
ACTION_NONE or ACTION_DESTROY. In a perfect world, those just wouldn't
be conflated in the same function with update logic.
I've also cleaned up the stage_logic routine a bit. Moved most of the
dialog handling into dialog.c and gave higher priority to boss
processing. The later is currently necessary to let boss-spawned
projectiles and particles to get called with t==0.
2018-05-16 01:38:47 +02:00
|
|
|
}
|