2018-09-08 14:30:03 +02:00
|
|
|
/**
|
2018-09-11 09:19:09 +02:00
|
|
|
General definitions common for Pokitto raycasting demos.
|
|
|
|
|
|
|
|
The demos use mode 13: 1 byte per pixel = 256 colors. Bitmaps (textures,
|
|
|
|
sprites, ...) are also in this format (use the provided python script to
|
|
|
|
convert png images).
|
2018-09-08 14:30:03 +02:00
|
|
|
|
|
|
|
author: Miloslav "drummyfish" Ciz
|
|
|
|
license: CC0 1.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef RAYCAST_DEMO_GENERAL_HPP
|
|
|
|
#define RAYCAST_DEMO_GENERAL_HPP
|
|
|
|
|
2018-10-23 08:01:49 +02:00
|
|
|
//#include "stdio.h" // for debugging raycastlibg
|
2018-09-08 14:30:03 +02:00
|
|
|
|
2018-10-02 15:18:05 +02:00
|
|
|
#define RCL_VERTICAL_FOV RCL_UNITS_PER_SQUARE /* Redefine camera vertical FOV:
|
2018-09-17 17:55:33 +02:00
|
|
|
RCL_UNITS_PER_SQUARE would normally mean
|
|
|
|
360 degrees, but it's not an actual
|
|
|
|
angle, just linear approximation, so
|
2018-10-02 15:18:05 +02:00
|
|
|
this is okay. */
|
2018-09-17 17:55:33 +02:00
|
|
|
#define RCL_PIXEL_FUNCTION pixelFunc
|
2018-09-17 07:14:35 +02:00
|
|
|
/* ^ This has to be defined to the name of the function that will render
|
|
|
|
pixels. */
|
|
|
|
|
2018-09-08 14:30:03 +02:00
|
|
|
#include "raycastlib.h"
|
|
|
|
#include "Pokitto.h"
|
|
|
|
|
|
|
|
Pokitto::Core pokitto;
|
|
|
|
|
2018-09-08 14:55:14 +02:00
|
|
|
#ifndef FPS
|
2018-10-02 15:18:05 +02:00
|
|
|
#define FPS 255
|
2018-09-08 14:55:14 +02:00
|
|
|
#endif
|
|
|
|
|
2018-09-08 15:53:11 +02:00
|
|
|
#ifndef PLAYER_SPEED
|
2018-09-17 17:55:33 +02:00
|
|
|
#define PLAYER_SPEED (4 * RCL_UNITS_PER_SQUARE)
|
2018-09-08 15:53:11 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef PLAYER_ROTATION_SPEED
|
2018-09-17 17:55:33 +02:00
|
|
|
#define PLAYER_ROTATION_SPEED (RCL_UNITS_PER_SQUARE / 2)
|
2018-09-08 15:53:11 +02:00
|
|
|
#endif
|
|
|
|
|
2018-09-10 18:23:53 +02:00
|
|
|
#ifndef PLAYER_JUMP_SPEED
|
|
|
|
#define PLAYER_JUMP_SPEED 500
|
|
|
|
#endif
|
|
|
|
|
2018-09-10 19:28:36 +02:00
|
|
|
#ifndef HEAD_BOB_HEIGHT
|
|
|
|
#define HEAD_BOB_HEIGHT 100
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef HEAD_BOB_STEP
|
|
|
|
#define HEAD_BOB_STEP 10
|
|
|
|
#endif
|
|
|
|
|
2018-09-08 15:53:11 +02:00
|
|
|
#ifndef GRAVITY_ACCELERATION
|
2018-09-17 17:55:33 +02:00
|
|
|
#define GRAVITY_ACCELERATION ((3 * RCL_UNITS_PER_SQUARE) / 2)
|
2018-09-08 15:53:11 +02:00
|
|
|
#endif
|
|
|
|
|
2018-09-08 14:30:03 +02:00
|
|
|
#define SCREEN_WIDTH 110
|
|
|
|
#define SCREEN_HEIGHT 88
|
|
|
|
|
|
|
|
#define MIDDLE_ROW (SCREEN_HEIGHT / 2)
|
|
|
|
#define MIDDLE_COLUMN (SCREEN_WIDTH / 2)
|
|
|
|
|
|
|
|
#ifndef SUBSAMPLE
|
|
|
|
#define SUBSAMPLE 2
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define SUBSAMPLED_WIDTH (SCREEN_WIDTH / SUBSAMPLE)
|
|
|
|
|
2018-09-29 08:45:58 +02:00
|
|
|
#define TRANSPARENT_COLOR 0x8f ///< Transparent color for sprites and GUI.
|
2018-09-08 14:30:03 +02:00
|
|
|
|
2018-10-02 15:18:05 +02:00
|
|
|
#define HUE(c) (c * 16 + 8) ///< Gives a middle color of given hue (0 to 15).
|
2018-09-28 20:52:27 +02:00
|
|
|
|
2018-09-17 17:55:33 +02:00
|
|
|
RCL_Unit zBuffer[SUBSAMPLED_WIDTH]; ///< 1D z-buffer for visibility determination.
|
2018-09-08 14:30:03 +02:00
|
|
|
|
2018-09-18 15:28:43 +02:00
|
|
|
RCL_RayConstraints defaultConstraints;
|
|
|
|
|
2018-09-08 14:55:14 +02:00
|
|
|
unsigned short palette[256];
|
|
|
|
|
2018-09-12 07:55:04 +02:00
|
|
|
#ifdef POK_SIM
|
2018-09-18 18:09:19 +02:00
|
|
|
inline void putSubsampledPixel(int32_t x, int32_t y, uint8_t color)
|
2018-09-18 15:28:43 +02:00
|
|
|
{
|
2018-09-18 18:09:19 +02:00
|
|
|
pokitto.display.drawPixel(x * SUBSAMPLE,y,color);
|
|
|
|
pokitto.display.drawPixel(x * SUBSAMPLE + 1,y,color);
|
2018-09-18 15:28:43 +02:00
|
|
|
}
|
2018-09-12 07:55:04 +02:00
|
|
|
#else
|
2018-09-18 15:28:43 +02:00
|
|
|
// This code breaks the simulator.
|
2018-09-18 18:09:19 +02:00
|
|
|
inline void putSubsampledPixel(int32_t x, int32_t y, uint8_t color)
|
2018-09-18 15:28:43 +02:00
|
|
|
{
|
|
|
|
uint8_t *buf = pokitto.display.screenbuffer;
|
|
|
|
|
2018-09-18 18:09:19 +02:00
|
|
|
buf += x * SUBSAMPLE;
|
|
|
|
buf += y * SCREEN_WIDTH;
|
2018-09-18 15:28:43 +02:00
|
|
|
|
|
|
|
for (uint8_t i = 0; i < SUBSAMPLE - 1; ++i)
|
|
|
|
*buf++ = color;
|
|
|
|
|
|
|
|
*buf = color;
|
|
|
|
}
|
2018-09-12 07:55:04 +02:00
|
|
|
#endif
|
|
|
|
|
2018-09-27 10:30:52 +02:00
|
|
|
uint8_t encodeHSV(uint8_t hue, uint8_t saturation, uint8_t value)
|
|
|
|
{
|
|
|
|
if (value > 15)
|
|
|
|
{
|
|
|
|
if (saturation > 84)
|
|
|
|
return ((saturation / 85 - 1) << 7) | ((hue / 32) << 4) | ((value - 16) / 15);
|
|
|
|
// "normal" color, as 0bSHHHVVVV, VVVV != 0
|
|
|
|
else
|
|
|
|
return value / 16;
|
|
|
|
// saturation near 0 => gray, as 0bVVVV0000, VVVV != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
// value near 0 => black, as 0b00000000
|
|
|
|
}
|
|
|
|
|
|
|
|
void decodeHSV(uint8_t hsv, uint8_t *hue, uint8_t *saturation, uint8_t *value)
|
|
|
|
{
|
|
|
|
uint8_t topHalf = hsv & 0b11110000;
|
|
|
|
uint8_t bottomHalf = hsv & 0b00001111;
|
|
|
|
|
|
|
|
if (topHalf != 0)
|
|
|
|
{
|
|
|
|
// "normal" color
|
|
|
|
|
|
|
|
*value = bottomHalf != 15 ? (bottomHalf + 1) * 16 : 255;
|
|
|
|
*saturation = (1 + ((hsv & 0b10000000) >> 7)) * 127;
|
|
|
|
*hue = ((hsv & 0b01110000) >> 4) * 32;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// gray/white/black
|
|
|
|
*hue = 0;
|
|
|
|
*saturation = 0;
|
|
|
|
*value = bottomHalf * 17;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void convertHSVtoRGB(uint8_t hue, uint8_t saturation, uint8_t value,
|
|
|
|
uint8_t *red, uint8_t *green, uint8_t *blue)
|
|
|
|
{
|
2018-09-27 12:55:03 +02:00
|
|
|
#define M 16
|
|
|
|
// ^ adds precision
|
2018-09-27 10:30:52 +02:00
|
|
|
|
|
|
|
int32_t chroma = (value * saturation) / 256;
|
|
|
|
|
2018-09-27 12:55:03 +02:00
|
|
|
int32_t h = (hue * M) / 42;
|
2018-09-27 10:30:52 +02:00
|
|
|
|
2018-09-27 12:55:03 +02:00
|
|
|
int32_t a = (h % (2 * M)) - M;
|
2018-09-27 10:30:52 +02:00
|
|
|
a = a < 0 ? -a : a; // abs
|
|
|
|
|
2018-09-27 12:55:03 +02:00
|
|
|
int32_t x = (chroma * (M - a)) / M;
|
2018-09-27 10:30:52 +02:00
|
|
|
|
2018-09-27 12:55:03 +02:00
|
|
|
if (h <= 1 * M)
|
2018-09-27 10:30:52 +02:00
|
|
|
{ *red = chroma; *green = x; *blue = 0; }
|
2018-09-27 12:55:03 +02:00
|
|
|
else if (h <= 2 * M)
|
2018-09-27 10:30:52 +02:00
|
|
|
{ *red = x; *green = chroma; *blue = 0; }
|
2018-09-27 12:55:03 +02:00
|
|
|
else if (h <= 3 * M)
|
2018-09-27 10:30:52 +02:00
|
|
|
{ *red = 0; *green = chroma; *blue = x; }
|
2018-09-27 12:55:03 +02:00
|
|
|
else if (h <= 4 * M)
|
2018-09-27 10:30:52 +02:00
|
|
|
{ *red = 0; *green = x; *blue = chroma; }
|
2018-09-27 12:55:03 +02:00
|
|
|
else if (h <= 5 * M)
|
2018-09-27 10:30:52 +02:00
|
|
|
{ *red = x; *green = 0; *blue = chroma; }
|
2018-09-27 12:55:03 +02:00
|
|
|
else if (h <= 6 * M)
|
2018-09-27 10:30:52 +02:00
|
|
|
{ *red = chroma; *green = 0; *blue = x; }
|
|
|
|
else
|
|
|
|
{ *red = 0; *green = 0; *blue = 0; }
|
2018-09-27 12:55:03 +02:00
|
|
|
|
|
|
|
#undef M
|
2018-09-27 10:30:52 +02:00
|
|
|
|
|
|
|
int32_t m = value - chroma;
|
|
|
|
|
|
|
|
*red += m;
|
|
|
|
*green += m;
|
|
|
|
*blue += m;
|
|
|
|
}
|
|
|
|
|
2018-09-08 14:55:14 +02:00
|
|
|
/**
|
|
|
|
Inits and loads a general 256 color palette.
|
|
|
|
*/
|
|
|
|
void initPalette()
|
|
|
|
{
|
2018-09-29 08:45:58 +02:00
|
|
|
/* the palette is HSV-based because it makes brightness addition fast, which
|
2018-09-27 12:55:03 +02:00
|
|
|
is important for fog/shadow diminishing */
|
2018-09-27 10:30:52 +02:00
|
|
|
|
2018-09-27 12:55:03 +02:00
|
|
|
for (uint16_t i = 0; i < 256; ++i)
|
|
|
|
{
|
|
|
|
uint8_t h,s,v,r,g,b;
|
2018-09-27 10:30:52 +02:00
|
|
|
|
2018-09-27 12:55:03 +02:00
|
|
|
decodeHSV(i,&h,&s,&v);
|
|
|
|
convertHSVtoRGB(h,s,v,&r,&g,&b);
|
|
|
|
palette[i] =
|
|
|
|
pokitto.display.RGBto565(r,g,b);
|
|
|
|
}
|
2018-09-08 14:55:14 +02:00
|
|
|
|
|
|
|
pokitto.display.load565Palette(palette);
|
|
|
|
}
|
|
|
|
|
2018-09-08 14:30:03 +02:00
|
|
|
/**
|
|
|
|
Adds given intensity to a color.
|
|
|
|
*/
|
2019-07-13 00:22:38 +02:00
|
|
|
inline uint8_t addIntensity(uint8_t color, int8_t add)
|
2018-09-08 14:30:03 +02:00
|
|
|
{
|
2019-07-13 00:22:38 +02:00
|
|
|
uint8_t newValue = color + add;
|
|
|
|
|
|
|
|
if ((newValue >> 4) == (color >> 4))
|
|
|
|
return newValue;
|
|
|
|
else
|
|
|
|
return add > 0 ? (color | 0x0f) : 0;
|
|
|
|
}
|
2018-09-27 10:30:52 +02:00
|
|
|
|
2019-07-13 00:22:38 +02:00
|
|
|
static inline uint8_t plusIntensity(uint8_t color, uint8_t plus)
|
|
|
|
{
|
|
|
|
uint8_t newValue = color + plus;
|
|
|
|
return ((newValue >> 4) == (color >> 4)) ? newValue : (color | 0x0f);
|
|
|
|
}
|
2018-09-27 10:30:52 +02:00
|
|
|
|
2019-07-13 00:22:38 +02:00
|
|
|
static inline uint8_t minusIntensity(uint8_t color, uint8_t minus)
|
|
|
|
{
|
|
|
|
uint8_t newValue = color - minus;
|
|
|
|
return ((newValue >> 4) == (color >> 4)) ? newValue : 0;
|
2018-09-25 10:04:16 +02:00
|
|
|
}
|
|
|
|
|
2018-09-08 14:30:03 +02:00
|
|
|
/**
|
|
|
|
Samples an image by normalized coordinates - each coordinate is in range
|
2018-09-17 17:55:33 +02:00
|
|
|
0 to RCL_UNITS_PER_SQUARE (from raycastlib).
|
2018-09-08 14:30:03 +02:00
|
|
|
*/
|
2018-09-17 17:55:33 +02:00
|
|
|
inline uint8_t sampleImage(const unsigned char *image, RCL_Unit x, RCL_Unit y)
|
2018-09-08 14:30:03 +02:00
|
|
|
{
|
2018-09-17 17:55:33 +02:00
|
|
|
x = RCL_wrap(x,RCL_UNITS_PER_SQUARE);
|
|
|
|
y = RCL_wrap(y,RCL_UNITS_PER_SQUARE);
|
2018-09-08 14:30:03 +02:00
|
|
|
|
|
|
|
int32_t index =
|
2018-09-18 12:54:19 +02:00
|
|
|
(x / (RCL_UNITS_PER_SQUARE / TEXTURE_W)) * TEXTURE_H +
|
|
|
|
(y / (RCL_UNITS_PER_SQUARE / TEXTURE_W));
|
2018-09-08 14:30:03 +02:00
|
|
|
|
|
|
|
return image[2 + index];
|
|
|
|
}
|
|
|
|
|
2018-09-12 12:01:16 +02:00
|
|
|
/**
|
|
|
|
Draws a scaled sprite on screen in an optimized way. The sprite has to be
|
2018-10-02 15:18:05 +02:00
|
|
|
square in resolution for that.
|
2018-09-12 12:01:16 +02:00
|
|
|
*/
|
2019-07-13 00:22:38 +02:00
|
|
|
static void inline drawSpriteSquare(const unsigned char *sprite, int16_t x, int16_t y, RCL_Unit depth, int16_t size, int16_t intensity)
|
2018-09-08 14:30:03 +02:00
|
|
|
{
|
2018-09-12 12:01:16 +02:00
|
|
|
if (size < 0 || size > 200 || // let's not mess up with the incoming array
|
|
|
|
sprite[0] != sprite[1]) // only draw square sprites
|
2018-09-11 10:33:47 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
int16_t samplingIndices[size];
|
|
|
|
|
|
|
|
// optimization: precompute the indices
|
|
|
|
|
2018-09-17 17:55:33 +02:00
|
|
|
for (RCL_Unit i = 0; i < size; ++i)
|
2018-09-12 12:01:16 +02:00
|
|
|
samplingIndices[i] = (i * sprite[0]) / size;
|
2018-09-08 14:30:03 +02:00
|
|
|
|
|
|
|
x -= size / 2;
|
|
|
|
y -= size / 2;
|
|
|
|
|
|
|
|
uint8_t c;
|
|
|
|
|
|
|
|
int16_t jTo = size - max(0,y + size - 88);
|
|
|
|
int16_t iTo = size - max(0,x + size - 110);
|
|
|
|
|
2018-09-17 17:55:33 +02:00
|
|
|
for (RCL_Unit i = max(-1 * x,0); i < iTo; ++i)
|
2018-09-08 14:30:03 +02:00
|
|
|
{
|
|
|
|
int16_t xPos = x + i;
|
|
|
|
|
|
|
|
if (zBuffer[xPos / SUBSAMPLE] <= depth)
|
|
|
|
continue;
|
|
|
|
|
2018-09-12 12:01:16 +02:00
|
|
|
int16_t columnLocation = 2 + samplingIndices[i] * sprite[0];
|
|
|
|
|
2018-09-17 17:55:33 +02:00
|
|
|
for (RCL_Unit j = max(-1 * y,0); j < jTo; ++j)
|
2018-09-08 14:30:03 +02:00
|
|
|
{
|
2018-09-12 12:01:16 +02:00
|
|
|
c = sprite[columnLocation + samplingIndices[j]];
|
2018-09-08 14:30:03 +02:00
|
|
|
|
|
|
|
if (c != TRANSPARENT_COLOR)
|
2018-09-27 14:52:31 +02:00
|
|
|
pokitto.display.drawPixel(xPos,y + j,addIntensity(c,intensity));
|
2018-09-08 14:30:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-11 09:19:09 +02:00
|
|
|
/// Faster than drawSprite.
|
2018-09-10 10:03:47 +02:00
|
|
|
void drawImage(const unsigned char *image, int16_t x, int16_t y)
|
|
|
|
{
|
|
|
|
for (int16_t i = 0; i < image[0]; ++i)
|
|
|
|
{
|
|
|
|
int16_t xPos = x + i;
|
|
|
|
int16_t column = 2 + i * image[1];
|
|
|
|
|
|
|
|
for (int16_t j = 0; j < image[1]; ++j)
|
|
|
|
{
|
|
|
|
char c = image[column + j];
|
|
|
|
|
|
|
|
if (c != TRANSPARENT_COLOR)
|
|
|
|
pokitto.display.drawPixel(xPos,y + j,image[column + j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-02 15:18:05 +02:00
|
|
|
/**
|
|
|
|
General player class stuffed with everything the demos need. You'll probably
|
|
|
|
want to write your own.
|
|
|
|
*/
|
2018-09-08 14:55:14 +02:00
|
|
|
class Player
|
|
|
|
{
|
|
|
|
public:
|
2018-09-17 17:55:33 +02:00
|
|
|
RCL_Camera mCamera;
|
|
|
|
RCL_Unit mVericalSpeed;
|
2018-09-30 14:48:39 +02:00
|
|
|
RCL_Unit mVericalSpeedPrev; /* In order to detect whether player is standing
|
|
|
|
on ground (for jumping) we need the derivative
|
|
|
|
of vertical speed (not just the vertical
|
|
|
|
speed) => we need two values. */
|
2018-09-09 16:39:08 +02:00
|
|
|
bool mRunning;
|
2018-09-17 17:55:33 +02:00
|
|
|
RCL_Unit mHeadBob;
|
2018-09-10 19:28:36 +02:00
|
|
|
bool mHeadBobUp;
|
2018-09-08 14:55:14 +02:00
|
|
|
|
|
|
|
Player()
|
|
|
|
{
|
2018-09-17 17:55:33 +02:00
|
|
|
RCL_initCamera(&mCamera);
|
2018-09-15 17:06:07 +02:00
|
|
|
|
2018-09-08 14:55:14 +02:00
|
|
|
mCamera.position.x = 0;
|
|
|
|
mCamera.position.y = 0;
|
|
|
|
mCamera.direction = 0;
|
2018-09-17 17:55:33 +02:00
|
|
|
mCamera.height = RCL_UNITS_PER_SQUARE * 3;
|
2018-09-08 14:55:14 +02:00
|
|
|
mCamera.resolution.x = SCREEN_WIDTH / SUBSAMPLE;
|
|
|
|
mCamera.resolution.y = SCREEN_HEIGHT;
|
|
|
|
mCamera.shear = 0;
|
|
|
|
mVericalSpeed = 0;
|
2018-09-30 14:48:39 +02:00
|
|
|
mVericalSpeedPrev = 0;
|
2018-09-09 16:39:08 +02:00
|
|
|
mRunning = false;
|
2018-09-10 19:28:36 +02:00
|
|
|
mHeadBob = 0;
|
|
|
|
mHeadBobUp = true;
|
2018-09-08 14:55:14 +02:00
|
|
|
}
|
|
|
|
|
2018-09-17 17:55:33 +02:00
|
|
|
void setPosition(RCL_Unit x, RCL_Unit y)
|
2018-09-08 14:55:14 +02:00
|
|
|
{
|
|
|
|
mCamera.position.x = x;
|
|
|
|
mCamera.position.y = y;
|
|
|
|
}
|
|
|
|
|
2018-09-17 17:55:33 +02:00
|
|
|
void setPosition(RCL_Unit x, RCL_Unit y, RCL_Unit z, RCL_Unit direction)
|
2018-09-12 07:55:04 +02:00
|
|
|
{
|
|
|
|
mCamera.position.x = x;
|
|
|
|
mCamera.position.y = y;
|
|
|
|
mCamera.height = z;
|
|
|
|
mCamera.direction = direction;
|
|
|
|
}
|
|
|
|
|
2018-09-08 14:55:14 +02:00
|
|
|
void setPositionSquare(int16_t squareX, int16_t squareY)
|
|
|
|
{
|
|
|
|
setPosition(
|
2018-09-17 17:55:33 +02:00
|
|
|
squareX * RCL_UNITS_PER_SQUARE + RCL_UNITS_PER_SQUARE / 2,
|
|
|
|
squareY * RCL_UNITS_PER_SQUARE + RCL_UNITS_PER_SQUARE / 2);
|
2018-09-08 14:55:14 +02:00
|
|
|
}
|
2018-09-10 18:23:53 +02:00
|
|
|
|
|
|
|
void update(int16_t moveDirection, bool strafe, int16_t turnDirection, bool jump,
|
2018-09-17 17:55:33 +02:00
|
|
|
int16_t shearDirection, RCL_ArrayFunction floorHeightFunction,
|
|
|
|
RCL_ArrayFunction ceilingHeightFunction, bool computeHeight, uint32_t dt)
|
2018-09-10 18:23:53 +02:00
|
|
|
{
|
2018-09-17 17:55:33 +02:00
|
|
|
RCL_Vector2D moveOffset;
|
2018-09-10 18:23:53 +02:00
|
|
|
|
|
|
|
moveOffset.x = 0;
|
|
|
|
moveOffset.y = 0;
|
|
|
|
|
|
|
|
if (moveDirection != 0)
|
|
|
|
{
|
|
|
|
int16_t horizontalStep = (dt * PLAYER_SPEED * (mRunning ? 2 : 1)) / 1000 *
|
|
|
|
(moveDirection > 0 ? 1 : -1);
|
|
|
|
|
2018-09-17 17:55:33 +02:00
|
|
|
moveOffset = RCL_angleToDirection(mCamera.direction + (strafe ? RCL_UNITS_PER_SQUARE / 4 : 0));
|
2018-09-10 18:23:53 +02:00
|
|
|
|
2018-09-17 17:55:33 +02:00
|
|
|
moveOffset.x = (moveOffset.x * horizontalStep) / RCL_UNITS_PER_SQUARE;
|
|
|
|
moveOffset.y = (moveOffset.y * horizontalStep) / RCL_UNITS_PER_SQUARE;
|
2018-09-10 19:28:36 +02:00
|
|
|
|
|
|
|
mHeadBob += mHeadBobUp ? HEAD_BOB_STEP : -HEAD_BOB_STEP;
|
|
|
|
|
|
|
|
if (mHeadBob > HEAD_BOB_HEIGHT)
|
|
|
|
mHeadBobUp = false;
|
|
|
|
else if (mHeadBob < -HEAD_BOB_HEIGHT)
|
|
|
|
mHeadBobUp = true;
|
2018-09-10 18:23:53 +02:00
|
|
|
}
|
2018-09-10 19:28:36 +02:00
|
|
|
else
|
|
|
|
mHeadBob /= 2;
|
2018-09-10 18:23:53 +02:00
|
|
|
|
|
|
|
if (turnDirection != 0)
|
|
|
|
{
|
|
|
|
int16_t rotationStep = (dt * PLAYER_ROTATION_SPEED) / 1000;
|
2018-09-17 17:55:33 +02:00
|
|
|
mCamera.direction = RCL_wrap(mCamera.direction + turnDirection * rotationStep,RCL_UNITS_PER_SQUARE);
|
2018-09-10 18:23:53 +02:00
|
|
|
}
|
|
|
|
|
2018-09-17 17:55:33 +02:00
|
|
|
RCL_Unit prevHeight = mCamera.height;
|
2018-09-10 18:23:53 +02:00
|
|
|
|
2018-09-17 17:55:33 +02:00
|
|
|
RCL_moveCameraWithCollision(&mCamera,moveOffset,mVericalSpeed,
|
2018-09-12 07:55:04 +02:00
|
|
|
floorHeightFunction, ceilingHeightFunction, computeHeight ? 1 : 0, 0);
|
2018-09-10 18:23:53 +02:00
|
|
|
|
2018-09-17 17:55:33 +02:00
|
|
|
RCL_Unit heightDiff = mCamera.height - prevHeight;
|
2018-09-10 18:23:53 +02:00
|
|
|
|
|
|
|
if (heightDiff == 0)
|
|
|
|
mVericalSpeed = 0; // hit floor/ceiling
|
|
|
|
|
2018-09-30 14:48:39 +02:00
|
|
|
if (jump && mVericalSpeed == 0 && mVericalSpeedPrev == 0)
|
|
|
|
mVericalSpeed = PLAYER_JUMP_SPEED; // jump
|
2018-09-10 18:23:53 +02:00
|
|
|
|
|
|
|
if (shearDirection != 0)
|
2018-09-17 17:55:33 +02:00
|
|
|
mCamera.shear = RCL_clamp(mCamera.shear + shearDirection * 10,
|
2018-09-10 18:23:53 +02:00
|
|
|
-1 * mCamera.resolution.y, mCamera.resolution.y);
|
|
|
|
else
|
|
|
|
mCamera.shear /= 2;
|
|
|
|
|
2018-09-30 14:48:39 +02:00
|
|
|
mVericalSpeedPrev = mVericalSpeed;
|
|
|
|
|
2018-09-10 18:23:53 +02:00
|
|
|
if (computeHeight)
|
|
|
|
mVericalSpeed -= (dt * GRAVITY_ACCELERATION) / 1000; // gravity
|
|
|
|
}
|
2018-09-08 14:55:14 +02:00
|
|
|
};
|
|
|
|
|
2018-10-02 15:18:05 +02:00
|
|
|
/**
|
|
|
|
Sprite class, again just bare minimum to fit the needs. Prefer writing your
|
|
|
|
own.
|
|
|
|
*/
|
2018-09-08 14:55:14 +02:00
|
|
|
class Sprite
|
|
|
|
{
|
|
|
|
public:
|
2018-09-15 17:06:07 +02:00
|
|
|
const unsigned char *mImage;
|
2018-09-17 17:55:33 +02:00
|
|
|
RCL_Vector2D mPosition;
|
|
|
|
RCL_Unit mHeight;
|
|
|
|
RCL_Unit mPixelSize;
|
2018-09-15 17:06:07 +02:00
|
|
|
|
2018-09-17 17:55:33 +02:00
|
|
|
Sprite(const unsigned char *image, int16_t squareX, int16_t squareY, RCL_Unit z,
|
|
|
|
RCL_Unit pixelSize):
|
2018-09-08 14:55:14 +02:00
|
|
|
mImage(image),
|
|
|
|
mPixelSize(pixelSize)
|
|
|
|
{
|
2018-09-17 17:55:33 +02:00
|
|
|
mPosition.x = squareX * RCL_UNITS_PER_SQUARE + RCL_UNITS_PER_SQUARE / 2;
|
|
|
|
mPosition.y = squareY * RCL_UNITS_PER_SQUARE + RCL_UNITS_PER_SQUARE / 2;
|
|
|
|
mHeight = z * RCL_UNITS_PER_SQUARE + RCL_UNITS_PER_SQUARE / 2;
|
2018-09-08 14:55:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Sprite():
|
|
|
|
mImage(0), mHeight(0), mPixelSize(1)
|
|
|
|
{
|
|
|
|
mPosition.x = 0;
|
|
|
|
mPosition.y = 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-09-09 17:52:30 +02:00
|
|
|
void initGeneral()
|
|
|
|
{
|
|
|
|
pokitto.begin();
|
|
|
|
pokitto.setFrameRate(FPS);
|
|
|
|
pokitto.display.setFont(fontTiny);
|
|
|
|
pokitto.display.persistence = 1;
|
2018-09-27 10:30:52 +02:00
|
|
|
pokitto.display.setInvisibleColor(-1);
|
2018-09-09 17:52:30 +02:00
|
|
|
|
2018-09-18 15:28:43 +02:00
|
|
|
RCL_initRayConstraints(&defaultConstraints);
|
|
|
|
|
2018-09-09 17:52:30 +02:00
|
|
|
initPalette();
|
|
|
|
|
|
|
|
for (uint8_t i = 0; i < SUBSAMPLED_WIDTH; ++i)
|
|
|
|
zBuffer[i] = 0;
|
|
|
|
}
|
|
|
|
|
2018-09-11 09:19:09 +02:00
|
|
|
/**
|
|
|
|
Computes an average color of given texture.
|
|
|
|
*/
|
2018-09-29 18:43:53 +02:00
|
|
|
unsigned char computeAverageColor(const unsigned char *texture, int16_t excludeColor=-1)
|
2018-09-10 08:03:09 +02:00
|
|
|
{
|
2018-09-27 12:55:03 +02:00
|
|
|
uint8_t h,s,v;
|
|
|
|
uint32_t sumH = 0;
|
|
|
|
uint32_t sumS = 0;
|
|
|
|
uint32_t sumV = 0;
|
2018-09-10 08:03:09 +02:00
|
|
|
uint32_t pixels = texture[0] * texture[1];
|
2018-09-30 14:40:27 +02:00
|
|
|
uint32_t count = 0;
|
2018-09-10 08:03:09 +02:00
|
|
|
|
|
|
|
for (uint16_t i = 0; i < pixels; ++i)
|
|
|
|
{
|
2018-09-29 18:43:53 +02:00
|
|
|
uint8_t color = texture[2 + i];
|
|
|
|
|
|
|
|
if (color == excludeColor)
|
|
|
|
continue;
|
|
|
|
|
2018-09-27 12:55:03 +02:00
|
|
|
decodeHSV(texture[2 + i],&h,&s,&v);
|
|
|
|
|
|
|
|
sumH += h;
|
|
|
|
sumS += s;
|
|
|
|
sumV += v;
|
2018-09-30 14:40:27 +02:00
|
|
|
count++;
|
2018-09-10 08:03:09 +02:00
|
|
|
}
|
|
|
|
|
2018-09-30 14:40:27 +02:00
|
|
|
return encodeHSV(sumH / count,sumS / count, sumV / count);
|
2018-09-10 08:03:09 +02:00
|
|
|
}
|
|
|
|
|
2018-09-08 14:30:03 +02:00
|
|
|
#endif
|