I don't even know what I did

This commit is contained in:
Heath Mitchell 2022-12-01 17:24:09 +00:00
parent fa23347b0d
commit a46e0a4187
23 changed files with 694 additions and 357 deletions

View File

@ -45,6 +45,8 @@ if (DEFINED FXSDK_PLATFORM_LONG)
# assets-cg/bg.png
assets-cg/bg.png
assets-cg/bg_sunset.png
assets-cg/skygarden_bg.png
assets-cg/bush.png
assets-cg/boost_0.png
assets-cg/boost_1.png
@ -107,12 +109,13 @@ if (DEFINED FXSDK_PLATFORM_LONG)
assets-cg/smoke_0.png
assets-cg/smoke_1.png
assets-cg/smoke_2.png
assets-cg/tree.png
)
fxconv_declare_assets(${ASSETS} ${ASSETS_fx} ${ASSETS_cg} WITH_METADATA)
add_executable(mariokart ${SOURCES} ${ASSETS} ${ASSETS_${FXSDK_PLATFORM}})
target_compile_options(mariokart PRIVATE -Wall -Wextra -Werror-implicit-function-declaration -Ofast -flto)
target_compile_options(mariokart PRIVATE -Wall -Wextra -Werror-implicit-function-declaration -Ofast -flto -g)
target_link_libraries(mariokart Gint::Gint)
target_link_libraries(mariokart LibProf::LibProf)
@ -132,6 +135,6 @@ else()
set(CMAKE_EXECUTABLE_SUFFIX ".html")
add_executable(mariokart ${SOURCES})
# -fsanitize=undefined on both
target_compile_options(mariokart PRIVATE -Wall -Wextra -Werror=implicit-function-declaration -Werror=incompatible-pointer-types -g)
target_compile_options(mariokart PRIVATE -Wall -Wextra -Werror=implicit-function-declaration -Werror=incompatible-pointer-types -O3 -g)
target_link_options(mariokart PRIVATE --shell-file=../emscripten.html -g)
endif()

View File

@ -249,3 +249,13 @@ bg_sunset.png:
bg.png:
type: bopti-image
name: b_bg
skygarden_bg.png:
type: bopti-image
name: b_skygarden_bg
skygarden_bg2.png:
type: bopti-image
name: b_skygarden_bg2
tree.png:
type: bopti-image
name: b_tree
profile: rgb565a

File diff suppressed because one or more lines are too long

View File

@ -209,3 +209,12 @@ const struct image* img_bg_sunset = &internal_img_bg_sunset;
extern const bopti_image_t b_bg;
const struct image internal_img_bg = { 0, 0, &b_bg };
const struct image* img_bg = &internal_img_bg;
extern const bopti_image_t b_skygarden_bg;
const struct image internal_img_skygarden_bg = { 0, 0, &b_skygarden_bg };
const struct image* img_skygarden_bg = &internal_img_skygarden_bg;
extern const bopti_image_t b_skygarden_bg2;
const struct image internal_img_skygarden_bg2 = { 0, 0, &b_skygarden_bg2 };
const struct image* img_skygarden_bg2 = &internal_img_skygarden_bg2;
extern const bopti_image_t b_tree;
const struct image internal_img_tree = { 0, 0, &b_tree };
const struct image* img_tree = &internal_img_tree;

View File

@ -72,3 +72,6 @@ extern const struct image* img_loop_old;
extern const struct image* img_loop;
extern const struct image* img_bg_sunset;
extern const struct image* img_bg;
extern const struct image* img_skygarden_bg;
extern const struct image* img_skygarden_bg2;
extern const struct image* img_tree;

File diff suppressed because one or more lines are too long

View File

@ -72,3 +72,6 @@ extern const struct image* img_loop_old;
extern const struct image* img_loop;
extern const struct image* img_bg_sunset;
extern const struct image* img_bg;
extern const struct image* img_skygarden_bg;
extern const struct image* img_skygarden_bg2;
extern const struct image* img_tree;

175
src/3d.c
View File

