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"
20 * Checks string equality but does a hash check first
22 static inline int const_str_eq( u32 hash
, const char *str
, const char *cmp
)
24 if( hash
== vg_strdjb2(cmp
) )
25 if( !strcmp( str
, cmp
) )
32 * Get an existing cache instance, allocate a new one to be loaded, or NULL if
35 VG_STATIC
struct cache_board
*skateshop_cache_fetch_board( u32 registry_index
)
37 addon_reg
*reg
= NULL
;
39 if( registry_index
< addon_count( k_workshop_file_type_board
) ){
40 reg
= get_addon_from_index( k_workshop_file_type_board
, registry_index
);
47 SDL_AtomicLock( &addon_system
.sl_cache
);
48 struct cache_board
*min_board
= vg_pool_lru( &addon_system
.board_cache
);
51 if( min_board
->state
== k_cache_board_state_loaded
){
52 player_board_unload( &min_board
->board
);
53 min_board
->reg_ptr
->userdata
= NULL
;
57 vg_info( "Allocating board (reg:%u) '%s'\n",
58 registry_index
, reg
->foldername
);
61 vg_info( "Pre-allocating board (reg:%u) 'null'\n", registry_index
);
64 min_board
->reg_ptr
= reg
;
65 min_board
->reg_index
= registry_index
;
66 min_board
->state
= k_cache_board_state_load_request
;
69 vg_error( "No free boards to load registry!\n" );
72 SDL_AtomicUnlock( &addon_system
.sl_cache
);
77 VG_STATIC
void skateshop_update_viewpage(void){
78 u32 page
= global_skateshop
.selected_board_id
/SKATESHOP_VIEW_SLOT_MAX
;
80 struct addon_cache
*cache
= &addon_system
.cache
[k_workshop_file_type_board
];
81 vg_pool
*pool
= &cache
->pool
;
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
];
88 vg_pool_unwatch( pool
, slot
->cache_id
);
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
;
95 u16 id
= addon_cache_fetch( k_workshop_file_type_board
, request_id
);
97 id
= addon_cache_alloc( k_workshop_file_type_board
, request_id
);
100 SDL_AtomicLock( &addon_system
.sl_cache_using_resources
);
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
);
106 if( entry
->state
== k_addon_cache_state_loaded
)
107 dynamic_model_unload( &board
->mdl
);
109 entry
->state
= k_addon_cache_state_load_request
;
110 SDL_AtomicUnlock( &addon_system
.sl_cache_using_resources
);
115 vg_pool_watch( pool
, id
);
122 * op/subroutine: k_workshop_op_item_load
123 * -----------------------------------------------------------------------------
127 * Thread(or subroutine of thread), for checking view slots that weve installed.
128 * Load the model if a view slot wants it
130 VG_STATIC
void workshop_visibile_load_loop(void)
132 vg_info( "Running load loop\n" );
136 struct addon_cache
*cache
= &addon_system
.cache
[k_workshop_file_type_board
];
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
);
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
);
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
);
155 SDL_AtomicUnlock( &addon_system
.sl_cache_using_resources
);
157 /* continue with the request */
158 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_board
,
160 entry
->reg_ptr
= reg
;
163 vg_strnull( &folder
, path_buf
, 4096 );
164 if( !addon_get_content_folder( reg
, &folder
) )
168 /* load content files
169 * --------------------------------- */
170 vg_str content_path
= folder
;
173 root
.buf
= reg
->metadata
;
174 root
.len
= reg
->metadata_len
;
175 root
.max
= sizeof(reg
->metadata
);
177 const char *kv_content
= vg_msg_seekkvstr( &root
, "content", 0 );
179 vg_strcat( &content_path
, "/" );
180 vg_strcat( &content_path
, kv_content
);
183 vg_error( " No content paths in metadata\n" );
187 if( !vg_strgood( &content_path
) ) {
188 vg_error( " Metadata path too long\n" );
192 vg_info( " Load content: %s\n", content_path
.buffer
);
194 player_board_load( board
, content_path
.buffer
);
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
);
203 vg_success( " loaded (%s)\n", entry
->reg_ptr
->foldername
);
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
);
212 SDL_AtomicUnlock( &addon_system
.sl_cache_using_resources
);
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 );
225 * Asynchronous scan of local disk for worlds
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
);
232 VG_STATIC
void board_processview_thread( void *_args
){
233 workshop_visibile_load_loop();
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 );
242 board_processview_thread(NULL
);
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
);
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
);
257 * -----------------------------------------------------------------------------
263 VG_STATIC
void skateshop_init(void){
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
;
275 VG_STATIC
void pointcloud_async_end(void *_
, u32 __
)
277 pointcloud_animate( k_pointcloud_anim_opening
);
280 VG_STATIC
void pointcloud_clear_async(void *_
, u32 __
)
282 pointcloud
.count
= 0;
283 pointcloud_animate( k_pointcloud_anim_opening
);
286 VG_STATIC
void skateshop_preview_loader_thread( void *_data
)
288 addon_reg
*reg
= _data
;
292 vg_strnull( &path
, path_buf
, 4096 );
293 addon_get_content_folder( reg
, &path
);
294 vg_strcat( &path
, "/preview.bin" );
296 vg_linear_clear(vg_mem
.scratch
);
299 void *data
= vg_file_read( vg_mem
.scratch
, path_buf
, &size
);
301 if( size
< sizeof(pointcloud_buffer
) ){
302 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
306 vg_async_item
*call
= vg_async_alloc(size
);
307 pointcloud_buffer
*pcbuf
= call
->payload
;
308 memcpy( pcbuf
, data
, size
);
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
;
316 vg_async_dispatch( call
, async_pointcloud_sub
);
317 vg_async_call( pointcloud_async_end
, NULL
, 0 );
320 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
324 VG_STATIC
void skateshop_preview_loader_thread_and_end( void *_data
){
325 skateshop_preview_loader_thread( _data
);
329 VG_STATIC
void skateshop_load_world_preview( addon_reg
*reg
)
331 skaterift_begin_op( k_async_op_world_load_preview
);
332 vg_loader_start( skateshop_preview_loader_thread_and_end
, reg
);
338 void temp_update_playermodel(void);
339 VG_STATIC
void global_skateshop_preupdate(void)
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
);
345 if( !global_skateshop
.active
) return;
347 world_instance
*world
= world_current_instance();
348 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
350 /* camera positioning */
351 ent_camera
*ref
= mdl_arritm( &world
->ent_camera
,
352 mdl_entity_id_id(shop
->id_camera
) );
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
);
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
) );
365 v3_sub( display
->transform
.co
, localplayer
.rb
.co
, lookat
);
368 else if( shop
->type
== k_skateshop_type_charshop
){
369 v3_sub( ref
->transform
.co
, localplayer
.rb
.co
, lookat
);
372 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
375 q_axis_angle( localplayer
.rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
},
376 atan2f(lookat
[0],lookat
[2]) );
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
;
383 if( shop
->type
== k_skateshop_type_boardshop
){
384 if( skaterift
.async_op
!= k_async_op_none
) return;
386 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
387 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
389 u16 cache_id
= skateshop_selected_cache_id();
391 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
396 * ----------------------
398 u32 opage
= global_skateshop
.selected_board_id
/SKATESHOP_VIEW_SLOT_MAX
;
400 if( button_down( k_srbind_mleft
) ){
401 if( global_skateshop
.selected_board_id
> 0 ){
402 global_skateshop
.selected_board_id
--;
406 if( button_down( k_srbind_mright
) ){
407 if( global_skateshop
.selected_board_id
+1 <
408 addon_count(k_workshop_file_type_board
) )
410 global_skateshop
.selected_board_id
++;
414 u32 npage
= global_skateshop
.selected_board_id
/SKATESHOP_VIEW_SLOT_MAX
;
416 if( opage
!= npage
){
417 skateshop_update_viewpage();
418 skateshop_op_processview();
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
);
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
);
428 if( localplayer
.board_view_slot
){
429 vg_pool_unwatch( &cache
->pool
, localplayer
.board_view_slot
);
432 localplayer
.board_view_slot
= cache_id
;
433 vg_pool_watch( &cache
->pool
, cache_id
);
434 global_skateshop_exit();
435 skaterift_write_savedata();
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" );
444 if( button_down( k_srbind_mleft
) ){
445 if( k_playermdl_id
> 0 ){
449 k_playermdl_id
= 2; /* HACK */
451 temp_update_playermodel(); /* HACK */
454 if( button_down( k_srbind_mright
) ){
455 if( k_playermdl_id
+1 < 3 ){
459 k_playermdl_id
= 0; /* HACK */
461 temp_update_playermodel(); /* HACK */
465 if( button_down( k_srbind_maccept
) ){
466 global_skateshop_exit();
469 else if( shop
->type
== k_skateshop_type_worldshop
){
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" );
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
),
490 if( button_down( k_srbind_mleft
) ){
491 if( global_skateshop
.selected_world_id
> 0 )
493 global_skateshop
.selected_world_id
--;
498 if( button_down( k_srbind_mright
) ){
499 if( global_skateshop
.selected_world_id
+1 <
500 addon_count(k_workshop_file_type_world
) )
502 global_skateshop
.selected_world_id
++;
508 if( change
&& pointcloud_idle() ){
509 pointcloud_animate( k_pointcloud_anim_hiding
);
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
);
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();
526 if( pointcloud
.anim
== k_pointcloud_anim_idle_closed
){
527 if( global_skateshop
.pointcloud_world_id
!=
528 global_skateshop
.selected_world_id
)
530 global_skateshop
.pointcloud_world_id
=
531 global_skateshop
.selected_world_id
;
532 skateshop_load_world_preview( reg
);
535 pointcloud_animate( k_pointcloud_anim_opening
);
538 else if( pointcloud
.anim
== k_pointcloud_anim_idle_open
){
539 if( global_skateshop
.pointcloud_world_id
!=
540 global_skateshop
.selected_world_id
)
542 pointcloud_animate( k_pointcloud_anim_hiding
);
549 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
552 if( button_down( k_srbind_mback
) ){
553 global_skateshop_exit();
558 VG_STATIC
void skateshop_render_boardshop(void)
560 world_instance
*world
= world_current_instance();
561 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
563 u32 slot_count
= vg_list_size(global_skateshop
.shop_view_slots
);
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
));
570 SDL_AtomicLock( &addon_system
.sl_cache_using_resources
);
571 struct addon_cache
*cache
= &addon_system
.cache
[k_workshop_file_type_board
];
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
;
578 if( !slot
->cache_id
)
581 addon_cache_entry
*entry
= vg_pool_item( &cache
->pool
, slot
->cache_id
);
583 if( entry
->state
!= k_addon_cache_state_loaded
)
586 struct player_board
*board
=
587 addon_cache_item( k_workshop_file_type_board
, slot
->cache_id
);
590 transform_identity( &xform
);
592 xform
.co
[0] = -((float)i
- ((float)slot_count
)*0.5f
)*0.45f
;
593 mdl_transform_mul( &mark_rack
->transform
, &xform
, &xform
);
596 if( entry
->reg_index
== global_skateshop
.selected_board_id
){
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
);
606 mdl_transform_m4x3( &xform
, mmdl
);
607 render_board( &main_camera
, world
, board
, mmdl
, k_board_shader_entity
);
610 float rate
= 5.0f
*vg
.time_delta
;
611 slot
->view_blend
= vg_lerpf( slot
->view_blend
, selected
, rate
);
614 ent_marker
*mark_info
= mdl_arritm( &world
->ent_marker
,
615 mdl_entity_id_id(shop
->boards
.id_info
));
617 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
618 mdl_transform_m4x3( &mark_rack
->transform
, mrack
);
621 const char *text_title
= "Fish - Title";
622 const char *text_author
= "by Shaniqua";
626 m4x3_identity( mlocal
);
631 font3d_bind( &gui
.font
, &main_camera
);
632 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
635 * ------------------------------------------------------------------ */
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
);
644 if( addon_count(k_workshop_file_type_board
) ){
647 i
+=highscore_intl( buf
+i
, global_skateshop
.selected_board_id
+1, 3 );
649 i
+=highscore_intl( buf
+i
, addon_count(k_workshop_file_type_board
), 3 );
652 font3d_simple_draw( &gui
.font
, 0, buf
, &main_camera
, mmdl
);
655 font3d_simple_draw( &gui
.font
, 0,
656 "Nothing installed", &main_camera
, mmdl
);
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
;
663 if( entry
) reg
= entry
->reg_ptr
;
666 SDL_AtomicUnlock( &addon_system
.sl_cache_using_resources
);
667 global_skateshop
.render
.item_title
= "";
668 global_skateshop
.render
.item_desc
= "";
672 if( global_skateshop
.render
.reg_id
!= global_skateshop
.selected_board_id
){
673 global_skateshop
.render
.item_title
= "";
674 global_skateshop
.render
.item_desc
= "";
676 root
.buf
= reg
->metadata
;
677 root
.len
= reg
->metadata_len
;
678 root
.max
= sizeof(reg
->metadata
);
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
;
685 const char *dsc
= vg_msg_seekkvstr( &workshop
, "author", 0 );
686 if( dsc
) global_skateshop
.render
.item_desc
= dsc
;
689 global_skateshop
.render
.reg_id
= global_skateshop
.selected_board_id
;
693 * ----------------------------------------------------------------- */
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
;
701 m4x3_mul( mtext
, mlocal
, mmdl
);
702 font3d_simple_draw( &gui
.font
, 0, global_skateshop
.render
.item_title
,
703 &main_camera
, mmdl
);
706 * ----------------------------------------------------------------- */
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
;
714 m4x3_mul( mtext
, mlocal
, mmdl
);
715 font3d_simple_draw( &gui
.font
, 0, global_skateshop
.render
.item_desc
,
716 &main_camera
, mmdl
);
718 SDL_AtomicUnlock( &addon_system
.sl_cache_using_resources
);
721 VG_STATIC
void skateshop_render_charshop(void)
725 VG_STATIC
void skateshop_render_worldshop(void)
727 world_instance
*world
= world_current_instance();
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
));
735 if( global_skateshop
.render
.world_reg
!= global_skateshop
.selected_world_id
){
736 global_skateshop
.render
.world_title
= "";
738 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
739 global_skateshop
.selected_world_id
);
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
,
749 global_skateshop
.render
.world_loc
= vg_msg_seekkvstr(&root
,"location",0);
750 global_skateshop
.render
.world_reg
= global_skateshop
.selected_world_id
;
754 char buftext
[128], bufsubtext
[128];
755 vg_str info
, subtext
;
756 vg_strnull( &info
, buftext
, 128 );
757 vg_strnull( &subtext
, bufsubtext
, 128 );
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
);
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';
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..." );
777 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
778 global_skateshop
.selected_world_id
);
780 if( reg
->workshop_id
)
781 vg_strcat( &subtext
, "(Workshop) " );
783 vg_strcat( &subtext
, global_skateshop
.render
.world_loc
);
787 vg_strcat( &info
, "No worlds installed" );
791 m4x3f mtext
,mlocal
,mtextmdl
;
792 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
794 font3d_bind( &gui
.font
, &main_camera
);
795 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
797 float scale
= 0.2f
, thickness
= 0.015f
, scale1
= 0.08f
;
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
;
804 m4x3_mul( mtext
, mlocal
, mtextmdl
);
805 font3d_simple_draw( &gui
.font
, 0, buftext
, &main_camera
, mtextmdl
);
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
);
816 mdl_transform_m4x3( &mark_display
->transform
, mmdl
);
817 m4x3_rotate_y( mmdl
, vg
.time
* 0.2 );
820 glBlendFunc(GL_ONE
, GL_ONE
);
821 glDisable(GL_DEPTH_TEST
);
822 pointcloud_render( world
, &main_camera
, mmdl
);
824 glEnable(GL_DEPTH_TEST
);
828 * World: render event
830 VG_STATIC
void skateshop_render(void)
832 if( !global_skateshop
.active
) return;
834 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
836 if( shop
->type
== k_skateshop_type_boardshop
){
837 skateshop_render_boardshop();
839 else if( shop
->type
== k_skateshop_type_charshop
){
840 skateshop_render_charshop();
842 else if( shop
->type
== k_skateshop_type_worldshop
){
843 skateshop_render_worldshop();
846 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
851 * Entity logic: entrance event
853 VG_STATIC
void ent_skateshop_call( world_instance
*world
, ent_call
*call
)
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" );
859 if( menu
.active
) return;
860 if( skaterift
.async_op
!= k_async_op_none
) return;
862 if( call
->function
== k_ent_function_trigger
){
863 if( localplayer
.subsystem
!= k_player_subsystem_walk
){
867 vg_info( "Entering skateshop\n" );
869 localplayer
.immobile
= 1;
870 menu
.disable_open
= 1;
871 global_skateshop
.active
= 1;
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
;
878 if( shop
->type
== k_skateshop_type_boardshop
){
879 skateshop_update_viewpage();
880 skateshop_op_board_scan();
882 else if( shop
->type
== k_skateshop_type_charshop
){
885 else if( shop
->type
== k_skateshop_type_worldshop
){
886 pointcloud_animate( k_pointcloud_anim_opening
);
887 skateshop_op_world_scan();
893 * Entity logic: exit event
895 VG_STATIC
void global_skateshop_exit(void)
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;
904 #endif /* ENT_SKATESHOP_C */