1 #ifndef ENT_SKATESHOP_C
2 #define ENT_SKATESHOP_C
6 #include "vg/vg_steam_ugc.h"
8 #include "ent_skateshop.h"
13 #include "pointcloud.h"
14 #include "highscores.h"
19 * Checks string equality but does a hash check first
21 static inline int const_str_eq( u32 hash
, const char *str
, const char *cmp
)
23 if( hash
== vg_strdjb2(cmp
) )
24 if( !strcmp( str
, cmp
) )
30 * Get an existing cache instance, allocate a new one to be loaded, or NULL if
33 VG_STATIC
struct cache_board
*skateshop_cache_fetch_board( u32 registry_index
)
35 addon_reg
*reg
= NULL
;
37 if( registry_index
< addon_count( k_workshop_file_type_board
) ){
38 reg
= get_addon_from_index( k_workshop_file_type_board
, registry_index
);
45 /* lru eviction. should be a linked list maybe... */
46 double min_time
= 1e300
;
47 struct cache_board
*min_board
= NULL
;
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
];
53 if( cache_ptr
->state
== k_cache_board_state_load_request
) continue;
54 if( cache_ptr
->ref_count
) continue;
56 if( cache_ptr
->last_use_time
< min_time
){
57 min_time
= cache_ptr
->last_use_time
;
58 min_board
= cache_ptr
;
63 if( min_board
->state
== k_cache_board_state_loaded
){
64 player_board_unload( &min_board
->board
);
65 min_board
->reg_ptr
->userdata
= NULL
;
69 vg_info( "Allocating board (reg:%u) '%s'\n",
70 registry_index
, reg
->foldername
);
73 vg_info( "Pre-allocating board (reg:%u) 'null'\n", registry_index
);
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
;
83 vg_error( "No free boards to load registry!\n" );
86 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
90 VG_STATIC
void skateshop_update_viewpage(void)
92 u32 page
= global_skateshop
.selected_board_id
/SKATESHOP_VIEW_SLOT_MAX
;
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
;
98 if( slot
->cache_ptr
) unwatch_cache_board( slot
->cache_ptr
);
100 slot
->cache_ptr
= skateshop_cache_fetch_board( request_id
);
101 if( slot
->cache_ptr
) watch_cache_board( slot
->cache_ptr
);
106 * op/subroutine: k_workshop_op_item_load
107 * -----------------------------------------------------------------------------
111 * Reciever for board completion; only promotes the status in the main thread
113 VG_STATIC
void skateshop_async_board_loaded( void *payload
, u32 size
)
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
;
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
);
126 * Thread(or subroutine of thread), for checking view slots that weve installed.
127 * Load the model if a view slot wants it
129 VG_STATIC
void workshop_visibile_load_loop(void)
131 vg_info( "Running load loop\n" );
134 for( u32 i
=0; i
<SKATESHOP_BOARD_CACHE_MAX
; i
++ ){
135 struct cache_board
*cache_ptr
= &global_skateshop
.cache
[i
];
137 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
138 if( cache_ptr
->state
== k_cache_board_state_load_request
){
139 if( cache_ptr
->reg_index
>= addon_count(k_workshop_file_type_board
) ){
140 /* should maybe have a different value for this case */
141 cache_ptr
->state
= k_cache_board_state_none
;
142 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
146 /* continue with the request */
147 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
148 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_board
,
149 cache_ptr
->reg_index
);
150 cache_ptr
->reg_ptr
= reg
;
153 vg_strnull( &folder
, path_buf
, 4096 );
154 if( !addon_get_content_folder( reg
, &folder
) )
158 /* load content files
159 * --------------------------------- */
161 vg_str content_path
= folder
;
165 root
.buf
= reg
->metadata
;
166 root
.len
= reg
->metadata_len
;
167 root
.max
= sizeof(reg
->metadata
);
169 const char *kv_content
= vg_msg_seekkvstr( &root
, "content", 0 );
171 vg_strcat( &content_path
, "/" );
172 vg_strcat( &content_path
, kv_content
);
175 vg_error( "No content paths in metadata\n" );
179 if( !vg_strgood( &content_path
) ) {
180 vg_error( "Metadata path too long\n" );
184 vg_info( "Load content: %s\n", content_path
.buffer
);
185 player_board_load( &cache_ptr
->board
, content_path
.buffer
);
186 vg_async_call( skateshop_async_board_loaded
, cache_ptr
, 0 );
190 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
191 cache_ptr
->state
= k_cache_board_state_none
;
192 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
195 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
200 VG_STATIC
void world_scan_thread( void *_args
){
201 addon_mount_content_folder( k_workshop_file_type_world
, "maps", ".mdl" );
202 addon_mount_workshop_items();
203 vg_async_call( async_addon_reg_update
, NULL
, 0 );
208 * Asynchronous scan of local disk for worlds
210 VG_STATIC
void skateshop_op_world_scan(void){
211 skaterift_begin_op( k_async_op_world_scan
);
212 vg_loader_start( world_scan_thread
, NULL
);
215 VG_STATIC
void board_processview_thread( void *_args
){
216 workshop_visibile_load_loop();
220 VG_STATIC
void board_scan_thread( void *_args
){
221 addon_mount_content_folder( k_workshop_file_type_board
, "boards", ".mdl" );
222 addon_mount_workshop_items();
223 vg_async_call( async_addon_reg_update
, NULL
, 0 );
225 board_processview_thread(NULL
);
228 VG_STATIC
void skateshop_op_board_scan(void){
229 skaterift_begin_op( k_async_op_board_scan
);
230 vg_loader_start( board_scan_thread
, NULL
);
233 VG_STATIC
void skateshop_op_processview(void){
234 skaterift_begin_op( k_async_op_board_scan
);
235 vg_loader_start( board_processview_thread
, NULL
);
240 * -----------------------------------------------------------------------------
243 /* we can only keep using a viewslot pointer for multiple frames if we watch it
244 * using this function */
245 VG_STATIC
void watch_cache_board( struct cache_board
*ptr
){
246 if( ptr
->ref_count
>= 32 ){
247 vg_fatal_error( "dynamic board watch missmatch (limit is 32)\n" );
250 ptr
->last_use_time
= vg
.time
;
254 /* after this is called, the calling code only has access to the pointer for the
255 * duration of the rest of the frame */
256 VG_STATIC
void unwatch_cache_board( struct cache_board
*ptr
){
257 if( ptr
->ref_count
== 0 ){
258 vg_fatal_error( "dynamic board unwatch missmatch (no watchers)\n" );
268 VG_STATIC
void skateshop_init(void){
269 u32 cache_size
= sizeof(struct cache_board
)*SKATESHOP_BOARD_CACHE_MAX
;
270 global_skateshop
.cache
= vg_linear_alloc( vg_mem
.rtmemory
, cache_size
);
271 memset( global_skateshop
.cache
, 0, cache_size
);
273 for( u32 i
=0; i
<SKATESHOP_BOARD_CACHE_MAX
; i
++ ){
274 struct cache_board
*board
= &global_skateshop
.cache
[i
];
275 board
->state
= k_cache_board_state_none
;
276 board
->reg_ptr
= NULL
;
277 board
->reg_index
= 0xffffffff;
278 board
->last_use_time
= -99999.9;
279 board
->ref_count
= 0;
283 VG_STATIC
struct cache_board
*skateshop_selected_cache_if_loaded(void)
285 if( addon_count(k_workshop_file_type_board
) ){
286 addon_reg
*reg
= get_addon_from_index(k_workshop_file_type_board
,
287 global_skateshop
.selected_board_id
);
289 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
291 struct cache_board
*cache_ptr
= reg
->userdata
;
292 if( cache_ptr
->state
== k_cache_board_state_loaded
){
293 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
297 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
303 VG_STATIC
void pointcloud_async_end(void *_
, u32 __
)
305 pointcloud_animate( k_pointcloud_anim_opening
);
309 VG_STATIC
void pointcloud_clear_async(void *_
, u32 __
)
311 pointcloud
.count
= 0;
312 pointcloud_animate( k_pointcloud_anim_opening
);
316 VG_STATIC
void skateshop_preview_loader_thread( void *_data
)
318 addon_reg
*reg
= _data
;
322 vg_strnull( &path
, path_buf
, 4096 );
323 addon_get_content_folder( reg
, &path
);
324 vg_strcat( &path
, "/preview.bin" );
326 vg_linear_clear(vg_mem
.scratch
);
329 void *data
= vg_file_read( vg_mem
.scratch
, path_buf
, &size
);
331 if( size
< sizeof(pointcloud_buffer
) ){
332 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
336 vg_async_item
*call
= vg_async_alloc(size
);
337 pointcloud_buffer
*pcbuf
= call
->payload
;
338 memcpy( pcbuf
, data
, size
);
340 u32 point_count
= (size
-sizeof(pointcloud_buffer
)) /
341 sizeof(struct pointcloud_vert
);
342 pcbuf
->max
= point_count
;
343 pcbuf
->count
= point_count
;
344 pcbuf
->op
= k_pointcloud_op_clear
;
346 vg_async_dispatch( call
, async_pointcloud_sub
);
347 vg_async_call( pointcloud_async_end
, NULL
, 0 );
350 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
354 VG_STATIC
void skateshop_load_world_preview( addon_reg
*reg
)
356 skaterift_begin_op( k_async_op_world_load_preview
);
357 vg_loader_start( skateshop_preview_loader_thread
, reg
);
363 void temp_update_playermodel(void);
364 VG_STATIC
void global_skateshop_preupdate(void)
366 float rate
= vg_minf( 1.0f
, vg
.time_frame_delta
* 2.0f
);
367 global_skateshop
.factive
= vg_lerpf( global_skateshop
.factive
,
368 global_skateshop
.active
, rate
);
370 if( !global_skateshop
.active
) return;
372 world_instance
*world
= world_current_instance();
373 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
375 /* camera positioning */
376 ent_camera
*ref
= mdl_arritm( &world
->ent_camera
,
377 mdl_entity_id_id(shop
->id_camera
) );
379 v3f dir
= {0.0f
,-1.0f
,0.0f
};
380 mdl_transform_vector( &ref
->transform
, dir
, dir
);
381 m3x3_mulv( localplayer
.invbasis
, dir
, dir
);
382 player_vector_angles( localplayer
.cam_override_angles
, dir
, 1.0f
, 0.0f
);
385 if( shop
->type
== k_skateshop_type_boardshop
||
386 shop
->type
== k_skateshop_type_worldshop
){
387 ent_marker
*display
= mdl_arritm( &world
->ent_marker
,
388 mdl_entity_id_id(shop
->boards
.id_display
) );
390 v3_sub( display
->transform
.co
, localplayer
.rb
.co
, lookat
);
393 else if( shop
->type
== k_skateshop_type_charshop
){
394 v3_sub( ref
->transform
.co
, localplayer
.rb
.co
, lookat
);
397 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
400 q_axis_angle( localplayer
.rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
},
401 atan2f(lookat
[0],lookat
[2]) );
403 v3_copy( ref
->transform
.co
, localplayer
.cam_override_pos
);
404 localplayer
.cam_override_fov
= ref
->fov
;
405 localplayer
.cam_override_strength
= global_skateshop
.factive
;
408 if( shop
->type
== k_skateshop_type_boardshop
){
409 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
410 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
412 struct cache_board
*selected_cache
= skateshop_selected_cache_if_loaded();
414 if( selected_cache
){
415 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
420 * ----------------------
423 u32 opage
= global_skateshop
.selected_board_id
/SKATESHOP_VIEW_SLOT_MAX
;
425 if( button_down( k_srbind_mleft
) ){
426 if( global_skateshop
.selected_board_id
> 0 ){
427 global_skateshop
.selected_board_id
--;
431 if( button_down( k_srbind_mright
) ){
432 if( global_skateshop
.selected_board_id
+1 <
433 addon_count(k_workshop_file_type_board
) )
435 global_skateshop
.selected_board_id
++;
439 u32 npage
= global_skateshop
.selected_board_id
/SKATESHOP_VIEW_SLOT_MAX
;
441 if( opage
!= npage
){
442 skateshop_update_viewpage();
443 skateshop_op_processview();
445 else if( selected_cache
&& button_down( k_srbind_maccept
) ){
446 vg_info( "chose board from skateshop (%u)\n",
447 global_skateshop
.selected_board_id
);
449 if( localplayer
.board_view_slot
){
450 unwatch_cache_board( localplayer
.board_view_slot
);
453 localplayer
.board_view_slot
= selected_cache
;
454 watch_cache_board( localplayer
.board_view_slot
);
455 global_skateshop_exit();
459 else if( shop
->type
== k_skateshop_type_charshop
){
460 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
461 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
462 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
464 if( button_down( k_srbind_mleft
) ){
465 if( k_playermdl_id
> 0 ){
469 k_playermdl_id
= 2; /* HACK */
471 temp_update_playermodel(); /* HACK */
474 if( button_down( k_srbind_mright
) ){
475 if( k_playermdl_id
+1 < 3 ){
479 k_playermdl_id
= 0; /* HACK */
481 temp_update_playermodel(); /* HACK */
485 if( button_down( k_srbind_maccept
) ){
486 global_skateshop_exit();
489 else if( shop
->type
== k_skateshop_type_worldshop
){
493 if( addon_count(k_workshop_file_type_world
) &&
494 ((skaterift
.async_op
== k_async_op_none
)||
495 (skaterift
.async_op
== k_async_op_world_load_preview
))){
496 gui_helper_action( axis_display_string(k_sraxis_mbrowse_h
), "browse" );
500 if( (skaterift
.async_op
== k_async_op_none
) &&
501 global_skateshop
.selected_world_id
> 0 ){
502 gui_helper_action( button_display_string(k_srbind_maccept
),
510 if( button_down( k_srbind_mleft
) ){
511 if( global_skateshop
.selected_world_id
> 0 )
513 global_skateshop
.selected_world_id
--;
518 if( button_down( k_srbind_mright
) ){
519 if( global_skateshop
.selected_world_id
+1 <
520 addon_count(k_workshop_file_type_world
) )
522 global_skateshop
.selected_world_id
++;
528 if( change
&& pointcloud_idle() ){
529 pointcloud_animate( k_pointcloud_anim_hiding
);
532 if( skaterift
.async_op
== k_async_op_none
){
533 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
534 global_skateshop
.selected_world_id
);
536 /* automatically load in clouds */
537 if( loadable
&& button_down( k_srbind_maccept
) ){
538 vg_info( "Select rift (%u)\n",
539 global_skateshop
.selected_world_id
);
540 world_loader
.reg
= reg
;
541 world_loader
.override_name
[0] = '\0';
542 skaterift_change_world_start();
546 if( pointcloud
.anim
== k_pointcloud_anim_idle_closed
){
547 if( global_skateshop
.pointcloud_world_id
!=
548 global_skateshop
.selected_world_id
)
550 global_skateshop
.pointcloud_world_id
=
551 global_skateshop
.selected_world_id
;
552 skateshop_load_world_preview( reg
);
555 pointcloud_animate( k_pointcloud_anim_opening
);
558 else if( pointcloud
.anim
== k_pointcloud_anim_idle_open
){
559 if( global_skateshop
.pointcloud_world_id
!=
560 global_skateshop
.selected_world_id
)
562 pointcloud_animate( k_pointcloud_anim_hiding
);
569 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
572 if( button_down( k_srbind_mback
) ){
573 global_skateshop_exit();
578 VG_STATIC
void skateshop_render_boardshop(void)
580 world_instance
*world
= world_current_instance();
581 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
583 u32 slot_count
= vg_list_size(global_skateshop
.shop_view_slots
);
585 ent_marker
*mark_rack
= mdl_arritm( &world
->ent_marker
,
586 mdl_entity_id_id(shop
->boards
.id_rack
)),
587 *mark_display
= mdl_arritm( &world
->ent_marker
,
588 mdl_entity_id_id(shop
->boards
.id_display
));
590 int visibility
[ SKATESHOP_VIEW_SLOT_MAX
];
591 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
592 for( u32 i
=0; i
<SKATESHOP_VIEW_SLOT_MAX
; i
++ ){
593 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
597 if( slot
->cache_ptr
== NULL
) visibility
[i
] = 0;
598 else if( slot
->cache_ptr
->state
!= k_cache_board_state_loaded
)
601 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
603 /* Render loaded boards in the view slots */
604 for( u32 i
=0; i
<slot_count
; i
++ ){
605 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
606 float selected
= 0.0f
;
608 if( !visibility
[i
] ) goto fade_out
;
611 transform_identity( &xform
);
613 xform
.co
[0] = -((float)i
- ((float)slot_count
)*0.5f
)*0.45f
;
614 mdl_transform_mul( &mark_rack
->transform
, &xform
, &xform
);
616 if( slot
->cache_ptr
->reg_index
== global_skateshop
.selected_board_id
){
620 float t
= slot
->view_blend
;
621 v3_lerp( xform
.co
, mark_display
->transform
.co
, t
, xform
.co
);
622 q_nlerp( xform
.q
, mark_display
->transform
.q
, t
, xform
.q
);
623 v3_lerp( xform
.s
, mark_display
->transform
.s
, t
, xform
.s
);
626 mdl_transform_m4x3( &xform
, mmdl
);
627 render_board( &main_camera
, world
, &slot
->cache_ptr
->board
, mmdl
,
628 k_board_shader_entity
);
631 float rate
= 5.0f
*vg
.time_delta
;
632 slot
->view_blend
= vg_lerpf( slot
->view_blend
, selected
, rate
);
635 ent_marker
*mark_info
= mdl_arritm( &world
->ent_marker
,
636 mdl_entity_id_id(shop
->boards
.id_info
));
638 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
639 mdl_transform_m4x3( &mark_rack
->transform
, mrack
);
642 const char *text_title
= "Fish - Title";
643 const char *text_author
= "by Shaniqua";
647 m4x3_identity( mlocal
);
652 font3d_bind( &gui
.font
, &main_camera
);
653 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
656 * ------------------------------------------------------------------ */
658 v3_zero( mlocal
[3] );
659 mlocal
[0][0] = -scale
*2.0f
;
660 mlocal
[1][2] = -scale
*2.0f
;
661 mlocal
[2][1] = -thickness
;
662 mlocal
[3][2] = -0.7f
;
663 m4x3_mul( mrack
, mlocal
, mmdl
);
665 if( addon_count(k_workshop_file_type_board
) ){
668 i
+=highscore_intl( buf
+i
, global_skateshop
.selected_board_id
+1, 3 );
670 i
+=highscore_intl( buf
+i
, addon_count(k_workshop_file_type_board
), 3 );
673 font3d_simple_draw( &gui
.font
, 0, buf
, &main_camera
, mmdl
);
676 font3d_simple_draw( &gui
.font
, 0,
677 "Nothing installed", &main_camera
, mmdl
);
680 struct cache_board
*cache_ptr
= skateshop_selected_cache_if_loaded();
683 global_skateshop
.render
.item_title
= "";
684 global_skateshop
.render
.item_desc
= "";
688 if( global_skateshop
.render
.reg_id
!= global_skateshop
.selected_board_id
){
689 global_skateshop
.render
.item_title
= "";
690 global_skateshop
.render
.item_desc
= "";
691 addon_reg
*reg
= cache_ptr
->reg_ptr
;
693 root
.buf
= reg
->metadata
;
694 root
.len
= reg
->metadata_len
;
695 root
.max
= sizeof(reg
->metadata
);
697 vg_msg workshop
= root
;
698 if( vg_msg_seekframe( &workshop
, "workshop", 0 ) ){
699 const char *title
= vg_msg_seekkvstr( &workshop
, "title", 0 );
700 if( title
) global_skateshop
.render
.item_title
= title
;
702 const char *dsc
= vg_msg_seekkvstr( &workshop
, "author", 0 );
703 if( dsc
) global_skateshop
.render
.item_desc
= dsc
;
706 global_skateshop
.render
.reg_id
= global_skateshop
.selected_board_id
;
709 addon_reg
*reg
= cache_ptr
->reg_ptr
;
712 * ----------------------------------------------------------------- */
714 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
715 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0,
716 global_skateshop
.render
.item_title
);
717 mlocal
[3][0] *= scale
*0.5f
;
720 m4x3_mul( mtext
, mlocal
, mmdl
);
721 font3d_simple_draw( &gui
.font
, 0, global_skateshop
.render
.item_title
,
722 &main_camera
, mmdl
);
725 * ----------------------------------------------------------------- */
727 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
728 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0,
729 global_skateshop
.render
.item_desc
);
730 mlocal
[3][0] *= scale
*0.5f
;
733 m4x3_mul( mtext
, mlocal
, mmdl
);
734 font3d_simple_draw( &gui
.font
, 0, global_skateshop
.render
.item_desc
,
735 &main_camera
, mmdl
);
738 VG_STATIC
void skateshop_render_charshop(void)
742 VG_STATIC
void skateshop_render_worldshop(void)
744 world_instance
*world
= world_current_instance();
746 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
747 ent_marker
*mark_display
= mdl_arritm( &world
->ent_marker
,
748 mdl_entity_id_id(shop
->worlds
.id_display
)),
749 *mark_info
= mdl_arritm( &world
->ent_marker
,
750 mdl_entity_id_id(shop
->boards
.id_info
));
752 if( global_skateshop
.render
.world_reg
!= global_skateshop
.selected_world_id
){
753 global_skateshop
.render
.world_title
= "";
755 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
756 global_skateshop
.selected_world_id
);
758 root
.buf
= reg
->metadata
;
759 root
.len
= reg
->metadata_len
;
760 root
.max
= sizeof(reg
->metadata
);
761 vg_msg workshop
= root
;
762 if( vg_msg_seekframe( &workshop
, "workshop", 0 ) ){
763 global_skateshop
.render
.world_title
= vg_msg_seekkvstr( &workshop
,
766 global_skateshop
.render
.world_loc
= vg_msg_seekkvstr(&root
,"location",0);
767 global_skateshop
.render
.world_reg
= global_skateshop
.selected_world_id
;
771 char buftext
[128], bufsubtext
[128];
772 vg_str info
, subtext
;
773 vg_strnull( &info
, buftext
, 128 );
774 vg_strnull( &subtext
, bufsubtext
, 128 );
776 if( addon_count(k_workshop_file_type_world
) ){
777 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
778 global_skateshop
.selected_world_id
);
780 info
.i
+=highscore_intl( info
.buffer
+info
.i
,
781 global_skateshop
.selected_world_id
+1, 3 );
782 info
.buffer
[info
.i
++] = '/';
783 info
.i
+=highscore_intl( info
.buffer
+info
.i
,
784 addon_count(k_workshop_file_type_world
), 3 );
785 info
.buffer
[info
.i
++] = ' ';
786 info
.buffer
[info
.i
] = '\0';
788 vg_strcat( &info
, global_skateshop
.render
.world_title
);
789 if( skaterift
.async_op
== k_async_op_world_loading
||
790 skaterift
.async_op
== k_async_op_world_preloading
){
791 vg_strcat( &subtext
, "Loading..." );
794 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
795 global_skateshop
.selected_world_id
);
797 if( reg
->workshop_id
)
798 vg_strcat( &subtext
, "(Workshop) " );
800 vg_strcat( &subtext
, global_skateshop
.render
.world_loc
);
804 vg_strcat( &info
, "No worlds installed" );
808 m4x3f mtext
,mlocal
,mtextmdl
;
809 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
811 font3d_bind( &gui
.font
, &main_camera
);
812 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
814 float scale
= 0.2f
, thickness
= 0.015f
, scale1
= 0.08f
;
816 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
817 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, buftext
);
818 mlocal
[3][0] *= scale
*0.5f
;
821 m4x3_mul( mtext
, mlocal
, mtextmdl
);
822 font3d_simple_draw( &gui
.font
, 0, buftext
, &main_camera
, mtextmdl
);
824 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale1
, scale1
, thickness
} );
825 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, bufsubtext
);
826 mlocal
[3][0] *= scale1
*0.5f
;
827 mlocal
[3][1] = -scale1
*0.3f
;
828 m4x3_mul( mtext
, mlocal
, mtextmdl
);
829 font3d_simple_draw( &gui
.font
, 0, bufsubtext
, &main_camera
, mtextmdl
);
833 mdl_transform_m4x3( &mark_display
->transform
, mmdl
);
834 m4x3_rotate_y( mmdl
, vg
.time
* 0.2 );
837 glBlendFunc(GL_ONE
, GL_ONE
);
838 glDisable(GL_DEPTH_TEST
);
839 pointcloud_render( world
, &main_camera
, mmdl
);
841 glEnable(GL_DEPTH_TEST
);
845 * World: render event
847 VG_STATIC
void skateshop_render(void)
849 if( !global_skateshop
.active
) return;
851 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
853 if( shop
->type
== k_skateshop_type_boardshop
){
854 skateshop_render_boardshop();
856 else if( shop
->type
== k_skateshop_type_charshop
){
857 skateshop_render_charshop();
859 else if( shop
->type
== k_skateshop_type_worldshop
){
860 skateshop_render_worldshop();
863 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
868 * Entity logic: entrance event
870 VG_STATIC
void ent_skateshop_call( world_instance
*world
, ent_call
*call
)
872 u32 index
= mdl_entity_id_id( call
->id
);
873 ent_skateshop
*shop
= mdl_arritm( &world
->ent_skateshop
, index
);
874 vg_info( "skateshop_call\n" );
876 if( menu
.active
) return;
877 if( skaterift
.async_op
!= k_async_op_none
) return;
879 if( call
->function
== k_ent_function_trigger
){
880 if( localplayer
.subsystem
!= k_player_subsystem_walk
){
884 vg_info( "Entering skateshop\n" );
886 localplayer
.immobile
= 1;
887 menu
.disable_open
= 1;
888 global_skateshop
.active
= 1;
890 v3_zero( localplayer
.rb
.v
);
891 v3_zero( localplayer
.rb
.w
);
892 localplayer
._walk
.move_speed
= 0.0f
;
893 global_skateshop
.ptr_ent
= shop
;
895 if( shop
->type
== k_skateshop_type_boardshop
){
896 skateshop_update_viewpage();
897 skateshop_op_board_scan();
899 else if( shop
->type
== k_skateshop_type_worldshop
){
900 pointcloud_animate( k_pointcloud_anim_opening
);
901 skateshop_op_world_scan();
907 * Entity logic: exit event
909 VG_STATIC
void global_skateshop_exit(void)
911 vg_info( "exit skateshop\n" );
912 localplayer
.immobile
= 0;
913 global_skateshop
.active
= 0;
914 menu
.disable_open
= 0;
915 srinput
.ignore_input_frames
= 2;
918 #endif /* ENT_SKATESHOP_C */