getting stuff working on windows again
[carveJwlIkooP6JGAAIwe30JlM.git] / ent_skateshop.c
1 #ifndef ENT_SKATESHOP_C
2 #define ENT_SKATESHOP_C
3
4 #define VG_GAME
5 #include "vg/vg.h"
6 #include "vg/vg_steam_ugc.h"
7 #include "vg/vg_msg.h"
8 #include "ent_skateshop.h"
9 #include "world.h"
10 #include "player.h"
11 #include "gui.h"
12 #include "menu.h"
13 #include "pointcloud.h"
14 #include "highscores.h"
15 #include "steam.h"
16 #include "addon.h"
17
18 /*
19 * Checks string equality but does a hash check first
20 */
21 static inline int const_str_eq( u32 hash, const char *str, const char *cmp )
22 {
23 if( hash == vg_strdjb2(cmp) )
24 if( !strcmp( str, cmp ) )
25 return 1;
26 return 0;
27 }
28
29 /*
30 * Get an existing cache instance, allocate a new one to be loaded, or NULL if
31 * there is no space
32 */
33 VG_STATIC struct cache_board *skateshop_cache_fetch_board( u32 registry_index )
34 {
35 addon_reg *reg = NULL;
36
37 if( registry_index < addon_count( k_workshop_file_type_board ) ){
38 reg = get_addon_from_index( k_workshop_file_type_board, registry_index );
39
40 if( reg->userdata ){
41 return reg->userdata;
42 }
43 }
44
45 /* lru eviction. should be a linked list maybe... */
46 double min_time = 1e300;
47 struct cache_board *min_board = NULL;
48
49 SDL_AtomicLock( &global_skateshop.sl_cache_access );
50 for( u32 i=0; i<SKATESHOP_BOARD_CACHE_MAX; i++ ){
51 struct cache_board *cache_ptr = &global_skateshop.cache[i];
52
53 if( cache_ptr->state == k_cache_board_state_load_request ) continue;
54 if( cache_ptr->ref_count ) continue;
55
56 if( cache_ptr->last_use_time < min_time ){
57 min_time = cache_ptr->last_use_time;
58 min_board = cache_ptr;
59 }
60 }
61
62 if( min_board ){
63 if( min_board->state == k_cache_board_state_loaded ){
64 player_board_unload( &min_board->board );
65 min_board->reg_ptr->userdata = NULL;
66 }
67
68 if( reg ){
69 vg_info( "Allocating board (reg:%u) '%s'\n",
70 registry_index, reg->foldername );
71 }
72 else{
73 vg_info( "Pre-allocating board (reg:%u) 'null'\n", registry_index );
74 }
75
76 min_board->reg_ptr = reg;
77 min_board->reg_index = registry_index;
78 min_board->last_use_time = vg.time;
79 min_board->ref_count = 0;
80 min_board->state = k_cache_board_state_load_request;
81 }
82 else{
83 vg_error( "No free boards to load registry!\n" );
84 }
85
86 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
87 return min_board;
88 }
89
90 VG_STATIC void skateshop_update_viewpage(void)
91 {
92 u32 page = global_skateshop.selected_board_id/SKATESHOP_VIEW_SLOT_MAX;
93
94 for( u32 i=0; i<SKATESHOP_VIEW_SLOT_MAX; i++ ){
95 struct shop_view_slot *slot = &global_skateshop.shop_view_slots[i];
96 u32 request_id = page*SKATESHOP_VIEW_SLOT_MAX + i;
97
98 if( slot->cache_ptr ) unwatch_cache_board( slot->cache_ptr );
99
100 slot->cache_ptr = skateshop_cache_fetch_board( request_id );
101 if( slot->cache_ptr ) watch_cache_board( slot->cache_ptr );
102 }
103 }
104
105 /*
106 * op/subroutine: k_workshop_op_item_load
107 * -----------------------------------------------------------------------------
108 */
109
110 /*
111 * Reciever for board completion; only promotes the status in the main thread
112 */
113 VG_STATIC void skateshop_async_board_loaded( void *payload, u32 size )
114 {
115 SDL_AtomicLock( &global_skateshop.sl_cache_access );
116 struct cache_board *cache_ptr = payload;
117 cache_ptr->last_use_time = vg.time;
118 cache_ptr->state = k_cache_board_state_loaded;
119
120 cache_ptr->reg_ptr->userdata = cache_ptr;
121 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
122 vg_success( "Async board loaded (%s)\n", cache_ptr->reg_ptr->foldername );
123 }
124
125 /*
126 * Thread(or subroutine of thread), for checking view slots that weve installed.
127 * Load the model if a view slot wants it
128 */
129 VG_STATIC void workshop_visibile_load_loop(void)
130 {
131 vg_info( "Running load loop\n" );
132 char path_buf[4096];
133 vg_str folder;
134
135 for( u32 i=0; i<SKATESHOP_BOARD_CACHE_MAX; i++ ){
136 struct cache_board *cache_ptr = &global_skateshop.cache[i];
137
138 SDL_AtomicLock( &global_skateshop.sl_cache_access );
139 if( cache_ptr->state == k_cache_board_state_load_request ){
140 if( cache_ptr->reg_index >= addon_count(k_workshop_file_type_board) ){
141 /* should maybe have a different value for this case */
142 cache_ptr->state = k_cache_board_state_none;
143 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
144 continue;
145 }
146
147 /* continue with the request */
148 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
149
150 cache_ptr->reg_ptr = get_addon_from_index( k_workshop_file_type_board,
151 cache_ptr->reg_index );
152
153 if( cache_ptr->reg_ptr->workshop_id ){
154 vg_async_item *call =
155 vg_async_alloc( sizeof(struct async_workshop_filepath_info) );
156
157 struct async_workshop_filepath_info *info = call->payload;
158 info->buf = path_buf;
159 info->id = cache_ptr->reg_ptr->workshop_id;
160 info->len = vg_list_size(path_buf);
161 vg_async_dispatch( call, async_workshop_get_filepath );
162 vg_async_stall(); /* too bad! */
163
164 if( path_buf[0] == '\0' ){
165 vg_error( "Failed SteamAPI_GetItemInstallInfo(" PRINTF_U64 ")\n",
166 cache_ptr->reg_ptr->workshop_id );
167 goto file_is_broken;
168 }
169
170 folder.buffer = path_buf;
171 folder.i = strlen(path_buf);
172 folder.len = 4096;
173 }
174 else{
175 vg_strnull( &folder, path_buf, 4096 );
176 vg_strcat( &folder, "boards/" );
177 vg_strcat( &folder, cache_ptr->reg_ptr->foldername );
178 }
179
180 /* load content files
181 * --------------------------------- */
182
183 vg_str content_path = folder;
184
185 int found = 0;
186 vg_msg msg;
187 vg_msg_init( &msg, cache_ptr->reg_ptr->metadata,
188 cache_ptr->reg_ptr->metadata_len );
189 vg_msg_cmd cmd;
190 while( vg_msg_next( &msg, &cmd ) ){
191 if( (msg.depth == 0) && (cmd.code == k_vg_msg_code_kvstring) ){
192 if( VG_STRDJB2_EQ( "content", cmd.key, cmd.key_djb2 ) ){
193 vg_strcat( &content_path, "/" );
194 vg_strcat( &content_path, cmd.value._buf );
195 found = 1;
196 break;
197 }
198 }
199 }
200
201 if( !vg_strgood( &content_path ) ) {
202 vg_error( "Metadata path too long\n" );
203 goto file_is_broken;
204 }
205
206 if( !found ){
207 vg_error( "No content paths in metadata\n" );
208 goto file_is_broken;
209 }
210
211 vg_info( "Load content: %s\n", content_path.buffer );
212 player_board_load( &cache_ptr->board, content_path.buffer );
213 vg_async_call( skateshop_async_board_loaded, cache_ptr, 0 );
214 continue;
215
216 file_is_broken:;
217 SDL_AtomicLock( &global_skateshop.sl_cache_access );
218 cache_ptr->state = k_cache_board_state_none;
219 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
220 }
221 else
222 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
223 }
224 }
225
226
227 VG_STATIC void world_scan_thread( void *_args ){
228 addon_mount_local_folder( k_workshop_file_type_world, "maps", ".mdl" );
229 addon_mount_workshop_items();
230 vg_async_call( async_addon_reg_update, NULL, 0 );
231 skaterift_end_op();
232 }
233
234 /*
235 * Asynchronous scan of local disk for worlds
236 */
237 VG_STATIC void skateshop_op_world_scan(void){
238 skaterift_begin_op( k_async_op_world_scan );
239 vg_loader_start( world_scan_thread, NULL );
240 }
241
242 VG_STATIC void board_scan_thread( void *_args ){
243 addon_mount_local_folder( k_workshop_file_type_board, "boards", ".mdl" );
244 addon_mount_workshop_items();
245 vg_async_call( async_addon_reg_update, NULL, 0 );
246 vg_async_stall();
247 workshop_visibile_load_loop();
248 skaterift_end_op();
249 }
250
251 VG_STATIC void skateshop_op_board_scan(void){
252 skaterift_begin_op( k_async_op_board_scan );
253 vg_loader_start( board_scan_thread, NULL );
254 }
255
256 /*
257 * Regular stuff
258 * -----------------------------------------------------------------------------
259 */
260
261 /* we can only keep using a viewslot pointer for multiple frames if we watch it
262 * using this function */
263 VG_STATIC void watch_cache_board( struct cache_board *ptr ){
264 if( ptr->ref_count >= 32 ){
265 vg_fatal_error( "dynamic board watch missmatch (limit is 32)\n" );
266 }
267
268 ptr->last_use_time = vg.time;
269 ptr->ref_count ++;
270 }
271
272 /* after this is called, the calling code only has access to the pointer for the
273 * duration of the rest of the frame */
274 VG_STATIC void unwatch_cache_board( struct cache_board *ptr ){
275 if( ptr->ref_count == 0 ){
276 vg_fatal_error( "dynamic board unwatch missmatch (no watchers)\n" );
277 }
278
279 ptr->ref_count --;
280 }
281
282
283 /*
284 * VG event init
285 */
286 VG_STATIC void skateshop_init(void){
287 u32 cache_size = sizeof(struct cache_board)*SKATESHOP_BOARD_CACHE_MAX;
288 global_skateshop.cache = vg_linear_alloc( vg_mem.rtmemory, cache_size );
289 memset( global_skateshop.cache, 0, cache_size );
290
291 for( u32 i=0; i<SKATESHOP_BOARD_CACHE_MAX; i++ ){
292 struct cache_board *board = &global_skateshop.cache[i];
293 board->state = k_cache_board_state_none;
294 board->reg_ptr= NULL;
295 board->reg_index = 0xffffffff;
296 board->last_use_time = -99999.9;
297 board->ref_count = 0;
298 }
299 }
300
301 VG_STATIC struct cache_board *skateshop_selected_cache_if_loaded(void)
302 {
303 if( addon_count(k_workshop_file_type_board) ){
304 addon_reg *reg = get_addon_from_index(k_workshop_file_type_board,
305 global_skateshop.selected_board_id);
306
307 SDL_AtomicLock( &global_skateshop.sl_cache_access );
308 if( reg->userdata ){
309 struct cache_board *cache_ptr = reg->userdata;
310 if( cache_ptr->state == k_cache_board_state_loaded ){
311 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
312 return cache_ptr;
313 }
314 }
315 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
316 }
317
318 return NULL;
319 }
320
321 VG_STATIC void pointcloud_async_end(void *_, u32 __)
322 {
323 pointcloud_animate( k_pointcloud_anim_opening );
324 skaterift_end_op();
325 }
326
327 VG_STATIC void pointcloud_clear_async(void *_, u32 __)
328 {
329 pointcloud.count = 0;
330 pointcloud_animate( k_pointcloud_anim_opening );
331 skaterift_end_op();
332 }
333
334 VG_STATIC void skateshop_preview_loader_thread( void *_data )
335 {
336 addon_reg *reg = _data;
337
338 if( reg->workshop_id ){
339 vg_error( "Workshop files unsupported\n" );
340 vg_async_call( pointcloud_clear_async, NULL, 0 );
341 }
342 else{
343 char path_buf[4096];
344 vg_str path;
345 vg_strnull( &path, path_buf, 4096 );
346 vg_strcat( &path, "maps/" );
347 vg_strcat( &path, reg->foldername );
348 vg_strcat( &path, "/preview.bin" );
349
350 vg_linear_clear(vg_mem.scratch);
351 u32 size;
352
353 void *data = vg_file_read( vg_mem.scratch, path_buf, &size );
354 if( data ){
355 if( size < sizeof(pointcloud_buffer) ){
356 vg_async_call( pointcloud_clear_async, NULL, 0 );
357 return;
358 }
359
360 vg_async_item *call = vg_async_alloc(size);
361 pointcloud_buffer *pcbuf = call->payload;
362 memcpy( pcbuf, data, size );
363
364 u32 point_count = (size-sizeof(pointcloud_buffer)) /
365 sizeof(struct pointcloud_vert);
366 pcbuf->max = point_count;
367 pcbuf->count = point_count;
368 pcbuf->op = k_pointcloud_op_clear;
369
370 vg_async_dispatch( call, async_pointcloud_sub );
371 vg_async_call( pointcloud_async_end, NULL, 0 );
372 }
373 else{
374 vg_async_call( pointcloud_clear_async, NULL, 0 );
375 }
376 }
377 }
378
379 VG_STATIC void skateshop_load_world_preview( addon_reg *reg )
380 {
381 skaterift_begin_op( k_async_op_world_load_preview );
382 vg_loader_start( skateshop_preview_loader_thread, reg );
383 }
384
385 /*
386 * VG event preupdate
387 */
388 void temp_update_playermodel(void);
389 VG_STATIC void global_skateshop_preupdate(void)
390 {
391 float rate = vg_minf( 1.0f, vg.time_frame_delta * 2.0f );
392 global_skateshop.factive = vg_lerpf( global_skateshop.factive,
393 global_skateshop.active, rate );
394
395 if( !global_skateshop.active ) return;
396
397 world_instance *world = world_current_instance();
398 ent_skateshop *shop = global_skateshop.ptr_ent;
399
400 /* camera positioning */
401 ent_camera *ref = mdl_arritm( &world->ent_camera,
402 mdl_entity_id_id(shop->id_camera) );
403
404 v3f dir = {0.0f,-1.0f,0.0f};
405 mdl_transform_vector( &ref->transform, dir, dir );
406 m3x3_mulv( localplayer.invbasis, dir, dir );
407 player_vector_angles( localplayer.cam_override_angles, dir, 1.0f, 0.0f );
408
409 v3f lookat;
410 if( shop->type == k_skateshop_type_boardshop ||
411 shop->type == k_skateshop_type_worldshop ){
412 ent_marker *display = mdl_arritm( &world->ent_marker,
413 mdl_entity_id_id(shop->boards.id_display) );
414
415 v3_sub( display->transform.co, localplayer.rb.co, lookat );
416
417 }
418 else if( shop->type == k_skateshop_type_charshop ){
419 v3_sub( ref->transform.co, localplayer.rb.co, lookat );
420 }
421 else{
422 vg_fatal_error( "Unknown store (%u)\n", shop->type );
423 }
424
425 q_axis_angle( localplayer.rb.q, (v3f){0.0f,1.0f,0.0f},
426 atan2f(lookat[0],lookat[2]) );
427
428 v3_copy( ref->transform.co, localplayer.cam_override_pos );
429 localplayer.cam_override_fov = ref->fov;
430 localplayer.cam_override_strength = global_skateshop.factive;
431
432 /* input */
433 if( shop->type == k_skateshop_type_boardshop ){
434 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h ), "browse" );
435 gui_helper_action( button_display_string( k_srbind_mback ), "exit" );
436
437 struct cache_board *selected_cache = skateshop_selected_cache_if_loaded();
438
439 if( selected_cache ){
440 gui_helper_action( button_display_string( k_srbind_maccept ), "pick" );
441 }
442
443 /*
444 * Controls
445 * ----------------------
446 */
447
448 if( button_down( k_srbind_mleft ) ){
449 if( global_skateshop.selected_board_id > 0 ){
450 global_skateshop.selected_board_id --;
451 }
452 }
453
454 if( button_down( k_srbind_mright ) ){
455 if( global_skateshop.selected_board_id+1 <
456 addon_count(k_workshop_file_type_board) )
457 {
458 global_skateshop.selected_board_id ++;
459 }
460 }
461
462 if( selected_cache && button_down( k_srbind_maccept ) ){
463 vg_info( "chose board from skateshop (%u)\n",
464 global_skateshop.selected_board_id );
465
466 if( localplayer.board_view_slot ){
467 unwatch_cache_board( localplayer.board_view_slot );
468 }
469
470 localplayer.board_view_slot = selected_cache;
471 watch_cache_board( localplayer.board_view_slot );
472 global_skateshop_exit();
473 return;
474 }
475 }
476 else if( shop->type == k_skateshop_type_charshop ){
477 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h ), "browse" );
478 gui_helper_action( button_display_string( k_srbind_mback ), "exit" );
479 gui_helper_action( button_display_string( k_srbind_maccept ), "pick" );
480
481 if( button_down( k_srbind_mleft ) ){
482 if( cl_playermdl_id > 0 ){
483 cl_playermdl_id --;
484 }
485 else{
486 cl_playermdl_id = 2; /* HACK */
487 }
488 temp_update_playermodel(); /* HACK */
489 }
490
491 if( button_down( k_srbind_mright ) ){
492 if( cl_playermdl_id+1 < 3 ){
493 cl_playermdl_id ++;
494 }
495 else{
496 cl_playermdl_id = 0; /* HACK */
497 }
498 temp_update_playermodel(); /* HACK */
499 /*lol*/
500 }
501
502 if( button_down( k_srbind_maccept ) ){
503 global_skateshop_exit();
504 }
505 }
506 else if( shop->type == k_skateshop_type_worldshop ){
507 int browseable = 0,
508 loadable = 0;
509
510 if( addon_count(k_workshop_file_type_world) &&
511 ((skaterift.async_op == k_async_op_none)||
512 (skaterift.async_op == k_async_op_world_load_preview))){
513 gui_helper_action( axis_display_string(k_sraxis_mbrowse_h), "browse" );
514 browseable = 1;
515 }
516
517 if( skaterift.async_op == k_async_op_none ){
518 gui_helper_action( button_display_string(k_srbind_maccept), "load" );
519 loadable = 1;
520 }
521
522 int change = 0;
523
524 if( browseable ){
525 if( button_down( k_srbind_mleft ) ){
526 if( global_skateshop.selected_world_id > 0 )
527 {
528 global_skateshop.selected_world_id --;
529 change = 1;
530 }
531 }
532
533 if( button_down( k_srbind_mright ) ){
534 if( global_skateshop.selected_world_id+1 <
535 addon_count(k_workshop_file_type_world) )
536 {
537 global_skateshop.selected_world_id ++;
538 change = 1;
539 }
540 }
541 }
542
543 if( change && pointcloud_idle() ){
544 pointcloud_animate( k_pointcloud_anim_hiding );
545 }
546
547 if( skaterift.async_op == k_async_op_none ){
548 addon_reg *reg = get_addon_from_index( k_workshop_file_type_world,
549 global_skateshop.selected_world_id );
550
551 /* automatically load in clouds */
552 if( loadable && button_down( k_srbind_maccept ) ){
553 vg_info( "Select world (%u)\n",
554 global_skateshop.selected_world_id );
555 skaterift_change_world( reg->foldername );
556 return;
557 }
558 else{
559 if( pointcloud.anim == k_pointcloud_anim_idle_closed ){
560 if( global_skateshop.pointcloud_world_id !=
561 global_skateshop.selected_world_id )
562 {
563 global_skateshop.pointcloud_world_id =
564 global_skateshop.selected_world_id;
565 skateshop_load_world_preview( reg );
566 }
567 else{
568 pointcloud_animate( k_pointcloud_anim_opening );
569 }
570 }
571 else if( pointcloud.anim == k_pointcloud_anim_idle_open ){
572 if( global_skateshop.pointcloud_world_id !=
573 global_skateshop.selected_world_id )
574 {
575 pointcloud_animate( k_pointcloud_anim_hiding );
576 }
577 }
578 }
579 }
580 }
581 else{
582 vg_fatal_error( "Unknown store (%u)\n", shop->type );
583 }
584
585 if( button_down( k_srbind_mback ) ){
586 global_skateshop_exit();
587 return;
588 }
589 }
590
591 VG_STATIC void skateshop_render_boardshop(void)
592 {
593 world_instance *world = world_current_instance();
594 ent_skateshop *shop = global_skateshop.ptr_ent;
595
596 u32 slot_count = vg_list_size(global_skateshop.shop_view_slots);
597
598 ent_marker *mark_rack = mdl_arritm( &world->ent_marker,
599 mdl_entity_id_id(shop->boards.id_rack)),
600 *mark_display = mdl_arritm( &world->ent_marker,
601 mdl_entity_id_id(shop->boards.id_display));
602
603 int visibility[ SKATESHOP_VIEW_SLOT_MAX ];
604 SDL_AtomicLock( &global_skateshop.sl_cache_access );
605 for( u32 i=0; i<SKATESHOP_VIEW_SLOT_MAX; i++ ){
606 struct shop_view_slot *slot = &global_skateshop.shop_view_slots[i];
607
608 visibility[i] = 1;
609
610 if( slot->cache_ptr == NULL ) visibility[i] = 0;
611 else if( slot->cache_ptr->state != k_cache_board_state_loaded )
612 visibility[i] = 0;
613 }
614 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
615
616 /* Render loaded boards in the view slots */
617 for( u32 i=0; i<slot_count; i++ ){
618 struct shop_view_slot *slot = &global_skateshop.shop_view_slots[i];
619 float selected = 0.0f;
620
621 if( !visibility[i] ) goto fade_out;
622
623 mdl_transform xform;
624 transform_identity( &xform );
625
626 xform.co[0] = -((float)i - ((float)slot_count)*0.5f)*0.45f;
627 mdl_transform_mul( &mark_rack->transform, &xform, &xform );
628
629 if( slot->cache_ptr->reg_index == global_skateshop.selected_board_id ){
630 selected = 1.0f;
631 }
632
633 float t = slot->view_blend;
634 v3_lerp( xform.co, mark_display->transform.co, t, xform.co );
635 q_nlerp( xform.q, mark_display->transform.q, t, xform.q );
636 v3_lerp( xform.s, mark_display->transform.s, t, xform.s );
637
638 m4x3f mmdl;
639 mdl_transform_m4x3( &xform, mmdl );
640 render_board( &main_camera, world, &slot->cache_ptr->board, mmdl,
641 k_board_shader_entity );
642
643 fade_out:;
644 float rate = 5.0f*vg.time_delta;
645 slot->view_blend = vg_lerpf( slot->view_blend, selected, rate );
646 }
647
648 ent_marker *mark_info = mdl_arritm( &world->ent_marker,
649 mdl_entity_id_id(shop->boards.id_info));
650 m4x3f mtext, mrack;
651 mdl_transform_m4x3( &mark_info->transform, mtext );
652 mdl_transform_m4x3( &mark_rack->transform, mrack );
653
654 #if 0
655 const char *text_title = "Fish - Title";
656 const char *text_author = "by Shaniqua";
657 #endif
658
659 m4x3f mlocal, mmdl;
660 m4x3_identity( mlocal );
661
662 float scale = 0.2f,
663 thickness = 0.03f;
664
665 font3d_bind( &gui.font, &main_camera );
666 shader_model_font_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} );
667
668 /* Selection counter
669 * ------------------------------------------------------------------ */
670 m3x3_zero( mlocal );
671 v3_zero( mlocal[3] );
672 mlocal[0][0] = -scale*2.0f;
673 mlocal[1][2] = -scale*2.0f;
674 mlocal[2][1] = -thickness;
675 mlocal[3][2] = -0.7f;
676 m4x3_mul( mrack, mlocal, mmdl );
677
678 if( addon_count(k_workshop_file_type_board) ){
679 char buf[16];
680 int i=0;
681 i+=highscore_intl( buf+i, global_skateshop.selected_board_id+1, 3 );
682 buf[i++] = '/';
683 i+=highscore_intl( buf+i, addon_count(k_workshop_file_type_board), 3 );
684 buf[i++] = '\0';
685
686 font3d_simple_draw( &gui.font, 0, buf, &main_camera, mmdl );
687 }
688 else{
689 font3d_simple_draw( &gui.font, 0,
690 "Nothing installed", &main_camera, mmdl );
691 }
692
693 struct cache_board *cache_ptr = skateshop_selected_cache_if_loaded();
694 if( !cache_ptr ) return;
695
696 addon_reg *reg = cache_ptr->reg_ptr;
697
698 /* Skin title
699 * ----------------------------------------------------------------- */
700 m3x3_zero( mlocal );
701 m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
702 mlocal[3][0] = -font3d_string_width( &gui.font, 0, "Flubber" );
703 mlocal[3][0] *= scale*0.5f;
704 mlocal[3][1] = 0.1f;
705 mlocal[3][2] = 0.0f;
706 m4x3_mul( mtext, mlocal, mmdl );
707 font3d_simple_draw( &gui.font, 0, "Flubber", &main_camera, mmdl );
708
709 /* Author name
710 * ----------------------------------------------------------------- */
711 scale *= 0.4f;
712 m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
713 mlocal[3][0] = -font3d_string_width( &gui.font, 0, "JA" );
714 mlocal[3][0] *= scale*0.5f;
715 mlocal[3][1] = 0.0f;
716 mlocal[3][2] = 0.0f;
717 m4x3_mul( mtext, mlocal, mmdl );
718 font3d_simple_draw( &gui.font, 0, "JA", &main_camera, mmdl );
719 }
720
721 VG_STATIC void skateshop_render_charshop(void)
722 {
723 }
724
725 VG_STATIC void skateshop_render_worldshop(void)
726 {
727 world_instance *world = world_current_instance();
728
729 ent_skateshop *shop = global_skateshop.ptr_ent;
730 ent_marker *mark_display = mdl_arritm( &world->ent_marker,
731 mdl_entity_id_id(shop->worlds.id_display)),
732 *mark_info = mdl_arritm( &world->ent_marker,
733 mdl_entity_id_id(shop->boards.id_info));
734
735 /* Text */
736 char buftext[128], bufsubtext[128];
737 vg_str info, subtext;
738 vg_strnull( &info, buftext, 128 );
739 vg_strnull( &subtext, bufsubtext, 128 );
740
741 if( addon_count(k_workshop_file_type_world) ){
742 addon_reg *reg = get_addon_from_index( k_workshop_file_type_world,
743 global_skateshop.selected_world_id );
744
745 info.i+=highscore_intl( info.buffer+info.i,
746 global_skateshop.selected_world_id+1, 3 );
747 info.buffer[info.i++] = '/';
748 info.i+=highscore_intl( info.buffer+info.i,
749 addon_count(k_workshop_file_type_world), 3 );
750 info.buffer[info.i++] = ' ';
751 info.buffer[info.i] = '\0';
752
753 vg_strcat( &info, "AFAWJFKAW" );
754 if( skaterift.async_op == k_async_op_world_loading ||
755 skaterift.async_op == k_async_op_world_preloading ){
756 vg_strcat( &subtext, "Loading..." );
757 }
758 else{
759 vg_strcat( &subtext, "No information" );
760 }
761 }
762 else{
763 vg_strcat( &info, "No worlds installed" );
764 }
765
766
767 m4x3f mtext,mlocal,mtextmdl;
768 mdl_transform_m4x3( &mark_info->transform, mtext );
769
770 font3d_bind( &gui.font, &main_camera );
771 shader_model_font_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} );
772
773 float scale = 0.2f, thickness = 0.015f, scale1 = 0.08f;
774 m3x3_zero( mlocal );
775 m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
776 mlocal[3][0] = -font3d_string_width( &gui.font, 0, buftext );
777 mlocal[3][0] *= scale*0.5f;
778 mlocal[3][1] = 0.1f;
779 mlocal[3][2] = 0.0f;
780 m4x3_mul( mtext, mlocal, mtextmdl );
781 font3d_simple_draw( &gui.font, 0, buftext, &main_camera, mtextmdl );
782
783 m3x3_setdiagonalv3( mlocal, (v3f){ scale1, scale1, thickness } );
784 mlocal[3][0] = -font3d_string_width( &gui.font, 0, bufsubtext );
785 mlocal[3][0] *= scale1*0.5f;
786 mlocal[3][1] = -scale1*0.3f;
787 m4x3_mul( mtext, mlocal, mtextmdl );
788 font3d_simple_draw( &gui.font, 0, bufsubtext, &main_camera, mtextmdl );
789
790 /* pointcloud */
791 m4x3f mmdl;
792 mdl_transform_m4x3( &mark_display->transform, mmdl );
793 m4x3_rotate_y( mmdl, vg.time * 0.2 );
794
795 glEnable(GL_BLEND);
796 glBlendFunc(GL_ONE, GL_ONE);
797 glDisable(GL_DEPTH_TEST);
798 pointcloud_render( world, &main_camera, mmdl );
799 glDisable(GL_BLEND);
800 glEnable(GL_DEPTH_TEST);
801 }
802
803 /*
804 * World: render event
805 */
806 VG_STATIC void skateshop_render(void)
807 {
808 if( !global_skateshop.active ) return;
809
810 ent_skateshop *shop = global_skateshop.ptr_ent;
811
812 if( shop->type == k_skateshop_type_boardshop ){
813 skateshop_render_boardshop();
814 }
815 else if( shop->type == k_skateshop_type_charshop ){
816 skateshop_render_charshop();
817 }
818 else if( shop->type == k_skateshop_type_worldshop ){
819 skateshop_render_worldshop();
820 }
821 else{
822 vg_fatal_error( "Unknown store (%u)\n", shop->type );
823 }
824 }
825
826 /*
827 * Entity logic: entrance event
828 */
829 VG_STATIC void ent_skateshop_call( world_instance *world, ent_call *call )
830 {
831 u32 index = mdl_entity_id_id( call->id );
832 ent_skateshop *shop = mdl_arritm( &world->ent_skateshop, index );
833 vg_info( "skateshop_call\n" );
834
835 if( menu.active ) return;
836 if( skaterift.async_op != k_async_op_none ) return;
837
838 if( call->function == k_ent_function_trigger ){
839 if( localplayer.subsystem != k_player_subsystem_walk ){
840 return;
841 }
842
843 vg_info( "Entering skateshop\n" );
844
845 localplayer.immobile = 1;
846 menu.disable_open = 1;
847 global_skateshop.active = 1;
848
849 v3_zero( localplayer.rb.v );
850 v3_zero( localplayer.rb.w );
851 localplayer._walk.move_speed = 0.0f;
852 global_skateshop.ptr_ent = shop;
853
854 if( shop->type == k_skateshop_type_boardshop ){
855 skateshop_update_viewpage();
856 skateshop_op_board_scan();
857 }
858 else if( shop->type == k_skateshop_type_worldshop ){
859 pointcloud_animate( k_pointcloud_anim_opening );
860 skateshop_op_world_scan();
861 }
862 }
863 }
864
865 /*
866 * Entity logic: exit event
867 */
868 VG_STATIC void global_skateshop_exit(void)
869 {
870 vg_info( "exit skateshop\n" );
871 localplayer.immobile = 0;
872 global_skateshop.active = 0;
873 menu.disable_open = 0;
874 srinput.ignore_input_frames = 2;
875 }
876
877 #endif /* ENT_SKATESHOP_C */