167 asc_behavior_add(node, .func = fps_counter_update, .interval = asc_seconds(1)); |
176 asc_behavior_add(node, .func = fps_counter_update, .interval = asc_seconds(1)); |
168 asc_behavior_add(node, fps_counter_tie_to_corner); |
177 asc_behavior_add(node, fps_counter_tie_to_corner); |
169 asc_ui_add_node(node); |
178 asc_ui_add_node(node); |
170 } |
179 } |
171 |
180 |
|
181 |
|
182 static GameField *game_field; |
|
183 |
|
184 static bool game_field_tile_chown(asc_vec2u coords, Player *player) { |
|
185 unsigned x = coords.x, y = coords.y; |
|
186 asc_ptr_cast(AscRectangle, tile, game_field->nodes[x][y]); |
|
187 int old_owner = game_field->tile_data[x][y] & GAME_FIELD_TILE_OWNER_MASK; |
|
188 if (player == NULL) { |
|
189 asc_clear_flag(game_field->tile_data[x][y], GAME_FIELD_TILE_OWNER_MASK); |
|
190 tile->color = asc_col_itof(ASC_RGB(16, 50, 160)); |
|
191 } else { |
|
192 asc_set_flag_masked(game_field->tile_data[x][y], GAME_FIELD_TILE_OWNER_MASK, player->number); |
|
193 tile->color = player->color; |
|
194 } |
|
195 int new_owner = game_field->tile_data[x][y] & GAME_FIELD_TILE_OWNER_MASK; |
|
196 return old_owner != new_owner; |
|
197 } |
|
198 |
|
199 static void game_field_create() { |
|
200 // TODO: create a more interesting map than just a basic grid |
|
201 AscSceneNode *node = asc_scene_node_empty(); |
|
202 game_field = asc_scene_node_allocate_data(node, sizeof(GameField)); |
|
203 for (unsigned x = 0; x < GAME_FIELD_SIZE; x++) { |
|
204 for (unsigned y = 0; y < GAME_FIELD_SIZE; y++) { |
|
205 AscSceneNode *tile = asc_rectangle( |
|
206 .x = x*GAME_FIELD_TILE_SIZE, .y = y*GAME_FIELD_TILE_SIZE, .filled = true, .thickness = 2, |
|
207 .width = GAME_FIELD_TILE_SIZE, .height = GAME_FIELD_TILE_SIZE, |
|
208 .color = ASC_RGB(16, 50, 160), |
|
209 .border_color = ASC_RGB(20, 84, 128), |
|
210 ); |
|
211 |
|
212 game_field->tile_data[x][y] = GAME_FIELD_TILE_EXISTS_FLAG; |
|
213 game_field->nodes[x][y] = tile; |
|
214 |
|
215 asc_scene_node_link(node, tile); |
|
216 } |
|
217 } |
|
218 asc_scene_node_set_zindex(node, -2); |
|
219 asc_scene_add_node(MAIN_SCENE, node); |
|
220 } |
|
221 |
|
222 static asc_vec2u game_field_tile_at_position(asc_vec3f position) { |
|
223 return ASC_VEC2U((int)position.x / GAME_FIELD_TILE_SIZE, (int)position.y / GAME_FIELD_TILE_SIZE); |
|
224 } |
|
225 |
172 typedef struct { |
226 typedef struct { |
173 AscShaderProgram program; |
227 AscShaderProgram program; |
174 asc_uniform_loc map_albedo; |
228 asc_uniform_loc map_albedo; |
175 asc_uniform_loc map_color; |
229 asc_uniform_loc map_color; |
176 asc_uniform_loc color; |
230 asc_uniform_loc color; |
210 } |
264 } |
211 |
265 |
212 static void player_move(AscBehavior *behavior) { |
266 static void player_move(AscBehavior *behavior) { |
213 AscSceneNode *node = behavior->node; |
267 AscSceneNode *node = behavior->node; |
214 Player *player = node->user_data; |
268 Player *player = node->user_data; |
|
269 const float ts = (float) GAME_FIELD_TILE_SIZE; |
215 |
270 |
216 // check if the position is set programmatically |
271 // check if the position is set programmatically |
217 if (player->reset_position) { |
272 if (player->reset_position) { |
218 asc_scene_node_set_position2f(node, |
273 asc_scene_node_set_position2f(node, |
219 ASC_VEC2F( |
274 ASC_VEC2F( |
220 GAME_FIELD_TILE_SIZE * player->new_position.x, |
275 ts * (player->new_position.x + .5f), |
221 GAME_FIELD_TILE_SIZE * player->new_position.y)); |
276 ts * (player->new_position.y + .5f) |
|
277 )); |
222 player->reset_position = false; |
278 player->reset_position = false; |
223 return; |
279 return; |
224 } |
280 } |
225 |
281 |
226 // normal movement |
282 // normal movement |
227 const int ts = (int) GAME_FIELD_TILE_SIZE; |
283 const float speed = ts * player->speed * asc_context.frame_factor; |
228 const float fts = (float) ts; |
|
229 const float speed = fts * player->speed * asc_context.frame_factor; |
|
230 const asc_vec2i dir = directions[player->direction]; |
284 const asc_vec2i dir = directions[player->direction]; |
231 const asc_vec2f movement = asc_vec2f_scale(asc_vec2_itof(dir), speed); |
285 const asc_vec2f movement = asc_vec2f_scale(asc_vec2_itof(dir), speed); |
|
286 |
232 // check if we are supposed to change the direction |
287 // check if we are supposed to change the direction |
233 if (player->direction == player->target_direction) { |
288 if (player->direction == player->target_direction) { |
234 // move without changing the direction |
289 // move without changing the direction |
235 asc_scene_node_move2f(node, movement); |
290 asc_scene_node_move2f(node, movement); |
236 } else { |
291 } else { |
238 // and check if we are about to cross the center |
293 // and check if we are about to cross the center |
239 // this relies on positive positions! |
294 // this relies on positive positions! |
240 bool rotate = false; |
295 bool rotate = false; |
241 if (movement.x == 0) { |
296 if (movement.x == 0) { |
242 // vertical movement |
297 // vertical movement |
243 const int y0 = (int)node->position.y / ts; |
298 const float y_0 = floorf(node->position.y / ts); |
244 const int y1 = (int)(node->position.y+movement.y) / ts; |
299 const float y_curr = node->position.y / ts - y_0; |
245 rotate = y0 != y1 && asc_sgn(y1-y0) == dir.y; |
300 const float y_next = (node->position.y+movement.y) / ts - y_0; |
|
301 const bool side_curr = y_curr > 0.5f; |
|
302 const bool side_next = y_next > 0.5f; |
|
303 rotate = side_curr ^ side_next; |
246 } else { |
304 } else { |
247 // horizontal movement |
305 // horizontal movement |
248 const int x0 = (int)node->position.x / ts; |
306 const float x0 = floorf(node->position.x / ts); |
249 const int x1 = (int)(node->position.x+movement.x) / ts; |
307 const float x_curr = node->position.x / ts - x0; |
250 rotate = x0 != x1 && asc_sgn(x1-x0) == dir.x; |
308 const float x_next = (node->position.x+movement.x) / ts - x0; |
|
309 const bool side_curr = x_curr > 0.5f; |
|
310 const bool side_next = x_next > 0.5f; |
|
311 rotate = side_curr ^ side_next; |
251 } |
312 } |
252 if (rotate) { |
313 if (rotate) { |
253 // snap position to the center of the tile |
314 // snap position to the center of the tile |
254 node->position.x = roundf(node->position.x / fts) * fts; |
315 asc_scene_node_set_position2f(node, |
255 node->position.y = roundf(node->position.y / fts) * fts; |
316 ASC_VEC2F( |
|
317 (.5f+floorf(node->position.x / ts)) * ts, |
|
318 (.5f+floorf(node->position.y / ts)) * ts |
|
319 )); |
256 player->direction = player->target_direction; |
320 player->direction = player->target_direction; |
257 asc_scene_node_set_rotation(node, rotations[player->direction]); |
321 asc_scene_node_set_rotation(node, rotations[player->direction]); |
258 } else { |
322 } else { |
259 // changing the direction not permitted, yet, continue movement |
323 // changing the direction not permitted, yet, continue movement |
260 asc_scene_node_move2f(node, movement); |
324 asc_scene_node_move2f(node, movement); |
261 } |
325 } |
262 } |
326 } |
|
327 |
|
328 // TODO: collision detection |
|
329 |
|
330 // update the trace, if necessary. |
|
331 // remark: some calculations are repeated here, but they are cheap enough |
|
332 { |
|
333 const asc_vec2u tile_coords = game_field_tile_at_position(node->position); |
|
334 // TODO: player should have been destroyed before leaving the field |
|
335 if (tile_coords.x > GAME_FIELD_SIZE || tile_coords.y > GAME_FIELD_SIZE) return; |
|
336 if (game_field_tile_chown(tile_coords, player)) { |
|
337 // new owner of the tile! |
|
338 asc_vec2u p = tile_coords; |
|
339 cxListAdd(player->trace, &p); |
|
340 if (cxListSize(player->trace) > 7) { |
|
341 // TODO: implement power-up which makes the trace longer |
|
342 cxListPopFront(player->trace, &p); |
|
343 game_field_tile_chown(p, NULL); |
|
344 } |
|
345 } |
|
346 } |
263 } |
347 } |
264 |
348 |
265 static void player_position(Player *pl, int x, int y) { |
349 static void player_position(Player *pl, int x, int y) { |
266 pl->new_position.x = x; |
350 pl->new_position.x = x; |
267 pl->new_position.y = y; |
351 pl->new_position.y = y; |
268 pl->reset_position = true; |
352 pl->reset_position = true; |
269 } |
353 } |
270 |
354 |
|
355 static void player_destroy(CxAllocator *allocator, Player *player) { |
|
356 cxListFree(player->trace); |
|
357 cxFree(allocator, player); |
|
358 } |
|
359 |
271 static Player *player_create(void) { |
360 static Player *player_create(void) { |
272 AscSceneNode *sprite = asc_sprite( |
361 AscSceneNode *node = asc_sprite( |
273 .name = "Player", |
362 .name = "Player", |
274 .width = GAME_FIELD_TILE_SIZE, |
363 .width = GAME_FIELD_TILE_SIZE, |
275 .height = GAME_FIELD_TILE_SIZE, |
364 .height = GAME_FIELD_TILE_SIZE, |
276 .origin_x = GAME_FIELD_TILE_SIZE / 2, |
365 .origin_x = GAME_FIELD_TILE_SIZE / 2, |
277 .origin_y = GAME_FIELD_TILE_SIZE / 2, |
366 .origin_y = GAME_FIELD_TILE_SIZE / 2, |
278 ); |
367 ); |
279 asc_scene_add_node(MAIN_SCENE, sprite); |
368 asc_scene_add_node(MAIN_SCENE, node); |
280 Player *player = asc_scene_node_allocate_data(sprite, sizeof(Player)); |
369 Player *player = asc_scene_node_allocate_data(node, sizeof(Player)); |
281 player_position(player, 12, 8); |
370 player_position(player, 12, 8); |
282 player->speed = 3.f; // start with 3 tiles/sec |
371 player->speed = 3.f; // start with 3 tiles/sec |
|
372 player->number = 1; |
283 player->color = ASC_RGB_F(1, 0, 0); |
373 player->color = ASC_RGB_F(1, 0, 0); |
284 sprite->draw_func = player_draw; |
374 player->trace = cxLinkedListCreateSimple(sizeof(asc_vec2i)); |
285 asc_behavior_add(sprite, player_move); |
375 node->draw_func = player_draw; |
|
376 node->user_data_free_func = (cx_destructor_func2)player_destroy; |
|
377 asc_behavior_add(node, player_move); |
286 return player; |
378 return player; |
287 } |
|
288 |
|
289 static void gamefield_create() { |
|
290 // TODO: create a proper data structure and a more interesting map than just a basic grid |
|
291 AscSceneNode *node = asc_scene_node_empty(); |
|
292 GameField *data = asc_scene_node_allocate_data(node, sizeof(GameField)); |
|
293 for (unsigned x = 0; x < GAME_FIELD_SIZE; x++) { |
|
294 for (unsigned y = 0; y < GAME_FIELD_SIZE; y++) { |
|
295 AscSceneNode *tile = asc_rectangle( |
|
296 .x = (x+1)*GAME_FIELD_TILE_SIZE, .y = (y+1)*GAME_FIELD_TILE_SIZE, .filled = true, .thickness = 2, |
|
297 .origin_x = GAME_FIELD_TILE_SIZE / 2, .origin_y = GAME_FIELD_TILE_SIZE / 2, |
|
298 .width = GAME_FIELD_TILE_SIZE, .height = GAME_FIELD_TILE_SIZE, |
|
299 .color = ASC_RGB(16, 50, 160), |
|
300 .border_color = ASC_RGB(20, 84, 128), |
|
301 ); |
|
302 |
|
303 data->tile_data[x][y] = GAME_FIELD_TILE_EXISTS_FLAG; |
|
304 data->nodes[x][y] = tile; |
|
305 |
|
306 asc_scene_node_link(node, tile); |
|
307 } |
|
308 } |
|
309 asc_scene_node_set_zindex(node, -2); |
|
310 asc_scene_add_node(MAIN_SCENE, node); |
|
311 } |
379 } |
312 |
380 |
313 static asc_rect main_scene_viewport_update(asc_vec2u window_size) { |
381 static asc_rect main_scene_viewport_update(asc_vec2u window_size) { |
314 |
382 |
315 // margins |
383 // margins |
375 .type = ASC_CAMERA_ORTHO, |
443 .type = ASC_CAMERA_ORTHO, |
376 .projection_update_func = asc_camera_ortho_update_size |
444 .projection_update_func = asc_camera_ortho_update_size |
377 ); |
445 ); |
378 asc_scene_init(MAIN_SCENE, "main", |
446 asc_scene_init(MAIN_SCENE, "main", |
379 .type = ASC_CAMERA_ORTHO, |
447 .type = ASC_CAMERA_ORTHO, |
380 .ortho.rect = ASC_RECT(0, 0, |
448 .ortho.rect = ASC_RECT( |
381 (GAME_FIELD_SIZE+1)*GAME_FIELD_TILE_SIZE, |
449 -GAME_FIELD_TILE_SIZE, |
382 (GAME_FIELD_SIZE+1)*GAME_FIELD_TILE_SIZE |
450 -GAME_FIELD_TILE_SIZE, |
|
451 (GAME_FIELD_SIZE+2)*GAME_FIELD_TILE_SIZE, |
|
452 (GAME_FIELD_SIZE+2)*GAME_FIELD_TILE_SIZE |
383 ), |
453 ), |
384 .viewport_clear = true, |
454 .viewport_clear = true, |
385 .clear_color = ASC_RGB(0, 32, 16), |
455 .clear_color = ASC_RGB(0, 32, 16), |
386 .viewport_update_func = main_scene_viewport_update |
456 .viewport_update_func = main_scene_viewport_update |
387 ); |
457 ); |
388 |
458 |
389 // backdrop for letterbox/pillarbox |
459 // backdrop for letterbox/pillarbox |
390 backdrop_create(); |
460 backdrop_create(); |
391 |
461 |
392 // the game field |
462 // the game field |
393 gamefield_create(); |
463 game_field_create(); |
394 |
464 |
395 // create UI elements |
465 // create UI elements |
396 fps_counter_create(); |
466 fps_counter_create(); |
397 |
467 |
398 // create spaceship |
468 // create spaceship |