Files
zjui-ece385-final/ECE385_src/gamelogic.c

395 lines
13 KiB
C

#include "gamelogic.h"
#include <stdio.h>
#include "main.h"
#include "vga.h"
#include "resources/resource.h"
#include "comm.h"
#include "rng.h"
#include "resources/plane_player.h"
#include "resources/scoreboard_bg.h"
volatile uint32_t frame_count = 0;
int player_plane_id = 0;
volatile vga_sprite_info_t* player_plane_info = NULL;
volatile int player_score = 0;
int next_frame_count = 0;
void game_init() {
vga_set(0, 0, VGA_WIDTH, VGA_HEIGHT, background);
vga_fill(0, VGA_HEIGHT, VGA_WIDTH, VGA_STATUSBAR_HEIGHT, 0x0000);
sprites_init(VGA_SPRITE_PLANE);
sprites_init(VGA_SPRITE_BULLET);
// printf("Wait for keyboard ready\n");
// while(!keycode_comm->keyboard_present);
// printf("Keyboard ready\n");
frame_count = 0;
next_frame_count = 0;
player_score = 0;
vga_statusbar_string(0, (uint8_t*) "生命 18/18 积分 0");
// Create player's plane
do {
int id;
const sprite_data_t* plane_data = plane_player_seq + (frame_count / PLAYER_PLANE_IMG_UPDATE_INTERVAL) % PLANE_PLAYER_SEQ_LEN;
int width = plane_data->width;
int height = plane_data->height;
if(255 == (id = sprites_allocate(VGA_SPRITE_PLANE))) {
// printf("ERR sprites_allocate\n");
break;
}
if(255 == sprites_load_data(VGA_SPRITE_PLANE, id,
plane_data->data,
plane_data->width * plane_data->height)) {
// printf("ERR sprites_load_data\n");
break;
}
volatile vga_sprite_info_t* sprite_info = sprites_get(VGA_SPRITE_PLANE, id);
if(NULL == sprite_info) {
// printf("ERR sprites_get\n");
break;
}
sprite_info->physical->x = (VGA_SPRITE_WIDTH - (75 << VGA_SPRITE_HW_SHIFT_BITS)) / 2;
sprite_info->physical->y = VGA_SPRITE_HEIGHT - (100 << VGA_SPRITE_HW_SHIFT_BITS);
sprite_info->type = 0;
sprite_info->hp = PLAYER_PLANE_HP;
sprite_info->vx = 0;
sprite_info->vx_max = VGA_WIDTH << VGA_SPRITE_HW_SHIFT_BITS;
sprite_info->vx_min = -(VGA_WIDTH << VGA_SPRITE_HW_SHIFT_BITS);
sprite_info->vy = 0;
sprite_info->vy_max = VGA_HEIGHT << VGA_SPRITE_HW_SHIFT_BITS;
sprite_info->vy_min = -(VGA_HEIGHT << VGA_SPRITE_HW_SHIFT_BITS);
sprite_info->ax = 0;
sprite_info->ay = 0;
sprite_info->physical->width = width;
sprite_info->physical->height = height;
sprite_info->bullet_color = PLAYER_BULLET_COLOR;
// Set plane as player's plane
player_plane_id = id;
player_plane_info = sprite_info;
} while(0);
game_state = IN_GAME;
}
void game_loop() {
if(game_state != IN_GAME) return;
if(esc_pressed()) {
// Quit game immediately
game_state = GAME_OVER;
return;
}
static int io_vga_sync_prev = 0;
if((!io_vga_sync_prev) && (*io_vga_sync)) {
// New frame occured, do job
frame_count++;
if(p_pressed()) {
// Cheat code: set HP max
player_plane_info->hp = PLAYER_PLANE_HP;
if(player_score >= 10) {
player_score -= 10;
} else {
player_score = 0;
}
}
// Update statusbar
static int prev_hp = 0;
static int prev_score = 0;
if(prev_hp != player_plane_info->hp || prev_score != player_score) {
vga_statusbar_english(5, '0' + player_plane_info->hp / 10);
vga_statusbar_english(6, '0' + player_plane_info->hp % 10);
char buf[256];
snprintf(buf, 255, "%d ", player_score);
vga_statusbar_string(16, (uint8_t*) buf);
}
prev_hp = player_plane_info->hp;
prev_score = player_score;
if(player_plane_info->type == 0) {
// Player alive, allow moving
handle_player_plane_keyboard(player_plane_id, player_plane_info);
if(frame_count % PLAYER_PLANE_IMG_UPDATE_INTERVAL == 0) {
const sprite_data_t* plane_data = plane_player_seq + (frame_count / PLAYER_PLANE_IMG_UPDATE_INTERVAL) % PLANE_PLAYER_SEQ_LEN;
// Update player plane image every few frames
if(255 == sprites_load_data(VGA_SPRITE_PLANE, player_plane_id,
plane_data->data,
plane_data->width * plane_data->height)) {
// printf("ERR sprites_load_data\n");
}
}
} else {
// Disallow moving when exploding
player_plane_info->vx = 0;
player_plane_info->vy = 0;
player_plane_info->ax = 0;
player_plane_info->ay = 0;
}
handle_enemy_planes_spawn();
handle_enemy_planes_firing();
// Sprites manager job
sprites_tick(VGA_SPRITE_PLANE);
sprites_tick(VGA_SPRITE_BULLET);
sprites_collision_detect();
// Update player HP
*io_led_red = 0xffffffff << (18 - player_plane_info->hp);
}
io_vga_sync_prev = *io_vga_sync;
}
void handle_player_plane_keyboard(int player_plane_id, volatile vga_sprite_info_t* player_plane_info) {
uint8_t pressed_up = 0, pressed_left = 0, pressed_down = 0, pressed_right = 0;
for(int i = 0; i < 2; i++) {
int keycode = keycode_comm->keycode[i];
// if(i == 0) *io_hex = keycode;
switch(keycode) {
case 0x1A: // W
case 0x52: // Up
pressed_up = 1;
break;
case 0x04: // A
case 0x50: // Left
pressed_left = 1;
break;
case 0x16: // S
case 0x51: // Down
pressed_down = 1;
break;
case 0x07: // D
case 0x4F: // Right
pressed_right = 1;
break;
}
}
// Y input & resistance
if(pressed_up && pressed_down) {
// Player went crazy, do nothing
} else if(pressed_up) {
player_plane_info->vy -= PLAYER_PLANE_ACCELERATION;
if(player_plane_info->vy < -PLAYER_PLANE_SPEED_MAX) player_plane_info->vy = -PLAYER_PLANE_SPEED_MAX;
} else if(pressed_down) {
player_plane_info->vy += PLAYER_PLANE_ACCELERATION;
if(player_plane_info->vy > PLAYER_PLANE_SPEED_MAX) player_plane_info->vy = PLAYER_PLANE_SPEED_MAX;
} else if(player_plane_info->vy > 0) {
player_plane_info->vy -= PLAYER_PLANE_DEACCELERATION;
} else if(player_plane_info->vy < 0) {
player_plane_info->vy += PLAYER_PLANE_DEACCELERATION;
}
// X input & resistance
if(pressed_left && pressed_right) {
// Player went crazy, do nothing
} else if(pressed_left) {
player_plane_info->vx -= PLAYER_PLANE_ACCELERATION;
if(player_plane_info->vx < -PLAYER_PLANE_SPEED_MAX) player_plane_info->vx = -PLAYER_PLANE_SPEED_MAX;
} else if(pressed_right) {
player_plane_info->vx += PLAYER_PLANE_ACCELERATION;
if(player_plane_info->vx > PLAYER_PLANE_SPEED_MAX) player_plane_info->vx = PLAYER_PLANE_SPEED_MAX;
} else if(player_plane_info->vx > 0) {
player_plane_info->vx -= PLAYER_PLANE_DEACCELERATION;
} else if(player_plane_info->vx< 0) {
player_plane_info->vx += PLAYER_PLANE_DEACCELERATION;
}
// Prevent plane from flying out of screen
sprites_limit_speed(VGA_SPRITE_PLANE, player_plane_id);
if(frame_count % PLAYER_BULLET_INTERVAL == 0) {
for(int i = 0; i < 3; i++) {
int id;
if(255 == (id = sprites_allocate(VGA_SPRITE_BULLET))) {
// printf("ERR sprites_allocate\n");
break;
}
volatile vga_sprite_info_t* sprite_info = sprites_get(VGA_SPRITE_BULLET, id);
if(NULL == sprite_info) {
// printf("ERR sprites_get\n");
break;
}
sprite_info->physical->x = player_plane_info->physical->x
+ (player_plane_info->physical->width << VGA_SPRITE_HW_SHIFT_BITS) / 2;
sprite_info->physical->y = player_plane_info->physical->y;
sprite_info->physical->bullet_radius = PLAYER_BULLET_RADIUS;
sprite_info->physical->bullet_color = player_plane_info->bullet_color;
sprite_info->type = 0;
sprite_info->vx = PLAYER_BULLET_VX * (i - 1) + player_plane_info->vx;
sprite_info->vx_max = VGA_WIDTH << VGA_SPRITE_HW_SHIFT_BITS;
sprite_info->vx_min = -(VGA_WIDTH << VGA_SPRITE_HW_SHIFT_BITS);
sprite_info->vy = -PLAYER_BULLET_VY + player_plane_info->vy;
sprite_info->vy_max = VGA_HEIGHT << VGA_SPRITE_HW_SHIFT_BITS;
sprite_info->vy_min = -(VGA_HEIGHT << VGA_SPRITE_HW_SHIFT_BITS);
sprite_info->ax = PLAYER_BULLET_AX * (i - 1);
sprite_info->ay = -PLAYER_BULLET_AY;
};
}
}
void handle_enemy_planes_spawn() {
if(frame_count > next_frame_count) {
int res_id = rng_generate() % ENEMY_RESOURCE_NUM;
const uint16_t* resource = enemies[res_id].data;
int width = enemies[res_id].width;
int height = enemies[res_id].height;
int id;
if(255 == (id = sprites_allocate(VGA_SPRITE_PLANE))) {
// printf("ERR sprites_allocate\n");
return;
}
if(255 == sprites_load_data(VGA_SPRITE_PLANE, id, resource, width * height)) {
// printf("ERR sprites_load_data\n");
return;
}
volatile vga_sprite_info_t* sprite_info = sprites_get(VGA_SPRITE_PLANE, id);
if(NULL == sprite_info) {
// printf("ERR sprites_get\n");
return;
}
int generate_pos = rng_generate() % (VGA_WIDTH + VGA_HEIGHT);
if(generate_pos < VGA_HEIGHT / 2) {
// Left top side of screen
sprite_info->physical->x = ((-width) << VGA_SPRITE_HW_SHIFT_BITS) + 1;
sprite_info->physical->y = generate_pos << VGA_SPRITE_HW_SHIFT_BITS;
sprite_info->ax = rng_generate() % (2 * ENEMY_PLANE_AX) - ENEMY_PLANE_AX;
sprite_info->ay = rng_generate() % (2 * ENEMY_PLANE_AY) - ENEMY_PLANE_AY;
sprite_info->vx = rng_generate() % ENEMY_PLANE_VX;
if(sprite_info->ax < 0) {
sprite_info->vx_min = ENEMY_PLANE_VX / 2;
} else {
sprite_info->vx_min = 0;
}
sprite_info->vx_max = ENEMY_PLANE_VX * 2;
sprite_info->vy = rng_generate() % ENEMY_PLANE_VY;
if(generate_pos < VGA_HEIGHT / 4) {
sprite_info->vy_min = 0;
} else {
sprite_info->vy_min = -ENEMY_PLANE_VY / 4;
}
sprite_info->vy_max = ENEMY_PLANE_VY * 2;
} else if(generate_pos < VGA_HEIGHT) {
// Right top side of screen
sprite_info->physical->x = VGA_WIDTH << VGA_SPRITE_HW_SHIFT_BITS;
sprite_info->physical->y = (generate_pos - VGA_HEIGHT / 2) << VGA_SPRITE_HW_SHIFT_BITS;
sprite_info->ax = rng_generate() % (2 * ENEMY_PLANE_AX) - ENEMY_PLANE_AX;
sprite_info->ay = rng_generate() % (2 * ENEMY_PLANE_AY) - ENEMY_PLANE_AY;
sprite_info->vx = -(rng_generate() % ENEMY_PLANE_VX);
sprite_info->vx_min = -ENEMY_PLANE_VX * 2;
if(sprite_info->ax > 0) {
sprite_info->vx_max = -ENEMY_PLANE_VX / 2;
} else {
sprite_info->vx_max = 0;
}
sprite_info->vy = rng_generate() % ENEMY_PLANE_VY;
if(generate_pos < VGA_HEIGHT * 3 / 4) {
sprite_info->vy_min = 0;
} else {
sprite_info->vy_min = -ENEMY_PLANE_VY / 4;
}
sprite_info->vy_max = ENEMY_PLANE_VY * 2;
} else {
// Top side of screen
sprite_info->physical->x = (generate_pos - VGA_HEIGHT) << VGA_SPRITE_HW_SHIFT_BITS;
sprite_info->physical->y = 0;
sprite_info->vx = rng_generate() % (2 * ENEMY_PLANE_VX) - ENEMY_PLANE_VX;
if(generate_pos - VGA_HEIGHT < VGA_WIDTH / 4) {
sprite_info->vx_min = 0;
} else {
sprite_info->vx_min = -ENEMY_PLANE_VX;
}
if(generate_pos - VGA_HEIGHT > VGA_WIDTH * 3 / 4) {
sprite_info->vx_max = 0;
} else {
sprite_info->vx_max = ENEMY_PLANE_VX;
}
// BE CAREFUL WHEN USING GAME_DIFFICULTY!!! See definition
sprite_info->vx_max = ENEMY_PLANE_VX * GAME_DIFFICULTY;
sprite_info->vy = rng_generate() % ENEMY_PLANE_VY;
sprite_info->vy_min = ENEMY_PLANE_VY * GAME_DIFFICULTY / 2;
sprite_info->vy_max = ENEMY_PLANE_VY * GAME_DIFFICULTY * 4;
sprite_info->ax = rng_generate() % (2 * ENEMY_PLANE_AX) - ENEMY_PLANE_AX;
sprite_info->ay = rng_generate() % (2 * ENEMY_PLANE_AY) - ENEMY_PLANE_AY;
}
sprite_info->type = 1;
sprite_info->hp = ENEMY_PLANE_HP * GAME_DIFFICULTY;
sprite_info->bullet_color = enemies[res_id].bullet_color;
sprite_info->physical->width = width;
sprite_info->physical->height = height;
// Add difficulty as game progress
int interval_min = ENEMY_PLANE_SPAWN_INTERVAL_MIN - frame_count / 512;
if(interval_min < ENEMY_PLANE_SPAWN_INTERVAL_MIN / 2) {
interval_min = ENEMY_PLANE_SPAWN_INTERVAL_MIN;
}
next_frame_count = frame_count + interval_min
+ rng_generate() % (ENEMY_PLANE_SPAWN_INTERVAL_MAX - ENEMY_PLANE_SPAWN_INTERVAL_MIN);
}
}
void handle_enemy_planes_firing() {
vga_entity_manage_t* planes = VGA_SPRITE_PLANE;
for(int i = 0; i < planes->max_size; i++) {
volatile vga_sprite_info_t* plane_info = planes->info_arr + i;
if(!plane_info->used) continue;
if(plane_info->type != 1) continue;
int enemy_bullet_interval = ENEMY_BULLET_INTERVAL * GAME_DIFFICULTY_INV;
if((frame_count - plane_info->frame_created) % enemy_bullet_interval == 0) {
int id;
if(255 == (id = sprites_allocate(VGA_SPRITE_BULLET))) {
// printf("ERR sprites_allocate\n");
break;
}
volatile vga_sprite_info_t* sprite_info = sprites_get(VGA_SPRITE_BULLET, id);
if(NULL == sprite_info) {
// printf("ERR sprites_get\n");
break;
}
sprite_info->physical->x = plane_info->physical->x
+ (plane_info->physical->width << VGA_SPRITE_HW_SHIFT_BITS) / 2;
sprite_info->physical->y = plane_info->physical->y
+ (plane_info->physical->height << VGA_SPRITE_HW_SHIFT_BITS);
sprite_info->physical->bullet_radius = ENEMY_BULLET_RADIUS;
sprite_info->physical->bullet_color = plane_info->bullet_color;
sprite_info->type = 1;
sprite_info->vx = plane_info->vx + ENEMY_BULLET_VX * GAME_DIFFICULTY;
sprite_info->vy = plane_info->vy + ENEMY_BULLET_VY * GAME_DIFFICULTY;
sprite_info->vx_max = ENEMY_BULLET_VMAX * GAME_DIFFICULTY;
sprite_info->vx_min = -(ENEMY_BULLET_VMAX * GAME_DIFFICULTY);
sprite_info->vy_max = ENEMY_BULLET_VMAX * GAME_DIFFICULTY;
sprite_info->vy_min = -(ENEMY_BULLET_VMAX * GAME_DIFFICULTY);
sprite_info->ax = ENEMY_BULLET_AX;
sprite_info->ay = ENEMY_BULLET_AY;
}
}
}
void game_over() {
*io_vga_background_offset = 0;
vga_set(0, 0, VGA_WIDTH, VGA_HEIGHT, scoreboard_bg);
vga_fill(0, VGA_HEIGHT, VGA_WIDTH, VGA_STATUSBAR_HEIGHT, 0x0000);
sprites_init(VGA_SPRITE_PLANE);
sprites_init(VGA_SPRITE_BULLET);
char buf[256];
snprintf(buf, 255, "游戏结束 积分 %d 正在上传积分榜", player_score);
vga_statusbar_string(0, (uint8_t*) buf);
}