Many things, can't list them all

This commit is contained in:
Heath Mitchell 2022-07-02 20:20:17 +01:00
parent 02a940a55b
commit cc510ec793
56 changed files with 737 additions and 245 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

13
.idea/misc.xml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MakefileSettings">
<option name="linkedExternalProjectsSettings">
<MakefileProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="version" value="2" />
</MakefileProjectSettings>
</option>
</component>
<component name="MakefileWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -10,6 +10,10 @@
"display.h": "c",
"math.h": "c",
"generated_lut.h": "c",
"stdbool.h": "c"
"stdbool.h": "c",
"SYSTEM.C": "cpp",
"rtc.h": "c",
"particles.h": "c",
"images.h": "c"
}
}

View File

@ -119,7 +119,7 @@ browser: html/index.html
emrun --browser=chromium --browser_args=--auto-open-devtools-for-tabs html/index.html
$(BUILD):
@mkdir $@
@mkdir -p $@
#---------------------------------------------------------------------------------
export CYGWIN := nodosfilewarning

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets/img/smoke/0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

BIN
assets/img/smoke/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

BIN
assets/img/smoke/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

View File

@ -2,22 +2,32 @@ main.o: /home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/main.c \
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./main.h \
/home/heath/PrizmSDK-win-0.5.2/include/fxcg/display.h \
/home/heath/PrizmSDK-win-0.5.2/include/fxcg/keyboard.h \
/home/heath/PrizmSDK-win-0.5.2/include/fxcg/system.h \
/home/heath/PrizmSDK-win-0.5.2/include/fxcg/misc.h \
/home/heath/PrizmSDK-win-0.5.2/include/stddef.h \
/home/heath/PrizmSDK-win-0.5.2/include/fxcg/rtc.h \
/home/heath/PrizmSDK-win-0.5.2/include/fxcg/system.h \
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./3d.h \
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./tilemap.h \
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./sprites.h \
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./physics.h \
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/././buttons.h \
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./buttons.h \
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./debugHud.h \
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./particles.h \
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/../data-headers/images.h
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./main.h:
/home/heath/PrizmSDK-win-0.5.2/include/fxcg/display.h:
/home/heath/PrizmSDK-win-0.5.2/include/fxcg/keyboard.h:
/home/heath/PrizmSDK-win-0.5.2/include/fxcg/system.h:
/home/heath/PrizmSDK-win-0.5.2/include/fxcg/misc.h:
/home/heath/PrizmSDK-win-0.5.2/include/stddef.h:
/home/heath/PrizmSDK-win-0.5.2/include/fxcg/rtc.h:
/home/heath/PrizmSDK-win-0.5.2/include/fxcg/system.h:
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./3d.h:
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./tilemap.h:
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./sprites.h:
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./physics.h:
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/././buttons.h:
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./buttons.h:
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./debugHud.h:
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/./particles.h:
/home/heath/PrizmSDK-win-0.5.2/projects/mario-kart/src/../data-headers/images.h:

Binary file not shown.

2
credits.txt Normal file
View File

@ -0,0 +1,2 @@
https://www.spriters-resource.com/fullview/5477/
https://www.spriters-resource.com/game_boy_advance/mariokartsupercircuit/sheet/5490/

33
data-headers/images.c Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -29,13 +29,13 @@ int setKeyState(unsigned char key, unsigned char state) {
}
int keyDownEvent(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData) {
printf("keyDownEvent: %d\n", keyEvent->keyCode);
// printf("keyDownEvent: %d\n", (int) keyEvent->keyCode);
setKeyState(keyEvent->keyCode, 1);
return 0;
}
int keyUpEvent(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData) {
printf("keyUpEvent: %d\n", keyEvent->keyCode);
// printf("keyUpEvent: %d\n", (int) keyEvent->keyCode);
setKeyState(keyEvent->keyCode, 0);
return 0;
}

View File

@ -0,0 +1,3 @@
void PowerOff(int displayLogo) {
// nop
}

View File

@ -0,0 +1,56 @@
#ifdef __cplusplus
extern "C" {
#endif
// This adds in syscalls for interfacing with the OS and hardware.
void SetAutoPowerOffTime(int durationInMinutes);
int GetAutoPowerOffTime(); //returns duration in minutes
void SetBacklightDuration(char durationInHalfMinutes);
char GetBacklightDuration(); //returns duration in half-minutes
void SetBatteryType(int type);
int GetBatteryType(void);
int GetMainBatteryVoltage(int one); //parameter should be 1
void PowerOff(int displayLogo);
void Restart();
void SpecialMatrixcodeProcessing(int*col, int*row);
void TestMode(int);
void*GetStackPtr(void);
void SetSetupSetting(unsigned int SystemParameterNo, unsigned char SystemParameterValue);
unsigned char GetSetupSetting(unsigned int SystemParameterNo);
int Timer_Install(int InternalTimerID, void (*hander)(void), int elapse);
int Timer_Deinstall(int InternalTimerID);
int Timer_Start(int InternalTimerID);
int Timer_Stop(int InternalTimerID);
void TakeScreenshot(void);
void TakeScreenshot2(void); //seems to be the same as the one above
void DisplayMainMenu(void);
//Hold program execution:
void OS_InnerWait_ms(int);
void CMT_Delay_100micros(int); //does CMT stand for Composable Memory Transactions? Couldn't find documentation on this
void CMT_Delay_micros(int); // nor on this (gbl08ma)
void SetQuitHandler(void (*)()); // sets callback to be run when user exits through the main menu from one app to another. eActivity uses this in the "Save file?" dialog
void Alpha_SetData( char VarName, void* Src );
void Alpha_GetData( char VarName, void* Dest );
int CLIP_Store( unsigned char*buffer, int length ); // stores buffer of length length in the system clipboard.
int MB_ElementCount(char* buf); // like strlen but for the graphical length of multibyte strings
int MB_IsLead(char c); // returns 1 if the character is the first byte of a multibyte char
int GlibGetOSVersionInfo(char *major, char *minor, short int *c, short int *d);
#ifdef __cplusplus
}
#endif

