diff --git a/src/3d/3d_internal.h b/src/3d/3d_internal.h index 4900926..f4a07ab 100644 --- a/src/3d/3d_internal.h +++ b/src/3d/3d_internal.h @@ -42,6 +42,7 @@ struct model { int nr_bones; struct bone *bones; struct hash_table *bone_map; // bone id in POL/MOT -> struct bone * + struct hash_table *bone_name_map; // bone name -> (struct bone * | NULL) vec3 aabb[2]; // axis-aligned bounding box }; diff --git a/src/3d/model.c b/src/3d/model.c index 600a2c2..2bed90e 100644 --- a/src/3d/model.c +++ b/src/3d/model.c @@ -355,6 +355,14 @@ static struct bone *add_bone(struct model *model, struct pol *pol, struct pol_bo glm_quat_mat4(pol_bone->rotq, bone->inverse_bind_matrix); glm_translate(bone->inverse_bind_matrix, pol_bone->pos); + // Update bone_name_map. If the bone name is not unique in the POL, set the + // map value to NULL so that ID matching will be used. + struct ht_slot *slot = ht_put(model->bone_name_map, pol_bone->name, bone); + if (slot->value != bone) { + NOTICE("%s: non-unique bone %s", model->path, pol_bone->name); + slot->value = NULL; + } + model->nr_bones++; return bone; } @@ -401,6 +409,7 @@ struct model *model_load(struct archive *aar, const char *path) if (pol->nr_bones > MAX_BONES) ERROR("%s: Too many bones (%u)", model->path, pol->nr_bones); model->bone_map = ht_create(pol->nr_bones * 3 / 2); + model->bone_name_map = ht_create(pol->nr_bones * 3 / 2); model->bones = xcalloc(pol->nr_bones, sizeof(struct bone)); for (uint32_t i = 0; i < pol->nr_bones; i++) { add_bone(model, pol, &pol->bones[i]); @@ -470,6 +479,8 @@ void model_free(struct model *model) free(model->bones); if (model->bone_map) ht_free_int(model->bone_map); + if (model->bone_name_map) + ht_free(model->bone_name_map); free(model->path); free(model); @@ -505,9 +516,13 @@ struct motion *motion_load(const char *name, struct RE_instance *instance, struc // Reorder mot->motions so that motion for model->bones[i] can be // accessed by mot->motions[i]. for (uint32_t i = 0; i < mot->nr_bones; i++) { - struct bone *bone = ht_get_int(model->bone_map, mot->motions[i]->id, NULL); + // Match by name first, since some MOT have wrong bone IDs (e.g. maidsan_ahoge_*). + struct bone *bone = ht_get(model->bone_name_map, mot->motions[i]->name, NULL); + // If it is not found or is NULL (non-unique bone name), match by bone ID. if (!bone) - ERROR("%s: invalid bone id %d", name, mot->motions[i]->id); + bone = ht_get_int(model->bone_map, mot->motions[i]->id, NULL); + if (!bone) + ERROR("%s: invalid bone \"%s\" (%d)", name, mot->motions[i]->name, mot->motions[i]->id); mot->motions[i]->id = bone->index; } qsort(mot->motions, mot->nr_bones, sizeof(struct mot_bone *), cmp_motions_by_bone_id); diff --git a/src/effect.c b/src/effect.c index 9804ce6..847b548 100644 --- a/src/effect.c +++ b/src/effect.c @@ -217,6 +217,21 @@ static void effect_oscillate(float rate) gfx_copy(dst, delta_x, delta_y, &effect.new, 0, 0, dst->w, dst->h); } +static void effect_tv_switch_off(float rate) +{ + Texture *dst = gfx_main_surface(); + if (rate < 0.5f) { + int h = max(1, (int)(dst->h * (1.0f - rate * 2.0f))); + gfx_copy_stretch(dst, 0, dst->h * rate, dst->w, h, + &effect.old, 0, 0, effect.old.w, effect.old.h); + } else { + rate -= 0.5f; + int w = dst->w * (1.0f - rate * 2.0f); + gfx_copy_stretch(dst, dst->w * rate, dst->h / 2, w, 1, + &effect.old, 0, 0, effect.old.w, effect.old.h); + } +} + static void effect_zoom_in_crossfade(float rate) { Texture *dst = gfx_main_surface(); @@ -238,6 +253,7 @@ static effect_fun effect_functions[NR_EFFECTS] = { [EFFECT_WHITEOUT] = effect_whiteout, [EFFECT_WHITEIN] = effect_whitein, [EFFECT_OSCILLATE] = effect_oscillate, + [EFFECT_TV_SWITCH_OFF] = effect_tv_switch_off, [EFFECT_ZOOM_LR] = effect_zoom_lr, [EFFECT_ZOOM_RL] = effect_zoom_rl, [EFFECT_ZOOM_IN_CROSSFADE] = effect_zoom_in_crossfade, diff --git a/src/hll/MsgSkip.c b/src/hll/MsgSkip.c index c35ed46..257f379 100644 --- a/src/hll/MsgSkip.c +++ b/src/hll/MsgSkip.c @@ -80,14 +80,14 @@ static int MsgSkip_Init(struct string *name) static void MsgSkip_SetFlag(int msgnum) { - if (msgnum >= nr_flags) + if (msgnum < 0 || msgnum >= nr_flags) return; flags[msgnum] = 1; } static int MsgSkip_GetFlag(int msgnum) { - if (msgnum >= nr_flags) + if (msgnum < 0 || msgnum >= nr_flags) return 0; return !!flags[msgnum]; }