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