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
) )
31 * Get an existing cache instance, allocate a new one to be loaded, or NULL if
34 VG_STATIC
struct cache_board
*skateshop_cache_fetch_board( u32 registry_index
)
36 addon_reg
*reg
= NULL
;
38 if( registry_index
< addon_count( k_workshop_file_type_board
) ){
39 reg
= get_addon_from_index( k_workshop_file_type_board
, registry_index
);
47 /* lru eviction. should be a linked list maybe... */
48 double min_time
= 1e300
;
49 struct cache_board
*min_board
= NULL
;
51 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
52 for( u32 i
=0; i
<SKATESHOP_BOARD_CACHE_MAX
; i
++ ){
53 struct cache_board
*cache_ptr
= &global_skateshop
.cache
[i
];
55 if( cache_ptr
->state
== k_cache_board_state_load_request
) continue;
56 if( cache_ptr
->ref_count
) continue;
58 if( cache_ptr
->last_use_time
< min_time
){
59 min_time
= cache_ptr
->last_use_time
;
60 min_board
= cache_ptr
;
65 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
66 struct cache_board
*min_board
= lru_volatile_cache_board();
71 if( min_board
->state
== k_cache_board_state_loaded
){
72 player_board_unload( &min_board
->board
);
73 min_board
->reg_ptr
->userdata
= NULL
;
77 vg_info( "Allocating board (reg:%u) '%s'\n",
78 registry_index
, reg
->foldername
);
81 vg_info( "Pre-allocating board (reg:%u) 'null'\n", registry_index
);
84 min_board
->reg_ptr
= reg
;
85 min_board
->reg_index
= registry_index
;
86 min_board
->ref_count
= 0;
87 min_board
->state
= k_cache_board_state_load_request
;
90 vg_error( "No free boards to load registry!\n" );
93 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
97 VG_STATIC
void skateshop_update_viewpage(void)
99 u32 page
= global_skateshop
.selected_board_id
/SKATESHOP_VIEW_SLOT_MAX
;
101 for( u32 i
=0; i
<SKATESHOP_VIEW_SLOT_MAX
; i
++ ){
102 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
103 u32 request_id
= page
*SKATESHOP_VIEW_SLOT_MAX
+ i
;
105 if( slot
->cache_ptr
) unwatch_cache_board( slot
->cache_ptr
);
107 slot
->cache_ptr
= skateshop_cache_fetch_board( request_id
);
108 if( slot
->cache_ptr
) watch_cache_board( slot
->cache_ptr
);
113 * op/subroutine: k_workshop_op_item_load
114 * -----------------------------------------------------------------------------
118 * Reciever for board completion; only promotes the status in the main thread
120 VG_STATIC
void skateshop_async_board_loaded( void *payload
, u32 size
)
122 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
123 struct cache_board
*cache_ptr
= payload
;
124 cache_ptr
->state
= k_cache_board_state_loaded
;
126 cache_ptr
->reg_ptr
->userdata
= cache_ptr
;
127 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
128 vg_success( "Async board loaded (%s)\n", cache_ptr
->reg_ptr
->foldername
);
132 * Thread(or subroutine of thread), for checking view slots that weve installed.
133 * Load the model if a view slot wants it
135 VG_STATIC
void workshop_visibile_load_loop(void)
137 vg_info( "Running load loop\n" );
140 for( u32 i
=0; i
<SKATESHOP_BOARD_CACHE_MAX
; i
++ ){
141 struct cache_board
*cache_ptr
= &global_skateshop
.cache
[i
];
143 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
144 if( cache_ptr
->state
== k_cache_board_state_load_request
){
145 if( cache_ptr
->reg_index
>= addon_count(k_workshop_file_type_board
) ){
146 /* should maybe have a different value for this case */
147 cache_ptr
->state
= k_cache_board_state_none
;
148 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
152 /* continue with the request */
153 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
154 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_board
,
155 cache_ptr
->reg_index
);
156 cache_ptr
->reg_ptr
= reg
;
159 vg_strnull( &folder
, path_buf
, 4096 );
160 if( !addon_get_content_folder( reg
, &folder
) )
164 /* load content files
165 * --------------------------------- */
167 vg_str content_path
= folder
;
171 root
.buf
= reg
->metadata
;
172 root
.len
= reg
->metadata_len
;
173 root
.max
= sizeof(reg
->metadata
);
175 const char *kv_content
= vg_msg_seekkvstr( &root
, "content", 0 );
177 vg_strcat( &content_path
, "/" );
178 vg_strcat( &content_path
, kv_content
);
181 vg_error( "No content paths in metadata\n" );
185 if( !vg_strgood( &content_path
) ) {
186 vg_error( "Metadata path too long\n" );
190 vg_info( "Load content: %s\n", content_path
.buffer
);
191 player_board_load( &cache_ptr
->board
, content_path
.buffer
);
192 vg_async_call( skateshop_async_board_loaded
, cache_ptr
, 0 );
196 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
197 cache_ptr
->state
= k_cache_board_state_none
;
198 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
201 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
206 VG_STATIC
void world_scan_thread( void *_args
){
207 addon_mount_content_folder( k_workshop_file_type_world
, "maps", ".mdl" );
208 addon_mount_workshop_items();
209 vg_async_call( async_addon_reg_update
, NULL
, 0 );
214 * Asynchronous scan of local disk for worlds
216 VG_STATIC
void skateshop_op_world_scan(void){
217 skaterift_begin_op( k_async_op_world_scan
);
218 vg_loader_start( world_scan_thread
, NULL
);
221 VG_STATIC
void board_processview_thread( void *_args
){
222 workshop_visibile_load_loop();
226 VG_STATIC
void board_scan_thread( void *_args
){
227 addon_mount_content_folder( k_workshop_file_type_board
, "boards", ".mdl" );
228 addon_mount_workshop_items();
229 vg_async_call( async_addon_reg_update
, NULL
, 0 );
231 board_processview_thread(NULL
);
234 VG_STATIC
void skateshop_op_board_scan(void){
235 skaterift_begin_op( k_async_op_board_scan
);
236 vg_loader_start( board_scan_thread
, NULL
);
239 VG_STATIC
void skateshop_op_processview(void){
240 skaterift_begin_op( k_async_op_board_scan
);
241 vg_loader_start( board_processview_thread
, NULL
);
246 * -----------------------------------------------------------------------------
249 /* adds one more watch */
250 VG_STATIC
void watch_cache_board( struct cache_board
*ptr
){
251 if( ptr
->ref_count
>= 32 ){
252 vg_fatal_error( "dynamic board watch missmatch (limit is 32)\n" );
258 /* if after this no more watches, places back into the volatile list */
259 VG_STATIC
void unwatch_cache_board( struct cache_board
*ptr
){
260 if( ptr
->ref_count
== 0 ){
261 vg_fatal_error( "dynamic board unwatch missmatch (no watchers)\n" );
265 if( !ptr
->ref_count
){
266 struct cache_board
*head
= global_skateshop
.cache_head
,
267 *tail
= global_skateshop
.cache_tail
;
269 if( tail
) tail
->right
= ptr
;
271 global_skateshop
.cache_tail
= ptr
;
273 if( !head
) global_skateshop
.cache_head
= ptr
;
277 /* retrieve oldest pointer from the volatile list (and remove it) */
278 VG_STATIC
struct cache_board
*lru_volatile_cache_board(void){
279 struct cache_board
*head
= global_skateshop
.cache_head
,
280 *tail
= global_skateshop
.cache_tail
;
283 if( head
== tail
) global_skateshop
.cache_tail
= NULL
;
284 global_skateshop
.cache_head
= head
->right
;
296 VG_STATIC
void skateshop_init(void){
297 u32 cache_size
= sizeof(struct cache_board
)*SKATESHOP_BOARD_CACHE_MAX
;
298 global_skateshop
.cache
= vg_linear_alloc( vg_mem
.rtmemory
, cache_size
);
299 memset( global_skateshop
.cache
, 0, cache_size
);
301 for( i32 ib
=0; ib
<SKATESHOP_BOARD_CACHE_MAX
; ib
++ ){
302 i32 ia
= ib
-1, ic
= ib
+1;
303 struct cache_board
*arr
= global_skateshop
.cache
,
305 *pa
= ia
>=0? &arr
[ia
]: NULL
,
306 *pc
= ic
<SKATESHOP_BOARD_CACHE_MAX
? &arr
[ic
]: NULL
;
310 pb
->state
= k_cache_board_state_none
;
312 pb
->reg_index
= 0xffffffff;
316 global_skateshop
.cache_head
= global_skateshop
.cache
;
317 global_skateshop
.cache_tail
=
318 &global_skateshop
.cache
[SKATESHOP_BOARD_CACHE_MAX
-1];
321 VG_STATIC
struct cache_board
*skateshop_selected_cache_if_loaded(void)
323 if( addon_count(k_workshop_file_type_board
) ){
324 addon_reg
*reg
= get_addon_from_index(k_workshop_file_type_board
,
325 global_skateshop
.selected_board_id
);
327 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
329 struct cache_board
*cache_ptr
= reg
->userdata
;
330 if( cache_ptr
->state
== k_cache_board_state_loaded
){
331 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
335 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
341 VG_STATIC
void pointcloud_async_end(void *_
, u32 __
)
343 pointcloud_animate( k_pointcloud_anim_opening
);
346 VG_STATIC
void pointcloud_clear_async(void *_
, u32 __
)
348 pointcloud
.count
= 0;
349 pointcloud_animate( k_pointcloud_anim_opening
);
352 VG_STATIC
void skateshop_preview_loader_thread( void *_data
)
354 addon_reg
*reg
= _data
;
358 vg_strnull( &path
, path_buf
, 4096 );
359 addon_get_content_folder( reg
, &path
);
360 vg_strcat( &path
, "/preview.bin" );
362 vg_linear_clear(vg_mem
.scratch
);
365 void *data
= vg_file_read( vg_mem
.scratch
, path_buf
, &size
);
367 if( size
< sizeof(pointcloud_buffer
) ){
368 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
372 vg_async_item
*call
= vg_async_alloc(size
);
373 pointcloud_buffer
*pcbuf
= call
->payload
;
374 memcpy( pcbuf
, data
, size
);
376 u32 point_count
= (size
-sizeof(pointcloud_buffer
)) /
377 sizeof(struct pointcloud_vert
);
378 pcbuf
->max
= point_count
;
379 pcbuf
->count
= point_count
;
380 pcbuf
->op
= k_pointcloud_op_clear
;
382 vg_async_dispatch( call
, async_pointcloud_sub
);
383 vg_async_call( pointcloud_async_end
, NULL
, 0 );
386 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
390 VG_STATIC
void skateshop_preview_loader_thread_and_end( void *_data
){
391 skateshop_preview_loader_thread( _data
);
395 VG_STATIC
void skateshop_load_world_preview( addon_reg
*reg
)
397 skaterift_begin_op( k_async_op_world_load_preview
);
398 vg_loader_start( skateshop_preview_loader_thread_and_end
, reg
);
404 void temp_update_playermodel(void);
405 VG_STATIC
void global_skateshop_preupdate(void)
407 float rate
= vg_minf( 1.0f
, vg
.time_frame_delta
* 2.0f
);
408 global_skateshop
.factive
= vg_lerpf( global_skateshop
.factive
,
409 global_skateshop
.active
, rate
);
411 if( !global_skateshop
.active
) return;
413 world_instance
*world
= world_current_instance();
414 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
416 /* camera positioning */
417 ent_camera
*ref
= mdl_arritm( &world
->ent_camera
,
418 mdl_entity_id_id(shop
->id_camera
) );
420 v3f dir
= {0.0f
,-1.0f
,0.0f
};
421 mdl_transform_vector( &ref
->transform
, dir
, dir
);
422 m3x3_mulv( localplayer
.invbasis
, dir
, dir
);
423 player_vector_angles( localplayer
.cam_override_angles
, dir
, 1.0f
, 0.0f
);
426 if( shop
->type
== k_skateshop_type_boardshop
||
427 shop
->type
== k_skateshop_type_worldshop
){
428 ent_marker
*display
= mdl_arritm( &world
->ent_marker
,
429 mdl_entity_id_id(shop
->boards
.id_display
) );
431 v3_sub( display
->transform
.co
, localplayer
.rb
.co
, lookat
);
434 else if( shop
->type
== k_skateshop_type_charshop
){
435 v3_sub( ref
->transform
.co
, localplayer
.rb
.co
, lookat
);
438 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
441 q_axis_angle( localplayer
.rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
},
442 atan2f(lookat
[0],lookat
[2]) );
444 v3_copy( ref
->transform
.co
, localplayer
.cam_override_pos
);
445 localplayer
.cam_override_fov
= ref
->fov
;
446 localplayer
.cam_override_strength
= global_skateshop
.factive
;
449 if( shop
->type
== k_skateshop_type_boardshop
){
450 if( skaterift
.async_op
!= k_async_op_none
) return;
452 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
453 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
455 struct cache_board
*selected_cache
= skateshop_selected_cache_if_loaded();
457 if( selected_cache
){
458 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
463 * ----------------------
465 * TODO: Crash if switch page too quick, delist browse if loading....
468 u32 opage
= global_skateshop
.selected_board_id
/SKATESHOP_VIEW_SLOT_MAX
;
470 if( button_down( k_srbind_mleft
) ){
471 if( global_skateshop
.selected_board_id
> 0 ){
472 global_skateshop
.selected_board_id
--;
476 if( button_down( k_srbind_mright
) ){
477 if( global_skateshop
.selected_board_id
+1 <
478 addon_count(k_workshop_file_type_board
) )
480 global_skateshop
.selected_board_id
++;
484 u32 npage
= global_skateshop
.selected_board_id
/SKATESHOP_VIEW_SLOT_MAX
;
486 if( opage
!= npage
){
487 skateshop_update_viewpage();
488 skateshop_op_processview();
490 else if( selected_cache
&& button_down( k_srbind_maccept
) ){
491 vg_info( "chose board from skateshop (%u)\n",
492 global_skateshop
.selected_board_id
);
494 if( localplayer
.board_view_slot
){
495 unwatch_cache_board( localplayer
.board_view_slot
);
498 localplayer
.board_view_slot
= selected_cache
;
499 watch_cache_board( localplayer
.board_view_slot
);
500 global_skateshop_exit();
501 skaterift_write_savedata();
505 else if( shop
->type
== k_skateshop_type_charshop
){
506 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
507 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
508 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
510 if( button_down( k_srbind_mleft
) ){
511 if( k_playermdl_id
> 0 ){
515 k_playermdl_id
= 2; /* HACK */
517 temp_update_playermodel(); /* HACK */
520 if( button_down( k_srbind_mright
) ){
521 if( k_playermdl_id
+1 < 3 ){
525 k_playermdl_id
= 0; /* HACK */
527 temp_update_playermodel(); /* HACK */
531 if( button_down( k_srbind_maccept
) ){
532 global_skateshop_exit();
535 else if( shop
->type
== k_skateshop_type_worldshop
){
539 if( addon_count(k_workshop_file_type_world
) &&
540 ((skaterift
.async_op
== k_async_op_none
)||
541 (skaterift
.async_op
== k_async_op_world_load_preview
))){
542 gui_helper_action( axis_display_string(k_sraxis_mbrowse_h
), "browse" );
546 if( (skaterift
.async_op
== k_async_op_none
) &&
547 global_skateshop
.selected_world_id
> 0 ){
548 gui_helper_action( button_display_string(k_srbind_maccept
),
556 if( button_down( k_srbind_mleft
) ){
557 if( global_skateshop
.selected_world_id
> 0 )
559 global_skateshop
.selected_world_id
--;
564 if( button_down( k_srbind_mright
) ){
565 if( global_skateshop
.selected_world_id
+1 <
566 addon_count(k_workshop_file_type_world
) )
568 global_skateshop
.selected_world_id
++;
574 if( change
&& pointcloud_idle() ){
575 pointcloud_animate( k_pointcloud_anim_hiding
);
578 if( skaterift
.async_op
== k_async_op_none
){
579 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
580 global_skateshop
.selected_world_id
);
582 /* automatically load in clouds */
583 if( loadable
&& button_down( k_srbind_maccept
) ){
584 vg_info( "Select rift (%u)\n",
585 global_skateshop
.selected_world_id
);
586 world_loader
.reg
= reg
;
587 world_loader
.override_name
[0] = '\0';
588 skaterift_change_world_start();
592 if( pointcloud
.anim
== k_pointcloud_anim_idle_closed
){
593 if( global_skateshop
.pointcloud_world_id
!=
594 global_skateshop
.selected_world_id
)
596 global_skateshop
.pointcloud_world_id
=
597 global_skateshop
.selected_world_id
;
598 skateshop_load_world_preview( reg
);
601 pointcloud_animate( k_pointcloud_anim_opening
);
604 else if( pointcloud
.anim
== k_pointcloud_anim_idle_open
){
605 if( global_skateshop
.pointcloud_world_id
!=
606 global_skateshop
.selected_world_id
)
608 pointcloud_animate( k_pointcloud_anim_hiding
);
615 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
618 if( button_down( k_srbind_mback
) ){
619 global_skateshop_exit();
624 VG_STATIC
void skateshop_render_boardshop(void)
626 world_instance
*world
= world_current_instance();
627 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
629 u32 slot_count
= vg_list_size(global_skateshop
.shop_view_slots
);
631 ent_marker
*mark_rack
= mdl_arritm( &world
->ent_marker
,
632 mdl_entity_id_id(shop
->boards
.id_rack
)),
633 *mark_display
= mdl_arritm( &world
->ent_marker
,
634 mdl_entity_id_id(shop
->boards
.id_display
));
636 int visibility
[ SKATESHOP_VIEW_SLOT_MAX
];
637 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
638 for( u32 i
=0; i
<SKATESHOP_VIEW_SLOT_MAX
; i
++ ){
639 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
643 if( slot
->cache_ptr
== NULL
) visibility
[i
] = 0;
644 else if( slot
->cache_ptr
->state
!= k_cache_board_state_loaded
)
647 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
649 /* Render loaded boards in the view slots */
650 for( u32 i
=0; i
<slot_count
; i
++ ){
651 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
652 float selected
= 0.0f
;
654 if( !visibility
[i
] ) goto fade_out
;
657 transform_identity( &xform
);
659 xform
.co
[0] = -((float)i
- ((float)slot_count
)*0.5f
)*0.45f
;
660 mdl_transform_mul( &mark_rack
->transform
, &xform
, &xform
);
662 if( slot
->cache_ptr
->reg_index
== global_skateshop
.selected_board_id
){
666 float t
= slot
->view_blend
;
667 v3_lerp( xform
.co
, mark_display
->transform
.co
, t
, xform
.co
);
668 q_nlerp( xform
.q
, mark_display
->transform
.q
, t
, xform
.q
);
669 v3_lerp( xform
.s
, mark_display
->transform
.s
, t
, xform
.s
);
672 mdl_transform_m4x3( &xform
, mmdl
);
673 render_board( &main_camera
, world
, &slot
->cache_ptr
->board
, mmdl
,
674 k_board_shader_entity
);
677 float rate
= 5.0f
*vg
.time_delta
;
678 slot
->view_blend
= vg_lerpf( slot
->view_blend
, selected
, rate
);
681 ent_marker
*mark_info
= mdl_arritm( &world
->ent_marker
,
682 mdl_entity_id_id(shop
->boards
.id_info
));
684 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
685 mdl_transform_m4x3( &mark_rack
->transform
, mrack
);
688 const char *text_title
= "Fish - Title";
689 const char *text_author
= "by Shaniqua";
693 m4x3_identity( mlocal
);
698 font3d_bind( &gui
.font
, &main_camera
);
699 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
702 * ------------------------------------------------------------------ */
704 v3_zero( mlocal
[3] );
705 mlocal
[0][0] = -scale
*2.0f
;
706 mlocal
[1][2] = -scale
*2.0f
;
707 mlocal
[2][1] = -thickness
;
708 mlocal
[3][2] = -0.7f
;
709 m4x3_mul( mrack
, mlocal
, mmdl
);
711 if( addon_count(k_workshop_file_type_board
) ){
714 i
+=highscore_intl( buf
+i
, global_skateshop
.selected_board_id
+1, 3 );
716 i
+=highscore_intl( buf
+i
, addon_count(k_workshop_file_type_board
), 3 );
719 font3d_simple_draw( &gui
.font
, 0, buf
, &main_camera
, mmdl
);
722 font3d_simple_draw( &gui
.font
, 0,
723 "Nothing installed", &main_camera
, mmdl
);
726 struct cache_board
*cache_ptr
= skateshop_selected_cache_if_loaded();
729 global_skateshop
.render
.item_title
= "";
730 global_skateshop
.render
.item_desc
= "";
734 if( global_skateshop
.render
.reg_id
!= global_skateshop
.selected_board_id
){
735 global_skateshop
.render
.item_title
= "";
736 global_skateshop
.render
.item_desc
= "";
737 addon_reg
*reg
= cache_ptr
->reg_ptr
;
739 root
.buf
= reg
->metadata
;
740 root
.len
= reg
->metadata_len
;
741 root
.max
= sizeof(reg
->metadata
);
743 vg_msg workshop
= root
;
744 if( vg_msg_seekframe( &workshop
, "workshop", 0 ) ){
745 const char *title
= vg_msg_seekkvstr( &workshop
, "title", 0 );
746 if( title
) global_skateshop
.render
.item_title
= title
;
748 const char *dsc
= vg_msg_seekkvstr( &workshop
, "author", 0 );
749 if( dsc
) global_skateshop
.render
.item_desc
= dsc
;
752 global_skateshop
.render
.reg_id
= global_skateshop
.selected_board_id
;
755 addon_reg
*reg
= cache_ptr
->reg_ptr
;
758 * ----------------------------------------------------------------- */
760 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
761 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0,
762 global_skateshop
.render
.item_title
);
763 mlocal
[3][0] *= scale
*0.5f
;
766 m4x3_mul( mtext
, mlocal
, mmdl
);
767 font3d_simple_draw( &gui
.font
, 0, global_skateshop
.render
.item_title
,
768 &main_camera
, mmdl
);
771 * ----------------------------------------------------------------- */
773 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
774 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0,
775 global_skateshop
.render
.item_desc
);
776 mlocal
[3][0] *= scale
*0.5f
;
779 m4x3_mul( mtext
, mlocal
, mmdl
);
780 font3d_simple_draw( &gui
.font
, 0, global_skateshop
.render
.item_desc
,
781 &main_camera
, mmdl
);
784 VG_STATIC
void skateshop_render_charshop(void)
788 VG_STATIC
void skateshop_render_worldshop(void)
790 world_instance
*world
= world_current_instance();
792 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
793 ent_marker
*mark_display
= mdl_arritm( &world
->ent_marker
,
794 mdl_entity_id_id(shop
->worlds
.id_display
)),
795 *mark_info
= mdl_arritm( &world
->ent_marker
,
796 mdl_entity_id_id(shop
->boards
.id_info
));
798 if( global_skateshop
.render
.world_reg
!= global_skateshop
.selected_world_id
){
799 global_skateshop
.render
.world_title
= "";
801 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
802 global_skateshop
.selected_world_id
);
804 root
.buf
= reg
->metadata
;
805 root
.len
= reg
->metadata_len
;
806 root
.max
= sizeof(reg
->metadata
);
807 vg_msg workshop
= root
;
808 if( vg_msg_seekframe( &workshop
, "workshop", 0 ) ){
809 global_skateshop
.render
.world_title
= vg_msg_seekkvstr( &workshop
,
812 global_skateshop
.render
.world_loc
= vg_msg_seekkvstr(&root
,"location",0);
813 global_skateshop
.render
.world_reg
= global_skateshop
.selected_world_id
;
817 char buftext
[128], bufsubtext
[128];
818 vg_str info
, subtext
;
819 vg_strnull( &info
, buftext
, 128 );
820 vg_strnull( &subtext
, bufsubtext
, 128 );
822 if( addon_count(k_workshop_file_type_world
) ){
823 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
824 global_skateshop
.selected_world_id
);
826 info
.i
+=highscore_intl( info
.buffer
+info
.i
,
827 global_skateshop
.selected_world_id
+1, 3 );
828 info
.buffer
[info
.i
++] = '/';
829 info
.i
+=highscore_intl( info
.buffer
+info
.i
,
830 addon_count(k_workshop_file_type_world
), 3 );
831 info
.buffer
[info
.i
++] = ' ';
832 info
.buffer
[info
.i
] = '\0';
834 vg_strcat( &info
, global_skateshop
.render
.world_title
);
835 if( skaterift
.async_op
== k_async_op_world_loading
||
836 skaterift
.async_op
== k_async_op_world_preloading
){
837 vg_strcat( &subtext
, "Loading..." );
840 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
841 global_skateshop
.selected_world_id
);
843 if( reg
->workshop_id
)
844 vg_strcat( &subtext
, "(Workshop) " );
846 vg_strcat( &subtext
, global_skateshop
.render
.world_loc
);
850 vg_strcat( &info
, "No worlds installed" );
854 m4x3f mtext
,mlocal
,mtextmdl
;
855 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
857 font3d_bind( &gui
.font
, &main_camera
);
858 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
860 float scale
= 0.2f
, thickness
= 0.015f
, scale1
= 0.08f
;
862 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
863 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, buftext
);
864 mlocal
[3][0] *= scale
*0.5f
;
867 m4x3_mul( mtext
, mlocal
, mtextmdl
);
868 font3d_simple_draw( &gui
.font
, 0, buftext
, &main_camera
, mtextmdl
);
870 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale1
, scale1
, thickness
} );
871 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, bufsubtext
);
872 mlocal
[3][0] *= scale1
*0.5f
;
873 mlocal
[3][1] = -scale1
*0.3f
;
874 m4x3_mul( mtext
, mlocal
, mtextmdl
);
875 font3d_simple_draw( &gui
.font
, 0, bufsubtext
, &main_camera
, mtextmdl
);
879 mdl_transform_m4x3( &mark_display
->transform
, mmdl
);
880 m4x3_rotate_y( mmdl
, vg
.time
* 0.2 );
883 glBlendFunc(GL_ONE
, GL_ONE
);
884 glDisable(GL_DEPTH_TEST
);
885 pointcloud_render( world
, &main_camera
, mmdl
);
887 glEnable(GL_DEPTH_TEST
);
891 * World: render event
893 VG_STATIC
void skateshop_render(void)
895 if( !global_skateshop
.active
) return;
897 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
899 if( shop
->type
== k_skateshop_type_boardshop
){
900 skateshop_render_boardshop();
902 else if( shop
->type
== k_skateshop_type_charshop
){
903 skateshop_render_charshop();
905 else if( shop
->type
== k_skateshop_type_worldshop
){
906 skateshop_render_worldshop();
909 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
914 * Entity logic: entrance event
916 VG_STATIC
void ent_skateshop_call( world_instance
*world
, ent_call
*call
)
918 u32 index
= mdl_entity_id_id( call
->id
);
919 ent_skateshop
*shop
= mdl_arritm( &world
->ent_skateshop
, index
);
920 vg_info( "skateshop_call\n" );
922 if( menu
.active
) return;
923 if( skaterift
.async_op
!= k_async_op_none
) return;
925 if( call
->function
== k_ent_function_trigger
){
926 if( localplayer
.subsystem
!= k_player_subsystem_walk
){
930 vg_info( "Entering skateshop\n" );
932 localplayer
.immobile
= 1;
933 menu
.disable_open
= 1;
934 global_skateshop
.active
= 1;
936 v3_zero( localplayer
.rb
.v
);
937 v3_zero( localplayer
.rb
.w
);
938 localplayer
._walk
.move_speed
= 0.0f
;
939 global_skateshop
.ptr_ent
= shop
;
941 if( shop
->type
== k_skateshop_type_boardshop
){
942 skateshop_update_viewpage();
943 skateshop_op_board_scan();
945 else if( shop
->type
== k_skateshop_type_worldshop
){
946 pointcloud_animate( k_pointcloud_anim_opening
);
947 skateshop_op_world_scan();
953 * Entity logic: exit event
955 VG_STATIC
void global_skateshop_exit(void)
957 vg_info( "exit skateshop\n" );
958 localplayer
.immobile
= 0;
959 global_skateshop
.active
= 0;
960 menu
.disable_open
= 0;
961 srinput
.ignore_input_frames
= 2;
964 #endif /* ENT_SKATESHOP_C */