122
scripts/img2c.py Normal file
View File

@ -0,0 +1,122 @@
# Reads images, converts the colour data to RGB565, and writes it to a header file.
# 0x4fe0 is used as the transparent colour.
from PIL import Image
import sys
import os
def toData(path):
img = Image.open(path)
img = img.convert("RGBA")
outData = []
# Crop step
# Create a copy of the image with transparent pixels set to black, and non-transparent pixels set to white.
# This is used so that getbbox() can be used to crop out the transparent pixels.
newImg = img.copy()
for x in range(newImg.size[0]):
for y in range(newImg.size[1]):
if newImg.getpixel((x, y))[3] < 128:
newImg.putpixel((x, y), (0, 0, 0, 0))
else:
newImg.putpixel((x, y), (255, 255, 255, 255))
box = newImg.getbbox()
# Crop the image to the box.
img = img.crop(box)
# Add the height and width to the header.
outData.append(img.size[0])
outData.append(img.size[1])
# Add the offset (the first two elements of box) to the header.
outData.append(box[0])
outData.append(box[1])
# Iterate through the pixels.
for y in range(img.size[1]):
for x in range(img.size[0]):
# Get the colour data.
r, g, b, a = img.getpixel((x, y))
# print(r, g, b, a)
# Convert to RGB565.
rgb = ((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3);
if (rgb == 0x4fe0):
rgb = 0x4fe1
if a < 128:
rgb = 0x4fe0
outData.append(rgb)
return outData
def dataToCFile(data, name):
header = f"extern const unsigned short img_{name}[{len(data)}];"
out = f"const unsigned short img_{name}[{len(data)}] = {{"
for i in data:
out += f"0x{i:04x}, "
out = out[:-2] + "};"
return header, out
# def multiDataToHeader(dataArray, filename):
# # Check that the lengths of the data arrays are the same.
# for i in range(len(dataArray)):
# if len(dataArray[i]) != len(dataArray[0]):
# raise Exception("Images must have the same dimensions.")
# out = f"const unsigned short imgs_{filename}[{len(dataArray)}][{len(dataArray[0])}] = {{"
# for i in range(len(dataArray)):
# out += "{"
# for j in range(len(dataArray[i])):
# out += f"0x{dataArray[i][j]:04x}, "
# out = out[:-2] + "}, "
# out = out[:-2] + "};"
# return out
def multiDataToCFile(dataArray, filename):
result = ""
header = ""
for index, data in enumerate(dataArray):
head, data = dataToCFile(data, f"{filename.split('.')[0]}_{index}")
result += data + "\n"
header += head + "\n"
result += "const unsigned short* imgs_" + filename.split('.')[0] + "[" + str(len(dataArray)) + "] = {"
for index, data in enumerate(dataArray):
result += f"img_{filename.split('.')[0]}_{index}, "
result = result[:-2] + "};"
header += "extern const unsigned short* imgs_" + filename.split('.')[0] + "[" + str(len(dataArray)) + "];"
return header, result
all = ""
allHeader = ""
# Loop over the PNG files in ../assets/img/ (relative to the script)
os.chdir(os.path.dirname(os.path.realpath(__file__)))
for file in os.listdir("../assets/img/"):
if file.endswith(".png"):
header, result = dataToCFile(toData("../assets/img/" + file), file.split('.')[0])
print("Converted " + file)
all += result + "\n"
allHeader += header + "\n"
# If it's a directory, create an array containing the header for each image in the directory.
elif os.path.isdir("../assets/img/" + file):
dataArray = []
for n in range(9999):
# Check if n.png exists, and exit the loop if it doesn't.
subFile = f"../assets/img/{file}/{n}.png"
if not os.path.isfile(subFile):
break
print("Converting " + subFile)
result = toData(subFile)
dataArray.append(result)
header, result = multiDataToCFile(dataArray, file)
print("Converted " + file)
all += result + "\n"
allHeader += header + "\n"
else:
print("Skipping " + file)
# Write to a file.
with open("../data-headers/images.c", "w") as f:
f.write(all)
# Write the header file.
with open("../data-headers/images.h", "w") as f:
f.write(allHeader)

View File

@ -1,79 +0,0 @@
# Reads images, converts the colour data to RGB565, and writes it to a header file.
# 0x4fe0 is used as the transparent colour.
from PIL import Image
import sys
import os
def toData(path):
img = Image.open(path)
img = img.convert("RGBA")
outData = []
# Add the height and width to the header.
outData.append(img.size[0])
outData.append(img.size[1])
# Iterate through the pixels.
for y in range(img.size[1]):
for x in range(img.size[0]):
# Get the colour data.
r, g, b, a = img.getpixel((x, y))
# print(r, g, b, a)
# Convert to RGB565.
rgb = ((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3);
if (rgb == 0x4fe0):
rgb = 0x4fe1
if a < 128:
rgb = 0x4fe0
outData.append(rgb)
return outData
def dataToHeader(data, filename):
out = f"const unsigned short img_{filename.split('.')[0]}[{len(data)}] = {{"
for i in data:
out += f"0x{i:04x}, "
out = out[:-2] + "};"
return out
def multiDataToHeader(dataArray, filename):
# Check that the lengths of the data arrays are the same.
for i in range(len(dataArray)):
if len(dataArray[i]) != len(dataArray[0]):
raise Exception("Images must have the same dimensions.")
out = f"const unsigned short imgs_{filename}[{len(dataArray)}][{len(dataArray[0])}] = {{"
for i in range(len(dataArray)):
out += "{"
for j in range(len(dataArray[i])):
out += f"0x{dataArray[i][j]:04x}, "
out = out[:-2] + "}, "
out = out[:-2] + "};"
return out
all = ""
# Loop over the PNG files in ../assets/img/ (relative to the script)
os.chdir(os.path.dirname(os.path.realpath(__file__)))
for file in os.listdir("../assets/img/"):
if file.endswith(".png"):
result = dataToHeader(toData("../assets/img/" + file), file)
print("Converted " + file)
all += result + "\n"
# If it's a directory, create an array containing the header for each image in the directory.
elif os.path.isdir("../assets/img/" + file):
dataArray = []
for n in range(9999):
# Check if n.png exists, and exit the loop if it doesn't.
subFile = f"../assets/img/{file}/{n}.png"
if not os.path.isfile(subFile):
break
print("Converting " + subFile)
result = toData(subFile)
dataArray.append(result)
result = multiDataToHeader(dataArray, file)
print("Converted " + file)
all += result + "\n"
else:
print("Skipping " + file)
# Write the header to a file.
with open("../data-headers/images.h", "w") as f:
f.write(all)

68
scripts/sizeAnalyser.py Normal file
View File

@ -0,0 +1,68 @@
import os
import sys
import re
# Analyses the size of arrays in C files and lists them in order of size.
group = r"(?:\[([a-zA-Z0-9_ ]*)\])"
regex = r"^ *(?:const )?([a-zA-Z0-9_ ]*)" + group + ((group + "?") * 3) + r"\n? *\n?=\n? *\n?\{"
regex = re.compile(regex, re.MULTILINE)
sizes = {
"char": 1,
"short": 2,
"int": 4,
"long": 8,
"float": 4,
"double": 8
}
aliases = {
"angleWidth": 192,
"totalHeight": 108
}
includedDirectories = [
"./src/",
"./data-headers/"
]
os.chdir(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
# Get all the files in the included directories.
files = []
for directory in includedDirectories:
for file in os.listdir(directory):
if file.endswith(".c") or file.endswith(".h"):
files.append(os.path.join(directory, file))
# Read each file and get the size of each array.
arrays = []
for file in files:
with open(file, "r") as f:
content = f.read()
# The first group is the name and type of the array, the rest are the dimensions.
for match in regex.finditer(content):
nameAndType = match.group(1)
name = nameAndType.split(" ")[-1]
typeName = " ".join(nameAndType.split(" ")[:-1])
typeSize = sizes[typeName.split("unsigned ")[-1]]
dimensions = [match.group(i) for i in range(2, len(match.groups()) + 1)]
# Filter out the None values.
dimensions = [dimension for dimension in dimensions if dimension is not None]
# Resolve the aliases.
for alias in aliases:
if alias in dimensions:
dimensions[dimensions.index(alias)] = aliases[alias]
# Multiply the dimensions together.
size = typeSize
for dimension in dimensions:
size *= int(dimension)
arrays.append((name, size))
# Sort the arrays by size - biggest to smallest.
arrays.sort(key=lambda x: x[1], reverse=True)
# Print the arrays with human-readable sizes in KB.
for array in arrays:
print(f"{array[0]}: {array[1] / 1024:.2f} KB")

View File

@ -1,11 +1,20 @@
#include "./3d.h"
#include <fxcg/display.h>
#include "./main.h"
#include "./tilemap.h"
#include "./maths.h"
#include "../data-headers/generated_lut.h"
#define LCD_WIDTH_PX 384
#define LCD_HEIGHT_PX 216
inline void setPixel(int x, int y, color_t color) {
VRAM[y * LCD_WIDTH_PX + x] = color;
}
unsigned short getScreenPixel(unsigned short x, unsigned short y) {
// __builtin_expect(x == (int)(LUT_WIDTH / 2), 0);
// if (x < (int)(LUT_WIDTH / 2)) {
@ -27,4 +36,27 @@ unsigned short getScreenPixel(unsigned short x, unsigned short y) {
return samplePixel(xPos, yPos);
}
void new3D() {
int angleCos = fpcos(angle);
int angleSin = fpsin(angle);
for (unsigned short y = horizon + 2; y < LCD_HEIGHT_PX; y++) {
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;
// Rotate by angle
int newX = ((x2 * angleCos) >> 15) + ((y2 * angleSin) >> 15);
int newY = ((y2 * angleCos) >> 15) - ((x2 * angleSin) >> 15);
color_t col = samplePixel(newX >> 1, newY >> 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);
}
}
}

View File

@ -1,7 +1,7 @@
#ifndef _3D_H_
#define _3D_H_
unsigned char getTileType(short xPos, short yPos);
unsigned short getScreenPixel(unsigned short x, unsigned short y);
void new3D();
#endif // _3D_H_

View File

@ -2,8 +2,8 @@
#include <fxcg/keyboard.h>
buttonState buttons = {0};
buttonState lastButtons = {0};
ButtonState buttons = {0};
ButtonState lastButtons = {0};
// https://www.cemetech.net/forum/viewtopic.php?p=173836&sid=9eabb0dbeddeeb6507c19c8a65dbe249
#ifndef FXCG_MOCK

View File

@ -14,10 +14,10 @@ typedef struct {
bool accel;
bool hop;
bool debug;
} buttonState;
} ButtonState;
extern buttonState buttons;
extern buttonState lastButtons;
extern ButtonState buttons;
extern ButtonState lastButtons;
void scanButtons();

58
src/debugHud.c Normal file
View File

@ -0,0 +1,58 @@
#ifndef FXCG_MOCK
#include "./debugHud.h"
#include <fxcg/display.h>
#include <fxcg/rtc.h>
#include "./main.h"
#include "./buttons.h"
int lastTime = 0;
int debugType = 0;
int fpsCount = 0;
int currentFps = 0;
void handleDebugHud() {
if (buttons.debug && !lastButtons.debug) {
debugType++;
debugType = debugType % 3;
if (!debugType) {
// Put the sky back
fillSky(24, 34);
Bdisp_PutDisp_DD_stripe(24, 34);
}
}
int currentTime = RTC_GetTicks();
if (debugType == 1) {
int x = 8;
int y = 0;
char buffer[17] = "FPS: ";
itoa(currentFps, (unsigned char*)buffer + 5);
PrintMiniMini(&x, &y, buffer, 0, COLOR_BLACK, 0);
Bdisp_PutDisp_DD_stripe(24, 34);
} else if (debugType == 2) {
int x = 8;
int y = 0;
char buffer[17] = "Time: ";
itoa((totalFrameCount / 38), (unsigned char*)buffer + 6);
PrintMiniMini(&x, &y, buffer, 0, COLOR_BLACK, 0);
Bdisp_PutDisp_DD_stripe(24, 34);
}
// If 1 second has passed
if (currentTime - lastTime >= 128) {
lastTime = currentTime;
currentFps = fpsCount;
fpsCount = 0;
}
fpsCount++;
}
#endif // FXCG_MOCK

10
src/debugHud.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef _DEBUGHUD_H
#define _DEBUGHUD_H
#ifndef FXCG_MOCK
void handleDebugHud();
#endif // FXCG_MOCK
#endif // _DEBUGHUD_H

1
src/images.c Normal file
View File

@ -0,0 +1 @@
#include "../data-headers/images.c"

View File

@ -2,10 +2,10 @@
#include <fxcg/display.h>
#include <fxcg/keyboard.h>
#include <fxcg/system.h>
#ifndef FXCG_MOCK
#include <fxcg/misc.h>
#include <fxcg/rtc.h>
#include <fxcg/system.h>
#define debug_printf(...)
#else
@ -18,9 +18,12 @@
#endif
#include "./3d.h"
#include "./tilemap.h"
#include "./sprites.h"
#include "./physics.h"
#include "./buttons.h"
#include "./debugHud.h"
#include "./particles.h"
#include "../data-headers/images.h"
@ -104,6 +107,7 @@ void cameraBehind(short x, short y, short objectAngle, short distance) {
xOffset = x - cos(-objectAngle) * distance;
}
// TODO: max and mix
void fillSky(unsigned short yMin, unsigned short yMax) {
/* for (unsigned short x = 0; x < LCD_WIDTH_PX; x++) {
for (unsigned short y = yMin; y < yMax; y++) {
@ -161,12 +165,8 @@ float kartVel = 0;
float kartAngle = 90;
#define kartSpeed 2
int debugType = 0;
// For framerate counter
int lastTime;
int fpsCount = 0;
int totalFrameCount = 0;
int totalFrameCount = 0;
int jitter = 0;
int hopStage = 0;
@ -176,6 +176,7 @@ const int hopAnim[15] = {
4, 3, 2, 1, 1,
};
bool drifting = false;
int driftDir = 0;
Car kart;
@ -184,54 +185,9 @@ void main_loop() {
scanButtons();
#ifndef FXCG_MOCK
int currentTime = RTC_GetTicks();
// If 1 second has passed, print framerate
if (currentTime - lastTime >= 128) {
lastTime = currentTime;
if (debugType == 1) {
int x = 8;
int y = 0;
char buffer[17] = "FPS: ";
itoa(fpsCount, (unsigned char*)buffer + 5);
PrintMiniMini(&x, &y, buffer, 0, COLOR_BLACK, 0);
Bdisp_PutDisp_DD_stripe(24, 34);
} else if (debugType == 2) {
int x = 8;
int y = 0;
char buffer[17] = "Time: ";
itoa((totalFrameCount / 38), (unsigned char*)buffer + 6);
PrintMiniMini(&x, &y, buffer, 0, COLOR_BLACK, 0);
Bdisp_PutDisp_DD_stripe(24, 34);
}
fpsCount = 0;
}
handleDebugHud();
#endif
// Grass or sand = more friction
// unsigned char currentTile = getTileType(kartX / scale, kartY / scale);
// if (currentTile == 0 || currentTile == 7 || currentTile == 9 || currentTile == 12 || currentTile == 14 || currentTile == 15 || currentTile == 50 || currentTile == 52) {
// kartVel *= 0.8;
// } else {
// kartVel *= 0.9;
// }
// if (kartVel < 1.42) {
// kartVel = 0;
// }
// float oldKartX = kartX;
// float oldKartY = kartY;
// kartY += kartVel * sin(-kartAngle);
// kartX += kartVel * cos(-kartAngle);
// unsigned char newTile = getTileType(kartX / scale, kartY / scale);
// if (newTile >= 240 && newTile <= 243) { // Barrier
// kartX = oldKartX;
// kartY = oldKartY;
// }
unsigned char currentTile = getTileType(kartX / scale, kartY / scale);
if (currentTile == 0 || currentTile == 3 || currentTile == 4 || currentTile == 7 || currentTile == 8 ||
currentTile == 9 || currentTile == 10 || currentTile == 12 || currentTile == 13 || currentTile == 14 ||
@ -243,47 +199,73 @@ void main_loop() {
drag = 0.9;
}
// kartVel += kartSpeed;
// int key = PRGM_GetKey();
ControlState controls = {
up: buttons.accel,
down: 0,
left: buttons.right,
right: buttons.left
};
turnSpeed = drifting ? 0.003: 0.002;
updateWithControls(&kart, controls);
kartX = kart.x * 12;
kartY = kart.y * 12;
// Radians to degrees
kartAngle = -kart.angle * 180 / 3.1415926;
kartAngle += 90;
cameraBehind(kartX, kartY, kartAngle, 150); // TODO: calculate this rather than guessing
/* if (shiftPressed) {
kartVel += kartSpeed;
} */
// turnSpeed = drifting ? 0.003: 0.002;
if (!buttons.hop) {
drifting = false;
}
// #define maxSteerAnim 10
int maxSteerAnim = drifting ? 20 : 10;
if (buttons.left && !buttons.right/* && kartVel > 3 */) {
if (buttons.hop && !lastButtons.hop && hopStage == 0) {
hopStage = 1;
if (buttons.left || buttons.right) {
drifting = true;
driftDir = buttons.left ? -1 : 1;
}
}
if (hopStage != 0) {
hopStage++;
if (hopStage >= 15) {
hopStage = 0;
if (!drifting && (buttons.left || buttons.right)) {
drifting = true;
driftDir = buttons.left ? -1 : 1;
}
}
}
/* if (drifting) {
// debug_printf("driftDir: %d\n", driftDir);
if (driftDir == 1) {
buttons.right = true;
buttons.left = false;
} else {
buttons.left = true;
buttons.right = false;
}
} */
double oldKartY = kart.y;
double oldKartX = kart.x;
updateWithControls(&kart, buttons);
unsigned char newTile = getTileType(kart.x * 12 / scale, kart.y * 12 / scale);
if (newTile >= 240 && newTile <= 243) { // Barrier
kart.x = oldKartX;
kart.y = oldKartY;
}
kartX = kart.x * 12;
kartY = kart.y * 12;
// Radians to degrees
kartAngle = -kart.angle * 180 / 3.1415926;
kartAngle += 90;
// TODO: no / 2 when lower FOV?
cameraBehind(kartX, kartY, kartAngle, 150 / 2); // TODO: calculate this rather than guessing
// maxSteerAnim = drifting ? 20 : 10;
#define maxSteerAnim 10
/* if (buttons.left && !buttons.right) {
kartAngle -= kartVel / 10;
kartSteerAnim++;
if (kartSteerAnim > maxSteerAnim) {
kartSteerAnim = maxSteerAnim;
}
} else if (buttons.right && !buttons.left/* && kartVel > 3 */) {
} else if (buttons.right && !buttons.left) {
kartAngle += kartVel / 10;
kartSteerAnim--;
@ -302,7 +284,7 @@ void main_loop() {
kartSteerAnim = 0;
}
}
}
} */
// Control the distance
/* if (keydown(KEY_PRGM_UP)) {
@ -312,16 +294,6 @@ void main_loop() {
distance -= 2;
} */
if (buttons.debug && !lastButtons.debug) {
debugType++;
debugType = debugType % 3;
if (!debugType) {
// Put the sky back
fillSky(24, 34);
Bdisp_PutDisp_DD_stripe(24, 34);
}
}
#ifndef FXCG_MOCK
if (keydown(KEY_PRGM_MENU)) {
// Allow the OS to handle exiting to the menu
@ -341,9 +313,9 @@ void main_loop() {
kartAngle = fmod(kartAngle, 360);
angle = fmod(kartAngle + 45, 360) * angleWidth / 90;
angle = fmod(kartAngle + 90, 360) * angleWidth / 90;
for (unsigned short x = 0; x < LCD_WIDTH_PX / 2; x++) {
/* for (unsigned short x = 0; x < LCD_WIDTH_PX / 2; x++) {
index2 = x + angle;
index2 = mod(index2, (angleWidth * 4));
element = mod(index2, angleWidth);
@ -353,38 +325,58 @@ void main_loop() {
setPixel(x * 2, y, thing);
setPixel(x * 2 + 1, y, thing);
}
}
} */
new3D();
jitter++;
if (abs_double(kart.xVelocity) + abs_double(kart.yVelocity) < 0.02) {
jitter = 0;
} else if (abs_double(kart.xVelocity) + abs_double(kart.yVelocity) < 0.30) {
jitter = (totalFrameCount / 4) % 2;
jitter = (totalFrameCount / 4) % 4;
} else if (abs_double(kart.xVelocity) + abs_double(kart.yVelocity) < 0.50) {
jitter = (totalFrameCount / 3) % 2;
jitter = (totalFrameCount / 3) % 4;
} else if (abs_double(kart.xVelocity) + abs_double(kart.yVelocity) < 1) {
jitter = (totalFrameCount / 2) % 2;
jitter = (totalFrameCount / 2) % 4;
} else {
jitter = totalFrameCount % 2;
jitter = totalFrameCount % 4;
}
// debug_printf("totalFrameCount: %d\n", totalFrameCount);
if (buttons.hop && !lastButtons.hop && hopStage == 0) {
hopStage = 1;
if (buttons.left || buttons.right) {
drifting = true;
int targetAnim = 0;
if (buttons.left && !buttons.right) {
targetAnim = maxSteerAnim;
} else if (buttons.right && !buttons.left) {
targetAnim = -maxSteerAnim;
} else {
targetAnim = 0;
}
if (drifting) {
targetAnim += (driftDir == -1) ? 10 : -10;
if (driftDir == 1 && targetAnim > -7) {
targetAnim = -7;
}
if (driftDir == -1 && targetAnim < 7) {
targetAnim = 7;
}
// debug_printf("driftDir: %d, targetAnim: %d\n", driftDir, targetAnim);
if (totalFrameCount % 8 == 0) {
addParticle(0, 192 + -32 * driftDir, 180, -5 * driftDir, totalFrameCount % 16 == 0 ? -1 : 1);
}
}
if (hopStage != 0) {
hopStage++;
if (hopStage >= 15) {
hopStage = 0;
if (kartSteerAnim != targetAnim) {
if (kartSteerAnim > targetAnim) {
kartSteerAnim--;
} else {
kartSteerAnim++;
}
}
int animNo = abs_int(kartSteerAnim) / 2 + (jitter * 11);
int animNo = abs_int(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 (totalFrameCount % 2 == 0) {
@ -398,13 +390,21 @@ void main_loop() {
if (kartSteerAnim >= 0) {
// CopySpriteMasked(/*mksprites[kartSteerAnim / 2]*/sprite, (LCD_WIDTH_PX / 2) - 39, 128, 78, 81, 0x07e0);
// CopySpriteMasked(mksprites[kartSteerAnim / 4], (LCD_WIDTH_PX / 2) - 36, 128, 72, 80, 0x4fe0);
draw(imgs_kart[animNo], (LCD_WIDTH_PX / 2) - (96 / 2), 112 + jitter - (hopAnim[hopStage] * 3));
draw(imgs_kart[animNo], (LCD_WIDTH_PX / 2) - (96 / 2), 112 + (jitter % 2) - (hopAnim[hopStage] * 3));
if (newAnimNo != animNo) {
draw(imgs_kart[newAnimNo], (LCD_WIDTH_PX / 2) - (96 / 2), 112 + (jitter % 2) - (hopAnim[hopStage] * 3));
}
} else {
// CopySpriteMaskedFlipped(/*mksprites[-kartSteerAnim / 2]*/sprite, (LCD_WIDTH_PX / 2) - 39, 128, 78, 81, 0x07e0);
// CopySpriteMaskedFlipped(mksprites[-kartSteerAnim / 4], (LCD_WIDTH_PX / 2) - 36, 128, 72, 80, 0x4fe0);
draw_flipped(imgs_kart[animNo], (LCD_WIDTH_PX / 2) - (96 / 2), 112 + jitter - (hopAnim[hopStage] * 3));
draw_flipped(imgs_kart[animNo], (LCD_WIDTH_PX / 2) - (96 / 2), 112 + (jitter % 2) - (hopAnim[hopStage] * 3));
if (newAnimNo != animNo) {
draw_flipped(imgs_kart[newAnimNo], (LCD_WIDTH_PX / 2) - (96 / 2), 112 + (jitter % 2) - (hopAnim[hopStage] * 3));
}
}
tickParticles();
Bdisp_PutDisp_DD_stripe(horizon + 2, LCD_HEIGHT_PX);
// draw_loop_x(img_loop, 0, 0, angle, LCD_WIDTH_PX);
@ -413,7 +413,6 @@ void main_loop() {
// Bdisp_PutDisp_DD();
totalFrameCount += 1;
fpsCount++;
}
int main() {
@ -425,9 +424,6 @@ int main() {
Bdisp_EnableColor(1);
fillSky(0, LCD_HEIGHT_PX);
#ifndef FXCG_MOCK
lastTime = RTC_GetTicks();
#endif
kart.x = 3565 / 12;
kart.y = 2600 / 12;
@ -443,6 +439,8 @@ int main() {
kart.isTurningLeft = false;
kart.isTurningRight = false;
initParticles();
#ifdef FXCG_MOCK
#ifdef EMSCRIPTEN
emscripten_set_main_loop(main_loop, 60, 1);

View File

@ -3,10 +3,15 @@
#include <stdbool.h>
extern int totalFrameCount;
extern short angle;
extern short xOffset;
extern short yOffset;
extern bool drifting;
extern int driftDir;
extern short index2;
extern unsigned short element;
@ -18,4 +23,7 @@ int mod(int a, int b);
float sin(int angle);
float cos(int angle);
// TODO: Move into a separate file
void fillSky(unsigned short yMin, unsigned short yMax);
#endif // _MAIN_H

66
src/particles.c Normal file
View File

@ -0,0 +1,66 @@
#include "./particles.h"
#include "./sprites.h"
#include "../data-headers/images.h"
#define MAX_PARTICLES 32
ParticleType types[] = {
// Smoke
{
.maxAge = 12,
.animLength = 3,
.animDelay = 4,
.animFrames = imgs_smoke
}
};
ParticleState particles[MAX_PARTICLES];
void initParticles() {
for (int i = 0; i < MAX_PARTICLES; i++) {
particles[i].age = -1;
}
}
void tickParticles() {
for (int i = 0; i < MAX_PARTICLES; i++) {
ParticleState *particle = &particles[i];
// Age < 0 means the particle is not active
if (particle->age >= 0) {
if (particle->age > types[particle->type].maxAge) {
// Deactivate the particle
particle->age = -1;
continue;
}
particle->age++;
particle->x += particle->xVel;
particle->y += particle->yVel;
// Draw the particle
int frame = (particle->age / types[particle->type].animLength) % types[particle->type].animLength;
draw(types[particle->type].animFrames[frame], particle->x, particle->y);
}
}
}
void addParticle(int type, int x, int y, int xVel, int yVel) {
// Find the first free particle slot
int slot = -1;
for (int i = 0; i < MAX_PARTICLES; i++) {
if (particles[i].age < 0) {
slot = i;
break;
}
}
if (slot < 0) {
// No free particle slots
return;
}
ParticleState *particle = &particles[slot];
particle->type = type;
particle->x = x;
particle->y = y;
particle->xVel = xVel;
particle->yVel = yVel;
particle->age = 0;
}

26
src/particles.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef _PARTICLES_H_
#define _PARTICLES_H_
typedef struct {
short type;
short age;
short x;
short y;
char xVel;
char yVel;
} ParticleState;
typedef struct {
short maxAge;
short animLength;
char animDelay;
const unsigned short** animFrames;
} ParticleType;
void initParticles();
void tickParticles();
void addParticle(int type, int x, int y, int xVel, int yVel);
#endif // _PARTICLES_H_

View File

@ -1,11 +1,14 @@
#include "./physics.h"
#include "./main.h"
#include "./maths.h"
#include "./buttons.h"
#define angleWidth 192
// #define maxPower 0.075
#define maxPower 0.125
// #define maxPower 0.125
#define maxPower 0.1
#define maxReverse 0.0375
#define powerFactor 0.001
#define reverseFactor 0.0005
@ -35,6 +38,10 @@ double fmax(double a, double b) {
return a > b ? a : b;
}
double dmod(double a, double b) {
return a - (int)(a / b) * b;
}
void updateCar (Car *car) {
if (car->isThrottling) {
car->power += powerFactor * car->isThrottling;
@ -52,12 +59,16 @@ void updateCar (Car *car) {
double direction = car->power > car->reverse ? 1 : -1;
if (car->isTurningLeft) {
car->angularVelocity -= direction * turnSpeed * car->isTurningLeft;
}
if (car->isTurningRight) {
car->angularVelocity += direction * turnSpeed * car->isTurningRight;
double change = car->isTurningLeft ? -1 : car->isTurningRight ? 1 : 0;
if (drifting) {
if (driftDir == -1) {
change += 0.7;
} else {
change -= 0.7;
}
}
change *= direction * turnSpeed;
car->angularVelocity += change;
car->xVelocity += sin2(car->angle) * (car->power - car->reverse);
car->yVelocity += cos2(car->angle) * (car->power - car->reverse);
@ -67,6 +78,7 @@ void updateCar (Car *car) {
car->xVelocity *= drag;
car->yVelocity *= drag;
car->angle += car->angularVelocity;
car->angle = dmod(car->angle, 3.1415926 * 2);
car->angularVelocity *= angularDrag;
}
@ -86,21 +98,23 @@ void updateCar (Car *car) {
// isTurningRight: false,
// };
void updateWithControls(Car *car, ControlState controls) {
void updateWithControls(Car *car, ButtonState controls) {
bool changed;
bool canTurn = car->power > 0.0025 || car->reverse;
double throttle = controls.up ? 1 : 0;
double reverse = controls.down ? 1 : 0;
double throttle = controls.accel ? 1 : 0;
// double reverse = controls.down ? 1 : 0;
double reverse = 0;
if (car->isThrottling != throttle || car->isReversing != reverse) {
changed = true;
car->isThrottling = throttle;
car->isReversing = reverse;
}
bool turnLeft = canTurn && controls.left;
bool turnRight = canTurn && controls.right;
// Controls are reversed for now
bool turnLeft = canTurn && /*controls.left*/ controls.right;
bool turnRight = canTurn && /*controls.right*/ controls.left;
if (car->isTurningLeft != turnLeft) {
changed = true;

View File

@ -3,12 +3,7 @@
#include <stdbool.h>
typedef struct {
bool up;
bool left;
bool right;
bool down;
} ControlState;
#include "./buttons.h"
typedef struct {
double x;
@ -29,6 +24,6 @@ typedef struct {
extern double drag;
extern double turnSpeed;
void updateWithControls(Car *car, ControlState controls);
void updateWithControls(Car *car, ButtonState controls);
#endif // _PHYSICS_H

View File

@ -83,38 +83,50 @@ void draw(const unsigned short* data, int x, int y) {
// The height and width of the sprite are the first two elements in the data array
int width = data[0];
int height = data[1];
// The data array starts at index 2
const unsigned short* data2 = data + 2;
// The offsets of the x and y positions are the third and fourth elements in the data array
int xOffset = data[2];
int yOffset = data[3];
// The data array starts at index 4
const unsigned short* data2 = data + 4;
// Now draw the sprite
CopySpriteMasked(data2, x, y, width, height, 0x4fe0);
CopySpriteMasked(data2, x + xOffset, y + yOffset, width, height, 0x4fe0);
}
void draw_alpha(const unsigned short* data, int x, int y, int alpha) {
// The height and width of the sprite are the first two elements in the data array
int width = data[0];
int height = data[1];
// The data array starts at index 2
const unsigned short* data2 = data + 2;
// The offsets of the x and y positions are the third and fourth elements in the data array
int xOffset = data[2];
int yOffset = data[3];
// The data array starts at index 4
const unsigned short* data2 = data + 4;
// Now draw the sprite
CopySpriteMaskedAlpha(data2, x, y, width, height, 0x4fe0, alpha);
CopySpriteMaskedAlpha(data2, x + xOffset, y + yOffset, width, height, 0x4fe0, alpha);
}
void draw_flipped(const unsigned short* data, int x, int y) {
// The height and width of the sprite are the first two elements in the data array
int width = data[0];
int height = data[1];
// The data array starts at index 2
const unsigned short* data2 = data + 2;
// The offsets of the x and y positions are the third and fourth elements in the data array
int xOffset = data[2];
int yOffset = data[3];
// The data array starts at index 4
const unsigned short* data2 = data + 4;
// Now draw the sprite
CopySpriteMaskedFlipped(data2, x, y, width, height, 0x4fe0);
CopySpriteMaskedFlipped(data2, x + xOffset, y + yOffset, width, height, 0x4fe0);
}
void draw_loop_x(const unsigned short* data, int x, int y, int xOffset, int drawWidth) {
// The height and width of the sprite are the first two elements in the data array
int width = data[0];
int height = data[1];
// The data array starts at index 2
const unsigned short* data2 = data + 2;
// The offsets of the x and y positions are the third and fourth elements in the data array
int xOffset2 = data[2];
int yOffset = data[3];
// The data array starts at index 4
const unsigned short* data2 = data + 4;
// Now draw the sprite
CopySpriteLoopX(data2, x, y, width, height, xOffset, drawWidth, 0x4fe0);
CopySpriteLoopX(data2, x + xOffset2, y + yOffset, width, height, xOffset, drawWidth, 0x4fe0);
}

View File

@ -8,7 +8,7 @@
unsigned char getTileType(short xPos, short yPos) {
// __builtin_expect(xPos < 0 || xPos >= trackImageWidth || yPos < 0 || yPos >= trackImageHeight, 0);
if (xPos < 0 || xPos >= trackImageWidth || yPos < 0 || yPos >= trackImageHeight) {
if((unsigned short) xPos >= trackImageWidth || (unsigned short) yPos >= trackImageHeight) {
return 0; // Grass
} else {
// Divide by 8