@ -5,7 +5,12 @@
#include "./tilemap.h"
#include "./maths.h"
#include <stdio.h>
#include <limits.h>
#include "../data-headers/generated_lut.h"
#include "platforms/gint.h"
// #include "./3d-bg-dat.h"
#define lowResCutoff (LCD_HEIGHT_PX * 3 / 4) - 30
@ -22,8 +27,6 @@ void normalFov() {
int dist = newLut[y - horizon];
int wx = -(LCD_WIDTH_PX / 2) / 2 * dist;
for (unsigned short x = 0; x < LCD_WIDTH_PX / 2; x++) {
wx += dist;
int x2 = wx >> 6;
int y2 = dist;
@ -32,10 +35,16 @@ void normalFov() {
int newY = ((y2 * angleCos) >> 15) - ((x2 * angleSin) >> 15);
color_t col = samplePixel(newX >> 1, newY >> 1);
// if (col == 0) {
// color_t* img_data = (color_t*) data_3d_bg + 4;
// col = img_data[(LCD_WIDTH_PX * (y - (horizon + 2))) + (x << 1)];
// }
// setPixel(x * 2, y, col);
// setPixel(x * 2 + 1, y, col);
// Cast to an unsigned int array so two pixels are stored at once.
((unsigned int *)VRAM)[y * (LCD_WIDTH_PX / 2) + x] = (col << 16 | col);
wx += dist;
}
}
// Fill in the low resolution area at half vertical resolution.
@ -44,8 +53,6 @@ void normalFov() {
int dist = newLut[y - horizon];
int wx = -(LCD_WIDTH_PX / 2) / 2 * dist;
for (unsigned short x = 0; x < LCD_WIDTH_PX / 2; x++) {
wx += dist;
int x2 = wx >> 6;
int y2 = dist;
@ -54,15 +61,150 @@ void normalFov() {
int newY = ((y2 * angleCos) >> 15) - ((x2 * angleSin) >> 15);
color_t col = samplePixel(newX >> 1, newY >> 1);
// if (col == 0) {
// color_t* img_data = (color_t*) data_3d_bg + 4;
// col = img_data[(LCD_WIDTH_PX * (y - (horizon + 2))) + (x << 1)];
// }
// setPixel(x * 2, y, col);
// setPixel(x * 2 + 1, y, col);
// Cast to an unsigned int array so two pixels are stored at once.
((unsigned int *)VRAM)[y * (LCD_WIDTH_PX / 2) + x] = (col << 16 | col);
((unsigned int *)VRAM)[(y + 1) * (LCD_WIDTH_PX / 2) + x] = (col << 16 | col);
wx += dist;
}
}
}
// void normalFov() {
// for (unsigned short y = horizon + 2; y < LCD_HEIGHT_PX; y++) {
// for (unsigned short x = 0; x < LCD_WIDTH_PX / 2; x++) {
// int wx;
// int wy;
// screenToWorldSpace(x, y, &wx, &wy);
// color_t col = samplePixel(wx, wy);
// ((unsigned int *)VRAM)[y * (LCD_WIDTH_PX / 2) + x] = (col << 16 | col);
// }
// }
// }
void screenToWorldSpace(int x, int y, int* worldX, int* worldY) {
int angleCos = fpcos(angle);
int angleSin = fpsin(angle);
x /= 2;
int dist = newLut[y - horizon];
int wx = -(LCD_WIDTH_PX / 2) / 2 * dist;
wx += dist * x;
int x2 = wx >> 6;
int y2 = dist;
// Rotate by angle
int newX = ((x2 * angleCos) >> 15) + ((y2 * angleSin) >> 15);
int newY = ((y2 * angleCos) >> 15) - ((x2 * angleSin) >> 15);
*worldX = newX >> 1;
*worldY = newY >> 1;
*worldX += xOffset;
*worldY += yOffset;
*worldX >>= 2;
*worldY >>= 2;
}
#define abs(x) ((x) < 0 ? -(x) : (x))
// void worldToScreenSpace(int worldX, int worldY, int* x, int* y) {
// // RIDICULOUSLY inefficient way of doing this for testing purposes:
// // Loop through all the pixels on the screen and find the one that
// // is closest to the world coordinates.
// int minDist = INT_MAX;
// for (int i = 0; i < LCD_WIDTH_PX; i++) {
// for (int j = horizon + 2; j < LCD_HEIGHT_PX; j++) {
// int wx;
// int wy;
// screenToWorldSpace(i, j, &wx, &wy);
// int dist = abs(worldX - wx) + abs(worldY - wy);
// if (dist < minDist) {
// minDist = dist;
// *x = i;
// *y = j;
// }
// }
// }
// printf("minDist: %d\n", minDist);
// }
// TODO: Take FOV into account
void worldToScreenSpace(int worldX, int worldY, int* x, int* y, int* dist) {
worldX <<= 2;
worldY <<= 2;
worldX -= xOffset;
worldY -= yOffset;
worldX <<= 1;
worldY <<= 1;
// Undo the rotation
int angleCos = fpcos(-angle);
int angleSin = fpsin(-angle);
int x2 = ((worldX * angleCos) >> 15) + ((worldY * angleSin) >> 15);
int y2 = ((worldY * angleCos) >> 15) - ((worldX * angleSin) >> 15);
// Find the closest distance in the lookup table
int minDist = INT_MAX;
int minDistIndex = 0;
for (int i = 0; i < LCD_HEIGHT_PX - horizon - 2; i++) {
int dist = abs(newLut[i] - y2);
if (dist < minDist) {
minDist = dist;
minDistIndex = i;
}
}
*y = minDistIndex + horizon;
// int dist2 = newLut[minDistIndex];
int dist2 = y2;
*dist = dist2;
// *x = LCD_WIDTH_PX / 2;
minDist = INT_MAX;
minDistIndex = 0;
// Find the closest x value
// int wx = -(LCD_WIDTH_PX / 2) / 2 * dist2;
// for (int i = 0; i < LCD_WIDTH_PX / 2; i++) {
// int dist = abs((wx >> 6) - x2);
// if (dist < minDist) {
// minDist = dist;
// minDistIndex = i;
// }
// wx += dist2;
// }
// Better way to do it:
int wx = -(LCD_WIDTH_PX / 2) / 2 * dist2;
// We are asking how many dist2s you need to wx add to get to x2 from wx
// So just directly calculate that instead of doing the loop
if ((dist2 >> 1) == 0) {
*x = INT_MAX;
return;
}
// int numDist2s = (x2 - (wx >> 6)) / (dist2 >> 6);
// Do the division before the shift for increased precision
// int numDist2s = ((x2 << 6) - wx) / (dist2 >> 6);
// numDist2s >>= 6;
// int numDist2s = ((x2 << 6) - wx) / dist2;
// minDistIndex = numDist2s /*- 1*/;
// *x = minDistIndex * 2;
// TODO: Division is slow on the SH4 CPU as there's no hardware divider
// Can this be optimized? Or is the division just necessary?
// Or is this done infrequently enough that it doesn't matter?
*x = ((x2 << 6) - wx) / (dist2 >> 1);
printf("x: %d\n", *x);
}
void fullRes() {
int angleCos = fpcos(angle);
int angleSin = fpsin(angle);
@ -70,8 +212,6 @@ void fullRes() {
int dist = newLut[y - horizon];
int wx = -(LCD_WIDTH_PX / 2) / 2 * dist;
for (unsigned short x = 0; x < LCD_WIDTH_PX / 2; x++) {
wx += dist;
int x2 = wx >> 6;
int y2 = dist;
@ -80,10 +220,16 @@ void fullRes() {
int newY = ((y2 * angleCos) >> 15) - ((x2 * angleSin) >> 15);
color_t col = samplePixel(newX >> 1, newY >> 1);
// if (col == 0) {
// color_t* img_data = (color_t*) data_3d_bg + 4;
// col = img_data[(LCD_WIDTH_PX * (y - (horizon + 2))) + (x << 1)];
// }
// setPixel(x * 2, y, col);
// setPixel(x * 2 + 1, y, col);
// Cast to an unsigned int array so two pixels are stored at once.
((unsigned int *)VRAM)[y * (LCD_WIDTH_PX / 2) + x] = (col << 16 | col);
wx += dist;
}
}
}
@ -97,8 +243,6 @@ void changedFov() {
int wx = -(LCD_WIDTH_PX / 2) / 2;
wx *= dx;
for (unsigned short x = 0; x < LCD_WIDTH_PX / 2; x++) {
wx += dx;
int x2 = wx >> 6;
int y2 = dist;
@ -107,10 +251,15 @@ void changedFov() {
int newY = ((y2 * angleCos) >> 15) - ((x2 * angleSin) >> 15);
color_t col = samplePixel(newX >> 1, newY >> 1);
// setPixel(x * 2, y, col);
// if (col == 0) {
// color_t* img_data = (color_t*) data_3d_bg + 4;
// col = img_data[(LCD_WIDTH_PX * (y - (horizon + 2))) + (x << 1)];
// } // setPixel(x * 2, y, col);
// setPixel(x * 2 + 1, y, col);
// Cast to an unsigned int array so two pixels are stored at once.
((unsigned int *)VRAM)[y * (LCD_WIDTH_PX / 2) + x] = (col << 16 | col);
wx += dx;
}
}
// Fill in the low resolution area at half vertical resolution.
@ -121,8 +270,6 @@ void changedFov() {
int wx = -(LCD_WIDTH_PX / 2) / 2;
wx *= dx;
for (unsigned short x = 0; x < LCD_WIDTH_PX / 2; x++) {
wx += dx;
int x2 = wx >> 6;
int y2 = dist;
@ -131,11 +278,17 @@ void changedFov() {
int newY = ((y2 * angleCos) >> 15) - ((x2 * angleSin) >> 15);
color_t col = samplePixel(newX >> 1, newY >> 1);
// if (col == 0) {
// color_t* img_data = (color_t*) data_3d_bg + 4;
// col = img_data[(LCD_WIDTH_PX * (y - (horizon + 2))) + (x << 1)];
// }
// setPixel(x * 2, y, col);
// setPixel(x * 2 + 1, y, col);
// Cast to an unsigned int array so two pixels are stored at once.
((unsigned int *)VRAM)[y * (LCD_WIDTH_PX / 2) + x] = (col << 16 | col);
((unsigned int *)VRAM)[(y + 1) * (LCD_WIDTH_PX / 2) + x] = (col << 16 | col);
wx += dx;
}
}
}

View File

@ -4,6 +4,8 @@
#include <stdbool.h>
unsigned short getScreenPixel(unsigned short x, unsigned short y);
void screenToWorldSpace(int x, int y, int* worldX, int* worldY);
void worldToScreenSpace(int worldX, int worldY, int* x, int* y, int* dist);
void draw3D(bool highQuality);
extern int hFovModifier;

View File

@ -36,7 +36,7 @@ EM_JS(void, initSliders, (), {
max: 1,
step: 0.01,
value: 0.1,
description: "A multiplier that is applied to the velocity every frame. This also affects the top speed, so if you adjust this, you'll probably want to adjust maxPower unless this is intended (as in the case of being off road)."
description: "A multiplier that is applied to the velocity every frame. The value multiplied by is one minus this value. This also affects the top speed, so if you adjust this, you'll probably want to adjust maxPower unless this is intended (as in the case of being off road)."
},
{
name: "offRoadDrag",
@ -51,14 +51,14 @@ EM_JS(void, initSliders, (), {
min: 0,
max: 1,
step: 0.01,
value: 0.9,
description: "A multiplier that is applied to the angular velocity every frame. Turning this up makes turning feel more slippery, as you will continue to turn for a bit after you stop pressing a direction."
value: 0.1,
description: "A multiplier that is applied to the angular velocity every frame. The value multiplied by is one minus this value. Turning this up makes turning feel more slippery, as you will continue to turn for a bit after you stop pressing a direction."
},
{
name: "maxPower",
min: 0,
max: 0.5,
step: 0.0001,
step: 0.01,
value: 0.1,
description: "The maximum acceleration that can be applied to the kart - this is the main way of changing the top speed, along with drag."
},
@ -175,7 +175,7 @@ void updateConstants() {
offRoadDrag = 1.0 - EM_ASM_DOUBLE({
return window.constantValues.offRoadDrag;
});
angularDrag = EM_ASM_DOUBLE({
angularDrag = 1.0 - EM_ASM_DOUBLE({
return window.constantValues.angularDrag;
});
maxPower = EM_ASM_DOUBLE({

View File

@ -3,9 +3,11 @@
// #include "../data-headers/compressedTrack.h"
#include "../data-headers/track.h"
#include "../data-headers/images.h"
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// extern unsigned char* compressedTrack;
// extern int compressedTrackSize;
@ -35,6 +37,9 @@ trackData tracks[] = {
.tileTypes = peach_circuit_tileattr,
.startX = 3565,
.startY = 2600,
.bg = 0,
.fullBg = false,
.drawLoop = true,
},
// Sunset Wilds
{
@ -48,6 +53,9 @@ trackData tracks[] = {
.tileTypes = sunset_wilds_tileattr,
.startX = 7425,
.startY = 3673,
.bg = 0,
.fullBg = false,
.drawLoop = false,
},
// Sky Garden
{
@ -61,6 +69,9 @@ trackData tracks[] = {
.tileTypes = sky_garden_tileattr,
.startX = 7060,
.startY = 2900,
.bg = 0,
.fullBg = true,
.drawLoop = false,
},
};
@ -70,6 +81,17 @@ void initData(int trackID) {
// uncompress(trackData, &trackDataSize, compressedTrack, compressedTrackSize);
track = tracks[trackID];
switch (trackID) {
case 0:
track.bg = img_bg;
break;
case 1:
track.bg = img_bg_sunset;
break;
case 2:
track.bg = img_skygarden_bg;
break;
}
mz_ulong size;

View File

@ -3,21 +3,29 @@
#include "./platform.h"
#include <stdbool.h>
// https://github.com/thomasgoldstein/epicedit/blob/0a48fc/EpicEdit/Rom/Tracks/Road/RoadTileGenre.cs
enum TileType {
JumpBar = 0x10,
// Edge of Choco Island road bump
BumpEdge = 0x12,
ItemBlock = 0x14,
Zipper = 0x16,
BoostPad = 0x16,
OilSlick = 0x18,
Coin = 0x1A,
// Choco Island road bump
Bump = 0x1C,
WoodPlank = 0x1E,
// Air where you fall off the road (e.g. on Rainbow Road)
Empty = 0x20,
Water = 0x22,
Lava = 0x24,
OutOfBounds = 0x26,
// Empty border that delimits the road and the void, on Rainbow Road
EmptyBorder = 0x28,
Road = 0x40,
// Ghost Valley plank intersection
PlankJunction = 0x42,
BrickRoad = 0x44,
Clay = 0x46,
@ -33,8 +41,11 @@ enum TileType {
Grass = 0x5A,
ShallowWater = 0x5C,
Mud = 0x5E,
// Boundary that can't be driven through or jumped over
SolidBlock = 0x80,
// Breaks when hit
FrailBlock = 0x82,
// Also breaks when hit
IceBlock = 0x84
};
@ -50,6 +61,9 @@ typedef struct {
int startX;
int startY;
// color_t skyColor;
const struct image* bg;
bool fullBg;
bool drawLoop;
} trackData;
extern trackData track;

View File

@ -13,13 +13,26 @@
#include "./configurableConstants.h"
#include "../data-headers/images.h"
#include "platforms/gint.h"
#ifndef __EMSCRIPTEN__
#include "gint/display-cg.h"
#include "gint/display.h"
#include "gint/keyboard.h"
#include "gint/keycodes.h"
#endif
#include <stdbool.h>
#include <stdio.h>
#ifdef PROFILING_ENABLED
#include "libprof.h"
#endif
// TODO: remove
// #include <math.h>
double sqrt(double arg);
#include "./data.h"
// #ifdef PROFILING_ENABLED
// #include "libprof.h"
// #endif
#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
#define MAX(X, Y) (((X) < (Y)) ? (Y) : (X))
@ -91,7 +104,7 @@ void printTileType(enum TileType tileType) {
case ItemBlock:
printf("ItemBlock\n");
break;
case Zipper:
case BoostPad:
printf("Zipper\n");
break;
case OilSlick:
@ -189,10 +202,6 @@ void printTileType(enum TileType tileType) {
}
#endif
enum TileType getTileType(int tileID) {
return (enum TileType) track.tileTypes[tileID];
}
short index2;
unsigned short element;
@ -303,7 +312,9 @@ int timerFrames;
int freezeForFrames = 0;
int freezeTime;
bool didFinishLap = false;
#define LAPS 3
int lapTimes[LAPS];
bool hudUpdated = false;
bool minutesUpdated = false;
bool secondsUpdated = false;
@ -311,9 +322,9 @@ bool millisecondsUpdated = false;
bool wholeTimerUpdated = false;
bool trackNeedsUpdate = true;
void drawTimer() {
void drawTimer(bool didFinishLap) {
// Calculate the total time in mm:ss:xx format
if (state.player.lapCount <= 3) {
if (state.player.lapCount <= LAPS) {
timerFrames = state.totalFrameCount - 180;
}
// timerFrames *= 8;
@ -323,6 +334,7 @@ void drawTimer() {
if (didFinishLap && state.player.lapCount > 1) {
freezeForFrames = 150;
freezeTime = (state.totalFrameCount - 180) - lastLapTime;
lapTimes[state.player.lapCount - 2] = freezeTime;
lastLapTime = (state.totalFrameCount - 180);
}
@ -369,10 +381,32 @@ void drawTimer() {
// Draw text
draw_time(timeStr, LCD_WIDTH_PX - 90, 8);
static bool fullTimerDisplayed = false;
// If the player finished all 3 laps and the timer isn't frozen
// Only do it once, though
if (state.player.lapCount >= (LAPS + 1) && freezeForFrames == 0 && !fullTimerDisplayed) {
// Print the times for each lap below the timer
for (int i = 0; i < LAPS; i++) {
int lapTime = lapTimes[i];
int lapMinutes = lapTime / 60 / 60;
int lapSeconds = (lapTime / 60) % 60;
int lapMilliseconds = ((lapTime % 60) * 16667) / 1000;
if (lapMilliseconds >= 1000) {
lapMilliseconds = 999;
}
char lapTimeStr[9];
sprintf(lapTimeStr, "%02d:%02d:%02d", lapMinutes, lapSeconds, lapMilliseconds / 10);
draw_time(lapTimeStr, LCD_WIDTH_PX - 90, 8 + (i + 2) * 12);
displayUpdate(0, LCD_HEIGHT_PX - 1);
}
fullTimerDisplayed = true;
}
}
}
void drawParallaxBackground(int angle) {
if (!track.drawLoop) return;
draw_loop_x(img_loop, 0, 88, angle / 2, LCD_WIDTH_PX);
draw_loop_x(img_bush, 0, 100, angle, LCD_WIDTH_PX);
}
@ -385,11 +419,15 @@ void fillSky(unsigned short yMin, unsigned short yMax) {
}
} */
// TODO: more efficient drawing?
for (int x = 0; x < LCD_WIDTH_PX; x++) {
draw(img_bg, x, 0);
if (track.fullBg) {
draw(track.bg, 0, 0);
} else {
for (int x = 0; x < LCD_WIDTH_PX; x++) {
draw(track.bg, x, 0);
}
}
drawLapCount();
drawTimer();
drawTimer(false);
drawParallaxBackground(angle);
// extern bopti_image_t img_bg;
// dimage(0, 0, &img_bg);
@ -404,13 +442,10 @@ void main_loop() {
0xB882, 0xCBCE, 0xF6BA
};
palette[0x3C] = paletteAnim[(state.totalFrameCount / 3) % 3];
// for (int i = 0; i < 256; i++) {
// palette[i] = 0xFFFF;
// }
updateConstants();
didFinishLap = false;
bool didFinishLap = false;
hudUpdated = false;
minutesUpdated = false;
secondsUpdated = false;
@ -418,14 +453,6 @@ void main_loop() {
wholeTimerUpdated = false;
// Main game loop
#ifdef PROFILING_ENABLED
prof_t prof_logic = prof_make();
prof_t prof_logic1 = prof_make();
prof_t prof_logic2 = prof_make();
prof_t prof_logic3 = prof_make();
prof_t prof_sprites = prof_make();
prof_enter(prof_logic);
#endif
scanButtons();
@ -441,144 +468,17 @@ void main_loop() {
state = savedState;
}
#ifdef PROFILING_ENABLED
prof_leave(prof_logic);
#endif
timeDebugHud = profile({
handleDebugHud();
});
#ifdef PROFILING_ENABLED
prof_enter(prof_logic);
#endif
// printTileType(tileType);
// turnSpeed = state.player.drifting ? 0.003: 0.002;
unsigned char currentTile = getTileID(kartX / scale, kartY / scale);
enum TileType tileType = getTileType(currentTile);
printTileType(tileType);
bool isOffRoad = tileType == Offroad || tileType == Grass || tileType == ShallowWater;
if (tileType == Zipper) {
state.player.boostTime = 100;
addParticle(2, LCD_WIDTH_PX / 2 - 28, LCD_HEIGHT_PX - 70, 0, 0);
}
if (state.player.drifting && !isOffRoad) {
if ((buttons.left && state.player.driftDir == -1) || (buttons.right && state.player.driftDir == 1)) {
state.player.driftCharge += 2;
} else {
state.player.driftCharge++;
}
} else {
state.player.driftCharge = 0;
/* for (int x = 0; x < LCD_WIDTH_PX / 2; x++) {
for (int y = 0; y < 4; y++) {
// setPixel(i, j, 0xF800);
((unsigned int *)VRAM)[y * (LCD_WIDTH_PX / 2) + x] = (0x0CDF << 16) | 0x0CDF;
}
} */
}
// if (state.player.driftCharge > 60) {
// state.player.driftCharge = 60;
// }
/* if (state.player.driftCharge > 0) {
// Draw a 4px red bar at the top of the screen
int barWidth = state.player.driftCharge * LCD_WIDTH_PX / 60 / 2;
for (int x = 0; x < barWidth; x++) {
for (int y = 0; y < 4; y++) {
// setPixel(i, j, 0xF800);
((unsigned int *)VRAM)[y * (LCD_WIDTH_PX / 2) + x] = (0xF800 << 16) | 0xF800;
}
}
} */
#ifdef PROFILING_ENABLED
prof_leave(prof_logic);
prof_enter(prof_logic1);
#endif
if (!buttons.hop) {
if (state.player.drifting && state.player.driftCharge >= 60) {
if (state.player.driftCharge > 360) {
state.player.boostTime = 100;
addParticle(2, LCD_WIDTH_PX / 2 - 28, LCD_HEIGHT_PX - 70, 0, 0);
} else if (state.player.driftCharge >= 180) {
state.player.boostTime = 50;
addParticle(1, LCD_WIDTH_PX / 2 - 28, LCD_HEIGHT_PX - 70, 0, 0);
} else {
state.player.boostTime = 20;
addParticle(3, LCD_WIDTH_PX / 2 - 28, LCD_HEIGHT_PX - 70, 0, 0);
}
}
state.player.drifting = false;
}
if (buttons.hop && !lastButtons.hop && state.player.hopStage == 0) {
state.player.hopStage = 1;
if (buttons.left || buttons.right) {
state.player.drifting = true;
state.player.driftDir = buttons.left ? -1 : 1;
}
}
if (state.player.hopStage != 0) {
state.player.hopStage++;
if (state.player.hopStage >= 15) {
state.player.hopStage = 0;
if (!state.player.drifting && (buttons.left || buttons.right)) {
state.player.drifting = true;
state.player.driftDir = buttons.left ? -1 : 1;
}
}
}
/* if (state.player.drifting) {
// debug_printf("state.player.driftDir: %d\n", state.player.driftDir);
if (state.player.driftDir == 1) {
buttons.right = true;
buttons.left = false;
} else {
buttons.left = true;
buttons.right = false;
}
} */
#ifdef PROFILING_ENABLED
prof_leave(prof_logic1);
prof_enter(prof_logic2);
#endif
/* if (buttons.debug_boost) {
state.player.boostTime = 30;
addParticle(1, LCD_WIDTH_PX / 2 - 28, LCD_HEIGHT_PX - 70, 0, 0);
} */
bool boosting = false;
if (state.player.boostTime >= 0) {
state.player.boostTime--;
boosting = true;
}
if (boosting) {
// maxPower = 0.15;
// powerFactor = 0.002;
applyBoost = true;
state.player.power = maxPower;
buttons.accel = true;
// hFovModifier = (1 << 12) * 1.2;
if (hFovModifier < (1 << 12) * 1.2) {
hFovModifier += (1 << 12) * 0.05;
}
} else {
// maxPower = 0.1;
// powerFactor = 0.001;
applyBoost = false;
// hFovModifier = 1 << 12;
if (hFovModifier > 1 << 12) {
hFovModifier -= (1 << 12) * 0.02;
}
@ -587,35 +487,16 @@ void main_loop() {
}
}
if (!boosting && isOffRoad) {
applyOffRoadDrag = true;
} else {
applyOffRoadDrag = false;
}
double oldKartY = state.player.y;
double oldKartX = state.player.x;
#ifdef PROFILING_ENABLED
prof_leave(prof_logic2);
#endif
int lastLapCount = state.player.lapCount;
timePhysics = profile({
updateWithControls(&state.player, buttons);
});
#ifdef PROFILING_ENABLED
prof_enter(prof_logic2);
#endif
unsigned char newTile = getTileID(state.player.x * 12 / scale, state.player.y * 12 / scale);
if (getTileType(newTile) == SolidBlock) {
state.player.x = oldKartX;
state.player.y = oldKartY;
if (state.player.lapCount > lastLapCount) {
didFinishLap = true;
}
kartX = state.player.x * 12;
kartY = state.player.y * 12;
kartX = state.player.x;
kartY = state.player.y;
// Radians to degrees
kartAngle = -state.player.angle * 180 / 3.1415926;
@ -627,30 +508,6 @@ void main_loop() {
kartAngle = fmod(kartAngle, 360);
angle = fmod(kartAngle + 90, 360) * angleWidth / 90;
/* for (unsigned short x = 0; x < LCD_WIDTH_PX / 2; x++) {
index2 = x + angle;
index2 = mod(index2, (angleWidth * 4));
element = mod(index2, angleWidth);
// TODO: Plus 2?
for (unsigned short y = horizon + 2; y < LCD_HEIGHT_PX; y += 1) {
unsigned short thing = getScreenPixel(x, y);
setPixel(x * 2, y, thing);
setPixel(x * 2 + 1, y, thing);
}
} */
if (newTile == 254 && currentTile != 254) {
state.player.lapCount++;
didFinishLap = true;
#ifdef __EMSCRIPTEN__
printf("Lap %d\n", state.player.lapCount);
#endif
}
#ifdef PROFILING_ENABLED
prof_leave(prof_logic2);
#endif
}
if (abs_double(state.player.xVelocity) + abs_double(state.player.yVelocity) < 0.02) {
@ -683,7 +540,6 @@ void main_loop() {
if (state.player.driftDir == -1 && targetAnim < 7) {
targetAnim = 7;
}
// debug_printf("state.player.driftDir: %d, targetAnim: %d\n", state.player.driftDir, targetAnim);
}
if (state.player.kartSteerAnim != targetAnim) {
@ -729,6 +585,7 @@ void main_loop() {
time3D = profile({
static bool lastTrackNeedsUpdate = true;
// fillSky(0, LCD_HEIGHT_PX);
if (trackNeedsUpdate) {
// Only use high quality during the countdown
draw3D(state.totalFrameCount <= 240);
@ -742,15 +599,11 @@ void main_loop() {
trackNeedsUpdate = false;
});
#ifdef PROFILING_ENABLED
prof_enter(prof_logic3);
#endif
// Draw parallax background if the angle has changed
// TODO: Can we use the existing variable here?
static int lastAngle2 = 99999;
bool bgRedraw = false;
if (lastAngle2 != angle) {
if (lastAngle2 != angle && track.drawLoop) {
bgRedraw = true;
drawParallaxBackground(angle);
lastAngle2 = angle;
@ -780,10 +633,7 @@ void main_loop() {
x -= v / 2;
x += (state.totalFrameCount / 2) % 3 * sign;
y += (state.totalFrameCount / 2) % 2;
#ifdef PROFILING_ENABLED
prof_leave(prof_logic3);
prof_enter(prof_sprites);
#endif
if (sign == 1) {
draw(imgs_fire[fireStage], x, y);
draw(imgs_fire[fireStage], x + (state.totalFrameCount / 2) % 2 * 3, y + 5);
@ -791,15 +641,8 @@ void main_loop() {
draw_flipped(imgs_fire[fireStage], x, y);
draw_flipped(imgs_fire[fireStage], x - (state.totalFrameCount / 2) % 2 * 3, y + 5);
}
#ifdef PROFILING_ENABLED
prof_leave(prof_sprites);
prof_enter(prof_logic3);
#endif
}
// debug_printf("state.totalFrameCount: %d\n", state.totalFrameCount);
if (state.player.drifting) {
if (state.totalFrameCount % 8 == 0) {
addParticle(0, 192 + -32 * state.player.driftDir, 180, -5 * state.player.driftDir, state.totalFrameCount % 16 == 0 ? -1 : 1);
@ -808,26 +651,50 @@ void main_loop() {
int animNo = abs_int(state.player.kartSteerAnim) / 2;
int newAnimNo = animNo + ((jitter / 2) * 11);
// debug_printf("animNo: %d\n", animNo);
// draw_alpha(img_shadow, (LCD_WIDTH_PX / 2) - (96 / 2), 116, 2);
// if (state.totalFrameCount % 2 == 0) {
// draw(img_shadow1, (LCD_WIDTH_PX / 2) - (96 / 2), 112);
// int y = LCD_HEIGHT_PX - 100;
// int x = LCD_WIDTH_PX / 2;
// VRAM[y * LCD_WIDTH_PX + x] = 0xF800;
// int worldX;
// int worldY;
// screenToWorldSpace(x, y, &worldX, &worldY);
#define tileSize 8
#define trackImageWidth 256 * tileSize
#define trackImageHeight 256 * tileSize
// // Set the tile ID at worldX, worldY to 0
// if (!(worldX < 0 || worldX >= trackImageWidth || worldY < 0 || worldY >= trackImageHeight)) {
// int xPixel = worldX >> 3;
// int yPixel = worldY >> 3;
// int tileID = tilemap[((yPixel * (trackImageWidth / tileSize)) + xPixel)];
// if (tileID != 0) {
// printf("Setting: %d, %d\n", worldX, worldY);
// tilemap[((yPixel * (trackImageWidth / tileSize)) + xPixel)] = 0;
// trackNeedsUpdate = true;
// } else {
// printf("Tile already 0\n");
// }
// } else {
// draw(img_shadow2, (LCD_WIDTH_PX / 2) - (96 / 2), 112);
// printf("Out of bounds: %d, %d\n", worldX, worldY);
// }
tilemap[(((239 >> 3) * (trackImageWidth / tileSize)) + (888 >> 3))] = 0;
// for (int i = -1; i <= 1; i++) {
// for (int j = -1; j <= 1; j++) {
// if ((y + i) >= 0 && (y + i) < LCD_HEIGHT_PX && (x + j) >= 0 && (x + j) < LCD_WIDTH_PX) {
// VRAM[(y + i) * LCD_WIDTH_PX + (x + j)] = 0xF800;
// }
// }
// }
#ifdef PROFILING_ENABLED
prof_leave(prof_logic3);
#endif
timeKartSprite = profile({
if (state.player.hopStage != 0) {
draw(img_shadow1, (LCD_WIDTH_PX / 2) - (96 / 2), 112);
}
if (state.player.kartSteerAnim >= 0) {
// CopySpriteMasked(/*mksprites[state.player.kartSteerAnim / 2]*/sprite, (LCD_WIDTH_PX / 2) - 39, 128, 78, 81, 0x07e0);
// CopySpriteMasked(mksprites[state.player.kartSteerAnim / 4], (LCD_WIDTH_PX / 2) - 36, 128, 72, 80, 0x4fe0);
if (newAnimNo == animNo) {
draw(imgs_kart[animNo], (LCD_WIDTH_PX / 2) - (96 / 2), horizon + 4 + (jitter % 2) - (hopAnim[state.player.hopStage] * 3));
} else {
@ -835,8 +702,6 @@ void main_loop() {
draw(imgs_kart[newAnimNo], (LCD_WIDTH_PX / 2) - (96 / 2), horizon + 4 + (jitter % 2) - (hopAnim[state.player.hopStage] * 3));
}
} else {
// CopySpriteMaskedFlipped(/*mksprites[-state.player.kartSteerAnim / 2]*/sprite, (LCD_WIDTH_PX / 2) - 39, 128, 78, 81, 0x07e0);
// CopySpriteMaskedFlipped(mksprites[-state.player.kartSteerAnim / 4], (LCD_WIDTH_PX / 2) - 36, 128, 72, 80, 0x4fe0);
if (newAnimNo == animNo) {
draw_flipped(imgs_kart[animNo], (LCD_WIDTH_PX / 2) - (96 / 2), horizon + 4 + (jitter % 2) - (hopAnim[state.player.hopStage] * 3));
} else {
@ -844,22 +709,13 @@ void main_loop() {
draw_flipped(imgs_kart[newAnimNo], (LCD_WIDTH_PX / 2) - (96 / 2) - (state.player.kartSteerAnim == -20 ? 1 : 0), horizon + 4 + (jitter % 2) - (hopAnim[state.player.hopStage] * 3));
}
}
// draw_scaled(imgs_kart[animNo], (LCD_WIDTH_PX / 2) - (96 / 2), horizon + 4 + (jitter % 2) - (hopAnim[state.player.hopStage] * 3) + 30, (1 / 1.5), (1 / 1.5));
});
#ifdef PROFILING_ENABLED
prof_enter(prof_sprites);
#endif
// TODO: Don't profile all of it as sprites?
bool particlesUpdated = tickParticles();
// TODO: Make this happen before the 3D is drawn
trackNeedsUpdate = trackNeedsUpdate || particlesUpdated;
// Bdisp_PutDisp_DD_stripe(horizon + 2, LCD_HEIGHT_PX);
// dupdate();
// const int fontWidths[] = {10, 5, 10, 9, 11, };
#define skyColor 0x0CDF
// #define skyColor 0xD80C
if (debugType <= 1) {
@ -870,10 +726,6 @@ void main_loop() {
}
}
}
#ifdef PROFILING_ENABLED
prof_leave(prof_sprites);
prof_enter(prof_logic3);
#endif
if (state.totalFrameCount < 240) {
int stage = state.totalFrameCount / 60;
@ -881,7 +733,8 @@ void main_loop() {
if (stage != lastStage) {
fillSky(0, horizon);
}
draw(imgs_countdown[3 - stage], offX(-152 / 2), offY(-66 / 2));
// draw(imgs_countdown[3 - stage], offX(-152 / 2), offY(-66 / 2));
// draw_scaled(imgs_countdown[3 - stage], offX(-152 / 2), offY(-66 / 2), 0.5, 0.5);
if (stage != lastStage) {
displayUpdate(0, horizon + 2);
}
@ -896,12 +749,7 @@ void main_loop() {
displayUpdate(0, horizon + 2);
}
#ifdef PROFILING_ENABLED
prof_leave(prof_logic3);
prof_enter(prof_sprites);
#endif
drawTimer();
drawTimer(didFinishLap);
// Lap count
static int lastLap = -1;
@ -912,64 +760,66 @@ void main_loop() {
lastLap = lap;
}
#ifdef PROFILING_ENABLED
prof_leave(prof_sprites);
prof_enter(prof_logic3);
#endif
// Draw a sprite at 888, 239 in world space
// First calculate the distance from the camera
// int dx = 888 - (xOffset >> 2);
// int dy = 239 - (yOffset >> 2);
// float dist = sqrt(dx * dx + dy * dy);
// printf("Dist: %f\n", dist);
int x;
int y;
int dist;
worldToScreenSpace(888, 239, &x, &y, &dist);
printf("Dist: %d\n", dist);
const float size = 200;
if (dist >= 1 && (y - (64 * size / dist) > (LCD_HEIGHT_PX / 2) - 2)) {
VRAM[y * LCD_WIDTH_PX + x] = 0xF800;
draw_scaled(img_tree, x - (28 * size / dist), y - (64 * size / dist), size / dist, size / dist);
}
#ifdef PROFILING_ENABLED
prof_leave(prof_logic3);
timeLogic = prof_time(prof_logic);
timeLogic2 = prof_time(prof_logic2);
timeLogic3 = prof_time(prof_logic3);
timeSprites = prof_time(prof_sprites);
#endif
timeUpdate = profile({
// draw(img_loop, angle, 92);
if (bgRedraw) {
displayUpdate(88, 92);
displayUpdate(100, 108);
}
// draw(img_loop, angle, 92);
if (bgRedraw) {
displayUpdate(88, 92);
displayUpdate(100, 108);
}
// if (state.totalFrameCount % 2 == 0) {
// Update timer on screen
// if (state.totalFrameCount % 2 == 0) {
// Update timer on screen
// Update HUD
if (hudUpdated) {
displayUpdate(8, 24);
} else if (wholeTimerUpdated) {
displayUpdateBox(306, 8, 81, 12);
} else {
if (minutesUpdated) {
displayUpdateBox(306, 8, 24, 12);
}
if (secondsUpdated) {
displayUpdateBox(336, 8, 24, 12);
}
if (millisecondsUpdated) {
displayUpdateBox(365, 8, 24, 12);
}
// Update HUD
if (hudUpdated) {
displayUpdate(8, 24);
} else if (wholeTimerUpdated) {
displayUpdateBox(306, 8, 81, 12);
} else {
if (minutesUpdated) {
displayUpdateBox(306, 8, 24, 12);
}
if (secondsUpdated) {
displayUpdateBox(336, 8, 24, 12);
}
if (millisecondsUpdated) {
displayUpdateBox(365, 8, 24, 12);
}
}
// Update track on screen
if (trackDisplayUpdateNeeded) {
displayUpdate(horizon + 2, LCD_HEIGHT_PX);
}
// }
// Update track on screen
#ifdef PROFILING_ENABLED
// This means you don't need to be moving to get accurate profiling data
trackDisplayUpdateNeeded = true;
#endif
if (trackDisplayUpdateNeeded) {
displayUpdate(horizon + 2, LCD_HEIGHT_PX);
}
// displayUpdate(0, LCD_HEIGHT_PX);
});
// displayUpdate(0, LCD_HEIGHT_PX);
// draw_loop_x(img_loop, 0, 0, angle, LCD_WIDTH_PX);
// draw_loop_x(img_castle, 0, 0, angle * 1.5, LCD_WIDTH_PX);
// draw_loop_x(img_bush, 0, 0, angle * 2, LCD_WIDTH_PX);
// Bdisp_PutDisp_DD();
#ifdef __EMSCRIPTEN__
if (state.totalFrameCount % 30 == 0) {
printf("kartX: %f, kartY: %f\n", state.player.x, state.player.y);
}
// if (state.totalFrameCount % 30 == 0) {
// printf("kartX: %f, kartY: %f\n", state.player.x, state.player.y);
// }
#endif
state.totalFrameCount += 1;
@ -990,30 +840,28 @@ int main() {
return Number(prompt("Track ID:", "0"));
);
#else
trackId = 0;
dclear(C_WHITE);
drawText(8, 8, "F1 - Peach Circuit");
drawText(8, 20, "F2 - Sunset Wilds");
drawText(8, 32, "F3 - Sky Garden");
displayUpdate(0, LCD_HEIGHT_PX);
trackId = -1;
while (trackId == -1) {
key_event_t key = getkey();
if (key.type != KEYEV_DOWN) continue;
if (key.key == KEY_F1) trackId = 0;
if (key.key == KEY_F2) trackId = 1;
if (key.key == KEY_F3) trackId = 2;
}
#endif
initData(trackId);
fillSky(0, LCD_HEIGHT_PX);
/* state.player.x = 3565.0 / 12;
state.player.y = 2600.0 / 12;
state.player.xVelocity = 0;
state.player.yVelocity = 0;
state.player.power = 0;
state.player.reverse = 0;
state.player.angle = 0;
state.player.angularVelocity = 0;
state.player.isThrottling = false;
state.player.isReversing = false;
state.player.isShooting = false;
state.player.isTurningLeft = false;
state.player.isTurningRight = false; */
initState();
kartX = state.player.x * 12;
kartY = state.player.y * 12;
kartX = state.player.x;
kartY = state.player.y;
// Radians to degrees
kartAngle = -state.player.angle * 180 / 3.1415926;
kartAngle += 90;
@ -1029,15 +877,5 @@ int main() {
runMainLoop(main_loop, 60);
/* color_t* VRAM = (color_t*)0xA8000000; // emu address of VRAM
VRAM += (LCD_WIDTH_PX*y + x);
short j;
for (j=y; j<y+height; j++) {
for (int i = x; i< x + width; i++) {
*(VRAM++) = 0x07E0;
}
VRAM += LCD_WIDTH_PX - width;
} */
return 0;
}

View File

@ -5,7 +5,11 @@
#include "./maths.h"
#include "./buttons.h"
#include "./state.h"
#include "stdbool.h"
#include "./data.h"
#include "./particles.h"
#include "./tilemap.h"
#include <stdbool.h>
#define angleWidth 192
@ -57,6 +61,9 @@ double dmod(double a, double b) {
return a - (int)(a / b) * b;
}
#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
#define MAX(X, Y) (((X) < (Y)) ? (Y) : (X))
// #define maxSteerNormal 1
// #define minSteerDrift 0.2 * (1.7 / 2.2)
// #define neutralSteerDrift (1.7 / 2.2)
@ -66,7 +73,115 @@ extern double minSteerDrift;
extern double neutralSteerDrift;
extern double maxSteerDrift;
void boostKart(Kart *kart, int amount) {
kart->boostTime = MAX(kart->boostTime, amount);
if (!kart->isPlayer) return;
if (amount >= 100) {
addParticle(2, LCD_WIDTH_PX / 2 - 28, LCD_HEIGHT_PX - 70, 0, 0);
} else if (amount >= 50) {
addParticle(1, LCD_WIDTH_PX / 2 - 28, LCD_HEIGHT_PX - 70, 0, 0);
} else {
addParticle(3, LCD_WIDTH_PX / 2 - 28, LCD_HEIGHT_PX - 70, 0, 0);
}
}
// TODO: Remove this somehow or make it more clear
#define scale 4
void updateWithControls(Kart *kart, ButtonState controls) {
// Save the previous position, to restore if we hit a wall
double oldKartY = kart->y;
double oldKartX = kart->x;
// Get the tile the kart is on, and its type.
// TODO: Use all the tiles the kart is touching?
unsigned char currentTile = getTileID(kart->x / scale, kart->y / scale);
enum TileType tileType = getTileType(currentTile);
bool isOffRoad = tileType == Offroad || tileType == Grass || tileType == ShallowWater;
if (tileType == BoostPad) {
boostKart(kart, 100);
} else if (tileType == JumpBar) {
controls.hop = true;
}
// If the kart is still drifting...
if (kart->drifting && !isOffRoad) {
if ((controls.left && kart->driftDir == -1) || (controls.right && kart->driftDir == 1)) {
// Drift charge builds faster when holding the same direction as the drift
kart->driftCharge += 2;
} else {
kart->driftCharge++;
}
} else {
// If the kart is not drifting, or is off road, reset the drift charge to 0
kart->driftCharge = 0;
}
if (!controls.hop) {
// When the player lets go of the drift button...
if (kart->drifting && kart->driftCharge >= 60) {
// apply the relevant boost...
if (kart->driftCharge > 360) {
boostKart(kart, 100);
} else if (kart->driftCharge >= 180) {
boostKart(kart, 50);
} else {
boostKart(kart, 20);
}
}
// ...and cancel the drift
kart->drifting = false;
}
// If the hop button was pressed, and the kart is not currently hopping...
if (controls.hop && !kart->lastHop && kart->hopStage == 0) {
// ...start the hop animation.
kart->hopStage = 1;
if (controls.left || controls.right) {
// If a direction is held, start a drift (this can also be done when the hop finishes)
kart->drifting = true;
kart->driftDir = controls.left ? -1 : 1;
}
}
// If a hop is in progress...
if (kart->hopStage != 0) {
// ...continue the hop animation.
kart->hopStage++;
if (kart->hopStage >= 15) {
kart->hopStage = 0;
if (!kart->drifting && (controls.left || controls.right)) {
// If a direction is held, start a drift
kart->drifting = true;
kart->driftDir = controls.left ? -1 : 1;
}
}
}
bool boosting = false;
if (kart->boostTime >= 0) {
// If we are boosting, set a flag and decrement the remaining time
kart->boostTime--;
boosting = true;
}
if (boosting) {
// TODO: Remove this seemingly duplicate variable
applyBoost = true;
kart->power = maxPower;
controls.accel = true;
} else {
applyBoost = false;
}
if (!boosting && isOffRoad) {
applyOffRoadDrag = true;
} else {
applyOffRoadDrag = false;
}
bool canTurn = kart->power > 0.0025/* || car->reverse*/;
// Controls are reversed for now
@ -132,12 +247,26 @@ void updateWithControls(Kart *kart, ButtonState controls) {
kart->xVelocity += sin2(kart->angle) * (kart->power /* - kart->reverse */);
kart->yVelocity += cos2(kart->angle) * (kart->power /* - kart->reverse */);
kart->x += kart->xVelocity;
kart->y -= kart->yVelocity;
kart->x += kart->xVelocity * 12;
kart->y -= kart->yVelocity * 12;
double calcDrag = applyOffRoadDrag ? offRoadDrag : drag;
kart->xVelocity *= calcDrag;
kart->yVelocity *= calcDrag;
kart->angle += kart->angularVelocity;
kart->angle = dmod(kart->angle, 3.1415926 * 2);
kart->angularVelocity *= angularDrag;
unsigned char newTile = getTileID(kart->x / scale, kart->y / scale);
if (getTileType(newTile) == SolidBlock) {
kart->x = oldKartX;
kart->y = oldKartY;
}
// Finish line is tile 254
if (newTile == 254 && currentTile != 254) {
kart->lapCount++;
}
kart->lastHop = controls.hop;
}

View File

@ -4,8 +4,11 @@
#include <stdbool.h>
#include "./buttons.h"
#include "./data.h"
typedef struct {
bool isPlayer;
double x;
double y;
double xVelocity;
@ -22,6 +25,8 @@ typedef struct {
int driftCharge;
int boostTime;
int lapCount;
bool lastHop;
} Kart;
extern bool applyOffRoadDrag;

View File

@ -39,6 +39,7 @@ void draw(const struct image *img, int x, int y);
void draw_partial(const struct image *img, int x, int y, int sx, int sy, int w, int h);
void draw_flipped(const struct image *img, int x, int y);
void draw_partial_flipped(const struct image *img, int x, int y, int sx, int sy, int w, int h);
void draw_scaled(const struct image *img, int x, int y, float scaleX, float scaleY);
int get_width(const struct image* img);
int get_height(const struct image* img);

View File

@ -72,7 +72,7 @@ void displayUpdate(int minY, int maxY) {
void drawText(int x, int y, const char *text) {
dtext_opt(x, y, C_BLACK, C_WHITE, DTEXT_LEFT, DTEXT_TOP, text, -1);
displayUpdate(y, y + 12);
// displayUpdate(y, y + 12);
}
int getTimeMS(void) {
@ -108,6 +108,23 @@ void draw_partial_flipped(const struct image *img, int x, int y, int sx, int sy,
dsubimage_p8_effect(x + img->xOffset + sx, y + img->yOffset + sy, img->data, sx, sy, w, h, IMAGE_HFLIP);
}
void draw_scaled(const struct image *img, int x, int y, float scaleX, float scaleY) {
// Create an image_linear_map
struct image_linear_map map;
image_scale(img->data, scaleX * (1 << 16), scaleY * (1 << 16), &map);
// If x is negative, cut the image off on the left.
const image_t* newData;
if (x < 0) {
// image_sub(const image_t *src, int x, int y, int w, int h, image_t *dst)
newData = image_sub(img->data, -x, 0, img->data->width + x, img->data->height);
} else {
newData = img->data;
}
// Use it to draw the image
// TODO: Free the created image or cache it
image_linear(newData, image_at(image_create_vram(), x, y), &map);
}
int get_width(const struct image* img) {
return img->data->width;
}

View File

@ -47,6 +47,7 @@ void draw(const struct image *img, int x, int y);
void draw_partial(const struct image *img, int x, int y, int sx, int sy, int w, int h);
void draw_flipped(const struct image *img, int x, int y);
void draw_partial_flipped(const struct image *img, int x, int y, int sx, int sy, int w, int h);
void draw_scaled(const struct image *img, int x, int y, float scaleX, float scaleY);
int get_width(const struct image* img);
int get_height(const struct image* img);

View File

@ -126,6 +126,78 @@ void draw_partial_flipped(const struct image *img, int x, int y, int sx, int sy,
}
}
// void draw_scaled(const struct image *img, int x, int y, float scaleX, float scaleY) {
// const unsigned short* data = img->data;
// // The height and width of the sprite are the first two elements in the data array
// int width = data[0];
// int height = data[1];
// int screenWidth = width * scaleX;
// int screenHeight = height * scaleY;
// // The offsets of the x and y positions are the third and fourth elements in the data array
// x += data[2];
// y += data[3];
// // Now draw the sprite
// // The data array starts at index 4
// color_t* datac = (color_t*) (data + 4);
// for (int j = y; j < y + screenHeight; j++) {
// for (int i = x; i < x + screenWidth; i++) {
// // Skip if out of bounds
// if (i < 0 || i >= LCD_WIDTH_PX || j < 0 || j >= LCD_HEIGHT_PX) {
// continue;
// }
// color_t colour = datac[(int) ((j - y) / scaleY) * width + (int) ((i - x) / scaleX)];
// if (colour != 0x4fe0) {
// VRAM[j * LCD_WIDTH_PX + i] = colour;
// }
// }
// }
// }
void draw_scaled(const struct image *img, int x, int y, float scaleX, float scaleY) {
const unsigned short* data = img->data;
// The height and width of the sprite are the first two elements in the data array
int width = data[0];
int height = data[1];
int screenWidth = width * scaleX;
int screenHeight = height * scaleY;
// The offsets of the x and y positions are the third and fourth elements in the data array
x += data[2];
y += data[3];
// Calculate an offset for each pixel to be incremeneted by in 16:16 fixed point
int offsetX = (1 << 16) / scaleX;
int offsetY = (1 << 16) / scaleY;
int currentX = 0;
int currentY = 0;
// Now draw the sprite
// The data array starts at index 4
color_t* datac = (color_t*) (data + 4);
for (int j = y; j < y + screenHeight; j++) {
currentX = 0;
for (int i = x; i < x + screenWidth; i++) {
int sampleX = currentX >> 16;
int sampleY = currentY >> 16;
// Skip if out of bounds
if (i < 0 || i >= LCD_WIDTH_PX || j < 0 || j >= LCD_HEIGHT_PX) {
continue;
}
color_t colour = datac[sampleY * width + sampleX];
if (colour != 0x4fe0) {
VRAM[j * LCD_WIDTH_PX + i] = colour;
}
currentX += offsetX;
}
currentY += offsetY;
}
}
// // Copy a sprite that loops around with an X offset
// void CopySpriteLoopX(const void* datar, int x, int y, int width, int height, int xOffset, int drawWidth, int maskcolor) {
// color_t* data = (color_t*)datar;

View File

@ -37,7 +37,7 @@ typedef enum {
REC_KEY_DEBUG,
REC_KEY_SAVE,
REC_KEY_LOAD,
REC_KEY_DEBUG_BOOST,
// REC_KEY_FRAMECAP_TOGGLE,
} keyType;
typedef struct {
@ -104,12 +104,13 @@ void updateButtonState(ButtonState *state) {
if (state->load) {
addEvent(EVENT_PRESS, REC_KEY_LOAD);
}
if (state->debug_boost) {
addEvent(EVENT_PRESS, REC_KEY_DEBUG_BOOST);
}
// if (state->framecap_toggle) {
// addEvent(EVENT_PRESS, REC_KEY_FRAMECAP_TOGGLE);
// }
if (recording.events[recording.numEvents - 1].type == EVENT_WAIT) {
// If the last event was a wait event, increment the counter by 1
// TODO: Check for overflow
recording.events[recording.numEvents - 1].data++;
} else {
// Otherwise, add a new wait event

View File

@ -35,8 +35,9 @@ State state;
void initState() {
state = (State) {
.player = (Kart) {
.x = ((float) track.startX) / 12,
.y = ((float) track.startY) / 12,
.isPlayer = true,
.x = ((float) track.startX),
.y = ((float) track.startY),
.xVelocity = 0,
.yVelocity = 0,
.power = 0,

View File

@ -22,6 +22,39 @@ unsigned char getTileID(short xPos, short yPos) {
}
}
// unsigned char getTileID(short l0, short l1) {
// unsigned int si0, si1, si2;
// si0 = l0;
// si1 = l1;
// si0 |= si1;
// si1 = 2047u;
// si0 = si0 <= si1;
// if (si0) {
// si0 = l1;
// si1 = 65528u;
// si0 &= si1;
// si1 = 5u;
// si0 <<= (si1 & 31);
// si1 = l0;
// si2 = 65528u;
// si1 &= si2;
// si2 = 3u;
// si1 >>= (si2 & 31);
// si0 += si1;
// // si1 = 1024u;
// // si0 += si1;
// // si0 = load(si0);
// si0 = tilemap[si0];
// } else {
// si0 = 0u;
// }
// return si0;
// }
enum TileType getTileType(int tileID) {
return (enum TileType) track.tileTypes[tileID];
}
unsigned short samplePixel(short xPos, short yPos) {
xPos += xOffset;
yPos += yOffset;

View File

@ -4,6 +4,7 @@
#include <stdbool.h>
unsigned char getTileID(short xPos, short yPos);
enum TileType getTileType(int tileID);
unsigned short samplePixel(short xPos, short yPos);
#endif // _TILEMAP_H