OAIP_Mirror/lab16 with raylib/src/main with enemy.c

672 lines
17 KiB
C
Raw Normal View History

2024-12-20 21:27:27 +04:00
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <iso646.h>
#include "raylib.h"
#include "raymath.h"
#include "resource_dir.h"
#include "windows_functions.h"
#define RAYGUI_IMPLEMENTATION
//#define RAYGUI_PANEL_BORDER_WIDTH 2
#define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT 32
#define RAYGUI_MESSAGEBOX_BUTTON_HEIGHT 36
#include "raygui.h"
#define RAYLIB_NUKLEAR_IMPLEMENTATION
#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
#pragma warning(disable: 4116)
#include "raylib-nuklear.h"
#define M 10
#define N 15
#define HEIGHT 50
#define WIDTH 50
#define VOFFSET 50
#define FWIDTH (float)WIDTH
#define FHEIGHT (float)HEIGHT
#define PUREBLUE (Color) { 0, 0, 255, 255 }
#define BLACKGRAY (Color) {30, 30, 30, 255}
#define VSGRAY (Color) {78, 201, 176, 255}
// Коды ячеек:
// 0 - свободна
// 1 -
// 2 - препятствие
// 3 - золото
int map[M][N] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0},
{0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 3, 3, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 3, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 2, 0, 0, 2, 0},
{0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0},
{0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
int player_x = 1;
int player_y = 1;
bool player_turn = true;
int enemy_x = 14;
int enemy_y = 1;
int target_gold_x = 0;
int target_gold_y = 0;
int min_dist_to_gold = INT_MAX;
int enemy_gold = 0;
typedef enum { empty = 0, wall = 2, gold = 3 } obj_enum;
// TODO: do something with "empty" object
#define INVENTORY_SIZE 4
int inventory[INVENTORY_SIZE] = { 0, 0, 15, 0 };
obj_enum selected_element = gold;
typedef enum { left, right, up, down } enum_ways;
void movePlayer(enum_ways move) {
switch (move) {
case left:
if ((player_x > 0) and map[player_y][player_x - 1] != wall) player_x -= 1;
break;
case right:
if ((player_x < N - 1) and map[player_y][player_x + 1] != wall) player_x += 1;
break;
case up:
if ((player_y > 0) and map[player_y - 1][player_x] != wall) player_y -= 1;
break;
case down:
if ((player_y < M - 1) and map[player_y + 1][player_x] != wall) player_y += 1;
break;
}
if (map[player_y][player_x] == gold) {
map[player_y][player_x] = empty;
inventory[gold]++;
}
}
void moveEnemy() {
int dx = target_gold_x - enemy_x;
int dy = target_gold_y - enemy_y;
enum_ways move = -1;
if (dx) {
if (dx > 0) {
move = right;
}
else if (dx < 0) {
move = left;
}
}
else {
if (dy > 0) {
move = down;
}
else if (dy < 0) {
move = up;
}
}
switch (move) {
case left:
if ((enemy_x > 0) and map[enemy_y][enemy_x - 1] != wall) enemy_x -= 1;
break;
case right:
if ((enemy_x < N - 1) and map[enemy_y][enemy_x + 1] != wall) enemy_x += 1;
break;
case up:
if ((enemy_y > 0) and map[enemy_y - 1][enemy_x] != wall) enemy_y -= 1;
break;
case down:
if ((enemy_y < M - 1) and map[enemy_y + 1][enemy_x] != wall) enemy_y += 1;
break;
}
if (map[enemy_y][enemy_x] == gold) {
map[enemy_y][enemy_x] = empty;
enemy_gold++;
min_dist_to_gold = INT_MAX;
}
}
void putElement(enum_ways way, obj_enum element) {
if (element != empty && inventory[element] == 0) return;
switch (way) {
case left:
if ((player_x > 0) and map[player_y][player_x - 1] == 0) {
map[player_y][player_x - 1] = element;
inventory[element]--;
}
break;
case right:
if ((player_x < N - 1) and map[player_y][player_x + 1] == 0) {
map[player_y][player_x + 1] = element;
inventory[element]--;
}
break;
case up:
if ((player_y > 0) and map[player_y - 1][player_x] == 0) {
map[player_y - 1][player_x] = element;
inventory[element]--;
}
break;
case down:
if ((player_y < M - 1) and map[player_y + 1][player_x] == 0) {
map[player_y + 1][player_x] = element;
inventory[element]--;
}
break;
}
}
void deathbeam(enum_ways way) {
for (int i = player_x + 1; i < N; i++) {
if (map[player_y][i] != empty) inventory[map[player_y][i]] += 1;
map[player_y][i] = 0;
}
}
void stomp(int r) {
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
if ((player_y - r <= i) && (i <= player_y + r) && (player_x - r <= j) && (j <= player_x + r) && map[i][j] == wall) {
inventory[wall] += 1;
map[i][j] = empty;
}
}
}
}
void midasHand(int m, int n) {
if (map[m][n] == wall) {
map[m][n] = gold;
if (m > 0) midasHand(m - 1, n);
if (n > 0) midasHand(m, n - 1);
if (m < M - 1) midasHand(m + 1, n);
if (n < N - 1) midasHand(m, n + 1);
}
}
void doMidashand() {
if ((player_y < M - 1) and map[player_y + 1][player_x] == wall) midasHand(player_y + 1, player_x);
if ((player_x < N - 1) and map[player_y][player_x + 1] == wall) midasHand(player_y, player_x + 1);
if ((player_y > 0) and map[player_y - 1][player_x] == wall) midasHand(player_y - 1, player_x);
if ((player_x > 0) and map[player_y][player_x - 1] == wall) midasHand(player_y, player_x - 1);
}
bool netToggle = false;
void drawNet() {
for (int i = 0; i <= N * WIDTH; i += WIDTH) {
DrawLine(i, 0, i, M * HEIGHT, BLACK);
}
for (int i = 0; i <= M * HEIGHT; i += HEIGHT) {
DrawLine(0, i, N * WIDTH, i, BLACK);
}
}
void drawMap() {
// Коды ячеек:
// 0 - свободна
// 1 -
// 2 - препятствие
// 3 - золото
Color colors[4] = { LIGHTGRAY, PUREBLUE, BLACK, YELLOW };
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
int x1 = j * WIDTH;
int y1 = i * HEIGHT;
int dist = abs(j - enemy_x) + abs(i - enemy_y);
if (map[i][j] == gold and dist < min_dist_to_gold) {
min_dist_to_gold = dist;
target_gold_x = j;
target_gold_y = i;
}
DrawRectangle(x1, y1, WIDTH, HEIGHT, colors[map[i][j]]);
}
}
if (map[target_gold_y][target_gold_x] != gold) {
min_dist_to_gold = INT_MAX;
}
DrawRectangle(target_gold_x * WIDTH, target_gold_y * HEIGHT, WIDTH, HEIGHT, ORANGE);
}
void drawPlayer() {
int x1 = player_x * WIDTH;
int y1 = player_y * HEIGHT;;
DrawRectangle(x1, y1, WIDTH, HEIGHT, PUREBLUE);
}
void drawEnemy(Font font) {
int x1 = enemy_x * WIDTH;
int y1 = enemy_y * HEIGHT;;
DrawRectangle(x1, y1, WIDTH, HEIGHT, RED);
DrawTextEx(font, TextFormat("%d", enemy_gold), (Vector2) { (float)x1, (float)y1 }, 24, 0, BLACK);
}
void drawBottomBar(Font font, float fontSize) {
DrawRectangle(0, HEIGHT * M, WIDTH * N, VOFFSET, BLACKGRAY);
static char gold_string[50];
static char wall_string[50];
static char help_string[] = "wasd - move player G - change item F5 - save\narrows - place item M - Midas hand F9 - load";
sprintf(gold_string, " gold = %d", inventory[gold]);
sprintf(wall_string, " wall = %d", inventory[wall]);
if (selected_element == gold) gold_string[0] = '>';
else if (selected_element == wall) wall_string[0] = '>';
Vector2 goldpos = { WIDTH / 4, HEIGHT * M };
Vector2 wallpos = { WIDTH / 4, HEIGHT * M + fontSize };
Vector2 helppos = { WIDTH * N - 550 , HEIGHT * M };
DrawTextEx(font, gold_string, goldpos, fontSize, 0, VSGRAY);
DrawTextEx(font, wall_string, wallpos, fontSize, 0, VSGRAY);
DrawTextEx(font, help_string, helppos, fontSize, 0, VSGRAY);
}
void save() {
FILE* fout = fopen("savefile.txt", "w");
if (fout == NULL) {
printf("save error");
return;
}
fprintf(fout, "%d %d\n", M, N);
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
fprintf(fout, "%d ", map[i][j]);
}
fprintf(fout, "\n");
}
for (int i = 0; i < INVENTORY_SIZE; i++) {
fprintf(fout, "%d ", inventory[i]);
}
fprintf(fout, "\n");
fprintf(fout, "%d %d %d\n", player_x, player_y, selected_element);
fclose(fout);
}
typedef enum { OK = 0, saveError, loadError1, loadError2 } ErrorCodes;
ErrorCodes errorCode = OK;
void load() {
FILE* fin = fopen("savefile.txt", "r");
if (fin == NULL) {
printf("1) load error\n");
/*MessageBoxA(
GetActiveWindow(),
"Файл не найден\nПопробуйте сначала сохранить игру",
"Ошибка загрузки",
MB_ICONERROR
);*/
errorCode = loadError1;
return;
}
int m, n;
fscanf_s(fin, "%d%d", &m, &n);
if (m != M || n != N) {
printf("2) load error\n");
/*MessageBoxW(
NULL,
L"Неправильный размер карты!\nПроверьте целостность сохранения",
L"Ошибка загрузки",
MB_ICONERROR
);*/
errorCode = loadError2;
return;
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
fscanf_s(fin, "%d", &map[i][j]);
}
}
for (int i = 0; i < INVENTORY_SIZE; i++) {
fscanf_s(fin, "%d", &inventory[i]);
}
fscanf_s(fin, "%d%d%d", &player_x, &player_y, &selected_element);
fclose(fin);
}
bool editMap = 0;
void handleKeys() {
/*if (IsKeyPressedRepeat(KEY_W)) movePlayer(up);
if (IsKeyPressedRepeat(KEY_S)) movePlayer(down);
if (IsKeyPressedRepeat(KEY_D)) movePlayer(right);
if (IsKeyPressedRepeat(KEY_A)) movePlayer(left);*/
int key;
while (key = GetKeyPressed()) {
if (not editMap) {
switch (key)
{
case KEY_W:
movePlayer(up);
break;
case KEY_S:
movePlayer(down);
break;
case KEY_D:
movePlayer(right);
break;
case KEY_A:
movePlayer(left);
break;
case KEY_ONE:
stomp(1);
break;
case KEY_TWO:
stomp(2);
break;
case KEY_Z:
deathbeam(right);
break;
case KEY_M:
doMidashand();
break;
case KEY_LEFT:
putElement(left, selected_element);
break;
case KEY_RIGHT:
putElement(right, selected_element);
break;
case KEY_UP:
putElement(up, selected_element);
break;
case KEY_DOWN:
putElement(down, selected_element);
break;
}
}
switch (key)
{
case KEY_F5:
save();
break;
case KEY_F9:
load();
break;
case KEY_SPACE:
netToggle = !netToggle;
break;
case KEY_ENTER:
editMap = !editMap;
break;
case KEY_G:
if (selected_element == gold) selected_element = wall;
else selected_element = gold;
break;
default:
player_turn = false;
}
}
}
void drawRayguiErrorBoxes() {
const Rectangle errorBoxRect = { N * WIDTH / 2 - 200, M * HEIGHT / 2 - 75, 400, 150 };
int btn = -1;
switch (errorCode)
{
case OK:
break;
case saveError:
btn = GuiMessageBox(errorBoxRect,
u8"Ошибка сохранения",
u8"Невозможно создать файл",
u8"Ок;Выйти");
break;
case loadError1:
btn = GuiMessageBox(errorBoxRect,
u8"Ошибка загрузки",
u8"Файл не найден\nПопробуйте сначала сохранить игру",
u8"Игнорировать;Выйти из игры");
break;
case loadError2:
btn = GuiMessageBox(errorBoxRect,
u8"Ошибка загрузки",
u8"Неправильный размер карты!\nПроверьте целостность сохранения",
u8"Игнорировать;Выйти из игры");
break;
}
switch (btn)
{
case 0:
errorCode = OK;
break;
case 1:
errorCode = OK;
break;
case 2:
exit(0);
break;
}
}
int nk_error_box(struct nk_context* ctx, const char* title, const char* error, const char* description)
{
int result = -1;
if (nk_begin(ctx, title,
nk_rect(N * WIDTH / 2 - 200, M * HEIGHT / 2 - 72, 400, 144),
NK_WINDOW_TITLE | NK_WINDOW_BORDER | NK_WINDOW_NO_SCROLLBAR))
{
nk_layout_row_dynamic(ctx, 24, 1);
nk_label(ctx, error, NK_TEXT_CENTERED);
nk_layout_row_dynamic(ctx, 24, 1);
nk_label(ctx, description, NK_TEXT_CENTERED);
nk_layout_row_dynamic(ctx, 36, 2);
if (nk_button_label(ctx, u8"Игнорировать")) {
result = 1;
}
if (nk_button_label(ctx, u8"Выйти из игры")) {
result = 2;
}
}
else {
result = 0;
}
return result;
}
void callNKErrorBoxes(struct nk_context* ctx) {
int btn = -1;
switch (errorCode)
{
case OK:
break;
case saveError:
btn = nk_error_box(ctx,
u8"Ошибка сохранения",
u8"Невозможно создать файл",
u8"Проверьте целостность сохранения");
break;
case loadError1:
btn = nk_error_box(ctx,
u8"Ошибка загрузки",
u8"Файл не найден",
u8"Проверьте целостность сохранения");
break;
case loadError2:
btn = nk_error_box(ctx,
u8"Ошибка загрузки",
u8"Неправильный размер карты!",
u8"Проверьте целостность сохранения");
break;
}
switch (btn)
{
case 0:
errorCode = OK;
break;
case 1:
errorCode = OK;
break;
case 2:
exit(0);
break;
}
}
#define CPSIZE 213
int main()
{
SetConfigFlags(FLAG_WINDOW_HIGHDPI);
//SetConfigFlags(FLAG_MSAA_4X_HINT);
InitWindow(N * WIDTH, M * HEIGHT + VOFFSET, "lab16 with raylib");
SetTargetFPS(60);
SearchAndSetResourceDir("resources");
int codepoints[CPSIZE] = { 0 };
for (int i = 0; i < 127 - 32; i++) codepoints[i] = 32 + i; // Basic ASCII characters
for (int i = 0; i < 118; i++) codepoints[95 + i] = 1024 + i; // Cyrillic characters
//Font InconsolataRegular = LoadFontEx("Inconsolata-Regular.ttf", 24, NULL, 0);
//Font InconsolataSemiBold = LoadFontEx("Inconsolata-SemiBold.ttf", 48, codepoints, 512);
Font InconsolataBold = LoadFontEx("Inconsolata-LGC-Bold.ttf", 36, codepoints, CPSIZE);
SetTextureFilter(InconsolataBold.texture, TEXTURE_FILTER_BILINEAR);
//Font Arial = LoadFontEx("arial.ttf", 36, codepoints, CPSIZE);
//SetTextureFilter(Arial.texture, TEXTURE_FILTER_BILINEAR);
GuiSetFont(InconsolataBold);
GuiSetStyle(DEFAULT, TEXT_SIZE, 24);
GuiSetStyle(DEFAULT, TEXT_SPACING, 0);
GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 24);
GuiSetStyle(STATUSBAR, BORDER_WIDTH, 2);
// Create the Nuklear Context
struct nk_context* ctx = InitNuklearEx(InconsolataBold, 24);
Vector2 mousePos = { 0 };
int mouseCellX = 0;
int mouseCellY = 0;
// game loop
while (!WindowShouldClose()) // run the loop untill the user presses ESCAPE or presses the Close button on the window
{
//------------------------------------------------------------------
// Update game logic
//------------------------------------------------------------------
if (errorCode == OK)
{
if (player_turn) {
handleKeys();
}
else {
player_turn = true;
moveEnemy();
}
if (editMap) {
mousePos = GetMousePosition();
mouseCellX = (int)(Clamp(mousePos.x, 0, FWIDTH * N - 1) / WIDTH);
mouseCellY = (int)(Clamp(mousePos.y, 0, FHEIGHT * M - 1) / HEIGHT);
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) and not(mouseCellX == player_x and mouseCellY == player_y)) {
map[mouseCellY][mouseCellX] = selected_element;
}
else if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) {
map[mouseCellY][mouseCellX] = empty;
}
}
}
else {
//------------------------------------------------------------------
// Update Nuklear context
//------------------------------------------------------------------
UpdateNuklear(ctx);
callNKErrorBoxes(ctx);
nk_end(ctx);
}
//------------------------------------------------------------------
// Draw
//------------------------------------------------------------------
BeginDrawing();
// Setup the back buffer for drawing (clear color and depth buffers)
ClearBackground(WHITE);
drawMap();
drawPlayer();
drawEnemy(InconsolataBold);
drawBottomBar(InconsolataBold, 24);
const Rectangle RoundRect = { 100, 250, 185, 36 };
//DrawRectangleRoundedLinesEx(RoundRect, 0.3f, 20, 1, BLACK);
if (editMap) {
Rectangle rec = {
mouseCellX * FWIDTH,
mouseCellY * FHEIGHT,
FWIDTH, FHEIGHT
};
Color color = { 0, 0, 0, 255 };
if (mouseCellX == player_x and mouseCellY == player_y) {
color.r = 255;
}
else {
color.g = 255;
}
DrawRectangleLinesEx(rec, 2, color);
}
if (netToggle) {
drawNet();
}
if (errorCode > OK) {
// Render the Nuklear GUI
DrawNuklear(ctx);
//drawRayguiErrorBoxes();
}
//DrawTextEx(InconsolataBold, u8"Файл не найден\nПопробуйте сначала сохранить игру", (Vector2) { 100, 100 }, 24, 0, BLACK);
// show mouse position
//DrawText(TextFormat("%.1f %.1f", mousePos.x, mousePos.y), 5, M * HEIGHT - 30, 30, ORANGE);
// show FPS and frametime
//DrawText(TextFormat("%2d FPS", GetFPS()), 0, 0, 34, ORANGE);
//DrawText(TextFormat("%4f ms", GetFrameTime()), 0, 34, 34, BEIGE);
// end the frame and get ready for the next one (display frame, poll input, etc...)
EndDrawing();
}
//UnloadFont(InconsolataRegular);
UnloadFont(InconsolataBold);
//UnloadFont(Arial);
//UnloadFont(InconsolataBold);
// De-initialize the Nuklear GUI
UnloadNuklear(ctx);
// destroy the window and cleanup the OpenGL context
CloseWindow();
return 0;
}