summaryrefslogtreecommitdiff
path: root/examples/platform.c
blob: 1ac48fb08a3c7f475f8858a9ac82569aed898215 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*
 * platform: a box that runs and jumps around a tilemap with a camera that follows it, and a ray that follows the mouse
 *
 * build:  ./build.sh -d -m 2d,font obj/platform
 * run:    obj/platform
 *
 * left and right or a and d run, space or up jumps. the line from the box to the mouse stops at the first solid tile
 * the console is not needed, but with the debug module the grave key opens it and the variables gravity and jump are there
 */

#include <stdio.h>
#include <math.h>

#include "konprogram.h"
#include "kontile.h"
#include "kon2d.h"

#if !defined(KON_MODULE_2D) || !defined(KON_MODULE_TILE) || !defined(KON_MODULE_FONT)
#error "build with: ./build.sh -d -m 2d,font obj/platform"
#endif

#define TILE 16
#define COLUMNS 40
#define ROWS 22
#define VIEW_WIDTH 320
#define VIEW_HEIGHT 176

#define COL_BACKGROUND 0xFF1B1826u
#define COL_TILE       0xFF4A4468u
#define COL_TILE_TOP   0xFF6B5F92u
#define COL_PLAYER     0xFFF2A7C3u
#define COL_RAY        0xFF8FD3C8u
#define COL_HIT        0xFFF3DDA0u
#define COL_DIM        0xFF9A93B3u

static const char *level[ROWS] = {
	"########################################",
	"#......................................#",
	"#......................................#",
	"#......................................#",
	"#......................................#",
	"#..............#####...................#",
	"#......................................#",
	"#.........###..........................#",
	"#......................................#",
	"#.....................#####............#",
	"#......................................#",
	"#..####................................#",
	"#......................................#",
	"#..............####........#...........#",
	"#.....................................##",
	"#..........#.....................#######",
	"#..........##..........................#",
	"#..........###..................###....#",
	"#..........####........................#",
	"#.....#####............................#",
	"#......................................#",
	"########################################",
};

typedef struct state {
	kon_tilemap_t map;
	uint16_t tiles[COLUMNS * ROWS];
	uint8_t solid[2];

	kon_platformer_t player;
	kon_camera2_t camera;

	int left, right;
	int mouseX, mouseY;
	int rayHit;
	kon_hit2_t hit;
} state_t;

/*** program ***/

static int event(kon_program_t *program, const kon_event_t *ev, void *user)
{
	state_t *state = user;

	if (ev->type == KON_EVENT_MOUSE_MOVE) {
		kon_windowToGame(program->display, ev->mouseX, ev->mouseY, &state->mouseX, &state->mouseY);
	} else if (ev->type == KON_EVENT_KEY_DOWN || ev->type == KON_EVENT_KEY_UP) {
		int down = ev->type == KON_EVENT_KEY_DOWN;

		if (ev->key == KON_KEY_LEFT || ev->key == KON_KEY_A) state->left = down;
		else if (ev->key == KON_KEY_RIGHT || ev->key == KON_KEY_D) state->right = down;
		else if (ev->key == KON_KEY_SPACE || ev->key == KON_KEY_UP) state->player.jumpHeld = down;
		else return 0;
	} else {
		return 0;
	}
	return 1;
}

