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