Import blues from fc8bfa4b

This commit is contained in:
Gregory Montoir 2019-06-01 21:24:36 +08:00
parent 1b97da5894
commit b7e5429ee0
10 changed files with 472 additions and 142 deletions

View File

@ -5,13 +5,13 @@ This is a rewrite of the [Blues Brothers](https://www.mobygames.com/game/blues-b
![Screenshot1](blues1.png) ![Screenshot2](bbja2.png)
There is also early support for [Prehistork 2](https://www.mobygames.com/game/prehistorik-2).
There is also early support for [Prehistorik 2](https://www.mobygames.com/game/prehistorik-2).
## Requirements
### Blues Brothers
The game data files of the DOS or Amiga version are required.
The data files of the Amiga or DOS version, full game or [demo](https://archive.org/details/TheBluesBrothers_1020), are required.
```
*.BIN, *.CK1, *.CK2, *.SQL, *.SQV, *.SQZ
@ -21,7 +21,7 @@ For sounds and music, the Amiga version files or the [ExoticA](https://www.exoti
### Jukebox Adventure
The game data files of the DOS version are required.
The data files of the DOS version are required.
```
*.EAT, *.MOD
@ -29,7 +29,7 @@ The game data files of the DOS version are required.
### Prehistorik 2
The game data files of the DOS version are required.
The data files of the DOS version, full game or [demo](http://cd.textfiles.com/ccbcurrsh1/demos/pre2.zip), are required.
```
*.SQZ, *.TRK

View File

@ -87,16 +87,16 @@ struct object_t {
int16_t unk2E;
int8_t unk2F;
uint8_t op;
uint8_t grab_state; // 1:carry_crate
uint8_t grab_state; /* 1:carry_crate */
uint8_t grab_type;
uint8_t special_anim; // 2:flying
uint8_t special_anim; /* 2:flying */
// uint8_t unk35;
int16_t player_xdist;
int16_t player_ydist;
uint8_t sprite3_counter;
uint8_t visible_flag;
uint8_t moving_direction; // 0:right 1:left
uint8_t unk3D; // 1:umbrella 2:balloon
uint8_t moving_direction; /* 0:right 1:left */
uint8_t unk3D; /* 1:umbrella 2:balloon */
uint8_t carry_crate_flag;
// int16_t unk3F;
// uint8_t unk41;
@ -105,12 +105,12 @@ struct object_t {
uint8_t tile1_flags;
uint8_t tile2_flags;
int16_t tile012_xpos;
int8_t elevator_direction; // -1,1
int8_t elevator_direction; /* -1,1 */
const uint8_t *trigger3;
uint8_t trigger3_num;
// int16_t unk4E;
int16_t unk50;
uint8_t data51; // health for obj39/40, horizontal direction for other objects
uint8_t data51; /* health for obj39/40, horizontal direction for other objects */
uint8_t scrolling_lock_flag;
uint8_t unk54;
uint8_t unk55;
@ -121,10 +121,10 @@ struct object_t {
// uint8_t unk5C; // never read
uint8_t unk5D;
uint8_t blinking_counter;
uint8_t data5F; // music instrument number for obj39/40, counter for other objects
uint8_t data5F; /* music instrument number for obj39/40, counter for other objects */
uint8_t unk60;
uint8_t lifes_count;
uint8_t level_complete_flag; // set if music instrument was found
uint8_t level_complete_flag; /* set if music instrument was found */
uint8_t restart_level_flag;
};

View File

@ -18,17 +18,17 @@ extern struct options_t g_options;
#define PANEL_H 24
#define SOUND_0 0
#define SOUND_1 1 // monster knocked out
#define SOUND_2 2 // bouncing mushroom
#define SOUND_3 3 // decor hit
#define SOUND_4 4 // player hit
#define SOUND_5 5 // grab vinyl
#define SOUND_8 8 // no vinyl
#define SOUND_9 9 // throw vinyl
#define SOUND_10 10 // "rock,rock,rock and roll"
#define SOUND_1 1 /* monster knocked out */
#define SOUND_2 2 /* bouncing mushroom */
#define SOUND_3 3 /* decor hit */
#define SOUND_4 4 /* player hit */
#define SOUND_5 5 /* grab vinyl */
#define SOUND_8 8 /* no vinyl */
#define SOUND_9 9 /* throw vinyl */
#define SOUND_10 10 /* "rock,rock,rock and roll" */
#define SOUND_11 11
#define SOUND_14 14 // player lost life
#define SOUND_15 15 // running out time
#define SOUND_14 14 /* player lost life */
#define SOUND_15 15 /* running out time */
struct object_t {
int16_t x_pos;
@ -66,20 +66,20 @@ enum {
#define object_blinking_counter(obj) (obj)->data[6].b[1] // 0x13, shield, jump
// star
/* star */
#define object2_spr_count(obj) (obj)->data[0].b[0]
#define object2_spr_tick(obj) (obj)->data[0].b[1]
// vinyl
/* vinyl */
#define object22_xvelocity(obj) (obj)->data[0].w
#define object22_damage(obj) (obj)->data[1].w
#define object22_player_num(obj) (obj)->data[2].w
// crate
/* crate */
#define object64_counter(obj) (obj)->data[1].w
#define object64_yvelocity(obj) (obj)->data[2].w
// monster
/* monster */
#define object82_state(obj) (obj)->data[0].b[0]
#define object82_type(obj) (obj)->data[0].b[1]
#define object82_anim_data(obj) (obj)->data[1].p
@ -111,16 +111,18 @@ struct player_t {
#define TRIGGERS_COUNT 36
#define LEVELS_COUNT 30
#define OBJECTS_COUNT 165
// offset | count | comment
// 2 20 animated tiles/vinyls
// 22 10 vinyls
// 32 8 * 4 (rotating) platforms
// 64 8 crates
// 72 10 bonuses spr_num:190
// 82 20 monsters
// 102 10
// 112 9 vertical bars
// 121 8 dragon monster (level 26)
/*
offset count
2 20 : animated tiles/vinyls
22 10 : vinyls
32 8*4 : (rotating) platforms
64 8 : crates
72 10 : bonuses spr_num:190
82 20 : monsters
102 10
112 9 : vertical bars
121 8 : dragon monster (level 26)
*/
struct vars_t {
int level;
@ -128,7 +130,7 @@ struct vars_t {
bool input_keystate[128];
uint32_t timestamp;
uint8_t input_key_left, input_key_right, input_key_down, input_key_up, input_key_space;
uint16_t buffer[128 * 2]; // level objects state 0xFFFF, 0xFF20 or g_vars.objects_table index
uint16_t buffer[128 * 2]; /* level objects state 0xFFFF, 0xFF20 or g_vars.objects_table index */
int16_t dragon_coords[1 + 128];
struct player_t players_table[2];
int16_t player_xscroll, player_map_offset;
@ -147,7 +149,7 @@ struct vars_t {
uint8_t *tilemap_data;
uint16_t level_pos_num;
uint8_t tilemap_type, tilemap_flags;
uint8_t tilemap_lut_type[0x100]; // type:0,1,2
uint8_t tilemap_lut_type[0x100]; /* type:0,1,2 */
uint8_t tilemap_lut_init[6 * 0x100];
uint8_t tilemap_lut_init2[0x100];
const uint8_t *tilemap_current_lut;

View File

@ -78,11 +78,28 @@ static void do_credits() {
g_sys.update_screen(g_res.vga, 1);
}
static void update_screen_img(const uint8_t *src) {
const int size = GAME_SCREEN_W * GAME_SCREEN_H;
if (size < 64000) {
return;
} else if (size == 64000) {
g_sys.update_screen(src, 0);
} else {
memset(g_res.vga, 0, size);
const int y_offs = (GAME_SCREEN_H - 200) / 2;
const int x_offs = (GAME_SCREEN_W - 320) / 2;
for (int y = 0; y < 200; ++y) {
memcpy(g_res.vga + (y_offs + y) * GAME_SCREEN_W + x_offs, src + y * 320, 320);
}
g_sys.update_screen(g_res.vga, 0);
}
}
static void do_titus_screen() {
uint8_t *data = load_file("TITUS.SQZ");
if (data) {
g_sys.set_screen_palette(data, 0, 256, 6);
g_sys.update_screen(data + 768, 0);
update_screen_img(data + 768);
fade_in_palette();
wait_input(70);
fade_out_palette();
@ -94,7 +111,7 @@ static void do_present_screen() {
uint8_t *data = load_file("PRESENT.SQZ");
if (data) {
g_sys.set_screen_palette(data, 0, 256, 6);
g_sys.update_screen(data + 768, 0);
update_screen_img(data + 768);
fade_in_palette();
free(data);
}
@ -112,7 +129,7 @@ static void do_menu() {
uint8_t *data = load_file("MENU.SQZ");
if (data) {
g_sys.set_screen_palette(data, 0, 256, 6);
g_sys.update_screen(data + 768, 0);
update_screen_img(data + 768);
fade_in_palette();
free(data);
memset(g_vars.input.keystate, 0, sizeof(g_vars.input.keystate));

View File

@ -24,33 +24,33 @@ struct club_anim_t {
};
struct player_t {
int16_t hdir; /* left:-1 right:1 */ // 0x9
uint8_t current_anim_num; // 0xB
const uint8_t *anim; // 0xC
int16_t y_velocity; // 0xE
uint8_t special_anim_num; /* idle, exhausted */ // 0x10
int16_t hdir; /* left:-1 right:1 */
uint8_t current_anim_num;
const uint8_t *anim;
int16_t y_velocity;
uint8_t special_anim_num; /* idle, exhausted */
};
struct club_projectile_t {
const uint8_t *anim; // 0xC
int16_t y_velocity; // 0xE
const uint8_t *anim;
int16_t y_velocity;
};
struct monster_t {
uint8_t unk5;
void *ref; // 0x6
int16_t x_velocity; // 0x8
int16_t y_velocity; // 0xA
const uint8_t *anim; // 0xC
uint8_t state; // 0xE
uint8_t energy; // 0xF
uint8_t unk10; // 0x10
void *ref;
int16_t x_velocity;
int16_t y_velocity;
const uint8_t *anim;
uint8_t state;
uint8_t energy;
uint8_t unk10; /* score */
};
struct thing_t {
void *ref; // 0x9
int16_t counter; // 0xC
int16_t unkE; // 0xE
void *ref;
int16_t counter;
int16_t unkE;
};
struct object_t {
@ -58,34 +58,36 @@ struct object_t {
uint16_t spr_num;
int16_t x_velocity;
uint8_t x_friction;
union { // 0x9 - 0x10
union {
struct player_t p; /* objects[1] */
struct club_projectile_t c; /* objects[2..5] */
struct monster_t m; /* objects[11..22] */
struct thing_t t;
} data;
uint8_t hit_counter; // 0x11
}; // sizeof == 18
uint8_t hit_counter;
};
#define MONSTERS_COUNT 12
#define OBJECTS_COUNT 108
// offset count
// 0 1 : club
// 1 1 : player
// 2 4 : axe
// 6 5 : club hitting decor frames
// 11 12 : monsters
// 23 32 : secret bonuses
// 55 20 : items
// 75 16 : bonus scores
// 91 7 : decor
/*
offset count
0 1 : club
1 1 : player
2 4 : axe
6 5 : club hitting decor frames
11 12 : monsters
23 32 : secret bonuses
55 20 : items
75 16 : bonus scores
91 7 : decor
*/
struct rotation_t {
uint16_t x_pos;
uint16_t y_pos;
uint8_t index_tbl; /* cos_/sin_tbl */
uint8_t radius;
}; // sizeof == 6
};
struct collision_t {
uint16_t x_pos;
@ -94,7 +96,7 @@ struct collision_t {
uint8_t unk5;
uint8_t unk6;
uint8_t unk7;
}; // sizeof == 8
};
struct vars_t {
uint32_t starttime;
@ -140,6 +142,8 @@ struct vars_t {
uint8_t player_nojump_counter;
uint8_t player_jumping_counter;
uint8_t player_action_counter;
int16_t player_hit_monster_counter;
uint8_t player_jump_monster_flag;
uint8_t player_club_type;
uint8_t player_club_power;
uint8_t player_club_powerup_duration;
@ -186,6 +190,8 @@ struct vars_t {
struct {
struct object_t *current_object;
uint8_t type10_dist;
uint8_t hit_mask;
} monster;
struct {
int16_t x_pos, y_pos;
@ -243,6 +249,10 @@ extern void game_main();
/* level.c */
extern void do_level();
/* monsters.c */
extern void monster_change_next_anim(struct object_t *obj);
extern void monster_change_prev_anim(struct object_t *obj);
/* screen.c */
extern void video_draw_string(int offset, int hspace, const char *s);
extern void video_clear();

View File

@ -258,7 +258,16 @@ static void level_update_tilemap() {
return;
}
}
memcpy(g_res.vga, g_res.background, 320 * 200);
if (GAME_SCREEN_W * GAME_SCREEN_H == 64000) {
memcpy(g_res.vga, g_res.background, 320 * 200);
} else {
memset(g_res.vga, 0, GAME_SCREEN_W * GAME_SCREEN_H);
for (int y = 0; y < MIN(200, GAME_SCREEN_H); ++y) {
for (int x = 0; x < GAME_SCREEN_W; x += 320) {
memcpy(g_res.vga + y * GAME_SCREEN_W + x, g_res.background + y * 320, MIN(320, GAME_SCREEN_W - x));
}
}
}
g_vars.tile_attr2_flags = 0;
g_vars.animated_tiles_flag = 0;
uint16_t offset = (g_vars.tilemap_y << 8) | g_vars.tilemap_x;
@ -896,8 +905,8 @@ static void level_reset_objects() {
obj->spr_num = 0xFFFF;
obj->hit_counter = 0;
}
g_vars.objects_tbl[1].x_pos = (g_options.start_xpos16 < 0) ? g_res.level.start_x_pos : g_options.start_xpos16;
g_vars.objects_tbl[1].y_pos = (g_options.start_ypos16 < 0) ? g_res.level.start_y_pos : g_options.start_ypos16;
g_vars.objects_tbl[1].x_pos = (g_options.start_xpos16 < 0) ? g_res.level.start_x_pos : (g_options.start_xpos16 << 4);
g_vars.objects_tbl[1].y_pos = (g_options.start_ypos16 < 0) ? g_res.level.start_y_pos : (g_options.start_ypos16 << 4);
}
static void level_reset() {
@ -1059,6 +1068,14 @@ static bool level_objects_collide(const struct object_t *si, const struct object
return (a + b > d);
}
static void level_monster_update_anim(struct object_t *obj) {
const uint8_t *p = obj->data.m.anim;
do {
p += 2;
} while (READ_LE_UINT16(p) != 0x7D00);
obj->data.m.anim = p + 2;
}
static void level_monster_die(struct object_t *obj, struct level_monster_t *m) {
const int num = m->unk8 + 74;
static const uint8_t data[] = { 1, 2, 3, 4, 6, 8 };
@ -1082,6 +1099,19 @@ static void level_monster_die(struct object_t *obj, struct level_monster_t *m) {
g_vars.current_bonus.spr_num = 0x2046;
level_add_object23_bonus(48, -128, 6);
} else {
level_monster_update_anim(obj);
uint8_t al = m->flags & ~0x37;
if (al != 0x88) {
m->flags &= ~8;
}
int dy = MIN(g_vars.player_club_power, 25);
dy = (-dy) << 3;
obj->data.m.y_velocity = dy;
int dx = dy >> 1;
if ((obj->data.m.unk5 & 0x80) == 0) {
dx = -dx;
}
obj->data.m.x_velocity = dx;
}
}
@ -1280,6 +1310,76 @@ static void level_update_objects_axe() {
static void level_update_monsters_state() {
}
static int level_get_tile_monster_offset(uint8_t tile_num, struct object_t *obj) {
const uint8_t attr = g_res.level.tile_attributes1[tile_num];
if ((attr & 0x30) == 0) {
return attr;
}
int x = (obj->x_pos & 15) / 3;
if (attr & 0x10) {
x = (attr & 15) + x;
} else {
x = (attr & 15) - x;
}
return x;
}
static void level_update_monster_pos(struct object_t *obj, struct level_monster_t *m) {
const uint16_t pos = ((obj->y_pos >> 4) << 8) | (obj->x_pos >> 4);
uint8_t dl = level_get_tile(pos);
uint8_t dh = level_get_tile(pos - 0x100);
int x_vel = obj->data.m.x_velocity;
if (x_vel != 0 && x_vel <= 1) {
x_vel = -x_vel;
}
uint8_t al = level_get_tile(pos + x_vel - 0x100);
al = g_res.level.tile_attributes1[al];
if ((m->flags & 0x40) != 0 && (m->flags & 0x80) != 0) {
if (al != 0) {
obj->x_pos -= obj->data.m.x_velocity >> 4;
} else {
monster_change_prev_anim(obj);
obj->data.m.y_velocity = 0;
m->flags &= ~0x80;
obj->y_pos -= 16;
}
} else {
if (al != 0) {
if (m->flags & 0x40) {
monster_change_next_anim(obj);
m->flags |= 0x80;
obj->data.m.y_velocity &= ~15;
obj->x_pos += obj->data.m.x_velocity >> 2;
return;
}
obj->data.m.x_velocity = -obj->data.m.x_velocity;
obj->x_pos += obj->data.m.x_velocity >> 4;
}
obj->y_pos -= 16;
SWAP(dl, dh);
al = g_res.level.tile_attributes1[dl];
if (al == 0) {
obj->y_pos += 16;
al = g_res.level.tile_attributes1[dh];
if (al == 0) {
if (obj->data.m.y_velocity < 256) {
obj->data.m.y_velocity += 16;
}
return;
}
}
obj->y_pos &= ~15;
obj->y_pos += level_get_tile_monster_offset(dl, obj);
if ((m->flags & 0x20) == 0) {
int dy = (-obj->data.m.y_velocity) >> 1;
if (abs(dy) <= 32) {
dy = 0;
}
obj->data.m.y_velocity = dy;
}
}
}
extern void monster_func1(int type, struct object_t *obj); /* update */
extern bool monster_func2(int type, struct level_monster_t *m); /* init */
@ -1298,12 +1398,14 @@ static void level_update_objects_monsters() {
if (obj->spr_num == 0xFFFF) {
continue;
}
print_debug(DBG_GAME, "monster #%d pos %d,%d spr %d", i, obj->x_pos, obj->y_pos, obj->spr_num);
obj->y_pos += obj->data.m.y_velocity >> 4;
if (obj->data.m.x_velocity != -1) {
obj->x_pos += obj->data.m.x_velocity >> 4;
}
struct level_monster_t *m = obj->data.m.ref;
if (m->flags & 8) {
level_update_monster_pos(obj, m);
}
const uint8_t *p = obj->data.m.anim;
int16_t num;
@ -1315,6 +1417,7 @@ static void level_update_objects_monsters() {
p += num;
}
uint16_t dx = 305 + (num & 0x1FFF);
g_vars.monster.hit_mask = ((num >> 8) & 0xE0) | g_vars.player_monsters_unk_counter;
if (g_vars.player_monsters_unk_counter == 0) {
obj->data.m.anim = p + 2;
} else {
@ -2030,6 +2133,13 @@ static void level_update_player_anim_5(uint8_t al) {
level_update_player_club_power();
}
static void level_update_player_anim_8(uint8_t al) {
level_update_screen_x_velocity();
level_update_player_x_velocity();
const uint8_t *p = level_update_player_anim2_num(al, al * 2);
level_update_object_anim(p);
}
static void level_update_player_decor() {
const int y_pos = (g_vars.objects_tbl[1].y_pos >> 4) - 1;
const int spr_num = g_vars.objects_tbl[1].spr_num & 0x1FFF;
@ -2216,6 +2326,9 @@ static void level_update_player() {
case 5:
level_update_player_anim_5(al);
break;
case 8:
level_update_player_anim_8(al);
break;
default:
print_warning("Unhandled anim_lut %d mask %d", al, mask);
break;
@ -2271,6 +2384,9 @@ static void level_update_player_collision() {
if (obj->spr_num == 0xFFFF || (obj->spr_num & 0x2000) == 0) {
continue;
}
if (g_options.cheats & CHEATS_NO_HIT) {
continue;
}
struct level_monster_t *m = obj->data.m.ref;
if (m->flags & 0x10) {
continue;
@ -2281,7 +2397,64 @@ static void level_update_player_collision() {
if (!level_objects_collide(obj_player, obj)) {
continue;
}
print_warning("Unhandled level_update_player_collision 6A43");
if (g_vars.player_hit_monster_counter != 0) {
level_monster_die(obj, m);
continue;
}
if (!g_vars.player_jump_monster_flag && g_vars.objects_tbl[1].data.p.y_velocity < 0) {
m->flags &= ~1;
g_vars.objects_tbl[1].hit_counter = 44;
g_vars.player_anim_0x40_flag = 0;
g_vars.objects_tbl[1].data.p.y_velocity = -128;
g_vars.objects_tbl[1].x_velocity = -(g_vars.objects_tbl[1].x_velocity << 2);
g_vars.player_gravity_flag = 0;
if (g_vars.player_flying_flag != 0) {
g_vars.player_flying_flag = 0;
} else {
--g_vars.player_energy;
if (g_vars.player_energy < 0) {
level_player_die();
}
}
return;
}
if (g_vars.player_gravity_flag == 0) {
int num = obj->data.m.unk10 >> 2;
if (num != 11) {
obj->data.m.unk10 += 4;
++num;
if ((num & 1) == 0) {
static const uint16_t data[] = { 0xFF46, 0xE0, 0xE1, 0x12D, 0x12E, 0x12F };
level_add_object75_score(obj_player, data[num >> 1]);
}
}
g_vars.objects_tbl[1].data.p.y_velocity = (g_vars.input.key_up != 0) ? -224 : -64;
g_vars.player_jumping_counter = 0;
} else if (g_vars.objects_tbl[1].data.p.y_velocity <= 32) {
g_vars.objects_tbl[1].data.p.y_velocity = -96;
g_vars.player_jumping_counter = 0;
} else {
int num = (obj->data.m.unk10) & 3;
level_add_object75_score(obj_player, 82 + (num << 1));
obj->spr_num |= 0x4000;
if (num == 2) {
obj->data.m.state = 0xFF;
level_monster_update_anim(obj);
m->flags &= ~8;
obj->data.m.y_velocity = -200;
int delta = abs(g_vars.objects_tbl[1].data.p.y_velocity);
if (obj->x_pos <= g_vars.objects_tbl[1].x_pos) {
delta = -delta;
}
obj->data.m.x_velocity = delta * 3;
} else {
++obj->data.m.unk10;
g_vars.objects_tbl[1].data.p.y_velocity = -96;
g_vars.player_jumping_counter = 0;
}
}
return;
}
}
/* bonuses */
@ -2430,10 +2603,14 @@ static void level_update_player_collision() {
if (obj->spr_num == 0xFFFF) {
continue;
}
struct level_monster_t *m = obj->data.m.ref;
if (m->flags & 0x10) {
continue;
}
if ((obj->spr_num & 0x2000) == 0) {
continue;
}
print_warning("Unhandled bonus num 169");
level_monster_die(obj, m);
}
g_vars.shake_screen_counter = 9;
level_clear_item(obj);
@ -3114,6 +3291,7 @@ void do_level() {
level_clear_panel();
level_draw_panel();
level_sync();
g_vars.monster.type10_dist = 0;
random_reset();
while (!g_sys.input.quit) {
level_update_objects_hit_animation();

View File

@ -5,6 +5,15 @@
#include "resource.h"
#include "util.h"
static void monster_reset(struct object_t *obj, struct level_monster_t *m) {
m->current_tick = 0;
obj->spr_num = 0xFFFF;
m->flags &= ~4;
if ((m->flags & 2) == 0) {
m->spr_num = 0xFFFF;
}
}
static void monster_func1_helper(struct object_t *obj, int16_t x_pos, int16_t y_pos) {
struct level_monster_t *m = obj->data.m.ref;
if (obj->data.m.state == 0xFF) {
@ -25,12 +34,7 @@ static void monster_func1_helper(struct object_t *obj, int16_t x_pos, int16_t y_
m->flags &= ~4;
m->current_tick = 0;
} else {
m->current_tick = 0;
obj->spr_num = 0xFFFF;
m->flags &= ~4;
if ((m->flags & 2) == 0) {
m->spr_num = 0xFFFF;
}
monster_reset(obj, m);
}
}
@ -41,7 +45,7 @@ static bool monster_next_tick(struct level_monster_t *m) {
return ((m->current_tick >> 2) < m->total_ticks);
}
static void monster_change_next_anim(struct object_t *obj) {
void monster_change_next_anim(struct object_t *obj) {
const uint8_t *p = obj->data.m.anim;
while ((int16_t)READ_LE_UINT16(p) >= 0) {
p += 2;
@ -49,7 +53,7 @@ static void monster_change_next_anim(struct object_t *obj) {
obj->data.m.anim = p + 2;
}
static void monster_change_prev_anim(struct object_t *obj) {
void monster_change_prev_anim(struct object_t *obj) {
const uint8_t *p = obj->data.m.anim;
do {
p -= 2;
@ -67,7 +71,7 @@ static struct rotation_t *find_rotation() {
return 0;
}
static void monster_rotate_platform(struct level_monster_t *m, int index, int step) {
static void monster_rotate_tiles(struct level_monster_t *m, int index, int step) {
step >>= 2;
uint8_t radius = step;
for (int i = 0; i < 3; ++i) {
@ -83,9 +87,9 @@ static void monster_rotate_platform(struct level_monster_t *m, int index, int st
}
static void monster_rotate_pos(struct object_t *obj, struct level_monster_t *m) {
obj->x_pos = m->x_pos + ((m->type4.unkD * (cos_tbl[m->type4.angle] >> 2)) >> 4);
obj->y_pos = m->y_pos + ((m->type4.unkD * (sin_tbl[m->type4.angle] >> 2)) >> 4);
monster_rotate_platform(m, m->type4.angle, m->type4.unkD);
obj->x_pos = m->x_pos + ((m->type4.radius * (((int8_t)cos_tbl[m->type4.angle]) >> 2)) >> 4);
obj->y_pos = m->y_pos + ((m->type4.radius * (((int8_t)sin_tbl[m->type4.angle]) >> 2)) >> 4);
monster_rotate_tiles(m, m->type4.angle, m->type4.radius);
}
static void monster_update_y_velocity(struct object_t *obj, struct level_monster_t *m) {
@ -94,12 +98,7 @@ static void monster_update_y_velocity(struct object_t *obj, struct level_monster
obj->data.m.y_velocity += 15;
}
} else {
m->current_tick = 0;
obj->spr_num = 0xFFFF;
m->flags &= ~4;
if ((m->flags & 2) == 0) {
m->spr_num = 0xFFFF;
}
monster_reset(obj, m);
}
}
@ -108,7 +107,7 @@ static void monster_func1_type2(struct object_t *obj) {
struct level_monster_t *m = obj->data.m.ref;
const uint8_t state = obj->data.m.state;
if (state < 2) {
monster_rotate_platform(m, 0, obj->y_pos - m->y_pos);
monster_rotate_tiles(m, 0, obj->y_pos - m->y_pos);
if (state == 0) {
obj->data.m.y_velocity = m->type2.unkE << 4;
const int dy = obj->y_pos - m->y_pos;
@ -118,7 +117,7 @@ static void monster_func1_type2(struct object_t *obj) {
obj->data.m.state = 1;
monster_change_next_anim(obj);
} else if (state == 1) {
obj->data.m.y_velocity = m->type2.unkE << 4;
obj->data.m.y_velocity = -(m->type2.unkE << 4);
const int dy = obj->y_pos - m->y_pos;
if (dy >= 0) {
return;
@ -140,8 +139,8 @@ static void monster_func1_type4(struct object_t *obj) {
return;
}
const int dy = obj->y_pos - m->y_pos;
monster_rotate_platform(m, 0, dy);
if (m->type4.unkD > dy) {
monster_rotate_tiles(m, 0, dy);
if (m->type4.radius > dy) {
obj->y_pos += 2;
} else {
obj->data.m.state = 1;
@ -154,6 +153,7 @@ static void monster_func1_type4(struct object_t *obj) {
obj->data.m.state = 2;
}
} else if (state == 2) {
monster_rotate_pos(obj, m);
if (m->type4.angle & 0x80) {
++m->type4.unk10;
} else {
@ -245,6 +245,65 @@ static void monster_func1_type9(struct object_t *obj) {
}
}
static void monster_func1_type10(struct object_t *obj) {
monster_func1_helper(obj, obj->x_pos, obj->y_pos);
struct level_monster_t *m = obj->data.m.ref;
const uint8_t state = obj->data.m.state;
if (g_vars.level_num == 5 && g_vars.shake_screen_counter != 0 && state != 3 && state != 0xFF) {
m->current_tick = 1;
}
if (state == 0) {
m->flags |= 0x18;
if (obj->data.m.y_velocity == 0) {
obj->data.m.state = 1;
}
} else if (state == 1) {
if (obj->data.m.y_velocity != 0) {
obj->data.m.state = 0;
} else {
m->current_tick = 30;
obj->data.m.state = 2;
monster_change_next_anim(obj);
}
} else if (state == 2) {
const int dx = abs(obj->data.m.x_velocity);
if (dx < 16) {
if (g_vars.monster.hit_mask == 0) {
return;
}
m->flags = 0xF;
int x_vel = m->type10.unkD;
if (obj->x_pos >= g_vars.objects_tbl[1].x_pos) {
x_vel = -x_vel;
}
obj->data.m.x_velocity = x_vel;
}
if ((g_vars.level_draw_counter & 3) == 0) {
--m->current_tick;
if (m->current_tick == 0) {
obj->data.m.state = 3;
obj->data.m.y_velocity = 0;
obj->data.m.x_velocity >>= 15;
m->flags = 0x36;
monster_change_next_anim(obj);
}
}
} else if (state == 3) {
if (g_vars.monster.hit_mask == 0) {
return;
}
monster_reset(obj, m);
} else if (state == 0xFF) {
if ((m->flags & 1) != 0 || (obj->data.m.unk5 & 0x20) != 0 || g_vars.objects_tbl[1].y_pos >= obj->y_pos) {
if (obj->data.m.y_velocity < 240) {
obj->data.m.y_velocity += 15;
}
} else {
monster_reset(obj, m);
}
}
}
void monster_func1(int type, struct object_t *obj) {
switch (type) {
case 1:
@ -262,6 +321,9 @@ void monster_func1(int type, struct object_t *obj) {
case 9:
monster_func1_type9(obj);
break;
case 10:
monster_func1_type10(obj);
break;
default:
print_warning("monster_func1 unhandled monster type %d", type);
break;
@ -310,9 +372,7 @@ static bool monster_is_visible(int x_pos, int y_pos) {
}
static bool monster_func2_type1(struct level_monster_t *m) {
const int16_t x_pos = m->x_pos;
const int16_t y_pos = m->y_pos;
if (!monster_is_visible(x_pos, y_pos)) {
if (!monster_is_visible(m->x_pos, m->y_pos)) {
return false;
}
return monster_init_object(m);
@ -345,6 +405,8 @@ static bool monster_func2_type4(struct level_monster_t *m) {
return false;
}
m->flags = 5;
m->type4.angle = 0;
m->type4.unk10 = 0;
return true;
}
@ -379,20 +441,62 @@ static bool monster_func2_type10(struct level_monster_t *m) {
if (m->total_ticks > (m->current_tick >> 2)) {
return false;
}
const int dx = (g_vars.objects_tbl[1].x_pos >> 4) - m->x_pos;
const uint16_t x = m->x_pos;
const uint16_t y = m->y_pos;
const int dx = (g_vars.objects_tbl[1].x_pos >> 4) - (x & 255);
if (dx < 0) {
return false;
}
const int dy = (g_vars.objects_tbl[1].y_pos >> 4) - m->y_pos;
if ((y & 255) < dx) {
return false;
}
const int dy = (g_vars.objects_tbl[1].y_pos >> 4) - (x >> 8);
if (dy < 0) {
return false;
}
if ((y >> 8) < dy) {
return false;
}
struct object_t *obj = find_object_monster();
if (!obj) {
return false;
}
print_warning("monster_func2_type10 6579");
return true;
obj->data.m.unk10 = 0;
g_vars.monster.current_object = obj;
static const int16_t dist_tbl[] = { 120, 100, -90, 110, -120, 40, 80, 60 };
obj->x_pos = g_vars.objects_tbl[1].x_pos + dist_tbl[g_vars.monster.type10_dist & 7];
++g_vars.monster.type10_dist;
obj->data.m.x_velocity = 0;
uint8_t bh = (g_vars.objects_tbl[1].y_pos >> 4) + 4;
uint8_t bl = (obj->x_pos >> 4);
uint16_t bp = (bh << 8) | bl;
for (int i = 0; i < 10; ++i) {
if (bp < (g_vars.tilemap_h << 8)) {
bool init_spr = true;
for (int j = 0; j < 3; ++j) {
const uint8_t tile_num = g_res.leveldat[bp - j * 0x100];
if (g_res.level.tile_attributes1[tile_num] != 0) {
init_spr = false;
break;
}
}
if (init_spr) {
obj->y_pos = bh << 4;
obj->spr_num = m->spr_num;
obj->data.m.ref = m;
m->flags = 0x17;
obj->data.m.state = 0;
obj->data.m.y_velocity = 0;
obj->data.m.energy = m->energy;
return true;
}
}
bp -= 0x100;
if (bp < 0x300) {
return false;
}
}
return false;
}
static bool monster_func2_type11(struct level_monster_t *m) {

View File

@ -130,26 +130,36 @@ void load_leveldat(const uint8_t *p, struct level_t *level) {
m->x_pos = READ_LE_UINT16(p + 0x9);
m->y_pos = READ_LE_UINT16(p + 0xB);
switch (type) {
case 2:
case 2: /* vertical (eg. spider) */
assert(len == 15);
m->type2.y_range = p[0xD];
m->type2.unkE = p[0xE];
case 4:
m->type4.unkD = p[0xD];
break;
case 4: /* rotate (eg. spider) */
assert(len == 17);
m->type4.radius = p[0xD];
m->type4.unkE = p[0xE];
m->type4.angle = p[0xF];
m->type4.unk10 = p[0x10];
break;
case 8:
case 8: /* jumps (eg. leopard) */
assert(len == 17);
m->type8.x_range = p[0xD];
m->type8.unkE = p[0xE];
m->type8.unkF = p[0xF];
m->type8.y_range = p[0x10];
break;
case 9:
case 9: /* horizontal */
assert(len == 19);
m->type9.unkD = READ_LE_UINT16(p + 0xD);
m->type9.unkF = READ_LE_UINT16(p + 0xF);
m->type9.unk11 = p[0x11];
m->type9.unk12 = p[0x12];
break;
case 10: /* come out of the ground */
assert(len == 14);
m->type10.unkD = p[0xD];
break;
default:
break;
}

View File

@ -5,11 +5,11 @@
#include "intern.h"
struct level_gate_t {
uint16_t enter_pos; // (y << 8) | x
uint16_t enter_pos;
uint16_t tilemap_pos;
uint16_t dst_pos;
uint8_t scroll_flag;
}; // sizeof == 7
};
struct level_platform_t {
uint16_t tilemap_pos;
@ -17,22 +17,22 @@ struct level_platform_t {
uint8_t h;
uint16_t unk4;
uint16_t unk6;
uint8_t unk8; // y_offs
uint8_t unk8;
uint8_t unk9;
}; // sizeof == 10
};
struct level_bonus_t {
uint8_t tile_num0; /* new tile */
uint8_t tile_num1; /* original tile */
uint8_t count;
uint16_t pos; // (y << 8) | x
}; // sizeof == 5
uint16_t pos;
};
struct level_item_t {
int16_t x_pos, y_pos;
uint16_t spr_num;
int8_t y_delta;
}; // sizeof == 7
};
struct level_trigger_t {
uint16_t x_pos;
@ -44,9 +44,9 @@ struct level_trigger_t {
int8_t unk7;
uint8_t unk8;
uint8_t unk9;
uint8_t state; // 0xA
uint16_t y_delta; // 0xB
uint8_t counter; // 0xD
uint8_t state;
uint16_t y_delta;
uint8_t counter;
} type8;
struct {
int16_t unk7;
@ -56,42 +56,45 @@ struct level_trigger_t {
} other;
};
uint8_t unkE;
}; // sizeof == 15
};
struct level_monster_t {
uint8_t len;
uint8_t type;
uint16_t spr_num; // 0x2
uint8_t flags; // 0x4
uint8_t energy; // 0x5
uint16_t spr_num;
uint8_t flags;
uint8_t energy;
uint8_t total_ticks;
uint8_t current_tick;
uint8_t unk8;
uint16_t x_pos; // 0x9
uint16_t y_pos; // 0xB
uint16_t x_pos;
uint16_t y_pos;
union {
struct {
uint8_t y_range; // 0xD
int8_t unkE; // 0xE, cbw
uint8_t y_range;
int8_t unkE;
} type2;
struct {
uint8_t unkD;
uint8_t radius;
uint8_t unkE;
uint8_t angle;
uint8_t unk10;
} type4;
struct {
uint8_t x_range; // 0xD
int8_t unkE; // 0xE, cbw
int8_t unkF; // 0xF, cbw
uint8_t y_range; // 0x10
uint8_t x_range;
int8_t unkE;
int8_t unkF;
uint8_t y_range;
} type8;
struct {
int16_t unkD; // 0xD
int16_t unkF; // 0xF
int8_t unk11; // 0x11
uint8_t unk12; // 0x12
int16_t unkD;
int16_t unkF;
int8_t unk11;
uint8_t unk12;
} type9;
struct {
uint8_t unkD;
} type10;
};
};

View File

@ -76,12 +76,16 @@ void video_draw_string(int offset, int hspace, const char *s) {
void video_draw_panel_number(int offset, int num) {
const uint8_t *fnt = g_res.allfonts + 48 * 41 + 160 * 23;
decode_planar(fnt + num * 96, g_res.vga + offset * 8, 320, 16, 12, 0);
const int y = (offset * 8) / 320 + (GAME_SCREEN_H - 200);
const int x = (offset * 8) % 320 + (GAME_SCREEN_W - 320) / 2;
decode_planar(fnt + num * 96, g_res.vga + y * GAME_SCREEN_W + x, GAME_SCREEN_W, 16, 12, 0);
}
void video_draw_number(int offset, int num) {
const uint8_t *fnt = g_res.allfonts + 0x1C70;
decode_planar(fnt + num * 88, g_res.vga + offset * 8, 320, 16, 11, 0);
const int y = (offset * 8) / 320;
const int x = (offset * 8) % 320;
decode_planar(fnt + num * 88, g_res.vga + y * GAME_SCREEN_W + x, GAME_SCREEN_W, 16, 11, 0);
}
void video_clear() {
@ -93,7 +97,9 @@ void video_copy_img(const uint8_t *src) {
}
void video_draw_panel(const uint8_t *src) {
decode_planar(src, g_res.vga + TILEMAP_SCREEN_H * 320, 320, 320, 23, 0xFF);
const int h = GAME_SCREEN_H - PANEL_H;
const int x = (GAME_SCREEN_W - 320) / 2;
decode_planar(src, g_res.vga + h * GAME_SCREEN_W + x, GAME_SCREEN_W, 320, 23, 0xFF);
}
void video_draw_tile(const uint8_t *src, int x_offset, int y_offset) {