284 lines
6.9 KiB
C++
284 lines
6.9 KiB
C++
#include "render.h"
|
|
#include "world.h"
|
|
#include "camera.h"
|
|
#include "game.h"
|
|
#include <gint/keyboard.h>
|
|
#include <gint/timer.h>
|
|
#include <gint/rtc.h>
|
|
#include <gint/cpu.h>
|
|
#include <libprof.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
// #define NOONCRAFT_ENABLE_USB
|
|
|
|
#ifdef NOONCRAFT_ENABLE_USB
|
|
#include <gint/usb.h>
|
|
#include <gint/usb-ff-bulk.h>
|
|
#include <gint/display.h>
|
|
|
|
static bool getkey_recording = false;
|
|
|
|
static bool getkey_global_shortcuts(key_event_t ev)
|
|
{
|
|
if(usb_is_open() && ev.key == KEY_XOT && !ev.shift && !ev.alpha) {
|
|
usb_fxlink_screenshot(true);
|
|
return true;
|
|
}
|
|
if(usb_is_open() && ev.key == KEY_FRAC && !ev.shift && !ev.alpha) {
|
|
if(!getkey_recording) {
|
|
dupdate_set_hook(GINT_CALL(usb_fxlink_videocapture, (int)false));
|
|
usb_fxlink_videocapture(true);
|
|
getkey_recording = true;
|
|
}
|
|
else {
|
|
dupdate_set_hook(GINT_CALL_NULL);
|
|
getkey_recording = false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endif /* NOONCRAFT_ENABLE_USB */
|
|
|
|
|
|
static char debugMessage[128];
|
|
|
|
static void renderGame(Game *game)
|
|
{
|
|
Camera *camera = game->camera;
|
|
ItemStack const &hand = game->inventory[27 + game->hotbarCursor];
|
|
|
|
renderClear();
|
|
|
|
GlyphCluster playerCluster(GLYPH_DEGREE_UNDERLINE, ' ',
|
|
GLYPH_CAPITAL_LAMBDA, ' ');
|
|
GlyphCluster cursorCluster(0, GLYPH_OPLUS, 0, 0);
|
|
|
|
if(!hand.item.isBlock) {
|
|
ItemInfo *info = Nooncraft::getItemInfo(hand.item);
|
|
if(info && info->isTool) {
|
|
if(info->toolKind == ToolKind::Pickaxe)
|
|
cursorCluster.glyphs[1] = GLYPH_NORTH_EAST_ARROW;
|
|
else if(info->toolKind == ToolKind::Axe)
|
|
cursorCluster.glyphs[1] = 'P';
|
|
else if(info->toolKind == ToolKind::Shovel)
|
|
cursorCluster.glyphs[1] = 'b';
|
|
}
|
|
}
|
|
|
|
if(game->isBreakingBlock()) {
|
|
static Glyph breakingGlyphs[4] = {
|
|
GLYPH_MIDDLE_DOT, GLYPH_SMALL_SQUARE, GLYPH_CURRENCY_SIGN, 0x7f,
|
|
};
|
|
int stage = game->blockBreakingProgress();
|
|
cursorCluster.glyphs[1] = breakingGlyphs[stage];
|
|
}
|
|
|
|
Nooncraft::renderCamera(26, 12, camera, game->playerPos, playerCluster,
|
|
game->cursorPos, cursorCluster);
|
|
|
|
GlyphCluster invLeft(' ', '{', ' ', '{');
|
|
GlyphCluster invRight('}', ' ', '}', ' ');
|
|
int hudY = 27;
|
|
|
|
if(hand.qty > 1)
|
|
renderFormat(22, hudY+1, "HAND: %s (%d)", hand.name(), hand.qty);
|
|
else
|
|
renderFormat(22, hudY+1, "HAND: %s", hand.name());
|
|
renderFormat(46, hudY+1, "X%d Y%d", game->playerPos.x, game->playerPos.y);
|
|
|
|
BlockInfo *pointedBlockInfo = game->pointedBlockInfo();
|
|
char const *pointedBlockName =
|
|
pointedBlockInfo ? pointedBlockInfo->name : "(NOTHING)";
|
|
renderFormat(22, hudY+2, "CURSOR: %s", pointedBlockName);
|
|
|
|
renderFormat(22, hudY+3, debugMessage);
|
|
renderCluster(0, hudY+1, invLeft);
|
|
for(int i = 0; i < 9; i++) {
|
|
ItemStack const &stack = game->inventory[27 + i];
|
|
renderCluster(2 + 2*i, hudY+1, stack.cluster());
|
|
if(!stack.isNull() && stack.qty > 1)
|
|
renderFormat(2+2*i, hudY+3, "%2d", stack.qty);
|
|
}
|
|
renderCluster(20, hudY+1, invRight);
|
|
|
|
for(int i = 0; i < 28; i++) {
|
|
if(i == 1 + game->hotbarCursor)
|
|
renderText(2*i, hudY, "vv");
|
|
else
|
|
renderText(2*i, hudY, "--");
|
|
}
|
|
}
|
|
|
|
bool titleScreen(void)
|
|
{
|
|
int selection = 0;
|
|
|
|
while(1) {
|
|
renderClear();
|
|
renderText(22, 5, "+---------+");
|
|
renderText(22, 6, "|NOONCRAFT|");
|
|
renderText(22, 7, "+---------+");
|
|
|
|
if(selection == 0)
|
|
renderText(19, 10, "[PLAY DEMO WORLD]");
|
|
else
|
|
renderText(20, 10, "PLAY DEMO WORLD");
|
|
|
|
if(selection == 1)
|
|
renderText(18, 12, "[PLAY RANDOM WORLD]");
|
|
else
|
|
renderText(19, 12, "PLAY RANDOM WORLD");
|
|
|
|
renderUpdate();
|
|
|
|
int opt = GETKEY_DEFAULT & ~GETKEY_MOD_SHIFT & ~GETKEY_MOD_ALPHA;
|
|
int key = getkey_opt(opt, NULL).key;
|
|
if(key == KEY_EXE || key == KEY_SHIFT)
|
|
return selection == 1;
|
|
if(key == KEY_UP)
|
|
selection = 0;
|
|
if(key == KEY_DOWN)
|
|
selection = 1;
|
|
}
|
|
}
|
|
|
|
bool confirmExit(void)
|
|
{
|
|
renderClear();
|
|
renderText(1, 1, "QUIT? THE WORLD WILL BE LOST.");
|
|
renderText(1, 2, "F1: YES, QUIT");
|
|
renderText(1, 3, "F6: NO, GO BACK");
|
|
renderUpdate();
|
|
|
|
while(1) {
|
|
int opt = GETKEY_DEFAULT & ~GETKEY_MENU;
|
|
int key = getkey_opt(opt, NULL).key;
|
|
if(key == KEY_F1)
|
|
return true;
|
|
if(key == KEY_F6)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
prof_init();
|
|
|
|
bool random = titleScreen();
|
|
srand(random ? rtc_ticks() : 0xc0ffee);
|
|
|
|
#ifdef NOONCRAFT_ENABLE_USB
|
|
usb_interface_t const *interfaces[] = { &usb_ff_bulk, NULL };
|
|
usb_open(interfaces, GINT_CALL_NULL);
|
|
getkey_set_feature_function(getkey_global_shortcuts);
|
|
#endif /* NOONCRAFT_ENABLE_USB */
|
|
|
|
/* Should be a square */
|
|
World *world = Nooncraft::mkWorld(128, 128);
|
|
if(!world)
|
|
return 0;
|
|
|
|
renderClear();
|
|
renderText(0, 0, "GENERATING WORLD...");
|
|
renderUpdate();
|
|
Nooncraft::genWorld(world);
|
|
|
|
Camera *camera = new Camera(world);
|
|
camera->visible = WorldRect(-13, 14, -6, 6);
|
|
|
|
Game game;
|
|
game.world = world;
|
|
game.camera = camera;
|
|
game.playerPos = world->findSpawnPoint();
|
|
// TODO: Potential world border overflow here (unlikely)
|
|
game.cursorPos = game.playerPos + WorldCoord::Right;
|
|
game.damageTicks = -1;
|
|
game.hotbarCursor = 0;
|
|
game.inventory[27] = ItemStack(Item(ItemID::WOODEN_PICKAXE));
|
|
|
|
volatile int nextFrame = 1;
|
|
int t = timer_configure(TIMER_ANY, 1000000/20, GINT_CALL_SET(&nextFrame));
|
|
timer_start(t);
|
|
|
|
bool runMainLoop = true;
|
|
int frameDuration = 0;
|
|
int tick = 0;
|
|
|
|
while(runMainLoop) {
|
|
while(!nextFrame)
|
|
sleep();
|
|
nextFrame = 0;
|
|
tick++;
|
|
|
|
frameDuration = prof_exec({
|
|
sprintf(debugMessage, "FRAME: %d MS", frameDuration);
|
|
// sprintf(debugMessage, "VORONOI: %d MS", voronoiTime / 1000);
|
|
renderGame(&game);
|
|
renderUpdate();
|
|
}) / 1000;
|
|
|
|
key_event_t ev;
|
|
while((ev = pollevent()).type != KEYEV_NONE) {
|
|
if(ev.type == KEYEV_UP) {
|
|
if(ev.key == KEY_OPTN) {
|
|
game.damageTicks = -1;
|
|
continue;
|
|
}
|
|
continue;
|
|
}
|
|
#ifdef NOONCRAFT_ENABLE_USB
|
|
if(getkey_global_shortcuts(ev))
|
|
continue;
|
|
#endif
|
|
|
|
if(ev.key == KEY_MENU && confirmExit())
|
|
runMainLoop = false;
|
|
int digit = keycode_digit(ev.key);
|
|
if(digit >= 1 && digit <= 9 && !game.isBreakingBlock()) {
|
|
game.hotbarCursor = digit - 1;
|
|
}
|
|
}
|
|
|
|
/* Clamp speed to 10 blocks/s */
|
|
if(tick % 2 == 0 && !game.isBreakingBlock()) {
|
|
if(keydown(KEY_LEFT) && !keydown(KEY_SHIFT))
|
|
game.movePlayerBy(WorldCoord::Left);
|
|
if(keydown(KEY_RIGHT) && !keydown(KEY_SHIFT))
|
|
game.movePlayerBy(WorldCoord::Right);
|
|
if(keydown(KEY_UP) && !keydown(KEY_SHIFT))
|
|
game.movePlayerBy(WorldCoord::Up);
|
|
if(keydown(KEY_DOWN) && !keydown(KEY_SHIFT))
|
|
game.movePlayerBy(WorldCoord::Down);
|
|
|
|
if(keydown(KEY_LEFT) && keydown(KEY_SHIFT))
|
|
game.moveCursorBy(WorldCoord::Left);
|
|
if(keydown(KEY_RIGHT) && keydown(KEY_SHIFT))
|
|
game.moveCursorBy(WorldCoord::Right);
|
|
if(keydown(KEY_UP) && keydown(KEY_SHIFT))
|
|
game.moveCursorBy(WorldCoord::Up);
|
|
if(keydown(KEY_DOWN) && keydown(KEY_SHIFT))
|
|
game.moveCursorBy(WorldCoord::Down);
|
|
}
|
|
|
|
if(keydown(KEY_OPTN) && !game.isBreakingBlock()) {
|
|
if(game.world->canBreakBlock(game.cursorPos))
|
|
game.damageTicks = 0;
|
|
}
|
|
if(keydown(KEY_VARS) && !game.isBreakingBlock()) {
|
|
game.place();
|
|
}
|
|
|
|
game.updateBlockBreaking();
|
|
}
|
|
|
|
timer_stop(t);
|
|
delete camera;
|
|
delete world;
|
|
prof_quit();
|
|
|
|
return 1;
|
|
}
|