bunch of portrait and dialog-related shuffling around

This commit is contained in:
Andrei Alexeyev 2020-04-27 22:58:25 +03:00
parent ea90164f3a
commit 9f10dfef21
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
32 changed files with 321 additions and 199 deletions

View file

@ -15,6 +15,7 @@
#include "stagedraw.h"
#include "entity.h"
#include "util/glm.h"
#include "portrait.h"
static void ent_draw_boss(EntityInterface *ent);
static DamageResult ent_damage_boss(EntityInterface *ent, const DamageInfo *dmg);
@ -60,17 +61,18 @@ Boss* create_boss(char *name, char *ani, cmplx pos) {
return boss;
}
void boss_set_portrait(Boss *boss, Sprite *base, Sprite *face) {
void boss_set_portrait(Boss *boss, const char *name, const char *variant, const char *face) {
if(boss->portrait.tex != NULL) {
r_texture_destroy(boss->portrait.tex);
boss->portrait.tex = NULL;
}
if(base != NULL) {
if(name != NULL) {
assume(face != NULL);
render_character_portrait(base, face, &boss->portrait);
portrait_render_byname(name, variant, face, &boss->portrait);
} else {
assume(face == NULL);
assume(variant == NULL);
}
}
@ -1304,7 +1306,7 @@ void free_boss(Boss *boss) {
}
ent_unregister(&boss->ent);
boss_set_portrait(boss, NULL, NULL);
boss_set_portrait(boss, NULL, NULL, NULL);
aniplayer_free(&boss->ani);
free(boss->name);
free(boss);

View file

@ -190,7 +190,7 @@ Attack* boss_add_attack_from_info(Boss *boss, AttackInfo *info, char move)
attr_nonnull(1, 2) attr_returns_nonnull;
void boss_set_attack_bonus(Attack *a, int rank) attr_nonnull(1);
void boss_set_portrait(Boss *boss, Sprite *base, Sprite *face) attr_nonnull(1);
void boss_set_portrait(Boss *boss, const char *name, const char *variant, const char *face) attr_nonnull(1);
void boss_start_attack(Boss *b, Attack *a) attr_nonnull(1, 2);
void boss_finish_current_attack(Boss *boss) attr_nonnull(1);

View file

@ -10,6 +10,7 @@
#include "dialog.h"
#include "global.h"
#include "portrait.h"
void dialog_init(Dialog *d) {
memset(d, 0, sizeof(*d));
@ -222,50 +223,12 @@ static void dialog_actor_update_composite(DialogActor *a) {
log_debug("%s (%p) is dirty; face=%s; variant=%s", a->name, (void*)a, a->face, a->variant);
Sprite *spr_base, *spr_face;
size_t name_len = strlen(a->name);
size_t face_len = strlen(a->face);
size_t variant_len;
bool have_variant = a->variant;
size_t lenfull_base = sizeof("dialog/") + name_len - 1;
size_t lenfull_face = lenfull_base + sizeof("_face_") + face_len - 1;
if(have_variant) {
variant_len = strlen(a->variant);
lenfull_base += sizeof("_variant_") + variant_len - 1;
}
char buf[imax(lenfull_base, lenfull_face) + 1];
char *dst = buf;
dst = memcpy(dst, "dialog/", sizeof("dialog/") - 1);
dst = memcpy(dst + sizeof("dialog/") - 1, a->name, name_len + 1);
if(have_variant) {
char *tmp = dst;
dst = memcpy(dst + name_len, "_variant_", sizeof("_variant_") - 1);
dst = memcpy(dst + sizeof("_variant_") - 1, a->variant, variant_len + 1);
dst = tmp;
}
spr_base = get_sprite(buf);
log_debug("base: %s", buf);
assume(spr_base != NULL);
dst = memcpy(dst + name_len, "_face_", sizeof("_face_") - 1);
dst = memcpy(dst + sizeof("_face_") - 1, a->face, face_len + 1);
spr_face = get_sprite(buf);
log_debug("face: %s", buf);
assume(spr_face != NULL);
if(a->composite.tex != NULL) {
log_debug("destroyed texture at %p", (void*)a->composite.tex);
r_texture_destroy(a->composite.tex);
}
render_character_portrait(spr_base, spr_face, &a->composite);
portrait_render_byname(a->name, a->variant, a->face, &a->composite);
log_debug("created texture at %p", (void*)a->composite.tex);
a->composite_dirty = false;
}

View file

@ -33,7 +33,11 @@
#define WITH_EVENTS(_name, _events) \
typedef COEVENTS_ARRAY _events _name##DialogEvents; \
DEFINE_TASK_INTERFACE(_name##Dialog, { _name##DialogEvents **out_events; });
DEFINE_TASK_INTERFACE(_name##Dialog, { \
_name##DialogEvents **out_events; \
int called_for_preload; \
ResourceFlags preload_rflags; \
});
#define WITHOUT_EVENTS(_name) \
WITH_EVENTS(_name, (_dummy_fake_event_))
@ -59,21 +63,14 @@ typedef struct PlayerDialogTasks {
#undef WITH_EVENTS
#undef WITHOUT_EVENTS
// FIXME: might not be the best place for this
typedef struct PlayerDialogProcs {
void (*stage1_pre_boss)(Dialog *d);
void (*stage1_post_boss)(Dialog *d);
void (*stage2_pre_boss)(Dialog *d);
void (*stage2_post_boss)(Dialog *d);
void (*stage3_pre_boss)(Dialog *d);
void (*stage3_post_boss)(Dialog *d);
void (*stage4_pre_boss)(Dialog *d);
void (*stage4_post_boss)(Dialog *d);
void (*stage5_post_midboss)(Dialog *d);
void (*stage5_pre_boss)(Dialog *d);
void (*stage5_post_boss)(Dialog *d);
void (*stage6_pre_boss)(Dialog *d);
void (*stage6_pre_final)(Dialog *d);
} PlayerDialogProcs;
// FIXME: not used yet, because stage preload procs don't have reliable access to initialized player
#define DIALOG_PRELOAD(_player, _dialog_name, _preload_rflags) do { \
TASK_INDIRECT_TYPE(_dialog_name##Dialog) _dialog_ref = (_player)->mode->dialog->_dialog_name; \
TASK_IFACE_ARGS_TYPE(_dialog_name##Dialog) _dialog_args = { \
.called_for_preload = 1, \
.preload_rflags = (_preload_rflags), \
}; \
_dialog_ref._cotask_##_dialog_name##Dialog##_thunk(&_dialog_args); \
} while(0)
#endif // IGUARD_dialog_dialog_interface_h

View file

@ -13,8 +13,15 @@
#include "dialog.h"
#include "stage.h"
#include "portrait.h"
#define DIALOG_BEGIN(_interface) \
if(ARGS.called_for_preload) { \
if(ARGS.called_for_preload > 0) { \
log_warn("preload was not handled"); \
} \
return; \
} \
Dialog dialog; \
stage_begin_dialog(&dialog); \
_interface##DialogEvents events = { 0 }; \
@ -47,4 +54,18 @@
#define DIALOG_TASK(_protag, _interface) \
TASK_WITH_INTERFACE(_protag##_##_interface##Dialog, _interface##Dialog)
#define PRELOAD \
if(ARGS.called_for_preload) ARGS.called_for_preload = -1; \
if(ARGS.called_for_preload)
#define PRELOAD_CHAR(name) \
portrait_preload_base_sprite(#name, NULL, ARGS.preload_rflags); \
for(const char *_charname = #name; _charname; _charname = NULL)
#define PRELOAD_VARIANT(variant) \
portrait_preload_base_sprite(_charname, #variant, ARGS.preload_rflags)
#define PRELOAD_FACE(face) \
portrait_preload_face_sprite(_charname, #face, ARGS.preload_rflags)
#endif // IGUARD_dialog_dialog_macros_h

View file

@ -15,7 +15,20 @@
*/
DIALOG_TASK(reimu, Stage1PreBoss) {
// Initialization, must be at the very top.
PRELOAD {
PRELOAD_CHAR(reimu) {
PRELOAD_FACE(normal);
PRELOAD_FACE(unamused);
PRELOAD_FACE(sigh);
}
PRELOAD_CHAR(cirno) {
PRELOAD_FACE(normal);
PRELOAD_FACE(angry);
}
}
// Initialization, must be at the very top (after PRELOAD).
DIALOG_BEGIN(Stage1PreBoss);
ACTOR_LEFT(reimu);
@ -40,9 +53,9 @@ DIALOG_TASK(reimu, Stage1PreBoss) {
// MSG() makes the actor say a line, and then waits an unspecified amount of time (skippable).
// The timeout is determined by the dialog_util_estimate_wait_timeout_from_text() function in dialog.c
// MSG() also implies FOCUS()
MSG(reimu, "Unseasonable snow? I wonder if its that Secret God again…");
MSG(reimu, "Unseasonable snow? I wonder if its that Secret God again…");
MSG(cirno, "Secret God?");
MSG(cirno, "Secret God?");
// EVENT()s are handled by stage code.
// You can find the list of events per dialogue in dialog_interface.h

View file

@ -15,6 +15,7 @@
#include "global.h"
#include "video.h"
#include "util/glm.h"
#include "portrait.h"
#define SELECTED_SUBSHOT(m) (((CharMenuContext*)(m)->context)->subshot)
#define DESCRIPTION_WIDTH (SCREEN_W / 3 + 40)
@ -31,25 +32,25 @@ enum {
#define FACENAME_LEN 32
static const char facedefs[NUM_CHARACTERS][NUM_FACES][FACENAME_LEN] = {
[PLR_CHAR_REIMU] = {
[F_HAPPY] = "dialog/reimu_face_happy",
[F_NORMAL] = "dialog/reimu_face_normal",
[F_SMUG] = "dialog/reimu_face_smug",
[F_SURPRISED] = "dialog/reimu_face_surprised",
[F_UNAMUSED] = "dialog/reimu_face_unamused",
[F_HAPPY] = PORTRAIT_STATIC_FACE_SPRITE_NAME(reimu, happy),
[F_NORMAL] = PORTRAIT_STATIC_FACE_SPRITE_NAME(reimu, normal),
[F_SMUG] = PORTRAIT_STATIC_FACE_SPRITE_NAME(reimu, smug),
[F_SURPRISED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(reimu, surprised),
[F_UNAMUSED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(reimu, unamused),
},
[PLR_CHAR_MARISA] = {
[F_HAPPY] = "dialog/marisa_face_happy",
[F_NORMAL] = "dialog/marisa_face_normal",
[F_SMUG] = "dialog/marisa_face_smug",
[F_SURPRISED] = "dialog/marisa_face_surprised",
[F_UNAMUSED] = "dialog/marisa_face_unamused",
[F_HAPPY] = PORTRAIT_STATIC_FACE_SPRITE_NAME(marisa, happy),
[F_NORMAL] = PORTRAIT_STATIC_FACE_SPRITE_NAME(marisa, normal),
[F_SMUG] = PORTRAIT_STATIC_FACE_SPRITE_NAME(marisa, smug),
[F_SURPRISED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(marisa, surprised),
[F_UNAMUSED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(marisa, unamused),
},
[PLR_CHAR_YOUMU] = {
[F_HAPPY] = "dialog/youmu_face_happy",
[F_NORMAL] = "dialog/youmu_face_normal",
[F_SMUG] = "dialog/youmu_face_smug",
[F_SURPRISED] = "dialog/youmu_face_surprised",
[F_UNAMUSED] = "dialog/youmu_face_unamused",
[F_HAPPY] = PORTRAIT_STATIC_FACE_SPRITE_NAME(youmu, happy),
[F_NORMAL] = PORTRAIT_STATIC_FACE_SPRITE_NAME(youmu, normal),
[F_SMUG] = PORTRAIT_STATIC_FACE_SPRITE_NAME(youmu, smug),
[F_SURPRISED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(youmu, surprised),
[F_UNAMUSED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(youmu, unamused),
},
};
@ -173,7 +174,7 @@ void draw_char_menu(MenuData *menu) {
assert(pchar != NULL);
assert(pchar->id == i);
Sprite *spr = get_sprite(pchar->dialog_base_sprite_name);
Sprite *spr = portrait_get_base_sprite(pchar->lower_name, NULL); // TODO cache this
const char *name = pchar->full_name;
const char *title = pchar->title;
@ -377,6 +378,11 @@ static void char_menu_input(MenuData *menu) {
}
void preload_char_menu(void) {
for(int i = 0; i < NUM_CHARACTERS; ++i) {
PlayerCharacter *pchar = plrchar_get(i);
portrait_preload_base_sprite(pchar->lower_name, NULL, RESF_PERMANENT);
}
char *p = (char*)facedefs;
for(int i = 0; i < sizeof(facedefs) / FACENAME_LEN; ++i) {

View file

@ -292,10 +292,5 @@ void menu_preload(void) {
"menu",
NULL);
for(int i = 0; i < NUM_CHARACTERS; ++i) {
PlayerCharacter *pchar = plrchar_get(i);
preload_resource(RES_SPRITE, pchar->dialog_base_sprite_name, RESF_PERMANENT);
}
preload_char_menu();
}

View file

@ -82,6 +82,7 @@ taisei_src = files(
'move.c',
'player.c',
'plrmodes.c',
'portrait.c',
'progress.c',
'projectile.c',
'projectile_prototypes.c',

View file

@ -70,8 +70,8 @@ void player_stage_post_init(Player *plr) {
assert(plr->mode->character != NULL);
assert(plr->mode->dialog != NULL);
plrchar_make_bomb_portrait(plr->mode->character, &plr->bomb_portrait);
aniplayer_create(&plr->ani, get_ani(plr->mode->character->player_sprite_name), "main");
plrchar_render_bomb_portrait(plr->mode->character, &plr->bomb_portrait);
aniplayer_create(&plr->ani, plrchar_player_anim(plr->mode->character), "main");
plr->ent.draw_layer = LAYER_PLAYER;
plr->ent.draw_func = ent_draw_player;

View file

@ -11,6 +11,7 @@
#include "player.h"
#include "global.h"
#include "stage.h"
#include "portrait.h"
#include "plrmodes.h"
#include "plrmodes/marisa.h"
@ -32,7 +33,7 @@ static PlayerMode *player_modes[] = {
&plrmode_youmu_b,
};
PlayerCharacter* plrchar_get(CharacterID id) {
PlayerCharacter *plrchar_get(CharacterID id) {
assert((unsigned)id < NUM_CHARACTERS);
PlayerCharacter *pc = player_characters[id];
assert(pc->id == id);
@ -40,27 +41,32 @@ PlayerCharacter* plrchar_get(CharacterID id) {
}
void plrchar_preload(PlayerCharacter *pc) {
preload_resource(RES_ANIM, pc->player_sprite_name, RESF_DEFAULT);
preload_resource(RES_SPRITE, pc->dialog_base_sprite_name, RESF_DEFAULT);
}
const char *name = pc->lower_name;
portrait_preload_base_sprite(name, NULL, RESF_DEFAULT);
portrait_preload_face_sprite(name, "normal", RESF_DEFAULT);
void plrchar_make_bomb_portrait(PlayerCharacter *pc, Sprite *out_spr) {
Sprite *s_base = get_sprite(pc->dialog_base_sprite_name);
Sprite *s_face = plrchar_face_sprite(pc, "normal");
render_character_portrait(s_base, s_face, out_spr);
}
int plrchar_face_spritename(PlayerCharacter *pc, const char *face, char *buf, size_t bufsize) {
const char *basename = pc->dialog_base_sprite_name;
static const char face_suffix[] = "_face_normal";
assert(bufsize >= strlen(basename) + sizeof(face_suffix) + 1);
return snprintf(buf, bufsize, "%s%s", basename, face_suffix);
}
Sprite *plrchar_face_sprite(PlayerCharacter *pc, const char *face) {
char buf[64];
plrchar_face_spritename(pc, face, buf, sizeof(buf));
return get_sprite(buf);
plrchar_player_anim_name(pc, sizeof(buf), buf);
preload_resource(RES_ANIM, buf, RESF_DEFAULT);
}
void plrchar_render_bomb_portrait(PlayerCharacter *pc, Sprite *out_spr) {
const char *name = pc->lower_name;
Sprite *s_base = portrait_get_base_sprite(name, NULL);
Sprite *s_face = portrait_get_face_sprite(name, "normal");
portrait_render(s_base, s_face, out_spr);
}
int plrchar_player_anim_name(PlayerCharacter *pc, size_t bufsize, char buf[bufsize]) {
const char *name = pc->lower_name;
assert(bufsize >= strlen("player/") + strlen(name) + 1);
return snprintf(buf, bufsize, "player/%s", name);
}
Animation *plrchar_player_anim(PlayerCharacter *pc) {
char buf[64];
plrchar_player_anim_name(pc, sizeof(buf), buf);
return get_ani(buf);
}
int plrmode_repr(char *out, size_t outsize, PlayerMode *mode, bool internal) {
@ -73,7 +79,7 @@ int plrmode_repr(char *out, size_t outsize, PlayerMode *mode, bool internal) {
);
}
PlayerMode* plrmode_find(CharacterID char_id, ShotModeID shot_id) {
PlayerMode *plrmode_find(CharacterID char_id, ShotModeID shot_id) {
for(int i = 0; i < NUM_PLAYER_MODES; ++i) {
PlayerMode *mode = player_modes[i];
@ -85,7 +91,7 @@ PlayerMode* plrmode_find(CharacterID char_id, ShotModeID shot_id) {
return NULL;
}
PlayerMode* plrmode_parse(const char *name) {
PlayerMode *plrmode_parse(const char *name) {
CharacterID char_id = (CharacterID)-1;
ShotModeID shot_id = (ShotModeID)-1;
char buf[strlen(name) + 1];

View file

@ -63,8 +63,6 @@ typedef struct PlayerCharacter {
const char *proper_name;
const char *full_name;
const char *title;
const char *dialog_base_sprite_name;
const char *player_sprite_name;
const char *menu_texture_name;
struct {
@ -109,15 +107,15 @@ enum {
NUM_PLAYER_MODES = NUM_CHARACTERS * NUM_SHOT_MODES_PER_CHARACTER,
};
PlayerCharacter* plrchar_get(CharacterID id);
PlayerCharacter *plrchar_get(CharacterID id);
void plrchar_preload(PlayerCharacter *pc);
void plrchar_make_bomb_portrait(PlayerCharacter *pc, Sprite *out_spr);
int plrchar_face_spritename(PlayerCharacter *pc, const char *face, char *buf, size_t bufsize);
Sprite *plrchar_face_sprite(PlayerCharacter *pc, const char *face);
void plrchar_render_bomb_portrait(PlayerCharacter *pc, Sprite *out_spr);
int plrchar_player_anim_name(PlayerCharacter *pc, size_t bufsize, char buf[bufsize]);
Animation *plrchar_player_anim(PlayerCharacter *pc);
PlayerMode* plrmode_find(CharacterID charid, ShotModeID shotid);
PlayerMode *plrmode_find(CharacterID charid, ShotModeID shotid);
int plrmode_repr(char *out, size_t outsize, PlayerMode *mode, bool internal);
PlayerMode* plrmode_parse(const char *name);
PlayerMode *plrmode_parse(const char *name);
void plrmode_preload(PlayerMode *mode);
double player_property(Player *plr, PlrProperty prop);

View file

@ -19,8 +19,6 @@ PlayerCharacter character_marisa = {
.proper_name = "Marisa",
.full_name = "Kirisame Marisa",
.title = "Unbelievably Ordinary Magician",
.dialog_base_sprite_name = "dialog/marisa",
.player_sprite_name = "player/marisa",
.menu_texture_name = "marisa_bombbg",
.ending = {
.good = good_ending_marisa,

View file

@ -21,8 +21,6 @@ PlayerCharacter character_reimu = {
.proper_name = "Reimu",
.full_name = "Hakurei Reimu",
.title = "Shrine Maiden of Fantasy",
.dialog_base_sprite_name = "dialog/reimu",
.player_sprite_name = "player/reimu",
.menu_texture_name = "reimubg",
.ending = {
.good = good_ending_reimu,

View file

@ -19,8 +19,6 @@ PlayerCharacter character_youmu = {
.proper_name = "Yōmu",
.full_name = "Konpaku Yōmu",
.title = "Swordswoman Between Worlds",
.dialog_base_sprite_name = "dialog/youmu",
.player_sprite_name = "player/youmu",
.menu_texture_name = "youmu_bombbg1",
.ending = {
.good = good_ending_youmu,

121
src/portrait.c Normal file
View file

@ -0,0 +1,121 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "portrait.h"
#include "renderer/api.h"
#include "config.h"
#define RETURN_RESOURCE_NAME(name1, suffix, name2) \
assert(bufsize >= strlen(PORTRAIT_PREFIX) + strlen(name1) + strlen(suffix) + strlen(name2) + 1); \
return snprintf(buf, bufsize, PORTRAIT_PREFIX "%s" suffix "%s", name1, name2)
#define BUFFER_SIZE 128
int portrait_get_base_sprite_name(const char *charname, const char *variant, size_t bufsize, char buf[bufsize]) {
if(variant == NULL) {
RETURN_RESOURCE_NAME(charname, "", "");
} else {
RETURN_RESOURCE_NAME(charname, PORTRAIT_VARIANT_SUFFIX, variant);
}
}
Sprite *portrait_get_base_sprite(const char *charname, const char *variant) {
char buf[BUFFER_SIZE];
portrait_get_base_sprite_name(charname, variant, sizeof(buf), buf);
return get_sprite(buf);
}
void portrait_preload_base_sprite(const char *charname, const char *variant, ResourceFlags rflags) {
char buf[BUFFER_SIZE];
portrait_get_base_sprite_name(charname, variant, sizeof(buf), buf);
preload_resource(RES_SPRITE, buf, rflags);
}
int portrait_get_face_sprite_name(const char *charname, const char *face, size_t bufsize, char buf[bufsize]) {
RETURN_RESOURCE_NAME(charname, PORTRAIT_FACE_SUFFIX, face);
}
Sprite *portrait_get_face_sprite(const char *charname, const char *face) {
char buf[BUFFER_SIZE];
portrait_get_face_sprite_name(charname, face, sizeof(buf), buf);
return get_sprite(buf);
}
void portrait_preload_face_sprite(const char *charname, const char *face, ResourceFlags rflags) {
char buf[BUFFER_SIZE];
portrait_get_face_sprite_name(charname, face, sizeof(buf), buf);
preload_resource(RES_SPRITE, buf, rflags);
}
void portrait_render(Sprite *s_base, Sprite *s_face, Sprite *s_out) {
r_state_push();
IntRect itc = sprite_denormalized_int_tex_coords(s_base);
uint tex_w = itc.w;
uint tex_h = itc.h;
uint spr_w = s_base->extent.w;
uint spr_h = s_base->extent.h;
Texture *ptex = r_texture_create(&(TextureParams) {
.type = TEX_TYPE_RGBA_8,
.width = tex_w,
.height = tex_h,
.filter.min = TEX_FILTER_LINEAR_MIPMAP_LINEAR,
.filter.mag = TEX_FILTER_LINEAR,
.wrap.s = TEX_WRAP_CLAMP,
.wrap.t = TEX_WRAP_CLAMP,
.mipmap_mode = TEX_MIPMAP_AUTO,
.mipmaps = 3,
});
Framebuffer *fb = r_framebuffer_create();
r_framebuffer_attach(fb, ptex, 0, FRAMEBUFFER_ATTACH_COLOR0);
r_framebuffer_viewport(fb, 0, 0, tex_w, tex_h);
r_framebuffer(fb);
r_framebuffer_clear(fb, CLEAR_COLOR, RGBA(0, 0, 0, 0), 1);
r_mat_proj_push_ortho(spr_w, spr_h);
r_mat_mv_push_identity();
SpriteParams sp = { 0 };
sp.sprite_ptr = s_base;
sp.blend = BLEND_NONE;
sp.pos.x = spr_w * 0.5f - sprite_padded_offset_x(s_base);
sp.pos.y = spr_h * 0.5f - sprite_padded_offset_y(s_base);
sp.color = RGBA(1, 1, 1, 1);
sp.shader_ptr = r_shader_get("sprite_default"),
r_draw_sprite(&sp);
sp.blend = BLEND_PREMUL_ALPHA;
sp.sprite_ptr = s_face;
r_draw_sprite(&sp);
r_flush_sprites();
r_mat_mv_pop();
r_mat_proj_pop();
r_state_pop();
r_framebuffer_destroy(fb);
Sprite s = { 0 };
s.tex = ptex;
s.extent = s_base->extent;
s.padding = s_base->padding;
s.tex_area.w = 1.0f;
s.tex_area.h = 1.0f;
*s_out = s;
}
void portrait_render_byname(const char *charname, const char *variant, const char *face, Sprite *s_out) {
portrait_render(
portrait_get_base_sprite(charname, variant),
portrait_get_face_sprite(charname, face),
s_out
);
}

50
src/portrait.h Normal file
View file

@ -0,0 +1,50 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_portrait_h
#define IGUARD_portrait_h
#include "taisei.h"
#include "resource/sprite.h"
#define PORTRAIT_PREFIX "dialog/"
#define PORTRAIT_VARIANT_SUFFIX "_variant_"
#define PORTRAIT_FACE_SUFFIX "_face_"
#define PORTRAIT_STATIC_FACE_SPRITE_NAME(charname, face) \
PORTRAIT_PREFIX #charname PORTRAIT_FACE_SUFFIX #face
#define PORTRAIT_STATIC_VARIANT_SPRITE_NAME(charname, face) \
PORTRAIT_PREFIX #charname PORTRAIT_VARIANT_SUFFIX #face
int portrait_get_base_sprite_name(const char *charname, const char *variant, size_t bufsize, char buf[bufsize])
attr_nonnull(1, 4);
Sprite *portrait_get_base_sprite(const char *charname, const char *variant)
attr_nonnull(1) attr_returns_nonnull;
void portrait_preload_base_sprite(const char *charname, const char *variant, ResourceFlags rflags)
attr_nonnull(1);
int portrait_get_face_sprite_name(const char *charname, const char *face, size_t bufsize, char buf[bufsize])
attr_nonnull(1, 2, 4);
void portrait_preload_face_sprite(const char *charname, const char *variant, ResourceFlags rflags)
attr_nonnull(1, 2);
Sprite *portrait_get_face_sprite(const char *charname, const char *face)
attr_nonnull(1, 2) attr_returns_nonnull;
void portrait_render(Sprite *s_base, Sprite *s_face, Sprite *s_out)
attr_nonnull_all;
void portrait_render_byname(const char *charname, const char *variant, const char *face, Sprite *s_out)
attr_nonnull(1, 3, 4);
#endif // IGUARD_portrait_h

View file

@ -18,6 +18,7 @@
#include "resource/model.h"
#include "util/glm.h"
#include "common_tasks.h"
#include "portrait.h"
/*
* See the definition of AttackInfo in boss.h for information on how to set up the idmaps.
@ -464,13 +465,15 @@ static void stage1_start(void) {
}
static void stage1_preload(void) {
// DIALOG_PRELOAD(&global.plr, Stage1PreBoss, RESF_DEFAULT);
portrait_preload_base_sprite("cirno", NULL, RESF_DEFAULT);
portrait_preload_face_sprite("cirno", "normal", RESF_DEFAULT);
preload_resources(RES_BGM, RESF_OPTIONAL, "stage1", "stage1boss", NULL);
preload_resources(RES_SPRITE, RESF_DEFAULT,
"stage1/cirnobg",
"stage1/fog",
"stage1/snowlayer",
"stage1/waterplants",
"dialog/cirno",
NULL);
preload_resources(RES_TEXTURE, RESF_DEFAULT,
"stage1/horizon",

View file

@ -43,7 +43,7 @@ void cirno_pfreeze_bg(Boss *c, int time) {
Boss *stage1_spawn_cirno(cmplx pos) {
Boss *cirno = create_boss("Cirno", "cirno", pos);
boss_set_portrait(cirno, get_sprite("dialog/cirno"), get_sprite("dialog/cirno_face_normal"));
boss_set_portrait(cirno, "cirno", NULL, "normal");
cirno->shadowcolor = *RGBA_MUL_ALPHA(0.6, 0.7, 1.0, 0.25);
cirno->glowcolor = *RGB(0.2, 0.35, 0.5);
return cirno;

View file

@ -14,6 +14,7 @@
#include "global.h"
#include "stage.h"
#include "stageutils.h"
#include "portrait.h"
PRAGMA(message "Remove when this stage is modernized")
DIAGNOSTIC(ignored "-Wdeprecated-declarations")
@ -171,6 +172,8 @@ static void stage2_start(void) {
}
static void stage2_preload(void) {
portrait_preload_base_sprite("hina", NULL, RESF_DEFAULT);
portrait_preload_face_sprite("hina", "normal", RESF_DEFAULT);
preload_resources(RES_BGM, RESF_OPTIONAL, "stage2", "stage2boss", NULL);
preload_resources(RES_SPRITE, RESF_DEFAULT,
"stage2/border",
@ -179,7 +182,6 @@ static void stage2_preload(void) {
"stage2/roadstones",
"stage2/spellbg1",
"stage2/spellbg2",
"dialog/hina",
NULL);
preload_resources(RES_SHADER_PROGRAM, RESF_DEFAULT,
"bloom",

View file

@ -864,7 +864,7 @@ void hina_spell_bg(Boss *h, int time) {
Boss* stage2_spawn_hina(cmplx pos) {
Boss *hina = create_boss("Kagiyama Hina", "hina", pos);
boss_set_portrait(hina, get_sprite("dialog/hina"), get_sprite("dialog/hina_face_normal"));
boss_set_portrait(hina, "hina", NULL, "normal");
hina->glowcolor = *RGBA_MUL_ALPHA(0.7, 0.2, 0.3, 0.5);
hina->shadowcolor = hina->glowcolor;
return hina;

View file

@ -14,6 +14,7 @@
#include "global.h"
#include "stage.h"
#include "stageutils.h"
#include "portrait.h"
PRAGMA(message "Remove when this stage is modernized")
DIAGNOSTIC(ignored "-Wdeprecated-declarations")
@ -161,6 +162,10 @@ static void stage3_start(void) {
}
static void stage3_preload(void) {
portrait_preload_base_sprite("wriggle", NULL, RESF_DEFAULT);
portrait_preload_face_sprite("wriggle", "proud", RESF_DEFAULT);
portrait_preload_base_sprite("scuttle", NULL, RESF_DEFAULT);
portrait_preload_face_sprite("scuttle", "normal", RESF_DEFAULT);
preload_resources(RES_BGM, RESF_OPTIONAL, "stage3", "stage3boss", NULL);
preload_resources(RES_SPRITE, RESF_DEFAULT,
"stage3/border",
@ -169,8 +174,6 @@ static void stage3_preload(void) {
"stage3/wspellbg",
"stage3/wspellclouds",
"stage3/wspellswarm",
"dialog/wriggle",
"dialog/scuttle",
NULL);
preload_resources(RES_SHADER_PROGRAM, RESF_DEFAULT,
"tunnel",

View file

@ -735,7 +735,7 @@ void wriggle_spellbg(Boss *b, int time) {
Boss* stage3_spawn_scuttle(cmplx pos) {
Boss *scuttle = create_boss("Scuttle", "scuttle", pos);
boss_set_portrait(scuttle, get_sprite("dialog/scuttle"), get_sprite("dialog/scuttle_face_normal"));
boss_set_portrait(scuttle, "scuttle", NULL, "normal");
scuttle->glowcolor = *RGB(0.5, 0.6, 0.3);
scuttle->shadowcolor = *RGBA_MUL_ALPHA(0.7, 0.3, 0.1, 0.5);
return scuttle;
@ -1420,7 +1420,7 @@ static void stage3_boss_intro(Boss *boss, int time) {
Boss* stage3_spawn_wriggle_ex(cmplx pos) {
Boss *wriggle = create_boss("Wriggle EX", "wriggleex", pos);
boss_set_portrait(wriggle, get_sprite("dialog/wriggle"), get_sprite("dialog/wriggle_face_proud"));
boss_set_portrait(wriggle, "wriggle", NULL, "proud");
wriggle->glowcolor = *RGBA_MUL_ALPHA(0.2, 0.4, 0.5, 0.5);
wriggle->shadowcolor = *RGBA_MUL_ALPHA(0.4, 0.2, 0.6, 0.5);
return wriggle;

View file

@ -16,6 +16,7 @@
#include "stageutils.h"
#include "util/glm.h"
#include "resource/model.h"
#include "portrait.h"
PRAGMA(message "Remove when this stage is modernized")
DIAGNOSTIC(ignored "-Wdeprecated-declarations")
@ -218,6 +219,8 @@ static void stage4_start(void) {
}
static void stage4_preload(void) {
portrait_preload_base_sprite("kurumi", NULL, RESF_DEFAULT);
portrait_preload_face_sprite("kurumi", "normal", RESF_DEFAULT);
preload_resources(RES_BGM, RESF_OPTIONAL, "stage4", "stage4boss", NULL);
preload_resources(RES_SPRITE, RESF_DEFAULT,
"stage2/border", // Stage 2 is intentional!
@ -227,7 +230,6 @@ static void stage4_preload(void) {
"stage4/mansion",
"stage4/planks",
"stage4/wall",
"dialog/kurumi",
NULL);
preload_resources(RES_SPRITE, RESF_DEFAULT,
"stage6/scythe", // Stage 6 is also intentional

View file

@ -554,7 +554,7 @@ static void kurumi_global_rule(Boss *b, int time) {
Boss* stage4_spawn_kurumi(cmplx pos) {
Boss* b = create_boss("Kurumi", "kurumi", pos);
boss_set_portrait(b, get_sprite("dialog/kurumi"), get_sprite("dialog/kurumi_face_normal"));
boss_set_portrait(b, "kurumi", NULL, "normal");
b->glowcolor = *RGB(0.5, 0.1, 0.0);
b->global_rule = kurumi_global_rule;
return b;

View file

@ -15,6 +15,7 @@
#include "stageutils.h"
#include "global.h"
#include "resource/model.h"
#include "portrait.h"
PRAGMA(message "Remove when this stage is modernized")
DIAGNOSTIC(ignored "-Wdeprecated-declarations")
@ -153,9 +154,10 @@ static void stage5_start(void) {
}
static void stage5_preload(void) {
portrait_preload_base_sprite("iku", NULL, RESF_DEFAULT);
portrait_preload_face_sprite("iku", "normal", RESF_DEFAULT);
preload_resources(RES_BGM, RESF_OPTIONAL, "stage5", "stage5boss", NULL);
preload_resources(RES_SPRITE, RESF_DEFAULT,
"dialog/iku",
"part/blast_huge_halo",
"part/blast_huge_rays",
"stage5/noise",

View file

@ -1269,7 +1269,7 @@ void iku_extra(Boss *b, int t) {
Boss* stage5_spawn_iku(cmplx pos) {
Boss *b = create_boss("Nagae Iku", "iku", pos);
boss_set_portrait(b, get_sprite("dialog/iku"), get_sprite("dialog/iku_face_normal"));
boss_set_portrait(b, "iku", NULL, "normal");
b->glowcolor = *RGBA_MUL_ALPHA(0.2, 0.4, 0.5, 0.5);
b->shadowcolor = *RGBA_MUL_ALPHA(0.65, 0.2, 0.75, 0.5);
return b;

View file

@ -16,6 +16,7 @@
#include "global.h"
#include "resource/model.h"
#include "stagedraw.h"
#include "portrait.h"
PRAGMA(message "Remove when this stage is modernized")
DIAGNOSTIC(ignored "-Wdeprecated-declarations")
@ -275,6 +276,10 @@ static void stage6_start(void) {
}
static void stage6_preload(void) {
portrait_preload_base_sprite("elly", NULL, RESF_DEFAULT);
portrait_preload_face_sprite("elly", "normal", RESF_DEFAULT);
portrait_preload_base_sprite("elly", "beaten", RESF_DEFAULT);
portrait_preload_face_sprite("elly", "shouting", RESF_DEFAULT);
preload_resources(RES_BGM, RESF_OPTIONAL,
"stage6",
"stage6boss_phase1",
@ -286,7 +291,6 @@ static void stage6_preload(void) {
"stage6/towerwall",
NULL);
preload_resources(RES_SPRITE, RESF_DEFAULT,
"dialog/elly",
"part/blast_huge_halo",
"part/blast_huge_rays",
"part/myon",

View file

@ -2636,7 +2636,7 @@ static void elly_toe_laser_logic(Laser *l, int t) {
void elly_theory(Boss *b, int time) {
if(time == EVENT_BIRTH) {
global.shake_view = 10;
boss_set_portrait(b, get_sprite("dialog/elly_variant_beaten"), get_sprite("dialog/elly_face_shouting"));
boss_set_portrait(b, "elly", "beaten", "shouting");
return;
}
@ -2946,7 +2946,7 @@ static void elly_global_rule(Boss *b, int time) {
Boss* stage6_spawn_elly(cmplx pos) {
Boss *b = create_boss("Elly", "elly", pos);
boss_set_portrait(b, get_sprite("dialog/elly"), get_sprite("dialog/elly_face_normal"));
boss_set_portrait(b, "elly", NULL, "normal");
b->global_rule = elly_global_rule;
return b;
}

View file

@ -8,7 +8,6 @@
#include "taisei.h"
#include "stats.h"
#include "global.h"

View file

@ -186,61 +186,3 @@ void draw_framebuffer_attachment(Framebuffer *fb, double width, double height, F
void draw_framebuffer_tex(Framebuffer *fb, double width, double height) {
draw_framebuffer_attachment(fb, width, height, FRAMEBUFFER_ATTACH_COLOR0);
}
void render_character_portrait(Sprite *s_base, Sprite *s_face, Sprite *s_out) {
r_state_push();
IntRect itc = sprite_denormalized_int_tex_coords(s_base);
uint tex_w = itc.w;
uint tex_h = itc.h;
uint spr_w = s_base->extent.w;
uint spr_h = s_base->extent.h;
Texture *ptex = r_texture_create(&(TextureParams) {
.type = TEX_TYPE_RGBA_8,
.width = tex_w,
.height = tex_h,
.filter.min = TEX_FILTER_LINEAR_MIPMAP_LINEAR,
.filter.mag = TEX_FILTER_LINEAR,
.wrap.s = TEX_WRAP_CLAMP,
.wrap.t = TEX_WRAP_CLAMP,
.mipmap_mode = TEX_MIPMAP_AUTO,
.mipmaps = 3,
});
Framebuffer *fb = r_framebuffer_create();
r_framebuffer_attach(fb, ptex, 0, FRAMEBUFFER_ATTACH_COLOR0);
r_framebuffer_viewport(fb, 0, 0, tex_w, tex_h);
r_framebuffer(fb);
r_framebuffer_clear(fb, CLEAR_COLOR, RGBA(0, 0, 0, 0), 1);
r_mat_proj_push_ortho(spr_w, spr_h);
r_mat_mv_push_identity();
SpriteParams sp = { 0 };
sp.sprite_ptr = s_base;
sp.blend = BLEND_NONE;
sp.pos.x = spr_w * 0.5f - sprite_padded_offset_x(s_base);
sp.pos.y = spr_h * 0.5f - sprite_padded_offset_y(s_base);
sp.color = RGBA(1, 1, 1, 1);
sp.shader_ptr = r_shader_get("sprite_default"),
r_draw_sprite(&sp);
sp.blend = BLEND_PREMUL_ALPHA;
sp.sprite_ptr = s_face;
r_draw_sprite(&sp);
r_flush_sprites();
r_mat_mv_pop();
r_mat_proj_pop();
r_state_pop();
r_framebuffer_destroy(fb);
Sprite s = { 0 };
s.tex = ptex;
s.extent = s_base->extent;
s.padding = s_base->padding;
s.tex_area.w = 1.0f;
s.tex_area.h = 1.0f;
*s_out = s;
}

View file

@ -54,6 +54,4 @@ double draw_fraction(double value, Alignment a, double pos_x, double pos_y, Font
void draw_framebuffer_tex(Framebuffer *fb, double width, double height);
void draw_framebuffer_attachment(Framebuffer *fb, double width, double height, FramebufferAttachment attachment);
void render_character_portrait(Sprite *s_base, Sprite *s_face, Sprite *s_out);
#endif // IGUARD_util_graphics_h