static void update(kon_program_t *program, double dt, void *user)
{
	state_t *state = user;
	kon_box2_t *box = &state->player.box;
	kon_vec2_t from = KON_VEC2(box->x + box->w * 0.5f, box->y + box->h * 0.5f);
	kon_vec2_t dir;
	float length;
	int row, column;

	(void)program;
	state->player.moveX = state->right - state->left;
	kon_platformerUpdate(&state->player, &state->map, state->solid, 2, dt);
	kon_camera2Follow(&state->camera, from.x, from.y, (float)dt);

	/* the ray against every solid tile, nothing clever, the nearest one wins */
	dir = KON_VEC2((float)(state->mouseX + kon_camera2X(&state->camera)) - from.x,
	    (float)(state->mouseY + kon_camera2Y(&state->camera)) - from.y);
	length = kon_vec2Length(dir);
	state->rayHit = 0;
	if (length < 1.0f) return;
	dir = kon_vec2Scale(dir, 1.0f / length);
	for (row = 0; row < ROWS; row++) {
		for (column = 0; column < COLUMNS; column++) {
			kon_hit2_t hit;

			if (!state->solid[kon_tilemapAt(&state->map, column, row)]) continue;
			if (!kon_rayBox(from, dir, length, KON_BOX2(column * TILE, row * TILE, TILE, TILE), &hit)) continue;
			if (!state->rayHit || hit.time < state->hit.time) {
				state->hit = hit;
				state->rayHit = 1;
			}
		}
	}
}

static void draw(kon_program_t *program, kon_framebuffer_t *game, void *user)
{
	state_t *state = user;
	kon_box2_t *box = &state->player.box;
	int camX = kon_camera2X(&state->camera), camY = kon_camera2Y(&state->camera);
	int row, column, ex, ey;
	int fromX = (int)(box->x + box->w * 0.5f) - camX, fromY = (int)(box->y + box->h * 0.5f) - camY;

	kon_clearFramebuffer(game, COL_BACKGROUND);

	for (row = 0; row < ROWS; row++) {
		for (column = 0; column < COLUMNS; column++) {
			int x = column * TILE - camX, y = row * TILE - camY;

			if (!state->solid[kon_tilemapAt(&state->map, column, row)]) continue;
			kon_fillRectangle(game, x, y, TILE, TILE, COL_TILE);
			if (!state->solid[kon_tilemapAt(&state->map, column, row - 1)]) kon_fillRectangle(game, x, y, TILE, 2, COL_TILE_TOP);
		}
	}

	ex = state->rayHit ? (int)state->hit.point.x - camX : state->mouseX;
	ey = state->rayHit ? (int)state->hit.point.y - camY : state->mouseY;
	kon_drawLine(game, fromX, fromY, ex, ey, COL_RAY);
	if (state->rayHit) kon_fillCircle(game, ex, ey, 2, COL_HIT);

	kon_fillRectangle(game, (int)box->x - camX, (int)box->y - camY, (int)box->w, (int)box->h, COL_PLAYER);

	if (program->font) kon_drawText(game, program->font, 8, 16, "arrows or a d to run, space to jump", COL_DIM);
}

static int init(kon_program_t *program, void *user)
{
	state_t *state = user;
	int row, column;

	state->map.columns = COLUMNS;
	state->map.rows = ROWS;
	state->map.tileWidth = TILE;
	state->map.tileHeight = TILE;
	state->map.tiles = state->tiles;
	state->solid[1] = 1;
	for (row = 0; row < ROWS; row++) {
		for (column = 0; column < COLUMNS; column++) state->tiles[row * COLUMNS + column] = level[row][column] == '#';
	}

	kon_platformerInit(&state->player, KON_BOX2(40, 280, 10, 14));
	kon_camera2Init(&state->camera, VIEW_WIDTH, VIEW_HEIGHT, COLUMNS * TILE, ROWS * TILE);
	kon_camera2Snap(&state->camera, 45.0f, 287.0f);

	kon_debugVar(program->debug, "gravity", KON_VAR_FLOAT, &state->player.gravity);
	kon_debugVar(program->debug, "jump", KON_VAR_FLOAT, &state->player.jumpSpeed);
	return 1;
}

int main(void)
{
	state_t state = {0};
	kon_programConfig_t config = {0};

	config.title = "platform";
	config.gameWidth = VIEW_WIDTH;
	config.gameHeight = VIEW_HEIGHT;
	config.windowWidth = 1280;
	config.windowHeight = 704;
	config.user = &state;
	config.init = init;
	config.event = event;
	config.update = update;
	config.draw = draw;
	return kon_programRun(&config);
}