Số bài đã giải: 44
Hạng điểm: #640
Tổng điểm:
893,56
Đóng góp:
0
Đã tham gia 1 kỳ thi
Hạng rating: #397
Rating: 1266
Min. rating: 1266
Max rating: 1266
Thông tin
#include <bits/stdc++.h>
#include <io.h>
#include <fcntl.h>
#include <conio.h>
#include <winsock2.h>
#include <windows.h>
#include <cwchar>
#include <ws2tcpip.h>
#include <atomic>
#include <thread>
#pragma comment(lib, "ws2_32.lib")
#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx2,bmi,bmi2,lzcnt,popcnt")
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
#ifndef DISABLE_NEWLINE_AUTO_RETURN
#define DISABLE_NEWLINE_AUTO_RETURN 0x0008
#endif
using namespace std;
using namespace std::chrono;
typedef long long Long;
typedef string str;
typedef pair<Long,Long> pll;
// INIT -----------------------------------------------------------------------------------------------
double debug[5];
Long PORT = 8888;
int WIDTH=80,HEIGHT=50,scrposx = 10,scrposy = 10;
const int font_size = 16;
const int fps = 70;
const double frame_time = 1.0/fps;
const double key_delay = 0.35; // how muc time in sec need to hold to activate spam
const double key_repeat_rate = 0.05; // 1/key_repeat_rate words per sec
double dt = 0.0;
struct rgb{
int r = 255, g = 255, b = 255;
rgb(int r_ = 255,int g_ = 255,int b_ = 255){
r = r_;
g = g_;
b = b_;
}
};
struct img{
int w = 0,h = 0;
str im;
char nochar;
char nochar_replace;
vector<rgb> col;
vector<str> unicode;
char replace_ch;
img(int w_ = 0,int h_ = 0,str img_ = "",vector<rgb> col_ = {rgb()},
char nochar_ = ' ',char nochar_replace_ = '.',vector<str> unicode_ = {},char replace_ch_ = ' '){
w = w_;
h = h_;
im = img_;
col = col_;
nochar = nochar_;
nochar_replace = nochar_replace_;
unicode = unicode_;
replace_ch = replace_ch_;
}
};
vector<img> img_id;
struct gr{
Long t = 0;
char c =' ';
};
str grid[200][100];
// END INIT -----------------------------------------------------------------------------------------------
// MULTIPLAYER -----------------------------------------------------------------------------------------------
#pragma pack(push, 1)
enum class PacketType : uint8_t{
POSITION_UPDATE = 1,
HEARTBEAT = 2
};
struct GamePacket{
PacketType type;
int player_id;
double x;
double y;
};
#pragma pack(pop)
// END MULTIPLAYER -----------------------------------------------------------------------------------------------
// DEFINE --------------------------------------------------------------------------------------------
#define RESET "\033[0m"
#define GLOW "\033[5m"
string fg_rgb(rgb c){
return "\033[38;2;" + to_string(c.r) + ";" + to_string(c.g) + ";" + to_string(c.b) + "m";
}
string bg_rgb(rgb c){
return "\033[48;2;" + to_string(c.r) + ";" + to_string(c.g) + ";" + to_string(c.b) + "m";
}
rgb text_col[200][100];
rgb bg_col[200][100];
str prev_grid[200][100];
rgb prev_text_col[200][100];
rgb prev_bg_col[200][100];
bool prev_valid = 0;
// HOW TO TRANSFORM U+2665 -> u8"\u2665"
const str FUL_BLOCK = u8"\u2588";
const str HEART = u8"\u2665";
const str RIGHTARROW = u8"\u21E8";
const str LEFTARROW_THIN = u8"\u21FD";
const str TOPLEF_CORNER = u8"\u250C";
const str TOPRIG_CORNER = u8"\u2510";
const str BOTLEF_CORNER = u8"\u2514";
const str BOTRIG_CORNER = u8"\u2518";
const str LIGHT_HOZON = u8"\u2500";
const str LIGHT_VERTI = u8"\u2502";
const str TOPFUL = u8"\u2580";
const str BOTFUL = u8"\u2584";
const str LEFFUL = u8"\u258C";
const str RIGFUL = u8"\u2590";
const str VERTI_DUO = u8"\u2551";
const str HOZON_DUO = u8"\u2550";
const str TOPLEF_CORNER_DUO = u8"\u2554";
const str TOPRIG_CORNER_DUO = u8"\u2557";
const str BOTLEF_CORNER_DUO = u8"\u255A";
const str BOTRIG_CORNER_DUO = u8"\u255D";
const str MID_DOT = u8"\u00B7";
// END DEFINE --------------------------------------------------------------------------------------------
// BASIC FUNCT -------------------------------------------------------------------------------------------
mt19937_64 rnd(time(0));
Long random(Long st,Long ed){
if(ed < st) swap(ed,st);
return st + (Long)(rnd() % ((unsigned long long)ed-st+1));
}
void wait_clear(Long milisec){
if(milisec > -1) this_thread::sleep_for(std::chrono::milliseconds(milisec));
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
// chinh xac hon
void wait_ms_p(double seconds){
if(seconds <= 0) return;
auto start = steady_clock::now();
auto target = start + duration<double>(seconds);
while (steady_clock::now() < target - microseconds(100)) {
std::this_thread::yield();
}
while (steady_clock::now() < target){
#if defined(_M_X64) or defined(_M_IX86) or defined(__x86_64__)
_mm_pause();
#endif
}
}
void wait_ms(Long milisec){
this_thread::sleep_for(std::chrono::milliseconds(milisec));
//wait_ms_p((double)milisec/1000.0);
}
void set_square_font(int fontSize = 16){
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_FONT_INFOEX cfi = {0};
cfi.cbSize = sizeof(CONSOLE_FONT_INFOEX);
GetCurrentConsoleFontEx(hOut, FALSE, &cfi);
wcscpy(cfi.FaceName, L"Consolas");
cfi.FontFamily = FF_DONTCARE;
cfi.FontWeight = FW_NORMAL;
cfi.dwFontSize.X = fontSize;
cfi.dwFontSize.Y = fontSize;
SetCurrentConsoleFontEx(hOut, FALSE, &cfi);
}
void set_console_buffer_size(){
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SMALL_RECT tmpWin = {0, 0, 1, 1};
SetConsoleWindowInfo(hOut, TRUE, &tmpWin);
COORD bufferSize = { (SHORT)WIDTH, (SHORT)HEIGHT };
SetConsoleScreenBufferSize(hOut, bufferSize);
SMALL_RECT windowSize = { 0, 0, (SHORT)(WIDTH - 1), (SHORT)(HEIGHT - 1) };
SetConsoleWindowInfo(hOut, TRUE, &windowSize);
}
void hide_cursor(){
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cursorInfo;
GetConsoleCursorInfo(hOut, &cursorInfo);
cursorInfo.bVisible = FALSE; // Set visibility to false
SetConsoleCursorInfo(hOut, &cursorInfo);
}
void setup(Long col,Long row,Long posx,Long posy){
cout << "\033[?25l";
cout << "\033[H";
// resize terminal window (ANSI format is: ESC [ 8 ; rows ; cols t)
cout << "\033[8;" << row << ";" << col << "t";
cout << flush;
// move the physical window on your monitor
HWND consolewindow = GetConsoleWindow();
if(consolewindow != NULL){
SetWindowPos(consolewindow, 0, posx, posy, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
// disable text selection and mark mode
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode = 0;
GetConsoleMode(hInput, &mode);
mode &= ~ENABLE_QUICK_EDIT_MODE;
mode &= ~ENABLE_LINE_INPUT;
mode &= ~ENABLE_ECHO_INPUT;
// ENABLE_MOUSE_INPUT and ENABLE_EXTENDED_FLAGS
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
DWORD m = 0;
GetConsoleMode(hIn, &m);
m &= ~ENABLE_QUICK_EDIT_MODE;
m |= ENABLE_EXTENDED_FLAGS; // required to change quick-edit
m |= ENABLE_MOUSE_INPUT;
SetConsoleMode(hIn, m);
// (anti slow fallback)
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD out_mode;
GetConsoleMode(hOut, &out_mode);
SetConsoleMode(hOut, out_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN);
hide_cursor();
}
void set_con_size(Long col,Long row){
cout << "\033[8;" << row << ";" << col << "t";
cout << flush;
}
void set_con_pos(Long posx,Long posy){
HWND consolewindow = GetConsoleWindow();
if(consolewindow != NULL){
SetWindowPos(consolewindow, 0, posx, posy, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
}
pll getmousepos_monitor(){
POINT cursorPos;
GetCursorPos(&cursorPos);
return {cursorPos.x,cursorPos.y};
}
// -------- combined console guard (selection + right-click) --------
static std::atomic<bool> g_stop_guard{false};
static HHOOK g_mouse_hook = nullptr;
static std::thread g_guard_thread;
static DWORD g_guard_tid = 0;
static volatile LONG g_rc_down = 0;
static volatile LONG g_wheel_acc = 0; // <-- new
static LRESULT CALLBACK mouse_proc(int nCode, WPARAM wParam, LPARAM lParam){
if(nCode < 0) return CallNextHookEx(g_mouse_hook, nCode, wParam, lParam);
HWND fg = GetForegroundWindow();
HWND me = GetConsoleWindow();
if(fg != me) return CallNextHookEx(g_mouse_hook, nCode, wParam, lParam);
switch(wParam){
case WM_RBUTTONDOWN:
case WM_NCRBUTTONDOWN:
InterlockedExchange(&g_rc_down, 1);
return 1;
case WM_RBUTTONUP:
case WM_NCRBUTTONUP:
InterlockedExchange(&g_rc_down, 0);
return 1;
case WM_RBUTTONDBLCLK:
return 1;
case WM_MOUSEWHEEL: {
MSLLHOOKSTRUCT* ms = (MSLLHOOKSTRUCT*)lParam;
short delta = (short)HIWORD(ms->mouseData);
if(delta > 0) InterlockedIncrement(&g_wheel_acc);
else if(delta < 0) InterlockedDecrement(&g_wheel_acc);
// don't swallow — let it pass to any other hook in the chain
return CallNextHookEx(g_mouse_hook, nCode, wParam, lParam);
}
}
return CallNextHookEx(g_mouse_hook, nCode, wParam, lParam);
}
// -------- keyboard shortcut suppressor --------
static HHOOK g_kb_hook = nullptr;
static LRESULT CALLBACK kb_proc(int nCode, WPARAM wParam, LPARAM lParam){
if(nCode < 0) return CallNextHookEx(g_kb_hook, nCode, wParam, lParam);
HWND fg = GetForegroundWindow();
HWND me = GetConsoleWindow();
if(fg != me) return CallNextHookEx(g_kb_hook, nCode, wParam, lParam);
if(wParam != WM_KEYDOWN and wParam != WM_SYSKEYDOWN)
return CallNextHookEx(g_kb_hook, nCode, wParam, lParam);
KBDLLHOOKSTRUCT * kb = (KBDLLHOOKSTRUCT*)lParam;
DWORD vk = kb->vkCode;
bool ctrl = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
bool shift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
if(ctrl and (vk == 'A' or vk == 'M' or vk == 'F' or vk == 'S' or vk == 'Q')){
return 1;
}
if(shift and (vk == VK_LEFT or vk == VK_RIGHT or
vk == VK_UP or vk == VK_DOWN or
vk == VK_HOME or vk == VK_END or
vk == VK_PRIOR or vk == VK_NEXT)){
return 1;
}
return CallNextHookEx(g_kb_hook, nCode, wParam, lParam);
}
#define GUARD_TIMER_ID 1
static void check_selection_once(){
HWND hwnd = GetConsoleWindow();
if(!hwnd) return;
CONSOLE_SELECTION_INFO si{};
if(GetConsoleSelectionInfo(&si) && (si.dwFlags & CONSOLE_SELECTION_IN_PROGRESS)){
PostMessage(hwnd, WM_CANCELMODE, 0, 0);
PostMessage(hwnd, WM_KEYDOWN, VK_ESCAPE, 0);
PostMessage(hwnd, WM_KEYUP, VK_ESCAPE, 0);
}
}
static void guard_thread_func(){
g_guard_tid = GetCurrentThreadId();
g_mouse_hook = SetWindowsHookEx(WH_MOUSE_LL, mouse_proc, GetModuleHandle(NULL), 0);
g_kb_hook = SetWindowsHookEx(WH_KEYBOARD_LL, kb_proc, GetModuleHandle(NULL), 0);
UINT_PTR timer = SetTimer(NULL, GUARD_TIMER_ID, 20, NULL);
MSG msg;
while(!g_stop_guard.load() && GetMessage(&msg, NULL, 0, 0)){
if(msg.message == WM_TIMER && msg.wParam == GUARD_TIMER_ID){
check_selection_once();
continue;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(timer) KillTimer(NULL, timer);
if(g_mouse_hook) { UnhookWindowsHookEx(g_mouse_hook); g_mouse_hook = nullptr; }
if(g_kb_hook) { UnhookWindowsHookEx(g_kb_hook); g_kb_hook = nullptr; }
}
static void start_console_guard(){
g_guard_thread = std::thread(guard_thread_func);
g_guard_thread.detach();
}
static void stop_console_guard(){
g_stop_guard.store(true);
if(g_guard_tid) PostThreadMessage(g_guard_tid, WM_QUIT, 0, 0);
}
bool key[500];
bool key_pressed[500];
double key_time[500];
Long mousex = 0,mousey = 0;
bool mouseclick[2]; // 0 = left, 1 = right
int mousewheel = 0; // 1 = upwheel, 0 = normal, -1 = downwheel
void auto_keycheck_mousepos(){
mouseclick[0] = 0;
mouseclick[1] = 0;
mousewheel = 0;
// check if console in foreground (if background then turn off all)
HWND hConsole = GetConsoleWindow();
HWND hForeground = GetForegroundWindow();
if(hConsole != hForeground) {
for(int i = 0;i<500;i++){
key[i] = 0;
}
mouseclick[0] = 0;
mouseclick[1] = 0;
mousewheel = 0;
InterlockedExchange(&g_wheel_acc, 0);
return ;
}
POINT p;
if(GetCursorPos(&p)){
ScreenToClient(hConsole, &p);
int font_w = font_size;
int font_h = font_size;
int cx = p.x / font_w;
int cy = p.y / font_h;
if(cx >= 0 and cx < WIDTH) mousex = cx;
if(cy >= 0 and cy < HEIGHT) mousey = cy;
}
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
DWORD eventnum = 0;
GetNumberOfConsoleInputEvents(hInput,&eventnum);
mouseclick[0] = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0;
mouseclick[1] = (InterlockedCompareExchange(&g_rc_down, 0, 0) != 0);
// wheel — consume the accumulator, signed
LONG wd = InterlockedExchange(&g_wheel_acc, 0);
if(wd > 0) mousewheel = 1;
else if(wd < 0) mousewheel = -1;
else mousewheel = 0;
for(int i = 3; i < 500; i++){
key[i] = (GetAsyncKeyState(i) & 0x8000) != 0;
}
for(int i = 3;i<500;i++){
if(key[i]){
if(key_time[i] == 0 or key_time[i] + dt >= key_delay) key_pressed[i] = 1;
else key_pressed[i] = 0;
key_time[i] += dt;
if(key_time[i] >= key_delay){
key_time[i] -= key_repeat_rate;
}
}
else{
key_time[i] = 0;
key_pressed[i] = 0;
}
}
}
void reset_keys(){
for(int i = 0;i<500;i++){
key[i] = 0;
key_time[i] = 0;
key_pressed[i] = 0;
}
}
inline void append_num(string& s, int n){
if (n >= 100) s += (char)('0' + n / 100);
if (n >= 10) s += (char)('0' + (n / 10) % 10);
s += (char)('0' + n % 10);
}
inline void append_fg_rgb(string& s, const rgb& c){
s += "\033[38;2;";
append_num(s, c.r); s += ';';
append_num(s, c.g); s += ';';
append_num(s, c.b); s += 'm';
}
inline void append_bg_rgb(string& s, const rgb& c){
s += "\033[48;2;";
append_num(s, c.r); s += ';';
append_num(s, c.g); s += ';';
append_num(s, c.b); s += 'm';
}
void lock_window_size(){
HWND consoleWindow = GetConsoleWindow();
if (consoleWindow != NULL) {
LONG style = GetWindowLong(consoleWindow, GWL_STYLE);
style &= ~WS_MAXIMIZEBOX;
style &= ~WS_SIZEBOX;
SetWindowLong(consoleWindow, GWL_STYLE, style);
}
}
void kill_console_selection(){
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
DWORD m = 0;
if(!GetConsoleMode(hIn, &m)) return;
m &= ~ENABLE_QUICK_EDIT_MODE;
m &= ~ENABLE_INSERT_MODE;
m |= ENABLE_MOUSE_INPUT;
m |= ENABLE_EXTENDED_FLAGS;
m |= ENABLE_PROCESSED_INPUT;
SetConsoleMode(hIn, m);
}
// END BASIC FUNCT -------------------------------------------------------------------------------------------
// DRAW FUNCT ----------------------------------------------------------------------------------------------------------------------------------
// change rgb brightness
inline rgb scale_rgb(const rgb& c, double factor){
// > 1.0 -> brightness
// 1.0 -> bth
// < 1.0 -> darkness
return rgb(
(int)(c.r * factor),
(int)(c.g * factor),
(int)(c.b * factor)
);
}
void set_pixel(int x,int y,str ch,rgb fg,rgb bg){
if(x >= 0 and x < WIDTH and y >= 0 and y < HEIGHT){
grid[y][x] = ch;
if(fg.r != -1) text_col[y][x] = fg;
if(bg.r != -1) bg_col[y][x] = bg;
}
}
void draw_rect(int start_x,int start_y,int end_x,int end_y,
int layer = 1,vector<str> corner = {"O"},vector<str> side_horzontal = {"-"},vector<str> side_vertical = {"|"},vector<str> inside = {"."},
rgb corner_col = rgb(),vector<rgb> side_horz_col = {rgb()},vector<rgb> side_vert_col = {rgb()},vector<rgb> inside_col = {rgb()},
rgb bg_corner_col = {rgb(0,0,0)},vector<rgb> bg_side_horz_col = {rgb(0,0,0)},vector<rgb> bg_side_vert_col = {rgb(0,0,0)},vector<rgb> bg_inside_col = {rgb(0,0,0)}){
if(end_x < start_x) swap(end_x,start_x);
if(end_y < start_y) swap(end_y,start_y);
int tx = 0;
int ty = 0;
for(int y = start_y;y<=start_y + (end_y - start_y)/2;y++){
for(int x = start_x;x<=start_x + (end_x - start_x)/2;x++){
if((y < start_y + layer and x < start_x + layer)){
tx = x;
ty = y;
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[y][x] = corner[0];
text_col[y][x] = corner_col;
bg_col[y][x] = bg_corner_col;
}
tx = end_x - (x-start_x);
ty = end_y - (y-start_y);
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[end_y - (y-start_y)][end_x - (x-start_x)] = (corner.size() >= 4 ? corner[3] : corner[0]);
text_col[end_y - (y-start_y)][end_x - (x-start_x)] = corner_col;
bg_col[end_y - (y-start_y)][end_x - (x-start_x)] = bg_corner_col;
}
tx = x;
ty = end_y - (y-start_y);
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[end_y - (y-start_y)][x] = (corner.size() >= 3 ? corner[2] : corner[0]);
text_col[end_y - (y-start_y)][x] = corner_col;
bg_col[end_y - (y-start_y)][x] = bg_corner_col;
}
tx = end_x - (x-start_x);
ty = y;
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[y][end_x - (x-start_x)] = (corner.size() >= 2 ? corner[1] : corner[0]);
text_col[y][end_x - (x-start_x)] = corner_col;
bg_col[y][end_x - (x-start_x)] = bg_corner_col;
}
}
else if(y < start_y + layer){
tx = x;
ty = y;
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[y][x] = side_horzontal[(x - (start_x + layer))%(side_horzontal.size())];
text_col[y][x] = side_horz_col[(x - (start_x + layer))%(side_horz_col.size())];
bg_col[y][x] = bg_side_horz_col[(x - (start_x + layer))%(side_horz_col.size())];
}
tx = end_x - (x-start_x);
ty = y;
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[y][end_x - (x-start_x)] = side_horzontal[(x - (start_x + layer))%(side_horzontal.size())];
text_col[y][end_x - (x-start_x)] = side_horz_col[(x - (start_x + layer))%(side_horz_col.size())];
bg_col[y][end_x - (x-start_x)] = bg_side_horz_col[(x - (start_x + layer))%(side_horz_col.size())];
}
tx = x;
ty = end_y - (y-start_y);
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[end_y - (y-start_y)][x] = side_horzontal[(x - (start_x + layer))%(side_horzontal.size())];
text_col[end_y - (y-start_y)][x] = side_horz_col[(x - (start_x + layer))%(side_horz_col.size())];
bg_col[end_y - (y-start_y)][x] = bg_side_horz_col[(x - (start_x + layer))%(side_horz_col.size())];
}
tx = end_x - (x-start_x);
ty = end_y - (y-start_y);
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[end_y - (y-start_y)][end_x - (x-start_x)] = side_horzontal[(x - (start_x + layer))%(side_horzontal.size())];
text_col[end_y - (y-start_y)][end_x - (x-start_x)] = side_horz_col[(x - (start_x + layer))%(side_horz_col.size())];
bg_col[end_y - (y-start_y)][end_x - (x-start_x)] = bg_side_horz_col[(x - (start_x + layer))%(side_horz_col.size())];
}
}
else if(x < start_x + layer){
tx = x;
ty = y;
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[y][x] = side_vertical[(y - (start_y + layer))%(side_vertical.size())];
text_col[y][x] = side_vert_col[(y - (start_y + layer))%(side_vert_col.size())];
bg_col[y][x] = bg_side_vert_col[(y - (start_y + layer))%(side_vert_col.size())];
}
tx = end_x - (x-start_x);
ty = y;
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[y][end_x - (x-start_x)] = side_vertical[(y - (start_y + layer))%(side_vertical.size())];
text_col[y][end_x - (x-start_x)] = side_vert_col[(y - (start_y + layer))%(side_vert_col.size())];
bg_col[y][end_x - (x-start_x)] = bg_side_vert_col[(y - (start_y + layer))%(side_vert_col.size())];
}
tx = x;
ty = end_y - (y-start_y);
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[end_y - (y-start_y)][x] = side_vertical[(y - (start_y + layer))%(side_vertical.size())];
text_col[end_y - (y-start_y)][x] = side_vert_col[(y - (start_y + layer))%(side_vert_col.size())];
bg_col[end_y - (y-start_y)][x] = bg_side_vert_col[(y - (start_y + layer))%(side_vert_col.size())];
}
tx = end_x - (x-start_x);
ty = end_y - (y-start_y);
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[end_y - (y-start_y)][end_x - (x-start_x)] = side_vertical[(y - (start_y + layer))%(side_vertical.size())];
text_col[end_y - (y-start_y)][end_x - (x-start_x)] = side_vert_col[(y - (start_y + layer))%(side_vert_col.size())];
bg_col[end_y - (y-start_y)][end_x - (x-start_x)] = bg_side_vert_col[(y - (start_y + layer))%(side_vert_col.size())];
}
}
else{
tx = x;
ty = y;
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[y][x] = inside[(y - (start_y + layer) + x - (start_x + layer))%(inside.size())];
text_col[y][x] = inside_col[(y - (start_y + layer) + x - (start_x + layer))%(inside_col.size())];
bg_col[y][x] = bg_inside_col[(y - (start_y + layer) + x - (start_x + layer))%(inside_col.size())];
}
tx = end_x - (x-start_x);
ty = y;
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[y][end_x - (x-start_x)] = inside[(y - (start_y + layer) + x - (start_x + layer))%(inside.size())];
text_col[y][end_x - (x-start_x)] = inside_col[(y - (start_y + layer) + x - (start_x + layer))%(inside_col.size())];
bg_col[y][end_x - (x-start_x)] = bg_inside_col[(y - (start_y + layer) + x - (start_x + layer))%(inside_col.size())];
}
tx = x;
ty = end_y - (y-start_y);
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[end_y - (y-start_y)][x] = inside[(y - (start_y + layer) + x - (start_x + layer))%(inside.size())];
text_col[end_y - (y-start_y)][x] = inside_col[(y - (start_y + layer) + x - (start_x + layer))%(inside_col.size())];
bg_col[end_y - (y-start_y)][x] = bg_inside_col[(y - (start_y + layer) + x - (start_x + layer))%(inside_col.size())];
}
tx = end_x - (x-start_x);
ty = end_y - (y-start_y);
if(!(0 > tx or tx >= WIDTH or 0 > ty or ty >= HEIGHT)){
grid[end_y - (y-start_y)][end_x - (x-start_x)] = inside[(y - (start_y + layer) + x - (start_x + layer))%(inside.size())];
text_col[end_y - (y-start_y)][end_x - (x-start_x)] = inside_col[(y - (start_y + layer) + x - (start_x + layer))%(inside_col.size())];
bg_col[end_y - (y-start_y)][end_x - (x-start_x)] = bg_inside_col[(y - (start_y + layer) + x - (start_x + layer))%(inside_col.size())];
}
}
}
}
}
void fill(char ch,rgb fg = rgb(0,0,0),rgb bg = rgb(0,0,0),int start_x = 0,int start_y = 0,int end_x = WIDTH - 1,int end_y = HEIGHT - 1){
for(int y=start_y;y<=end_y;y++){
for(int x=start_x;x<=end_x;x++){
grid[y][x] = ch;
text_col[y][x] = fg;
bg_col[y][x] = bg;
}
}
}
void draw_text(int start_x,int start_y,
str text,rgb text_col_ = rgb(),rgb bg_text_col = rgb(0,0,0),
char space_char = ' ',rgb space_col = rgb(0,0,0),rgb bg_space_col = rgb(0,0,0),
vector<str> unicode = {},char replace_char = '$'){
if(start_x < 0 or start_x+text.size()-1 >= WIDTH or start_y < 0 or start_y >= HEIGHT) return;
if (text.empty()) return;
int pl =0;
for(int i = start_x;i<=start_x + text.size() - 1;i++){
if(text[i-(start_x)] == replace_char){
grid[start_y][i] = unicode[pl % unicode.size()];
if(text_col_.r != -1) text_col[start_y][i] = text_col_;
if(bg_text_col.r != -1) bg_col[start_y][i] = bg_text_col;
pl++;
}
else{
grid[start_y][i] = text[i-(start_x)];
if(text_col_.r != -1) text_col[start_y][i] = text_col_;
if(bg_text_col.r != -1) bg_col[start_y][i] = bg_text_col;
}
if(text[i-(start_x)] == ' '){
grid[start_y][i] = space_char;
if(space_col.r != -1) text_col[start_y][i] = space_col;
if(bg_space_col.r != -1) bg_col[start_y][i] = bg_space_col;
}
}
}
void draw_img(int start_x,int start_y,img image){
if(start_x < 0 or start_x + image.w - 1 >= WIDTH or start_y < 0 or start_y + image.h - 1 >= HEIGHT or image.h == 0 or image.w == 0) return;
int idx = 0;
Long pl = 0;
for(int i = start_y;i < start_y + image.h;i++){
for(int j = start_x;j < start_x + image.w;j++){
if(image.im[idx] == image.replace_ch){
grid[i][j] = image.unicode[pl];
text_col[i][j] = image.col[idx];
pl++;
}
else if(image.im[idx] != image.nochar){
grid[i][j] = image.im[idx];
text_col[i][j] = image.col[idx];
}
else{
grid[i][j] = image.nochar_replace;
text_col[i][j] = RGB(0,0,0);
}
idx++;
}
}
}
static string output;
void draw_flush(){
output.clear();
output.reserve(WIDTH * HEIGHT * 16);
rgb last_fg = {-1, -1, -1};
rgb last_bg = {-1, -1, -1};
int cur_x = -1, cur_y = -1;
for(int y = 0; y < HEIGHT; y++){
for(int x = 0; x < WIDTH; x++){
if(grid[y][x].empty()){
grid[y][x] = " ";
}
bool changed = !prev_valid
or grid[y][x] != prev_grid[y][x]
or text_col[y][x].r != prev_text_col[y][x].r
or text_col[y][x].g != prev_text_col[y][x].g
or text_col[y][x].b != prev_text_col[y][x].b
or bg_col[y][x].r != prev_bg_col[y][x].r
or bg_col[y][x].g != prev_bg_col[y][x].g
or bg_col[y][x].b != prev_bg_col[y][x].b;
if(!changed) continue;
if(cur_y != y or cur_x != x){
output += "\033[";
append_num(output, y + 1);
output += ';';
append_num(output, x + 1);
output += 'H';
cur_y = y;
cur_x = x;
}
if(text_col[y][x].r != last_fg.r or text_col[y][x].g != last_fg.g or text_col[y][x].b != last_fg.b){
append_fg_rgb(output, text_col[y][x]);
last_fg = text_col[y][x];
}
if(bg_col[y][x].r != last_bg.r or bg_col[y][x].g != last_bg.g or bg_col[y][x].b != last_bg.b){
append_bg_rgb(output, bg_col[y][x]);
last_bg = bg_col[y][x];
}
output += grid[y][x];
cur_x++;
prev_grid[y][x] = grid[y][x];
prev_text_col[y][x] = text_col[y][x];
prev_bg_col[y][x] = bg_col[y][x];
}
}
if(output.empty()) return;
output += RESET;
prev_valid = true;
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD written;
WriteFile(hOut, output.c_str(), (DWORD)output.length(), &written, NULL);
}
// END DRAW FUNCT ----------------------------------------------------------------------------------------------------------------------------------
// MAIN FUNCTION1 -----------------------------------------------------------------------------------------------------------------------------------
void threadFunc(){
return ;
}
// END MAIN FUNCTION1 -----------------------------------------------------------------------------------------------------------------------------------
bool init_socket_check = 0;
int prscr = 1;
int scr = 5;
int transcr = 1;
double scr_fade = 1.0;
int fade_state = 0;
void apply_scr_fade(double alpha){
alpha = max(0.0, min(1.0, alpha));
// > 1.0 -> brightness
// 1.0 -> bth
// < 1.0 -> darkness
for(int y = 0; y < HEIGHT; y++){
for(int x = 0; x < WIDTH; x++){
text_col[y][x] = scale_rgb(text_col[y][x], alpha);
bg_col[y][x] = scale_rgb(bg_col[y][x], alpha);
}
}
}
struct background{
int x,y;
str uni;
rgb col;
double fade;
double fade_minus;
background(int x_,int y_,str uni_,rgb col_,double fade_,double fade_minus_){
x = x_;
y = y_;
uni = uni_;
col = col_;
fade = fade_;
fade_minus = fade_minus_;
}
};
vector<background> deco;
double timer[50];
int tempx,tempy;
struct item_data{
// id = 0 is null
bool isblock = 0; // 0 -> item, 1-> block
int id = 0;
str name = "NULL"; //max at 64 character
int speed_plus = 0;
double hp_plus = 0;
double phy_dmg_plus = 0;
double mag_dmg_plus = 0;
int range_plus = 0; // tiles
double phy_armor_plus = 0;
double mag_armor_plus = 0;
double counter = 0;
double counter_percent = 0;
double misc[7] = {0,0,0,0,0,0,0};
/*
0 = amount
1 = mining efficiency on (bit count)
- 0 = nature block (wood and other living things)
- 1 = rock/stone/ore/...
- 2 = dirt/sand/snow/...
2 = tool level
*/
int max_stack = 32;
item_data() = default;
};
struct block_data{
// id = 0 is null
int id = 0;
int type = 1; // 0 -> under ground, 1 -> water, 2 -> ground, 3 -> wall (not block hovering), 4 -> higher wall (not block air)
str name = "NULL";
int x = 0;
int y = 0;
double hp = 10;
double phy_armor = 0;
double mag_armor = 0;
double counter = 0;
double counter_percent = 0;
item_data drops[5];
int drop_amount[5];
int drop_chance[5]; // 0 -> 100%
bool is_simple_drop = 1;
int level = 0;
int states = 0;
short open_gui_id = 0;
block_data() = default;
};
struct player_data{
str name = "Temp"; // min lengh = 1, max lengh = 16
int type = 2; // 0 = underground, 1 = water, 2 = ground, 3 = hovering, 4 = air
int active = 0;
double x = 0;
double y = 0;
int speed = 10; // tile / s
double hp = 10.0;
double dir = 0; // 0 = up, 180 = down
double range = 4.6;
int tool = 0;
double mining_speed = 1;
double phy_dmg = 0.75;
double mag_dmg = 0;
double phy_armor = 0;
double mag_armor = 0;
item_data armor[2]; // 0 is more priority than 1
item_data inventory[20];
int selected_hotbar = 0;
short gui = 0;
player_data() = default;
/*
Player data:
Speed: 10 tile/s
Sprint: 1.5x speed
direction: 0
Hp: 10.0
D1 = Physical dmg: 0.75
D2 = Magical dmg: 0.0
A1 = Armor points: 0
A2 = Magical armor points: 0
C = counter power
C% = counter percentage (%)
(balance at dmg = 2)
Physical dmg armor reduction fomula:
Normal: D1 * Hp / (Hp + A1)
Anti burst: D1 / (1 + 0.025 * A1 * D1^2)
Anti spam: max(0.124*D1,D1 - 0.1*A1)
Magical dmg armor reduction fomula:
normal: D2 * min(0.95,A2 / 20)
* magic dmg bypass all physical armor formula *
Counter dmg (count as magic dmg) (C% it will happening):
Physical D1 * (1-c/100)
Magical D1 * (1-c/100) * 0.5
*/
};
struct entity_data{
// id = 0 is null
int type = 2; // 0 = underground, 1 = water, 2 = ground, 3 = hovering, 4 = air
int id = 0;
str name = "Temp"; // min lengh = 1, max lengh = 16
double x = 0;
double y = 0;
int siz = 2;
int speed = 10;
double hp = 10.0;
double dir = 0; // 0 = up, 180 = down
int range = 3;
double mining_speed = 1;
double phy_dmg = 0.75;
double mag_dmg = 0;
double phy_armor = 0;
double mag_armor = 0;
double counter = 0;
double counter_percent = 0;
double misc[10] = {0,0,0,0,0,0,0,0,0,0};
item_data drops[5];
int drop_amount[5];
int drop_chance[5]; // 0 -> 100%
entity_data() = default;
};
enum BlockID : short{
ID_AIR = 0,
ID_DIRT,
ID_SANDSTONE,
ID_STONE,
ID_GRANITE,
ID_MAGMA,
ID_COPPER_ORE,
ID_IRON_ORE,
ID_GOLD_ORE,
ID_COAL_ORE,
ID_DIAMOND,
ID_RUBY,
ID_EMERALD,
ID_PACKED_ICE,
ID_GAS_PACKET,
ID_OBSIDIAN,
ID_WATER,
ID_LAVA,
ID_OIL,
ID_GRASS,
ID_MYCELIUM,
ID_SAND,
ID_SNOW,
ID_ICE,
ID_WOOD,
ID_LEAVES,
ID_MUSHROOM,
// man-made
IT_PLANKS,
// functional
IT_WORKBENCH,
ID_COUNT,
};
enum ItemID : short{
IT_AIR = ID_COUNT + 1,
IT_STICK,
IT_STONE_HAMMER,
IT_STONE_AXE,
};
struct item_entity{
item_data it;
double x = 0,y = 0;
int layer = 2;
double hp = 5.0;
double spawn_time = 0;
};
const Long chunk_size = 32;
const Long render_rad = 7; // (render_rad*2*chunk_size)^2 cell
const Long max_c = 3600/chunk_size+1;
struct chunk{
vector<block_data> blocks;
vector<entity_data> entities;
vector<item_entity> items;
};
pll get_chunk(Long x,Long y){
return {x/chunk_size,y/chunk_size};
}
struct world_data{
bool iscreated = 0;
bool gen = 0;
string name = ""; // min lengh = 1, max lengh = 16
chunk chunk_grid[max_c][max_c];
// block display
short grid[3600][3600][5]; // how many block types
player_data players[8]; // 0 is admin
bool isjoin = 0;
world_data() = default;
};
int worldid = 0;
world_data worlds[6] = {world_data(),world_data(),world_data(),world_data(),world_data(),world_data()};
int type_mask[10];
str type_input[100];
// ==================== Item / BLOCK DROPS INFO ========================================================
bool checkitem(const item_data & x,const item_data & y){
return (x.id == y.id and x.isblock == y.isblock and x.name == y.name and x.speed_plus == y.speed_plus and x.hp_plus == y.hp_plus and
x.phy_dmg_plus == y.phy_dmg_plus and x.mag_dmg_plus == y.mag_dmg_plus and x.range_plus == y.range_plus and x.phy_armor_plus == y.phy_armor_plus and
x.mag_armor_plus == y.mag_armor_plus and x.counter == y.counter and x.counter_percent == y.counter_percent and x.max_stack == y.max_stack and
x.misc[1] == y.misc[1] and x.misc[2] == y.misc[2] and x.misc[3] == y.misc[3] and x.misc[4] == y.misc[4] and x.misc[5] == y.misc[5] and x.misc[6] == y.misc[6]);
}
static str get_name(short id){
switch(id){
case ID_DIRT: return "Dirt";
case ID_GRASS: return "Grass";
case ID_MYCELIUM: return "Mycelium";
case ID_SAND: return "Sand";
case ID_SANDSTONE: return "Sandstone";
case ID_STONE: return "Stone";
case ID_GRANITE: return "Granite";
case ID_MAGMA: return "Magma";
case ID_OBSIDIAN: return "Obsidian";
case ID_COPPER_ORE: return "Copper Ore";
case ID_IRON_ORE: return "Iron Ore";
case ID_GOLD_ORE: return "Gold Ore";
case ID_COAL_ORE: return "Coal Ore";
case ID_DIAMOND: return "Diamond";
case ID_RUBY: return "Ruby";
case ID_EMERALD: return "Emerald";
case ID_PACKED_ICE: return "Packed Ice";
case ID_ICE: return "Ice";
case ID_SNOW: return "Snow";
case ID_WOOD: return "Wood";
case ID_LEAVES: return "Leaves";
case ID_MUSHROOM: return "Mushroom";
case IT_PLANKS: return "Wood Planks";
case IT_WORKBENCH: return "Workbench";
case IT_STICK: return "Stick";
case IT_STONE_HAMMER: return "Stone Hammer";
case IT_STONE_AXE: return "Stone Axe";
default: return "NULL";
}
}
static item_data get_item_data(Long id){
item_data bi = item_data();
bi.misc[0] = 0;
switch(id){
case ID_DIRT: bi.id=ID_DIRT; bi.isblock=1; bi.max_stack = 128; break;
case ID_GRASS: bi.id=ID_GRASS; bi.isblock=1; bi.max_stack = 128; break;
case ID_MYCELIUM: bi.id=ID_MYCELIUM; bi.isblock=1; bi.max_stack = 128; break;
case ID_SAND: bi.id=ID_SAND; bi.isblock=1; bi.max_stack = 32; break;
case ID_SANDSTONE: bi.id=ID_SANDSTONE; bi.isblock=1; bi.max_stack = 64; break;
case ID_STONE: bi.id=ID_STONE; bi.isblock=1; bi.max_stack = 64; break;
case ID_GRANITE: bi.id=ID_GRANITE; bi.isblock=1; bi.max_stack = 64; break;
case ID_MAGMA: bi.id=ID_MAGMA; bi.isblock=1; bi.max_stack = 32; break;
case ID_OBSIDIAN: bi.id=ID_OBSIDIAN; bi.isblock=1; bi.max_stack = 32; break;
case ID_COPPER_ORE: bi.id=ID_COPPER_ORE; bi.isblock=1; bi.max_stack = 64; break;
case ID_IRON_ORE: bi.id=ID_IRON_ORE; bi.isblock=1; bi.max_stack = 64; break;
case ID_GOLD_ORE: bi.id=ID_GOLD_ORE; bi.isblock=1; bi.max_stack = 64; break;
case ID_COAL_ORE: bi.id=ID_COAL_ORE; bi.isblock=1; bi.max_stack = 64; break;
case ID_DIAMOND: bi.id=ID_DIAMOND; bi.isblock=1; bi.max_stack = 32; break;
case ID_RUBY: bi.id=ID_RUBY; bi.isblock=1; bi.max_stack = 32; break;
case ID_EMERALD: bi.id=ID_EMERALD; bi.isblock=1; bi.max_stack = 32; break;
case ID_PACKED_ICE: bi.id=ID_PACKED_ICE; bi.isblock=1; bi.max_stack = 64; break;
case ID_ICE: bi.id=ID_ICE; bi.isblock=1; bi.max_stack = 128; break;
case ID_SNOW: bi.id=ID_SNOW; bi.isblock=1; bi.max_stack = 32; break;
case ID_WOOD: bi.id=ID_WOOD; bi.isblock=1; bi.max_stack = 64; break;
case ID_LEAVES: bi.id=ID_LEAVES; bi.isblock=1; bi.max_stack = 128; break;
case ID_MUSHROOM: bi.id=ID_MUSHROOM; bi.isblock=1; bi.max_stack = 128; break;
case IT_PLANKS: bi.id=IT_PLANKS; bi.isblock=1; bi.max_stack=64; break;
case IT_WORKBENCH: bi.id=IT_WORKBENCH; bi.isblock=1; bi.max_stack=32; break;
case IT_STICK: bi.id=IT_STICK; bi.isblock=0; bi.max_stack=128; break;
case IT_STONE_HAMMER: bi.id=IT_STONE_HAMMER; bi.isblock=0; bi.max_stack=1; bi.phy_dmg_plus=4; break;
case IT_STONE_AXE: bi.id=IT_STONE_AXE; bi.isblock=0; bi.max_stack=1; bi.phy_dmg_plus=3; break;
default: break;
}
return bi;
}
static block_data get_block_data(Long id){
block_data bi = block_data();
bi.hp = 1.0;
for(int i = 0;i<4;i++){
bi.drops[i].id = ID_AIR;
}
switch(id){
case ID_DIRT: bi.hp=1.2; bi.drops[0].id=ID_DIRT; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=0; break;
case ID_GRASS: bi.hp=1.2; bi.drops[0].id=ID_DIRT; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=0; break;
case ID_MYCELIUM: bi.hp=1.2; bi.drops[0].id=ID_MYCELIUM; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=0; break;
case ID_SAND: bi.hp=1; bi.drops[0].id=ID_SAND; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=0; break;
case ID_SANDSTONE: bi.hp=10; bi.drops[0].id=ID_SANDSTONE; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=1; break;
case ID_STONE: bi.hp=20; bi.drops[0].id=ID_STONE; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=1; break;
case ID_GRANITE: bi.hp=18.5; bi.drops[0].id=ID_GRANITE; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=1; break;
case ID_MAGMA: bi.hp=22; bi.drops[0].id=ID_MAGMA; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=80; bi.level=2; break;
case ID_OBSIDIAN: bi.hp=16; bi.drops[0].id=ID_OBSIDIAN; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=4; break;
case ID_COPPER_ORE: bi.hp=30; bi.drops[0].id=ID_COPPER_ORE; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=2; break;
case ID_IRON_ORE: bi.hp=35; bi.drops[0].id=ID_IRON_ORE; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=2; break;
case ID_GOLD_ORE: bi.hp=24; bi.drops[0].id=ID_GOLD_ORE; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=3; break;
case ID_COAL_ORE: bi.hp=29; bi.drops[0].id=ID_COAL_ORE; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=2; break;
case ID_DIAMOND: bi.hp=45; bi.drops[0].id=ID_DIAMOND; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=4; break;
case ID_RUBY: bi.hp=29; bi.drops[0].id=ID_RUBY; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=4; break;
case ID_EMERALD: bi.hp=28.7; bi.drops[0].id=ID_EMERALD; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=3; break;
case ID_PACKED_ICE: bi.hp=5.5; bi.drops[0].id=ID_PACKED_ICE; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=1; break;
case ID_ICE: bi.hp=2.5; bi.drops[0].id=ID_ICE; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=1; break;
case ID_SNOW: bi.hp=1.0; bi.drops[0].id=ID_SNOW; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=40; bi.level=0; break;
case ID_WOOD: bi.hp=5; bi.drops[0].id=ID_WOOD; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=0; break;
case ID_LEAVES: bi.hp=0.50; bi.drops[0].id=ID_LEAVES; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=50; bi.level=0; break;
case ID_MUSHROOM: bi.hp=0.17; bi.drops[0].id=ID_MUSHROOM; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=0; break;
case IT_PLANKS: bi.hp=4; bi.drops[0].id=IT_PLANKS; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=0; break;
case IT_WORKBENCH: bi.hp=4; bi.drops[0].id=IT_WORKBENCH; bi.drops[0].isblock=1; bi.drop_amount[0]=1; bi.drop_chance[0]=100; bi.level=0; break;
default: break;
}
return bi;
}
// ==================== END ITEM/BLOCK DROPS INFO ========================================================
// ==================== WORLD GEN ====================================================================
static inline void wg_place(world_data & w, int x, int y, int type, short id){
if((unsigned)x >= 3600u) return;
if((unsigned)y >= 3600u) return;
if(type < 0 or type > 4) return;
w.grid[y][x][type] = id;
}
static inline uint64_t wg_hash(uint64_t x, uint64_t y, uint64_t seed){
uint64_t h = x * 0x9E3779B97F4A7C15ULL;
h ^= y * 0xBF58476D1CE4E5B9ULL;
h ^= seed + 0x94D049BB133111EBULL;
h ^= h >> 30; h *= 0xBF58476D1CE4E5B9ULL;
h ^= h >> 27; h *= 0x94D049BB133111EBULL;
h ^= h >> 31;
return h;
}
static inline double wg_rand01(uint64_t x, uint64_t y, uint64_t seed){
return (wg_hash(x, y, seed) & 0xFFFFFF) / (double)0x1000000;
}
static inline int wg_randi(int lo, int hi, uint64_t x, uint64_t y, uint64_t seed){
if(hi < lo){ int t = lo; lo = hi; hi = t; }
uint64_t rng = (uint64_t)(hi - lo + 1);
return lo + (int)(wg_hash(x, y, seed) % rng);
}
// 2D value noise
static double wg_noise2(double x, double y, double freq, uint64_t seed){
double fx = x * freq, fy = y * freq;
int64_t ix = (int64_t)floor(fx), iy = (int64_t)floor(fy);
double tx = fx - (double)ix, ty = fy - (double)iy;
tx = tx * tx * (3.0 - 2.0 * tx);
ty = ty * ty * (3.0 - 2.0 * ty);
double a = wg_rand01((uint64_t)ix, (uint64_t)iy, seed);
double b = wg_rand01((uint64_t)(ix+1), (uint64_t)iy, seed);
double c = wg_rand01((uint64_t)ix, (uint64_t)(iy+1), seed);
double d = wg_rand01((uint64_t)(ix+1), (uint64_t)(iy+1), seed);
double ab = a + (b - a) * tx;
double cd = c + (d - c) * tx;
return ab + (cd - ab) * ty;
}
enum Biome {
BIOME_PLAINS, BIOME_FOREST, BIOME_RIVER, BIOME_DESERT,
BIOME_JUNGLE, BIOME_SNOWY_PLAINS, BIOME_FROZEN_RIVER,
BIOME_MUSHROOM_PLAINS, BIOME_STONY_MOUNTAIN, BIOME_MOUNTAIN_PEAK,
BIOME_VOLCANO, BIOME_COUNT
};
static Biome wg_get_biome(int x, int y, uint64_t seed){
double v = wg_noise2((double)x, (double)y, 1.0/400.0, seed ^ 0xABCD);
int bi = (int)(v * BIOME_COUNT);
if(bi >= BIOME_COUNT) bi = BIOME_COUNT - 1;
if(bi < 0) bi = 0;
Biome base = (Biome)bi;
// The base noise occasionally lands on the peak slot.
// Fold it into stony mountain so peaks ONLY appear as a sub-biome.
if(base == BIOME_MOUNTAIN_PEAK) base = BIOME_STONY_MOUNTAIN;
// Secondary noise carves peaks out of stony mountain.
// > 0.68 ? about 32% of stony mountain becomes peak
// 1/150 ? peaks are ~150-tile blobs
if(base == BIOME_STONY_MOUNTAIN){
double p = wg_noise2((double)x, (double)y, 1.0/150.0, seed ^ 0x5EED);
if(p > 0.68) return BIOME_MOUNTAIN_PEAK;
}
return base;
}
static Long wg_floor_id(Biome b){
switch(b){
case BIOME_PLAINS: return ID_GRASS;
case BIOME_FOREST: return ID_GRASS;
case BIOME_RIVER: return ID_SAND;
case BIOME_DESERT: return ID_SAND;
case BIOME_JUNGLE: return ID_LEAVES;
case BIOME_SNOWY_PLAINS: return ID_SNOW;
case BIOME_FROZEN_RIVER: return ID_ICE;
case BIOME_MUSHROOM_PLAINS: return ID_MYCELIUM;
case BIOME_STONY_MOUNTAIN: return ID_STONE;
case BIOME_MOUNTAIN_PEAK: return ID_PACKED_ICE;
case BIOME_VOLCANO: return ID_MAGMA;
default: return ID_GRASS;
}
}
static Long wg_underground_id(Biome b){
switch(b){
case BIOME_RIVER: return ID_GRANITE;
case BIOME_FROZEN_RIVER: return ID_GRANITE;
case BIOME_DESERT: return ID_SANDSTONE;
case BIOME_STONY_MOUNTAIN: return ID_STONE;
case BIOME_MOUNTAIN_PEAK: return ID_STONE;
case BIOME_VOLCANO: return ID_OBSIDIAN;
default: return ID_DIRT;
}
}
static void wg_tree(world_data & w, int cx, int cy, uint64_t seed,
int trunk_h, int leaf_r, int wall_type)
{
// trunk is a short vertical strip (top-down: wood is a "wall" a couple tiles tall)
for(int i = 0; i < trunk_h; i++){
for(int j = 0; j < trunk_h; j++){
wg_place(w, cx + i, cy + j, wall_type, ID_WOOD);
wg_place(w, cx + i, cy + j, 3, ID_WOOD);
}
}
// leaves: a circle blob around the top of the trunk
for(int dy = -leaf_r - trunk_h; dy <= leaf_r + trunk_h; dy++){
for(int dx = -leaf_r - trunk_h; dx <= leaf_r + trunk_h; dx++){
if(dx*dx + dy*dy > (leaf_r+trunk_h)*(leaf_r+trunk_h) + random(0,2) - 1) continue;
int x = cx + dx + trunk_h/2, y = cy + dy + trunk_h/2;
if((unsigned)x >= 3600u or (unsigned)y >= 3600u) continue;
// don't overwrite trunk
if(cx <= x and x <= cx + trunk_h-1 and cy <= y and y <= cy + trunk_h-1) continue;
wg_place(w, x, y, wall_type, ID_LEAVES);
wg_place(w, x, y, 3, ID_LEAVES);
}
}
}
void gen_world(world_data & world){
world.iscreated = 1;
world.players[0] = player_data();
uint64_t seed = (uint64_t)random(1, 1000000000LL);
const int WW = 3600;
// ---------- PASS 1: floor + underground for every tile ----------
for(int y = 0; y < WW; y++){
for(int x = 0; x < WW; x++){
Biome b = wg_get_biome(x, y, seed);
Long floor_id = wg_floor_id(b);
Long ug_id = wg_underground_id(b);
// River override (a "ridge" of water snaking through the map)
double rn = wg_noise2((double)x, (double)y, 1.0/500.0, seed ^ 0x9999);
if(fabs(rn - 0.5) <= 0.012){
world.grid[y][x][2] = ID_AIR;
world.grid[y][x][0] = ID_GRANITE;
if(b == BIOME_SNOWY_PLAINS) world.grid[y][x][2] = ID_ICE;
else if(b != BIOME_VOLCANO) world.grid[y][x][1] = ID_WATER;
else world.grid[y][x][1] = ID_LAVA;
continue;
}
world.grid[y][x][2] = floor_id;
world.grid[y][x][0] = ug_id;
}
}
// ---------- PASS 2: underground ore patches (type 0, invisible until mining) ----------
for(int i = 0; i < 10000; i++){
int ox = wg_randi(0, WW - 1, (uint64_t)i, 1, seed);
int oy = wg_randi(0, WW - 1, (uint64_t)i, 2, seed);
int rad = random(1,4);
double r = wg_rand01((uint64_t)i, 4, seed);
Biome b = wg_get_biome(ox, oy, seed);
Long ore;
if(b == BIOME_STONY_MOUNTAIN){
if(r <= 0.4){
rad = wg_randi(4, 9,(uint64_t)i, 3, seed);
ore = ID_COAL_ORE;
}
else if(r <= 0.8){
rad = wg_randi(2, 5,(uint64_t)i, 3, seed);
ore = ID_IRON_ORE;
}
else if(r <= 0.9){
rad = wg_randi(1, 3,(uint64_t)i, 3, seed);
ore = ID_GOLD_ORE;
}
else if(r <= 0.95){
rad = wg_randi(1, 2,(uint64_t)i, 3, seed);
ore = ID_EMERALD;
}
else{
rad = wg_randi(1, 2,(uint64_t)i, 3, seed);
ore = ID_RUBY;
}
}
else if(b == BIOME_MOUNTAIN_PEAK){
if(r <= 0.2){
rad = wg_randi(4, 9,(uint64_t)i, 3, seed);
ore = ID_COAL_ORE;
}
else if(r <= 0.5){
rad = wg_randi(3, 6,(uint64_t)i, 3, seed);
ore = ID_IRON_ORE;
}
else if(r <= 0.75){
rad = wg_randi(2, 4,(uint64_t)i, 3, seed);
ore = ID_GOLD_ORE;
}
else if(r <= 0.845){
rad = wg_randi(1, 3,(uint64_t)i, 3, seed);
ore = ID_EMERALD;
}
else if(r <= 0.94){
rad = wg_randi(1, 3,(uint64_t)i, 3, seed);
ore = ID_RUBY;
}
else{
rad = wg_randi(1, 2,(uint64_t)i, 3, seed);
ore = ID_DIAMOND;
}
}
else if(b == BIOME_FROZEN_RIVER or b == BIOME_RIVER){
if(r <= 0.4){
rad = wg_randi(5, 11,(uint64_t)i, 3, seed);
ore = ID_COAL_ORE;
}
else if(r <= 0.75){
rad = wg_randi(2, 4,(uint64_t)i, 3, seed);
ore = ID_COPPER_ORE;
}
else if(r <= 0.9){
rad = wg_randi(1, 4,(uint64_t)i, 3, seed);
ore = ID_IRON_ORE;
}
else if(r <= 0.96){
rad = wg_randi(1, 2,(uint64_t)i, 3, seed);
ore = ID_RUBY;
}
else{
rad = wg_randi(1, 2,(uint64_t)i, 3, seed);
ore = ID_DIAMOND;
}
}
else if(b == BIOME_VOLCANO){
if(r <= 0.3){
rad = wg_randi(6, 13,(uint64_t)i, 3, seed);
ore = ID_COAL_ORE;
}
else if(r <= 0.7){
rad = wg_randi(2, 6,(uint64_t)i, 3, seed);
ore = ID_GOLD_ORE;
}
else if(r <= 0.9){
rad = wg_randi(1, 2,(uint64_t)i, 3, seed);
ore = ID_DIAMOND;
}
else{
rad = wg_randi(1, 2,(uint64_t)i, 3, seed);
ore = ID_MAGMA;
}
}
else if(b == BIOME_DESERT){
if(r <= 0.2){
rad = wg_randi(5, 11,(uint64_t)i, 3, seed);
ore = ID_COAL_ORE;
}
else if(r <= 0.4){
rad = wg_randi(3, 5,(uint64_t)i, 3, seed);
ore = ID_OIL;
}
else if(r <= 0.7){
rad = wg_randi(2, 7,(uint64_t)i, 3, seed);
ore = ID_GOLD_ORE;
}
else if(r <= 0.8){
rad = wg_randi(1, 4,(uint64_t)i, 3, seed);
ore = ID_IRON_ORE;
}
else if(r <= 0.9){
rad = wg_randi(2, 5,(uint64_t)i, 3, seed);
ore = ID_COPPER_ORE;
}
else if(r <= 0.96){
rad = wg_randi(1, 2,(uint64_t)i, 3, seed);
ore = ID_WATER;
}
else{
rad = 1;
ore = ID_DIAMOND;
}
}
else if(b == BIOME_MUSHROOM_PLAINS){
if(r <= 0.4){
rad = wg_randi(3, 7,(uint64_t)i, 3, seed);
ore = ID_COPPER_ORE;
}
else if(r <= 0.7){
rad = wg_randi(2, 6,(uint64_t)i, 3, seed);
ore = ID_IRON_ORE;
}
else if(r <= 0.8){
rad = wg_randi(1, 4,(uint64_t)i, 3, seed);
ore = ID_GOLD_ORE;
}
else if(r <= 0.89){
rad = wg_randi(1, 2,(uint64_t)i, 3, seed);
ore = ID_RUBY;
}
else{
rad = 2;
ore = ID_MUSHROOM;
}
}
else{
if(r <= 0.45){
rad = wg_randi(2, 7,(uint64_t)i, 3, seed);
ore = ID_COAL_ORE;
}
else if(r <= 0.7){
rad = wg_randi(1, 5,(uint64_t)i, 3, seed);
ore = ID_COPPER_ORE;
}
else if(r <= 0.88){
rad = wg_randi(1, 4,(uint64_t)i, 3, seed);
ore = ID_IRON_ORE;
}
else{
rad = wg_randi(1, 2,(uint64_t)i, 3, seed);
ore = ID_GOLD_ORE;
}
}
for(int dy = -rad-4; dy <= rad+4; dy++){
for(int dx = -rad-4; dx <= rad+4; dx++){
Long rd = random(-1,1);
if(dx*dx + dy*dy > (rad+rd)*(rad+rd)) continue;
int x = ox + dx, y = oy + dy;
if((unsigned)x >= 3600u or (unsigned)y >= 3600u) continue;
Long cur = world.grid[y][x][0];
if(cur == ID_DIRT or cur == ID_STONE or cur == ID_GRANITE or cur == ID_SANDSTONE){
world.grid[y][x][0] = ore;
}
}
}
}
// ---------- PASS 3: biome features (trees, mushrooms, walls, snowdrifts) ----------
for(int y = 2; y < WW - 2; y++){
for(int x = 2; x < WW - 2; x++){
// skip tiles that are water
if(world.grid[y][x][1] != ID_AIR or world.grid[y][x][2] == ID_ICE) continue;
// skip tiles that already have a wall
if(world.grid[y][x][3] != ID_AIR) continue;
if(world.grid[y][x][4] != ID_AIR) continue;
Biome b = wg_get_biome(x, y, seed);
double r = wg_rand01((uint64_t)x, (uint64_t)y, seed ^ 0xCAFE);
switch(b){
case BIOME_PLAINS:
if(r < 0.005) wg_tree(world, x, y, seed, 1, 2, 3);
break;
case BIOME_FOREST:
if(r < 0.150) wg_tree(world, x, y, seed, 2, 2, 3);
break;
case BIOME_JUNGLE:
if(r < 0.220) wg_tree(world, x, y, seed, 3, 3, 4);
break;
case BIOME_SNOWY_PLAINS:
if(r < 0.005) wg_tree(world, x, y, seed, 1, 1, 3);
break;
case BIOME_DESERT:
if(r < 0.004) world.grid[y][x][3] = ID_WOOD; // dead tree
break;
case BIOME_MUSHROOM_PLAINS:
if(r < 0.130) world.grid[y][x][2] = ID_MUSHROOM;
break;
case BIOME_STONY_MOUNTAIN:
world.grid[y][x][3] = ID_STONE;
world.grid[y][x][2] = ID_STONE;
break;
case BIOME_MOUNTAIN_PEAK:
world.grid[y][x][3] = ID_STONE;
world.grid[y][x][2] = ID_STONE;
if(r < 0.750) world.grid[y][x][4] = ID_PACKED_ICE;
else if(r < 0.950) world.grid[y][x][4] = ID_ICE;
else world.grid[y][x][4] = ID_SNOW;
break;
case BIOME_VOLCANO:
if(r < 0.650) world.grid[y][x][2] = ID_STONE;
else if(r<0.690) world.grid[y][x][2] = ID_COAL_ORE;
else if(r<0.691) world.grid[y][x][2] = ID_DIAMOND;
break;
default: break;
}
}
}
// ---------- PASS 3.5: wall paths (grass in plains, snow in snowy plains) ----------
for(int y = 2; y < WW - 2; y++){
for(int x = 2; x < WW - 2; x++){
if(world.grid[y][x][1] != ID_AIR) continue; // don't cut through liquid
//if(world.grid[y][x][3] != ID_AIR) continue; // don't overwrite trees / walls
Biome b = wg_get_biome(x, y, seed);
if(b != BIOME_PLAINS and b != BIOME_SNOWY_PLAINS and b != BIOME_DESERT and b != BIOME_FOREST and b != BIOME_MUSHROOM_PLAINS) continue;
// Ridge noise — a thin band near 0.5 forms a snaking line
double v = wg_noise2((double)x, (double)y, 1.0/220.0, seed ^ 0xBEEF);
// Vary thickness a little so it doesn't look like a uniform stripe
double thick = 0.006 + wg_rand01((uint64_t)x, (uint64_t)y, seed ^ 0xBEEF2) * 0.010;
short uid = ID_GRASS;
short did = ID_DIRT;
if(b == BIOME_SNOWY_PLAINS) uid = ID_SNOW;
else if(b == BIOME_DESERT){
uid = ID_SAND;
did = ID_SANDSTONE;
}
else if(b == BIOME_MUSHROOM_PLAINS){
uid = ID_MYCELIUM;
}
if(fabs(v - 0.5) < thick){
world.grid[y][x][3] = uid;
world.grid[y][x][2] = did;
}
}
}
// ---------- PASS 4: surface ore mounds (wall1 shell + ore inside) ----------
for(int i = 0; i < 800; i++){
int mx = wg_randi(20, WW - 20, (uint64_t)i, 20, seed);
int my = wg_randi(20, WW - 20, (uint64_t)i, 21, seed);
Biome b = wg_get_biome(mx, my, seed);
Long outer = (b == BIOME_DESERT) ? ID_SANDSTONE : (random(1,100) <= 70 ? ID_STONE : ID_GRANITE);
Long inner;
double r = wg_rand01((uint64_t)i, 22, seed);
if(b == BIOME_DESERT){
inner = (r < 0.5) ? ID_GOLD_ORE : (r < 0.8 ? ID_IRON_ORE : ID_COAL_ORE);
}
else if(b == BIOME_MUSHROOM_PLAINS){
inner = (r < 0.7) ? ID_COPPER_ORE : ID_COAL_ORE;
}
else {
inner = (r < 0.6) ? ID_COAL_ORE : ID_IRON_ORE;
}
double radius = wg_randi(8, 14, (uint64_t)i, 23, seed);
for(int dy = -radius-5; dy <= radius+5; dy++){
for(int dx = -radius-5; dx <= radius+5; dx++){
double d = sqrt((double)(dx*dx + dy*dy));
if(d > radius + random(0,3)) continue;
int x = mx + dx, y = my + dy;
if((unsigned)x >= 3600u or (unsigned)y >= 3600u) continue;
if(d <= radius*0.4 + random(0,2)){
if(random(0,100) <= 25){
wg_place(world, mx + dx, my + dy, 3, inner);
}
else{
wg_place(world, mx + dx, my + dy, 3, outer);
}
}
else{
wg_place(world, mx + dx, my + dy, 3, outer);
}
}
}
}
// ---------- PASS 4.5: stony mountain / mountain peak surface ores ----------
for(int i = 0; i < 1500; i++){
int mx = wg_randi(20, WW - 20, (uint64_t)i, 70, seed);
int my = wg_randi(20, WW - 20, (uint64_t)i, 71, seed);
Biome b = wg_get_biome(mx, my, seed);
if(b != BIOME_STONY_MOUNTAIN) continue;
int radius = wg_randi(3, 7, (uint64_t)i, 72, seed);
double r = wg_rand01((uint64_t)i, 73, seed);
Long ore = ID_GRANITE;
for(int dy = -radius; dy <= radius; dy++){
for(int dx = -radius; dx <= radius; dx++){
double d = sqrt((double)(dx*dx + dy*dy));
double jitter = wg_rand01((uint64_t)i, (uint64_t)(dx*4099 + dy*131), seed ^ 0xACE);
if(d > radius + jitter * 1.5) continue;
int x = mx + dx, y = my + dy;
if((unsigned)x >= 3600u || (unsigned)y >= 3600u) continue;
if(world.grid[y][x][1] != ID_AIR) continue;
if(b == BIOME_MOUNTAIN_PEAK){
}
else{
if(world.grid[y][x][4] != ID_AIR) continue;
if(d < radius * 0.55){
world.grid[y][x][3] = ore;
if(random(1,100) < 45) world.grid[y][x][2] = ore;
}
else if(wg_rand01((uint64_t)x, (uint64_t)y, seed ^ 0xACE3) < 0.45){
world.grid[y][x][3] = ore;
if(random(1,100) < 35) world.grid[y][x][2] = ore;
}
}
}
}
}
for(int i = 0; i < 2000; i++){
int mx = wg_randi(20, WW - 20, (uint64_t)i, 60, seed);
int my = wg_randi(20, WW - 20, (uint64_t)i, 61, seed);
Biome b = wg_get_biome(mx, my, seed);
if(b != BIOME_STONY_MOUNTAIN && b != BIOME_MOUNTAIN_PEAK) continue;
int radius = wg_randi(3, 7, (uint64_t)i, 62, seed);
double r = wg_rand01((uint64_t)i, 63, seed);
Long ore;
if(b == BIOME_MOUNTAIN_PEAK){
// Surface emerald, with a bit of diamond / ruby as a bonus
if(r < 0.70) ore = ID_IRON_ORE;
else{
radius = wg_randi(1, 3, (uint64_t)i, 62, seed);
if(r < 0.90) ore = ID_EMERALD;
else if(r < 0.95) ore = ID_DIAMOND;
else ore = ID_RUBY;
}
}
else {
// Stony mountain: much more coal & iron
if(r < 0.55) ore = ID_COAL_ORE;
else if(r < 0.92) ore = ID_IRON_ORE;
else{
ore = ID_EMERALD;
radius = wg_randi(1, 3, (uint64_t)i, 62, seed);
}
}
for(int dy = -radius; dy <= radius; dy++){
for(int dx = -radius; dx <= radius; dx++){
double d = sqrt((double)(dx*dx + dy*dy));
double jitter = wg_rand01((uint64_t)i, (uint64_t)(dx*4099 + dy*131), seed ^ 0xACE);
if(d > radius + jitter * 1.5) continue;
int x = mx + dx, y = my + dy;
if((unsigned)x >= 3600u || (unsigned)y >= 3600u) continue;
if(world.grid[y][x][1] != ID_AIR) continue; // don't touch liquid
if(b == BIOME_MOUNTAIN_PEAK){
// Peaks render on layer 4/3 (packed ice / stone wall2),
// so put the ore there to make it visible.
if(d < radius * 0.55){
world.grid[y][x][4] = ore; // solid core
if(random(1,100) < 30) world.grid[y][x][3] = ore;
if(random(1,100) < 8) world.grid[y][x][2] = ore;
}
else if(wg_rand01((uint64_t)x, (uint64_t)y, seed ^ 0xACE2) < 0.45){
world.grid[y][x][4] = ore; // mottled rim
if(random(1,100) < 20) world.grid[y][x][3] = ore;
if(random(1,100) < 6) world.grid[y][x][2] = ore;
}
}
else{
if(world.grid[y][x][4] != ID_AIR) continue;
if(d < radius * 0.55){
world.grid[y][x][3] = ore; // solid core
if(random(1,100) < 45) world.grid[y][x][2] = ore;
}
else if(wg_rand01((uint64_t)x, (uint64_t)y, seed ^ 0xACE3) < 0.45){
world.grid[y][x][3] = ore; // mottled rim
if(random(1,100) < 35) world.grid[y][x][2] = ore;
}
}
}
}
mx = wg_randi(20, WW - 20, (uint64_t)i, 60, seed);
my = wg_randi(20, WW - 20, (uint64_t)i, 61, seed);
radius = wg_randi(3, 7, (uint64_t)i, 62, seed);
for(int dy = -radius; dy <= radius; dy++){
for(int dx = -radius; dx <= radius; dx++){
double d = sqrt((double)(dx*dx + dy*dy));
double jitter = wg_rand01((uint64_t)i, (uint64_t)(dx*4099 + dy*131), seed ^ 0xACE);
if(d > radius + jitter * 1.5) continue;
int x = mx + dx, y = my + dy;
if((unsigned)x >= 3600u || (unsigned)y >= 3600u) continue;
if(world.grid[y][x][1] != ID_AIR) continue;
if(b == BIOME_MOUNTAIN_PEAK){
if(d < radius * 0.55){
world.grid[y][x][2] = ore;
}
else if(wg_rand01((uint64_t)x, (uint64_t)y, seed ^ 0xACE2) < 0.45){
world.grid[y][x][2] = ore;
}
}
else{
if(world.grid[y][x][4] != ID_AIR) continue;
if(d < radius * 0.55){
world.grid[y][x][2] = ore;
}
else if(wg_rand01((uint64_t)x, (uint64_t)y, seed ^ 0xACE3) < 0.45){
world.grid[y][x][2] = ore;
}
}
}
}
}
// ---------- PASS 5: biome-specific ponds / lakes ----------
for(int i = 0; i < 400; i++){
int px = wg_randi(10, WW - 10, (uint64_t)i, 30, seed);
int py = wg_randi(10, WW - 10, (uint64_t)i, 31, seed);
Biome b = wg_get_biome(px, py, seed);
Long liquid = ID_AIR;
double chance = 0.0;
switch(b){
case BIOME_PLAINS: chance = 0.04; liquid = ID_WATER; break;
case BIOME_FOREST: chance = 0.05; liquid = ID_WATER; break;
case BIOME_JUNGLE: chance = 0.06; liquid = ID_WATER; break;
case BIOME_SNOWY_PLAINS: chance = 0.05; liquid = ID_ICE; break;
case BIOME_DESERT: chance = 0.10; liquid = ID_OIL; break;
case BIOME_VOLCANO: chance = 0.25; liquid = ID_LAVA; break;
default: chance = 0.0; break;
}
double r = wg_rand01((uint64_t)i, 32, seed);
if(r > chance) continue;
int rx = wg_randi(4, 14, (uint64_t)i, 33, seed);
int ry = wg_randi(4, 14, (uint64_t)i, 34, seed);
for(int dy = -ry; dy <= ry; dy++){
for(int dx = -rx; dx <= rx; dx++){
double d = (double)(dx*dx)/(rx*rx) + (double)(dy*dy)/(ry*ry);
if(d > 1.0) continue;
int x = px + dx, y = py + dy;
if((unsigned)x >= 3600u or (unsigned)y >= 3600u) continue;
world.grid[y][x][3] = ID_AIR; // clear any wall
world.grid[y][x][4] = ID_AIR;
world.grid[y][x][1] = liquid;
world.grid[y][x][2] = ID_AIR; // water replaces floor
if(liquid == ID_LAVA) world.grid[y][x][0] = ID_OBSIDIAN;
}
}
}
// ---------- PASS 6: volcano cones ----------
for(int i = 0; i < 90; i++){
int vx = wg_randi(80, WW - 80, (uint64_t)i, 40, seed);
int vy = wg_randi(80, WW - 80, (uint64_t)i, 41, seed);
// only place inside a volcano biome
if(wg_get_biome(vx, vy, seed) != BIOME_VOLCANO) continue;
double radius = wg_randi(18, 36, (uint64_t)i, 42, seed);
for(int dy = -radius - 2; dy <= radius + 2; dy++){
for(int dx = -radius - 2; dx <= radius + 2; dx++){
double d = sqrt((double)(dx*dx + dy*dy));
// ragged edge
double jitter = wg_rand01((uint64_t)i, (uint64_t)(dx*4099 + dy*131), seed ^ 0xFEED);
if(d > radius + jitter * 2.0) continue;
int x = vx + dx, y = vy + dy;
if((unsigned)x >= 3600u or (unsigned)y >= 3600u) continue;
if(d < radius * 0.30 + random(0,1)){
// core: lava pool (the only place layer 1 is used)
world.grid[y][x][0] = ID_OBSIDIAN;
world.grid[y][x][1] = ID_LAVA;
world.grid[y][x][2] = ID_AIR;
world.grid[y][x][3] = ID_AIR;
world.grid[y][x][4] = ID_AIR;
}
else if(d < radius * 0.55 + random(0,1)){
// inner cone: magma floor, magma wall1
world.grid[y][x][0] = ID_MAGMA;
world.grid[y][x][1] = ID_AIR;
world.grid[y][x][2] = ID_MAGMA;
world.grid[y][x][3] = ID_MAGMA;
world.grid[y][x][4] = ID_AIR;
}
else if(d < radius * 0.70 + random(0,1)){
// middle ring: obsidian floor, magma wall1, magma wall2
world.grid[y][x][0] = ID_OBSIDIAN;
world.grid[y][x][1] = ID_AIR;
world.grid[y][x][2] = ID_OBSIDIAN;
world.grid[y][x][3] = (random(0,100) <= 4 ? ID_DIAMOND : ID_MAGMA);
world.grid[y][x][4] = ID_MAGMA;
}
else if(d < radius * 0.9 + random(0,1)){
// outer ring: obsidian floor only, blends into surrounding magma biome
world.grid[y][x][0] = ID_OBSIDIAN;
world.grid[y][x][1] = ID_AIR;
world.grid[y][x][2] = (random(0,100) <= 2 ? ID_DIAMOND : ID_OBSIDIAN);
world.grid[y][x][3] = ID_AIR;
world.grid[y][x][4] = ID_AIR;
}
else{
world.grid[y][x][1] = ID_AIR;
world.grid[y][x][2] = ID_MAGMA;
world.grid[y][x][3] = ID_AIR;
world.grid[y][x][4] = ID_AIR;
}
}
}
}
// ---------- spawn ----------
int spawn_x = random(1600,2000), spawn_y = random(1600,2000);
world.grid[spawn_y][spawn_x][3] = world.grid[spawn_y][spawn_x][4] = ID_AIR;
world.grid[spawn_y+1][spawn_x][3] = world.grid[spawn_y+1][spawn_x][4] = ID_AIR;
world.grid[spawn_y][spawn_x+1][3] = world.grid[spawn_y][spawn_x+1][4] = ID_AIR;
world.grid[spawn_y+1][spawn_x+1][3] = world.grid[spawn_y+1][spawn_x+1][4] = ID_AIR;
world.grid[spawn_y][spawn_x][2] = ID_STONE;
world.grid[spawn_y+1][spawn_x][2] = ID_STONE;
world.grid[spawn_y][spawn_x+1][2] = ID_STONE;
world.grid[spawn_y+1][spawn_x+1][2] = ID_STONE;
world.players[0].x = (double)spawn_x;
world.players[0].y = (double)spawn_y;
world.gen = 1;
}
// ==================== END WORLD GEN ====================================================================
// ==================== WORLD UPDATE ================================================================================
int render_layer = 4;
static int minex = -1;
static int miney = -1;
static int mine_layer = -1;
static double mine_block_hp = 0;
static double mine_reset = 0;
static bool workbench_open = 0;
static item_data input_item[4];
static item_data output_item;
static int output_item_count = 0;
static int output_index = 0;
const int maxium_same_output = 64;
static int output_array[maxium_same_output];
static int match_recipes_co = 0;
static bool left_pt = 0;
static bool righ_pt = 0;
const double mine_reset_time = 7.0 / fps; // 7 frames at 70 fps
static void add_item_to_world(world_data & world, const item_entity & it){
pll cpos = get_chunk(it.x,it.y);
int cx = cpos.first;
int cy = cpos.second;
if(cx >= max_c or cy >= max_c) return;
world.chunk_grid[cy][cx].items.push_back(it);
}
static bool add_item(player_data & p,item_data & it){
if(it.id == 0 or it.misc[0] <= 0) return 0;
// stack onto existing
for(int i = 0;i<20;i++){
if(checkitem(p.inventory[i],it) and p.inventory[i].misc[0] < it.max_stack){
Long pl = min(p.inventory[i].max_stack - p.inventory[i].misc[0],it.misc[0]);
p.inventory[i].misc[0] += pl;
it.misc[0] -= pl;
if(it.misc[0] <= 0) return 1;
}
}
// first empty slot
for(int i = 0;i<20;i++){
if(p.inventory[i].id == 0){
Long pl = min(it.max_stack,(int) it.misc[0]);
p.inventory[i] = it;
p.inventory[i].misc[0] = pl;
it.misc[0] -= pl;
if(it.misc[0] <= 0) return 1;
}
}
return 0;
}
// ==================== CRAFTING ====================
struct recipe{
item_data ingr[4];
int ingr_count = 0;
item_data out;
int out_count = 1;
bool need_workbench = 0;
};
static vector<recipe> recipes;
static void init_recipes(){
if(!recipes.empty()) return;
auto mk_ingr = [](int id,int count){
item_data ri;
ri = get_item_data(id);
ri.name = get_name(id);
ri.misc[0] = count;
return ri;
};
auto add = [](vector<item_data> inp,item_data out, bool owb){
recipe r;
for(int i = 0;i<inp.size() and i<4; i++){
r.ingr[i] = inp[i];
}
r.ingr_count = inp.size();
r.out = out;
r.out_count = out.misc[0];
r.need_workbench = owb;
recipes.push_back(r);
};
// 1 wood -> 4 planks
add({ mk_ingr(ID_WOOD, 1) },
mk_ingr(IT_PLANKS, 4), 0);
// 1 planks -> 4 stick
add({ mk_ingr(IT_PLANKS, 1) },
mk_ingr(IT_STICK, 4), 0);
// 2 planks + 1 leaf -> workbench
add({ mk_ingr(IT_PLANKS, 2), mk_ingr(ID_LEAVES, 1) },
mk_ingr(IT_WORKBENCH, 1), 0);
// 1 stone + 2 stick + 3 leaf -> stone hammer
add({ mk_ingr(ID_STONE, 1), mk_ingr(IT_STICK, 2), mk_ingr(ID_LEAVES, 3) },
mk_ingr(IT_STONE_HAMMER, 1), 1);
// 2 stone + 2 stick + 4 leaf -> stone axe
add({ mk_ingr(ID_STONE, 2), mk_ingr(IT_STICK, 2), mk_ingr(ID_LEAVES, 4) },
mk_ingr(IT_STONE_AXE, 1), 1);
}
// count how many ingredients have same data in crafting slot
static int count_in_slots(item_data * slots, int n, const item_data & target){
int kq = 0;
for(int i = 0;i<n;i++){
if(slots[i].id == 0) continue;
if(checkitem(slots[i], target)) kq += (int)slots[i].misc[0];
}
return kq;
}
// check if a recipe matches the required ingredient
static bool recipe_can_match(const recipe & r, item_data * slots, int n_slots, bool is_wb){
if(r.need_workbench and !is_wb) return 0;
for(int i = 0;i<n_slots;i++){
if(slots[i].id == 0) continue;
bool che = 0;
for(int j = 0;j<r.ingr_count; j++){
if(checkitem(slots[i], r.ingr[j])){
che =1;
break;
}
}
if(!che) return 0;
}
for(int j = 0;j<r.ingr_count;j++){
if(count_in_slots(slots, n_slots, r.ingr[j]) < r.ingr[j].misc[0]) return 0;
}
for(int i = 0; i < n_slots; i++) if(slots[i].id != 0) return 1;
return 0;
}
// how many output it need to return
static int recipe_craft_count(const recipe & r, item_data * slots, int n_slots){
int best = 1 << 30;
for(int j = 0; j < r.ingr_count; j++){
int have = count_in_slots(slots, n_slots, r.ingr[j]);
int need = r.ingr[j].misc[0];
if(need <= 0) continue;
best = min(best, have / need);
}
return (best == (1 << 30)) ? 0 : best;
}
// update what output from the input
static void update_craft_matches(){
init_recipes();
match_recipes_co = 0;
int n_slots = workbench_open ? 4 : 2;
for(int i = 0;i<recipes.size();i++){
if(recipe_can_match(recipes[i], input_item, n_slots, workbench_open)){
if(match_recipes_co < maxium_same_output) output_array[match_recipes_co++] = i;
}
}
if(output_index >= match_recipes_co) output_index = 0;
if(match_recipes_co > 0){
const recipe & r = recipes[output_array[output_index]];
int c = 1;
// c = recipe_craft_count(r, input_item, n_slots);
output_item = r.out;
output_item_count = c * r.out_count;
}
else{
output_item = item_data();
output_item_count = 0;
}
}
// crafting update output thinggy
static bool do_craft(player_data & cp, world_data & world){
if(match_recipes_co == 0) return 0;
int n_slots = workbench_open ? 4 : 2;
const recipe & r = recipes[output_array[output_index]];
int count = 1;
//count = recipe_craft_count(r, input_item, n_slots);
if(count <= 0) return 0;
for(int j = 0;j<r.ingr_count;j++){
int need = r.ingr[j].misc[0] * count;
for(int s = 0;s<n_slots and need > 0;s++){
if(input_item[s].id == 0) continue;
if(checkitem(input_item[s], r.ingr[j])){
int pl = min((int)input_item[s].misc[0], need);
input_item[s].misc[0] -= pl;
need -= pl;
if(input_item[s].misc[0] <= 0) input_item[s] = item_data();
}
}
}
item_data out = r.out;
out.misc[0] = count * r.out_count;
if(!add_item(cp, out)){
item_entity it;
it.x = cp.x; it.y = cp.y;
it.it = out;
it.layer = 2;
it.hp = 5.0;
add_item_to_world(world, it);
}
update_craft_matches();
return 1;
}
// ==================== END CRAFTING ====================
static bool open_inv = 0;
static bool mouse_have_item = 0;
static bool mouse_click_pr = 0;
static item_data mouse_item;
static bool mouse_click_pr1 = 0;
int g_hl_x = -1, g_hl_y = -1;
static int pages = 0;
static int check_hover_slot(player_data & cp){
if(open_inv){
const int base_x = 27;
const int base_y = HEIGHT - 16;
for(int r = 0; r < 4; r++){
for(int c = 0; c < 5; c++){
int sx = base_x + c*5;
int sy = base_y + r*4;
if(mousex >= sx and mousex <= sx+4 and mousey >= sy and mousey <= sy+3){
return r*5 + c;
}
}
}
}
else{
const int base_x = 27;
const int base_y = HEIGHT - 4;
for(int i = 0; i < 5; i++){
int sx = base_x + i*5;
int sy = base_y;
if(mousex >= sx and mousex <= sx+4 and mousey >= sy and mousey <= sy+3) return i;
}
}
return -1;
}
static int check_hover_craft(int n_slots){
const int base_x = 54;
const int base_y = HEIGHT - 16;
for(int i = 0; i < n_slots; i++){
int sx = base_x + (i % 2) * 5;
int sy = base_y + (i / 2) * 4;
if(mousex >= sx and mousex <= sx+4 and mousey >= sy and mousey <= sy+3) return i;
}
int ox = base_x + 12;
int oy = base_y + (n_slots > 2 ? 2 : 0);
if(mousex >= ox and mousex <= ox+4 and mousey >= oy and mousey <= oy+3) return 4;
int ax = base_x;
int ay = base_y + (n_slots > 2 ? 9 : 5);
if(mousex >= ax and mousex <= ax+2 and mousey >= ay and mousey <= ay+2) return 5;
if(mousex >= ax+3 and mousex <= ax+5 and mousey >= ay and mousey <= ay+2) return 6;
return -1;
}
static void inv_input(player_data & cp,world_data & world){
if(key_pressed['E']){
open_inv = !open_inv;
}
int hov = check_hover_slot(cp);
for(int k = 0; k < 5; k++){
if(key_pressed['1' + k]){
if(hov >= 0 and hov != k and open_inv == 1){
swap(cp.inventory[hov], cp.inventory[k]);
}
else{
cp.selected_hotbar = k;
}
}
}
if(mousewheel == 1){
cp.selected_hotbar = (cp.selected_hotbar+1)%5;
}
else if(mousewheel == -1){
cp.selected_hotbar = (cp.selected_hotbar-1)%5;
if(cp.selected_hotbar < 0) cp.selected_hotbar = 4;
}
// right click
bool clicked = mouseclick[1] and !mouse_click_pr1;
mouse_click_pr1 = mouseclick[1];
if(clicked){
if(mouse_have_item){
if(hov >= 0 and cp.inventory[hov].id == 0){
cp.inventory[hov] = mouse_item;
cp.inventory[hov].misc[0] = 1;
mouse_item.misc[0] -= 1;
if(mouse_item.misc[0] <= 0){
mouse_item = item_data();
mouse_have_item = 0;
}
}
else if(hov >= 0 and checkitem(cp.inventory[hov],mouse_item)){
int empt = cp.inventory[hov].max_stack - (int)cp.inventory[hov].misc[0];
int pl = 1;
if(empt > 0){
cp.inventory[hov].misc[0] += pl;
mouse_item.misc[0] -= pl;
if(mouse_item.misc[0] <= 0){
mouse_item = item_data();
mouse_have_item = 0;
}
}
}
else if(hov >= 0){
swap(cp.inventory[hov],mouse_item);
}
else if(mouse_item.id != 0){
if(g_hl_x != -1){
item_entity it;
it.x = g_hl_x;
it.y = g_hl_y;
it.it = mouse_item;
it.it.misc[0] = 1;
int layer = -1;
for(int typ = 4; typ >= 0; typ--){
short bid = world.grid[g_hl_y][g_hl_x][typ];
if(bid != ID_AIR){
layer = typ;
break;
}
}
it.layer = layer;
it.hp = 5;
add_item_to_world(world,it);
mouse_item.misc[0]--;
if(mouse_item.misc[0] <= 0){
mouse_item = item_data();
mouse_have_item = 0;
}
}
else{
add_item(cp,mouse_item);
}
}
}
else{
if(hov >= 0 and cp.inventory[hov].id != 0){
mouse_item = cp.inventory[hov];
int hm = (mouse_item.misc[0] + 1)/2;
mouse_item.misc[0] = hm;
cp.inventory[hov].misc[0] -= hm;
if(cp.inventory[hov].misc[0] <= 0) cp.inventory[hov] = item_data();
mouse_have_item = 1;
}
}
}
// left click
clicked = mouseclick[0] and !mouse_click_pr;
mouse_click_pr = mouseclick[0];
if(!clicked) return;
if(22 <= mousex and mousex <= 23 and mousey == HEIGHT-2 and (pages < recipes.size()/4)){
pages += 1;
}
else if(3 <= mousex and mousex <= 4 and mousey == HEIGHT-2 and (pages > 0)){
pages -= 1;
}
if(mouse_have_item){
if(hov >= 0 and cp.inventory[hov].id == 0){
cp.inventory[hov] = mouse_item;
mouse_item = item_data();
mouse_have_item = 0;
}
else if(hov >= 0 and checkitem(cp.inventory[hov],mouse_item)){
int empt = cp.inventory[hov].max_stack - (int)cp.inventory[hov].misc[0];
int amount = (int)mouse_item.misc[0];
int pl = min(empt, amount);
cp.inventory[hov].misc[0] += pl;
mouse_item.misc[0] -= pl;
if(mouse_item.misc[0] <= 0) mouse_have_item = 0;
}
else if(hov >= 0){
swap(cp.inventory[hov],mouse_item);
}
else if(mouse_item.id != 0){
if(g_hl_x != -1){
item_entity it;
it.x = g_hl_x;
it.y = g_hl_y;
it.it = mouse_item;
int layer = -1;
for(int typ = 4; typ >= 0; typ--){
short bid = world.grid[g_hl_y][g_hl_x][typ];
if(bid != ID_AIR){
layer = typ;
break;
}
}
it.layer = layer;
it.hp = 5;
add_item_to_world(world,it);
mouse_item = item_data();
mouse_have_item = 0;
}
else{
add_item(cp,mouse_item);
}
}
}
else{
if(hov >= 0 and cp.inventory[hov].id != 0){
mouse_item = cp.inventory[hov];
cp.inventory[hov] = item_data();
mouse_have_item = 1;
}
}
}
// returns world tile the player is aiming at, max to cp.range.
static void get_mining_target(player_data & cp,int & tx,int & ty,double range = -1){
tx = ty = -1;
Long cam_start_x = (Long)cp.x - WIDTH/2 + 1;
Long cam_start_y = (Long)cp.y - HEIGHT/2 + 1;
double pcx = cp.x + 0.5;
double pcy = cp.y + 0.5;
double world_x = (double)(cam_start_x + mousex);
double world_y = (double)(cam_start_y + mousey);
double dx = world_x - pcx;
double dy = world_y - pcy;
double di = sqrt(dx*dx + dy*dy);
if(range == -1){
if(di > (double)cp.range){
dx = dx / di * (double)cp.range;
dy = dy / di * (double)cp.range;
world_x = pcx + dx;
world_y = pcy + dy;
}
}
else{
if(di > (double)range){
dx = dx / di * range;
dy = dy / di * range;
world_x = pcx + dx;
world_y = pcy + dy;
}
}
int itx = (int)floor(world_x);
int ity = (int)floor(world_y);
if(itx >= 3600 or ity >= 3600) return;
tx = itx;
ty = ity;
}
static bool check_mining_vision(player_data & cp,int tx,int ty,int target_layer,world_data & world){
double pcx = cp.x + 0.5, pcy = cp.y + 0.5;
double bcx = tx + 0.5, bcy = ty + 0.5;
double dx = bcx - pcx, dy = bcy - pcy;
double di = sqrt(dx*dx + dy*dy);
if(di < 0.01) return 1;
int steps = (int)(di * 4.0);
if(steps < 1) steps = 1;
int px = (int)floor(cp.x), py = (int)floor(cp.y);
auto is_player_tile = [&](int x,int y){
return (x == px or x == px+1) and (y == py or y == py+1);
};
int player_h = cp.type;
if(player_h < 0) player_h = 0;
if(player_h > 4) player_h = 4;
for(int s = 1; s < steps; s++){
double t = (double)s / (double)steps;
int itx = (int)floor(pcx + dx * t);
int ity = (int)floor(pcy + dy * t);
if((unsigned)itx >= 3600u or (unsigned)ity >= 3600u) continue;
if(itx == tx and ity == ty) continue;
if(is_player_tile(itx,ity)) continue;
for(int lay = 4; lay > player_h; lay--){
if(world.grid[ity][itx][lay] != ID_AIR){
extern Long mousex, mousey;
debug[1] = itx;
debug[2] = ity;
debug[3] = lay;
return false;
}
}
}
return 1;
}
void handle_mining(player_data & cp, world_data & world){
bool holding = (mouseclick[0] == 1);
int tx = -1,ty = -1;
for(double ran = cp.range;ran >= 0.0;ran-=0.5){
get_mining_target(cp, tx, ty,ran);
g_hl_x = g_hl_y = -1;
if(tx >= 0 and ty >= 0){
short top_block = ID_AIR;
int layer = -1;
for(int typ = cp.type+1;typ >= 0;typ--){
short bid = world.grid[ty][tx][typ];
if(bid != ID_AIR){
top_block = bid;
layer = typ;
break;
}
}
if(top_block != ID_AIR and check_mining_vision(cp, tx, ty, layer, world)){
g_hl_x = tx;
g_hl_y = ty;
break;
}
}
}
if(check_hover_slot(cp) != -1) return;
if(!holding){
if(minex != -1){
mine_reset += dt;
if(mine_reset > mine_reset_time){
minex = miney = mine_layer = -1;
mine_block_hp = 0;
mine_reset = 0;
}
}
return;
}
if(tx < 0 or ty < 0) return;
Long top_block = ID_AIR;
int layer = -1;
for(int typ = cp.type+1; typ >= 1; typ--){
short bid = world.grid[ty][tx][typ];
if(bid != ID_AIR){
top_block = bid;
layer = typ;
break;
}
}
if(top_block == ID_AIR) return;
if(!check_mining_vision(cp, tx, ty, layer, world)) return;
block_data bi = get_block_data(top_block);
if(bi.hp <= 0.0) return;
if(tx != minex or ty != miney or layer != mine_layer){
minex = tx;
miney = ty;
mine_layer = layer;
mine_block_hp = bi.hp;
}
mine_reset = 0;
mine_block_hp -= cp.mining_speed * dt;
if(mine_block_hp <= 0){
world.grid[ty][tx][layer] = ID_AIR;
for(int i = 0; i < 4; i++){
if(bi.drops[i].id == ID_AIR) continue;
if((int)random(1,100) <= bi.drop_chance[i]){
item_entity it;
if(bi.is_simple_drop){
it.it = get_item_data(bi.drops[i].id);
it.it.name = get_name(bi.drops[i].id);
}
else it.it = bi.drops[i];
it.it.misc[0] = bi.drop_amount[i];
it.x = tx + 0.5;
it.y = ty + 0.5;
it.layer = layer;
it.hp = 5.0;
it.spawn_time = 0;
add_item_to_world(world, it);
}
}
minex = miney = mine_layer = -1;
mine_block_hp = 0;
}
}
const double item_pickup_rad = 2.5;
const double item_pickup_delay = 0.4;
void update_world(world_data & world,double dt){
if(!world.iscreated) return;
for(int cy = 0;cy<max_c;cy++){
for(int cx = 0;cx<max_c;cx++){
chunk & ch = world.chunk_grid[cy][cx];
if(ch.items.empty() and ch.entities.empty() and ch.blocks.empty()) continue;
bool che = 0;
for(int pp = 0; pp < 8; pp++){
player_data & pl = world.players[pp];
if(pl.hp <= 0) continue;
if(pp > 0 and pl.active == 0) continue;
pll pos = get_chunk(pl.x,pl.y);
if(pos.first - render_rad <= cx and cx <= pos.first + render_rad and pos.second - render_rad <= cy and cy <= pos.second + render_rad){
che = 1;
break;
}
}
if(!che) continue;
// ---------- items ----------
for(int i = ch.items.size()-1;i>=0;i--){
item_entity & it = ch.items[i];
it.spawn_time += dt;
if(it.hp <= 0){
ch.items.erase(ch.items.begin() + i);
continue;
}
if(it.spawn_time < item_pickup_delay) continue;
int best_p = -1;
double best_d2 = item_pickup_rad * item_pickup_rad;
for(int pp = 0; pp < 8; pp++){
player_data & pl = world.players[pp];
if(pl.hp <= 0) continue;
if(pp > 0 and pl.active == 0) continue;
double ddx = it.x - (pl.x + 0.5);
double ddy = it.y - (pl.y + 0.5);
double d2 = ddx*ddx + ddy*ddy;
if(d2 < best_d2){
best_d2 = d2;
best_p = pp;
}
}
if(best_p < 0) continue;
if(add_item(world.players[best_p], it.it)){
ch.items.erase(ch.items.begin() + i);
}
}
// ---------- entities ----------
for(int e = 0; e < ch.entities.size();e++){
entity_data & en = ch.entities[e];
// TODO: entity AI, hp decay, movement
}
}
}
}
// ==================== END WORLD UPDATE ================================================================================
// ===================== RENDER =====================================================================================
void get_block_visuals(Long id, int layer, str & sym, rgb & fg, rgb & bg){
sym = " ";
fg = rgb(0, 0, 0);
bg = rgb(0, 0, 0);
switch (id){
case ID_DIRT: sym="."; fg=rgb(140,90,50); bg=rgb(70,45,25); break;
case ID_SANDSTONE: sym=":"; fg=rgb(215,190,130); bg=rgb(110,95,65); break;
case ID_STONE: sym="#"; fg=rgb(150,150,150); bg=rgb(75,75,75); break;
case ID_GRANITE: sym="#"; fg=rgb(190,140,140); bg=rgb(95,70,70); break;
case ID_MAGMA: sym=u8"\u2591"; fg=rgb(255,90,10); bg=rgb(90,25,0); break;
case ID_COPPER_ORE: sym="o"; fg=rgb(210,130,70); bg=rgb(85,55,30); break;
case ID_IRON_ORE: sym="o"; fg=rgb(210,190,170); bg=rgb(85,75,65); break;
case ID_GOLD_ORE: sym="o"; fg=rgb(255,215,40); bg=rgb(105,85,10); break;
case ID_COAL_ORE: sym="o"; fg=rgb(45,45,45); bg=rgb(20,20,20); break;
case ID_DIAMOND: sym=u8"\u25CF"; fg=rgb(120,255,255); bg=rgb(25,90,90); break;
case ID_RUBY: sym=u8"\u25CF"; fg=rgb(255,60,90); bg=rgb(90,15,25); break;
case ID_EMERALD: sym=u8"\u25CF"; fg=rgb(60,255,110); bg=rgb(15,90,35); break;
case ID_PACKED_ICE: sym=u8"\u2593"; fg=rgb(60,120,170); bg=rgb(70,130,180); break;
case ID_GAS_PACKET: sym="o"; fg=rgb(210,255,180); bg=rgb(65,85,45); break;
case ID_OBSIDIAN: sym="#"; fg=rgb(90,45,130); bg=rgb(35,15,55); break;
case ID_WATER: sym="~"; fg=rgb(70,130,230); bg=rgb(25,45,110); break;
case ID_LAVA: sym="~"; fg=rgb(255,110,25); bg=rgb(130,35,0); break;
case ID_OIL: sym="~"; fg=rgb(65,55,45); bg=rgb(25,20,15); break;
case ID_GRASS: sym="\"";fg=rgb(70,210,70); bg=rgb(25,85,25); break;
case ID_MYCELIUM: sym="\'";fg=rgb(190,160,210); bg=rgb(85,65,105); break;
case ID_SAND: sym=u8"\u2591"; fg=rgb(245,225,150); bg=rgb(125,115,65); break;
case ID_SNOW: sym="\"";fg=rgb(245,245,255); bg=rgb(125,125,145);break;
case ID_ICE: sym="."; fg=rgb(170,210,255); bg=rgb(65,105,155); break;
case ID_WOOD: sym="|"; fg=rgb(130,75,35); bg=rgb(65,35,15); break;
case ID_LEAVES: sym=u8"\u2592"; fg=rgb(45,160,45); bg=rgb(15,65,15); break;
case ID_MUSHROOM: sym=u8"\u2660"; fg=rgb(230,70,70); bg=rgb(85,25,25); break;
case IT_PLANKS: sym="="; fg=rgb(190,140,80); break;
case IT_WORKBENCH: sym="W"; fg=rgb(230,190,110); break;
}
double mul = 1.0;
switch(layer){
case 0: mul = 0.65; break; // underground
case 1: mul = 1.00; break; // liquid
case 2: mul = 1.00; break; // floor
case 3: mul = 1.20; break; // wall 1
case 4: mul = 1.45; break; // wall 2
}
if(layer == 4){ fg.b = min(255, fg.b + 30); bg.b = min(255, bg.b + 30); }
if(layer == 3){ fg.r = min(255, fg.r + 20); bg.r = min(255, bg.r + 20); }
fg = scale_rgb(fg, mul);
bg = scale_rgb(bg, mul);
}
static void get_item_visuals(int id, int isblock, str &sym, rgb & fg, rgb & bg){
if(isblock){
get_block_visuals(id, 2, sym, fg, bg);
fg = scale_rgb(fg, 1.30);
bg = rgb(0, 0, 0);
}
else{
bg = rgb(0, 0, 0);
switch(id){
case IT_STICK: sym="/"; fg = rgb(170,120,70); break;
case IT_STONE_HAMMER: sym=u8"\u2530"; fg=rgb(200,200,200); break;
case IT_STONE_AXE: sym="T"; fg=rgb(180,180,180); break;
default: sym="?"; fg=rgb(255,255,255); break;
}
}
}
void render(Long x, Long y, world_data & world){
Long cam_start_x = x - WIDTH/2 + 1;
Long cam_start_y = y - HEIGHT/2 + 1;
// block draw
for(int scr_y = 0; scr_y < HEIGHT; scr_y++){
for(int scr_x = 0; scr_x < WIDTH; scr_x++){
Long wx = cam_start_x + scr_x;
Long wy = cam_start_y + scr_y;
if(wx >= 0 and wx < 3600 and wy >= 0 and wy < 3600){
Long top_block = ID_AIR;
int get_typ = 4;
for(int typ = render_layer; typ >= 0; typ--){
Long bid = world.grid[wy][wx][typ];
if(bid != ID_AIR){
top_block = bid;
get_typ = typ;
break;
}
}
str uni = " ";
rgb fg = rgb(0,0,0), bg = rgb(0,0,0);
if(top_block != ID_AIR) get_block_visuals(top_block, get_typ, uni, fg, bg);
if(wy > 0){
bool above_is_wall1 = world.grid[wy-1][wx][3] != ID_AIR;
bool above_is_wall2 = world.grid[wy-1][wx][4] != ID_AIR;
if(above_is_wall2){
fg = scale_rgb(fg, 0.60);
bg = scale_rgb(bg, 0.60);
}
else if(above_is_wall1){
fg = scale_rgb(fg, 0.80);
bg = scale_rgb(bg, 0.80);
}
above_is_wall1 = world.grid[wy][wx][3] != ID_AIR;
above_is_wall2 = world.grid[wy][wx][4] != ID_AIR;
if((above_is_wall1 and render_layer <= 2) or (above_is_wall2 and render_layer <= 3)){
fg = scale_rgb(fg, 0.70);
bg = scale_rgb(bg, 0.70);
}
}
if(wx == g_hl_x and wy == g_hl_y){
fg = scale_rgb(fg, 1.55);
bg = scale_rgb(bg, 1.55);
}
set_pixel(scr_x, scr_y, uni, fg, bg);
}
else{
set_pixel(scr_x, scr_y, " ", rgb(0,0,0), rgb(0,0,0));
}
}
}
// item entity and entity draw
Long mi_ch_x = max(0LL, cam_start_x / chunk_size);
Long ma_ch_x = min(max_c - 1, (cam_start_x + WIDTH) / chunk_size);
Long mi_ch_y = max(0LL, cam_start_y / chunk_size);
Long ma_ch_y = min(max_c - 1, (cam_start_y + HEIGHT) / chunk_size);
for(Long cy = mi_ch_y; cy <= ma_ch_y; cy++){
for(Long cx = mi_ch_x; cx <= ma_ch_x; cx++){
chunk & ch = world.chunk_grid[cy][cx];
// ---- items ----
for(const item_entity & it : ch.items){
int wx = (int)floor(it.x);
int wy = (int)floor(it.y);
if((unsigned)wx >= 3600u or (unsigned)wy >= 3600u) continue;
Long sx = wx - cam_start_x;
Long sy = wy - cam_start_y;
if(sx < 0 or sx >= WIDTH or sy < 0 or sy >= HEIGHT) continue;
if(it.layer > render_layer) continue;
int top_visible = 0;
for(int d_lay = render_layer; d_lay >= 1; d_lay--){
if(world.grid[wy][wx][d_lay] != ID_AIR){
top_visible = d_lay;
break;
}
}
if(top_visible != 0 and it.layer < top_visible) continue;
str sym;
rgb fg,bg;
get_item_visuals(it.it.id, it.it.isblock, sym, fg, bg);
if(it.spawn_time < 0.35){
double k = 0.65 + 0.35 * sin(it.spawn_time * 40.0);
fg = scale_rgb(fg, k + 0.30);
}
set_pixel(sx, sy, sym, fg, bg);
}
// ---- entities ----
for(const auto & ent : ch.entities){
Long sx = (Long)ent.x - cam_start_x;
Long sy = (Long)ent.y - cam_start_y;
if(sx >= 0 and sx < WIDTH and sy >= 0 and sy < HEIGHT){
// TODO: draw entity
}
}
}
}
// player draw
for(int p_idx = 7; p_idx >= 0; p_idx--){
const player_data & p = world.players[p_idx];
if(p.hp <= 0 or (p.active == 0 and p_idx > 0)) continue;
bool check_local = (p_idx == 0);
str uni = check_local ? "@" : "P";
rgb p_fg = check_local ? rgb(0, 255, 220) : rgb(255, 200, 50);
rgb p_bg = check_local ? rgb(0, 60, 50) : rgb(60, 40, 0);
for(int dy = 0; dy <= 1; dy++){
for(int dx = 0; dx <= 1; dx++){
Long sx = (Long)(p.x + dx) - cam_start_x;
Long sy = (Long)(p.y + dy) - cam_start_y;
if(sx >= 0 and sx < WIDTH and sy >= 0 and sy < HEIGHT){
if(((p.type == 2 or p.type == 1) and p.type != 0 and (world.grid[(Long)(p.y + dy)][(Long)(p.x + dx)][3] == ID_AIR)+(render_layer < 3) and (world.grid[(Long)(p.y + dy)][(Long)(p.x + dx)][4] == ID_AIR)+(render_layer < 4)) or
(p.type == 3 and (world.grid[(Long)(p.y + dy)][(Long)(p.x + dx)][4] == ID_AIR)+(render_layer < 4)) or
(p.type == 4)){
set_pixel(sx, sy, uni, p_fg, p_bg);
}
}
}
}
}
}
bool can_pass(int type,int x,int y,world_data & world){
if(type == 4) return 1;
if(type == 3 and (world.grid[y][x][4] == ID_AIR or world.grid[y][x][4] == ID_LEAVES or world.grid[y][x][4] == ID_MUSHROOM)) return 1;
if(type == 2 and (world.grid[y][x][3] == ID_AIR or world.grid[y][x][3] == ID_LEAVES or world.grid[y][x][3] == ID_MUSHROOM)) return 1;
else return 0;
}
static void draw_slot(int sx,int sy,const item_data & it,bool highlight,bool select){
rgb border_col = select ? rgb(255,220,100) : highlight ? rgb(210,210,140) : rgb(120,120,120);
draw_rect(sx, sy, sx+4, sy+3, 1,
{TOPLEF_CORNER,TOPRIG_CORNER,BOTLEF_CORNER,BOTRIG_CORNER},
{LIGHT_HOZON},{LIGHT_VERTI},{" "},
{border_col},{border_col},{border_col},{border_col});
if(it.id != 0){
str sym; rgb fg, bg;
get_item_visuals(it.id, it.isblock, sym, fg, bg);
set_pixel(sx+2, sy+1, sym, fg, bg);
if(it.misc[0] >= 1){
int sl = (int)it.misc[0];
if(sl <= 9) draw_text(sx+2, sy+2, to_string(sl), rgb(255,255,200), rgb(-1));
else draw_text(sx+1, sy+2, to_string(sl), rgb(255,255,200), rgb(-1));
}
}
}
static void draw_inv_hotbar(player_data & cp){
const int base_x = 27;
const int base_y = HEIGHT - 4;
for(int i = 0; i < 5; i++){
int sx = base_x + i*5;
int sy = base_y;
bool select = (i == cp.selected_hotbar);
bool hover = (mousex >= sx and mousex <= sx+4 and mousey >= sy and mousey <= sy+3);
draw_slot(sx, sy, cp.inventory[i], hover, select);
draw_text(sx+2, sy-1, to_string(i+1), rgb(180,180,180), rgb(0,0,0));
}
}
static void draw_inv_expand(player_data & cp){
const int base_x = 27;
const int base_y = HEIGHT-16;
draw_rect(base_x - 1, base_y - 1,
base_x + 25, HEIGHT + 1,1,
{TOPLEF_CORNER,TOPRIG_CORNER,BOTLEF_CORNER,BOTRIG_CORNER},
{LIGHT_HOZON},{LIGHT_VERTI},{" "},
{rgb(80,80,80)},{rgb(80,80,80)},{rgb(80,80,80)},{rgb(0,0,0)});
for(int r = 0; r < 4; r++){
for(int c = 0; c < 5; c++){
int i = r*5 + c;
int sx = base_x + c*5;
int sy = base_y + r*4;
bool select = (i == cp.selected_hotbar);
bool hover = (mousex >= sx and mousex <= sx+4 and mousey >= sy and mousey <= sy+3);
if(r == 0) draw_text(sx+2, sy-1, to_string(i+1), rgb(180,180,180), rgb(0,0,0));
draw_slot(sx, sy, cp.inventory[i], hover, select);
}
}
}
static void draw_recipe_box(player_data & cp){
const int bx = 1, by = HEIGHT - 16;
const int bw = 24, bh = 16;
draw_rect(bx, by, bx + bw, by + bh, 1,
{TOPLEF_CORNER,TOPRIG_CORNER,BOTLEF_CORNER,BOTRIG_CORNER},
{LIGHT_HOZON},{LIGHT_VERTI},{" "},
{rgb(90,90,90)},{rgb(90,90,90)},{rgb(90,90,90)},{rgb(0,0,0)});
draw_text(bx + 1, by + 1, "Recipes: ", rgb(220,220,220));
init_recipes();
int row = by + 2;
for(int i = pages*4; i < recipes.size() and i < (pages+1)*4 and row < by + bh - 1; i++){
const recipe & r = recipes[i];
bool wben = !r.need_workbench or workbench_open;
rgb col = wben ? rgb(220,220,220) : rgb(120,120,120);
// output icon
str s; rgb f = rgb(0,0,0), b = rgb(0,0,0);
get_item_visuals(r.out.id, r.out.isblock, s, f, b);
set_pixel(bx + 1, row, s, f, b);
// output name
draw_text(bx + 3, row, r.out.name, col);
// required item
int x = bx + 3;
for(int j = 0; j < r.ingr_count and x + 6 < bx + bw; j++){
str is;
rgb ifg, ibg;
get_item_visuals(r.ingr[j].id, r.ingr[j].isblock, is, ifg, ibg);
set_pixel(x, row + 1, is, ifg, ibg);
draw_text(x + 1, row + 1, "x" + to_string((int)r.ingr[j].misc[0]), col);
x += 5;
}
if(r.need_workbench){
draw_text(bw, row, "W", rgb(200, 160, 100));
}
row += 3;
}
rgb lc = (pages > 0) ? rgb(220,220,220) : rgb(90,90,90);
rgb rc = (pages < recipes.size()/4) ? rgb(220,220,220) : rgb(90,90,90);
draw_text(3, HEIGHT-2, "<=", lc);
draw_text(22, HEIGHT-2, "=>", rc);
}
static void draw_craft_panel(player_data & cp){
int n_slots = workbench_open ? 4 : 2;
const int base_x = 54;
const int base_y = HEIGHT - 16;
// input slot
for(int i = 0; i < n_slots; i++){
int sx = base_x + (i % 2) * 5;
int sy = base_y + (i / 2) * 4;
bool h = (mousex >= sx and mousex <= sx+4 and mousey >= sy and mousey <= sy+3);
draw_slot(sx, sy, input_item[i], h, 0);
}
// arrow
int mx = base_x + 10, my = base_y + (n_slots > 2 ? 3 : 0);
draw_text(mx, my+1, "$$", rgb(200,200,200),rgb(0,0,0),' ',rgb(-1),rgb(-1),{LIGHT_HOZON});
draw_text(mx, my+2, "$$", rgb(200,200,200),rgb(0,0,0),' ',rgb(-1),rgb(-1),{LIGHT_HOZON});
// output slot
int ox = base_x + 12;
int oy = base_y + (n_slots > 2 ? 3 : 0);
draw_slot(ox, oy, output_item, (mousex >= ox and mousex <= ox+4 and mousey >= oy and mousey <= oy+3), 0);
// craft count text
if(output_item_count > 0){
draw_text(ox + 1, oy + 4, to_string(output_item_count) + "x", rgb(255,240,160));
}
// arrows (only if > 1 match)
int ax = base_x + 12;
int ay = base_y + (n_slots > 2 ? 8 : 4);
rgb lc = (match_recipes_co > 1) ? rgb(220,220,220) : rgb(90,90,90);
rgb rc = (match_recipes_co > 1) ? rgb(220,220,220) : rgb(90,90,90);
draw_text(ax, ay, "<=", lc);
draw_text(ax + 3, ay, "=>", rc);
}
static void draw_mouse_item(){
if(!mouse_have_item or mouse_item.id == 0) return;
str sym; rgb fg, bg;
get_item_visuals(mouse_item.id, mouse_item.isblock, sym, fg, bg);
set_pixel(mousex, mousey, sym, fg, bg);
if(mouse_item.misc[0] >= 1){
int sl = (int)mouse_item.misc[0];
if(sl <= 9) draw_text(mousex, mousey+1, to_string(sl), rgb(255,255,200), rgb(-1));
else draw_text(mousex-1, mousey+1, to_string(sl), rgb(255,255,200), rgb(-1));
}
}
// ===================== END RENDER =====================================================================================
int main(){
// INIT ==================================================
ios_base::sync_with_stdio(0); cin.tie(0);
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
set_square_font(font_size);
setup(WIDTH, HEIGHT, scrposx, scrposy);
set_console_buffer_size();
lock_window_size();
kill_console_selection();
start_console_guard();
fill(' ');
srand(time(0));
wait_clear(0);
wait_ms(200);
// END INIT ==================================================
// MULTIPLAYER SECTION ==================================================
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0){
init_socket_check = 1;
/*
cout << "FAH UR WINSOCK" << endl;
return 0;
*/
}
SOCKET netSocket = INVALID_SOCKET;
//END MULTIPLAYER SECTION ==================================================
// MAIN FUNCTION1 ========================================================
thread funcTest1(threadFunc); // main function2
bool cu = 0;
auto frame_start = std::chrono::steady_clock::now();
Long frame_count = 0;
double fps_timer = 0.0;
Long display_fps = 0;
auto last_send_time = std::chrono::steady_clock::now();
while(1){
// must start
auto now = std::chrono::steady_clock::now();
dt = duration<double>(now - frame_start).count(); // tinh bang giay
frame_start = now;
if(dt > 0.1) dt = 0.1;
double elapsed_ms = duration<double, milli>(now - last_send_time).count();
//kill_console_selection();
frame_count++;
fps_timer += dt;
if(fps_timer >= 0.5){
display_fps = (Long)(frame_count / fps_timer);
frame_count = 0;
fps_timer = 0.0;
}
fill(' ', rgb(0,0,0), rgb(0,0,0));
// must start
auto_keycheck_mousepos();
bool is_shift = (key[VK_SHIFT] or key[VK_LSHIFT] or key[VK_RSHIFT]);
if(scr == 1){ // main menu ---------------------------------------------------------------------------------------------------------------------
{
if(!(28<= mousex and mousex <= 52 and 5<= mousey and mousey <=15)){
draw_rect(28,5,52,15,2,{FUL_BLOCK},{HOZON_DUO},{VERTI_DUO},{" "},{rgb(0,150,0)},{rgb(0,150,0)},{rgb(0,150,0)});
draw_text(34,10,"P L A Y",rgb(50,175,50));
}
else{
draw_rect(28,5,52,15,2,{FUL_BLOCK},{HOZON_DUO},{VERTI_DUO},{" "},{rgb(190,150,0)},{rgb(190,150,0)},{rgb(190,150,0)});
draw_text(34,10,"P L A Y",rgb(175,50,50));
if(mouseclick[0] == 1 and fade_state == 0){
scr = 2;
}
}
timer[0]-=dt;
if(timer[0] <= 0){
if(!(28<= mousex and mousex <= 52 and 5<= mousey and mousey <=15)){
tempx = random(30,50);
tempy = random(7,13);
if(grid[tempy][tempx] == " ") deco.push_back(background(tempx,tempy,"+",rgb(random(200,255),random(200,255),random(200,255)),1,0.3));
timer[0] = 0.2;
}
else{
tempx = random(30,50);
tempy = random(7,13);
if(grid[tempy][tempx] == " ") deco.push_back(background(tempx,tempy,"?",rgb(random(200,255),random(0,10),random(0,10)),1,0.12));
timer[0] = 0.5;
}
}
if(!(8 <= mousex and mousex <= 32 and 20 <= mousey and mousey <=30)){
draw_rect(8,20,32,30,2,{FUL_BLOCK},{HOZON_DUO},{VERTI_DUO},{" "},{rgb(0,150,0)},{rgb(0,150,0)},{rgb(0,150,0)});
draw_text(13,25,"S E T T I N G S",rgb(50,175,50));
}
else{
draw_rect(8,20,32,30,2,{FUL_BLOCK},{HOZON_DUO},{VERTI_DUO},{" "},{rgb(190,150,0)},{rgb(190,150,0)},{rgb(190,150,0)});
draw_text(12,25,"S E T T I N G S ?",rgb(200,125,10));
if(mouseclick[0] == 1 and fade_state == 0){
scr = 3;
}
}
timer[1]-=dt;
if(timer[1] <= 0){
if(!(8 <= mousex and mousex <= 32 and 20 <= mousey and mousey <=30)){
tempx = random(10,30);
tempy = random(22,28);
if(grid[tempy][tempx] == " ") deco.push_back(background(tempx,tempy,"+",rgb(random(200,255),random(200,255),random(200,255)),1,0.3));
timer[1] = 0.2;
}
else{
tempx = random(10,30);
tempy = random(22,28);
if(grid[tempy][tempx] == " ") deco.push_back(background(tempx,tempy,"!",rgb(random(200,255),random(100,200),random(0,10)),1,0.12));
timer[1] = 0.4;
}
}
if(!(48 <= mousex and mousex <= 72 and 20 <= mousey and mousey <=30)){
draw_rect(48,20,72,30,2,{FUL_BLOCK},{HOZON_DUO},{VERTI_DUO},{" "},{rgb(0,150,0)},{rgb(0,150,0)},{rgb(0,150,0)});
draw_text(53,25,"T U T O R I A L",rgb(50,175,50));
}
else{
draw_rect(48,20,72,30,2,{FUL_BLOCK},{HOZON_DUO},{VERTI_DUO},{" "},{rgb(190,150,0)},{rgb(190,150,0)},{rgb(190,150,0)});
draw_text(52,25,"T U T O R I A L ?",rgb(200,125,10));
if(mouseclick[0] == 1 and fade_state == 0){
scr = 4;
}
}
timer[2]-=dt;
if(timer[2] <= 0){
if(!(48 <= mousex and mousex <= 72 and 20 <= mousey and mousey <=30)){
tempx = random(50,70);
tempy = random(22,28);
if(grid[tempy][tempx] == " ") deco.push_back(background(tempx,tempy,"+",rgb(random(200,255),random(200,255),random(200,255)),1,0.3));
timer[2] = 0.2;
}
else{
tempx = random(50,70);
tempy = random(22,28);
if(grid[tempy][tempx] == " ") deco.push_back(background(tempx,tempy,"+",rgb(random(100,200),random(200,255),random(0,10)),1,0.12));
timer[2] = 0.4;
}
}
timer[3]-=dt;
if(timer[3] <= 0){
tempx = random(0,WIDTH-1);
tempy = random(0,HEIGHT-1);
if(grid[tempy][tempx] == " ") deco.push_back(background(tempx,tempy,"+",rgb(random(100,155),random(200,255),random(100,155)),1,0.42));
timer[3] = 0.07;
}
vector<background> ndeco;
for(background & i : deco){
i.fade -= i.fade_minus*dt;
if(i.fade > 0.05){
draw_text(i.x,i.y,i.uni,scale_rgb(i.col,i.fade));
ndeco.push_back(background(i));
}
}
deco = move(ndeco);
//draw_text(1,2,to_string(timer[0]));
if(key[27] == 1 and fade_state == 0){
break;
}
}
}
else if(scr == 2){ // play ---------------------------------------------------------------------------------------------------------------------
{
if(!(28<= mousex and mousex <= 52 and 5<= mousey and mousey <=15)){
draw_rect(28,5,52,15,2,{FUL_BLOCK},{HOZON_DUO},{VERTI_DUO},{" "},{rgb(0,150,0)},{rgb(0,150,0)},{rgb(0,150,0)});
draw_text(33,10,"S U R V I V A L",rgb(50,175,50));
}
else{
draw_rect(28,5,52,15,2,{FUL_BLOCK},{HOZON_DUO},{VERTI_DUO},{" "},{rgb(200,100,0)},{rgb(200,100,0)},{rgb(200,100,0)});
draw_text(33,10,"S U R V I V A L",rgb(220,125,10));
if(mouseclick[0] == 1 and fade_state == 0){
scr = 5;
}
}
if(!(8 <= mousex and mousex <= 32 and 20 <= mousey and mousey <=30)){
draw_rect(8,20,32,30,2,{FUL_BLOCK},{HOZON_DUO},{VERTI_DUO},{" "},{rgb(0,150,0)},{rgb(0,150,0)},{rgb(0,150,0)});
draw_text(10,25,"F I G H T A R E N A",rgb(50,175,50));
}
else{
draw_rect(8,20,32,30,2,{FUL_BLOCK},{HOZON_DUO},{VERTI_DUO},{" "},{rgb(200,100,0)},{rgb(200,100,0)},{rgb(200,100,0)});
draw_text(10,25,"F I G H T A R E N A",rgb(220,125,10));
if(mouseclick[0] == 1 and fade_state == 0){
scr = 6;
}
}
if(!(48 <= mousex and mousex <= 72 and 20 <= mousey and mousey <=30)){
draw_rect(48,20,72,30,2,{FUL_BLOCK},{HOZON_DUO},{VERTI_DUO},{" "},{rgb(0,150,0)},{rgb(0,150,0)},{rgb(0,150,0)});
draw_text(55,24,"O N L I N E",rgb(50,175,50));
draw_text(56,26,"R O O M S",rgb(50,175,50));
}
else{
draw_rect(48,20,72,30,2,{FUL_BLOCK},{HOZON_DUO},{VERTI_DUO},{" "},{rgb(200,100,0)},{rgb(200,100,0)},{rgb(200,100,0)});
draw_text(55,24,"O N L I N E",rgb(220,125,10));
draw_text(56,26,"R O O M S",rgb(220,125,10));
if(mouseclick[0] == 1 and fade_state == 0){
scr = 7;
}
}
timer[3]-=dt;
if(timer[3] <= 0){
tempx = random(0,WIDTH-1);
tempy = random(0,HEIGHT-1);
if(grid[tempy][tempx] == " ") deco.push_back(background(tempx,tempy,"?",rgb(random(200,155),random(50,100),15),1,0.5));
timer[3] = 0.05;
}
vector<background> ndeco;
for(background & i : deco){
i.fade -= i.fade_minus*dt;
if(i.fade > 0.05){
draw_text(i.x,i.y,i.uni,scale_rgb(i.col,i.fade));
ndeco.push_back(background(i));
}
}
deco = move(ndeco);
if(key[27] == 1 and fade_state == 0){
scr = 1;
}
}
}
else if(scr == 3){ // settings ---------------------------------------------------------------------------------------------------------------------
draw_text(10,10,"setting WIP");
if(key[27] == 1 and fade_state == 0){
scr = 1;
}
}
else if(scr == 4){ // tutorial ---------------------------------------------------------------------------------------------------------------------
draw_text(10,10,"tuto WIP");
if(key[27] == 1 and fade_state == 0){
scr = 1;
}
}
else if(scr == 5){ // survival ---------------------------------------------------------------------------------------------------------------------
{
Long stx = 7;
Long sty = 10;
for(Long id = 0;id < 6;id++){
if(id == 3){
sty += 15;
stx = 7;
}
if(!(stx <= mousex and mousex <= stx + 18 and sty <= mousey and mousey <=sty + 6)){
draw_rect(stx,sty,stx+18,sty+6,1,{TOPLEF_CORNER,TOPRIG_CORNER,BOTLEF_CORNER,BOTRIG_CORNER},{LIGHT_HOZON},{LIGHT_VERTI},{" "},{rgb(0,200,70)},{rgb(0,200,70)},{rgb(0,200,70)});
if(worlds[id].iscreated) draw_text(stx+1,sty+3,worlds[id].name,rgb(0,200,70));
else draw_text(stx+1,sty+3,"Empty world!",rgb(0,200,70));
}
else{
draw_rect(stx,sty,stx+18,sty+6,1,{TOPLEF_CORNER,TOPRIG_CORNER,BOTLEF_CORNER,BOTRIG_CORNER},{LIGHT_HOZON},{LIGHT_VERTI},{" "},{rgb(220,140,40)},{rgb(220,140,40)},{rgb(220,140,40)});
if(worlds[id].iscreated) draw_text(stx+1,sty+3,worlds[id].name,rgb(220,140,40));
else draw_text(stx+1,sty+3,"Empty world!",rgb(220,140,40));
if(mouseclick[0] == 1 and fade_state == 0){
scr = 8;
worldid = id;
if(!worlds[id].iscreated) type_mask[0] = 0;
}
}
stx += 18+6;
}
timer[3]-=dt;
if(timer[3] <= 0){
tempx = random(0,WIDTH-1);
tempy = random(0,HEIGHT-1);
if(grid[tempy][tempx] == " ") deco.push_back(background(tempx,tempy,FUL_BLOCK,rgb(random(200,255),random(100,150),15),1,0.5));
timer[3] = 0.05;
}
vector<background> ndeco;
for(background & i : deco){
i.fade -= i.fade_minus*dt;
if(i.fade > 0.05){
draw_text(i.x,i.y,"$",scale_rgb(i.col,i.fade),rgb(0,0,0),' ',rgb(0,0,0),rgb(0,0,0),{i.uni});
ndeco.push_back(background(i));
}
}
deco = move(ndeco);
if(key[27] == 1 and fade_state == 0){
scr = 2;
}
}
}
else if(scr == 6){ // fight arena ---------------------------------------------------------------------------------------------------------------------
draw_text(10,10,"fight WIP");
if(key[27] == 1 and fade_state == 0){
scr = 2;
}
}
else if(scr == 7){ // online rooms ---------------------------------------------------------------------------------------------------------------------
draw_text(10,10,"online WIP");
if(key[27] == 1 and fade_state == 0){
scr = 2;
}
}
else if(scr == 8){ // WORLD ---------------------------------------------------------------------------------------------------------------------
{
if(worlds[worldid].iscreated == 0){
draw_rect(20,10,60,40,1,{TOPLEF_CORNER,TOPRIG_CORNER,BOTLEF_CORNER,BOTRIG_CORNER},{LIGHT_HOZON},{LIGHT_VERTI},{" "},{rgb(0,200,70)},{rgb(0,200,70)},{rgb(0,200,70)});
draw_text(22,13,"Enter World name: ",rgb(0,200,70));
draw_text(40,14,"$$$$$$$$$$$$$$$$$",rgb(0,200,70),rgb(0,0,0),' ',rgb(0,0,0),rgb(0,0,0),{TOPFUL});
if((40 <= mousex and mousex <= 56 and 13 <= mousey and mousey <= 13)){
if(mouseclick[0] == 1 and fade_state == 0){
type_mask[0] = (1 << 0);
}
}
else{
if(mouseclick[0] == 1 and fade_state == 0){
type_mask[0] = 0;
}
}
if((type_mask[0] >> 0) & 1){
if(type_input[0].size() < 16){
for(Long i = 65;i<=90;i++){
if(key_pressed[i] == 1){
if(is_shift == 0) type_input[0] += char(i + ('a'-'A'));
else type_input[0] += char(i);
}
}
for(Long i = 48;i<=57;i++){
if(key_pressed[i] == 1){
type_input[0] += char(i);
}
}
if(key_pressed[32] == 1){
type_input[0] += ' ';
}
}
if(key_pressed[8] == 1){
if(!type_input[0].empty()) type_input[0].pop_back();
}
draw_text(40 + type_input[0].size(),13,"|",rgb());
}
draw_text(40,13,type_input[0],rgb());
if(key_pressed[13] == 1){
if(type_input[0].size() <= 0 or type_input[0].size() > 16){
if(prev_grid[9][23] != "O"){
deco.push_back(background(22,9,"WORLD NAME MUST HAVE 1 TO 16 CHARACTER",rgb(),1,0.4));
}
}
else{
fill(' ');
draw_text(30,25,"... L O A D I N G ...");
draw_flush();
worlds[worldid].name = type_input[0];
type_input[0] = "";
reset_keys();
gen_world(worlds[worldid]);
}
}
vector<background> ndeco;
for(background & i : deco){
i.fade -= i.fade_minus*dt;
if(i.fade > 0.05){
draw_text(i.x,i.y,i.uni,scale_rgb(i.col,i.fade),rgb(0,0,0),' ',rgb(0,0,0),rgb(0,0,0),{i.uni});
ndeco.push_back(background(i));
}
}
deco = move(ndeco);
}
else{
if(worlds[worldid].gen == 0 or worlds[worldid].isjoin == 0){
draw_text(30,25,"... L O A D I N G ...");
open_inv = 0;
mouse_have_item = 0;
mouse_click_pr = 0;
mouse_item = item_data();
mouse_click_pr1 = 0;
render_layer = 4;
minex = -1;
miney = -1;
mine_layer = -1;
mine_block_hp = 0;
mine_reset = 0;
workbench_open = 0;
input_item[0] = input_item[1] = input_item[2] = input_item[3] = item_data();
output_item = item_data();
output_item_count = 0;
output_index = 0;
for(int lol = 0;lol<maxium_same_output;lol++) output_array[lol] = 0;
match_recipes_co = 0;
left_pt = 0;
righ_pt = 0;
g_hl_x = g_hl_y = -1;
pages = 0;
worlds[worldid].isjoin = 1;
}
else{
world_data & world = worlds[worldid];
player_data & cp = world.players[0];
double & x = cp.x;
double & y = cp.y;
if((key[37] or key[65])){
x -= cp.speed * dt;
if(!can_pass(cp.type,cp.x,cp.y,world) or
!can_pass(cp.type,cp.x+1,cp.y,world) or
!can_pass(cp.type,cp.x,cp.y+1,world) or
!can_pass(cp.type,cp.x+1,cp.y+1,world)){
x += cp.speed * dt;
}
}
if((key[39] or key[68])){
x += cp.speed * dt;
if(!can_pass(cp.type,cp.x,cp.y,world) or
!can_pass(cp.type,cp.x+1,cp.y,world) or
!can_pass(cp.type,cp.x,cp.y+1,world) or
!can_pass(cp.type,cp.x+1,cp.y+1,world)){
x -= cp.speed * dt;
}
}
if((key[38] or key[87])){
y -= cp.speed * dt;
if(!can_pass(cp.type,cp.x,cp.y,world) or
!can_pass(cp.type,cp.x+1,cp.y,world) or
!can_pass(cp.type,cp.x,cp.y+1,world) or
!can_pass(cp.type,cp.x+1,cp.y+1,world)){
y += cp.speed * dt;
}
}
if((key[40] or key[83])){
y += cp.speed * dt;
if(!can_pass(cp.type,cp.x,cp.y,world) or
!can_pass(cp.type,cp.x+1,cp.y,world) or
!can_pass(cp.type,cp.x,cp.y+1,world) or
!can_pass(cp.type,cp.x+1,cp.y+1,world)){
y -= cp.speed * dt;
}
}
inv_input(cp,world);
handle_mining(cp, world);
update_world(world,dt);
if(key_pressed['Q'] and timer[4] <= 0){
// 4 3 2
render_layer = render_layer - 1;
if(render_layer < 2) render_layer = 4;
timer[4] = 0.45;
}
if(timer[4] > 0) timer[4] -=dt;
render(cp.x, cp.y, world);
if(!open_inv) draw_inv_hotbar(cp);
else draw_inv_expand(cp);
if(open_inv){
draw_recipe_box(cp);
draw_craft_panel(cp);
}
draw_mouse_item();
draw_text(0, 2, "X: " + to_string(worlds[worldid].players[0].x),rgb(),rgb(-1),' ',rgb(),rgb(-1));
draw_text(0, 3, "Y: " + to_string(worlds[worldid].players[0].y),rgb(),rgb(-1),' ',rgb(),rgb(-1));
draw_text(0, 4, "Render layer: "+to_string(render_layer-1),rgb(),rgb(-1),' ',rgb(),rgb(-1));
if(minex != -1){
Long bid = world.grid[miney][minex][mine_layer];
block_data bi = get_block_data(bid);
double pct = 1.0 - mine_block_hp / bi.hp;
pct = max(0.0, min(1.0, pct));
int bar_w = 20;
int fill = (int)(pct * bar_w);
str bar = "[";
for(int i = 0; i < bar_w; i++) bar += (i < fill ? '#' : '-');
bar += "]";
draw_text(29 - get_name(bid).size() - 1, 0, get_name(bid) + " " + bar + " " + to_string((int)(pct * 100)) + "%", rgb(255, 220, 120));
}
}
}
if(key[27] == 1 and fade_state == 0 and !(worlds[worldid].iscreated == 1 and worlds[worldid].gen == 0)){
scr = 5;
type_input[0] = "";
worlds[worldid].isjoin = 0;
worldid = 0;
}
}
}
else{
draw_text(10,10,"WIP");
if(key[27] == 1 and fade_state == 0){
scr = 1;
}
}
if(prscr != scr and fade_state == 0){
fade_state = 1;
scr_fade = 1;
transcr = scr;
scr = prscr;
}
else if(fade_state == 1){
scr_fade -= dt * 3;
if(scr_fade <= 0){
fade_state = 2;
scr = transcr;
prscr = scr;
deco.clear();
reset_keys();
}
}
else if(fade_state == 2){
scr_fade += dt * 3;
if(scr_fade >= 1){
scr_fade = 1;
fade_state = 0;
}
}
if(scr_fade != 1.0){
apply_scr_fade(scr_fade);
}
Long pl = 14;
for(Long i = 0; i < 500; i++){
if(key_pressed[i] >= 1){
draw_text(1, pl, to_string(i));
pl++;
}
}
if(key[112] == 1){
break;
}
int hov = check_hover_slot(worlds[worldid].players[0]);
draw_text(0, 5, to_string(mousewheel),rgb(),rgb(-1),' ',rgb(),rgb(-1));
draw_text(0, 0, "FPS: " + to_string(display_fps),rgb(),rgb(-1),' ',rgb(),rgb(-1));
if(scr != 8) draw_text(0, HEIGHT-1, "press ESC to quit",rgb(),rgb(-1),' ',rgb(),rgb(-1));
else draw_text(0, HEIGHT-1, "Press ESC to go to options",rgb(),rgb(-1),' ',rgb(),rgb(-1));
// must end
draw_flush();
auto frame_end = std::chrono::steady_clock::now();
double elapsed = std::chrono::duration<double>(frame_end - frame_start).count();
if(elapsed < frame_time){
wait_ms_p(frame_time - elapsed);
}
// must end
}
// END MAIN FUNCTION1 ========================================================
stop_console_guard();
wait_clear(0);
cout << "EXIT COMPLETED" << endl;
/*
ofstream ofs("cp.log");
for(int y = 30; y <= 49; y++){
string row = "";
for(int x = 20; x <= 60; x++){
row += grid[y][x].empty() ? " " : grid[y][x];
}
ofs << "y=" << y << " |" << row << "|\n";
}
ofs.close();
*/
if(funcTest1.joinable()){
funcTest1.join();
}
else{
// -> detached
}
return 0;
}
Huy hiệu
Người dùng này không có huy hiệu nào.Lịch sử rating
, #