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" );
135 for( u32 i
=0; i
<SKATESHOP_BOARD_CACHE_MAX
; i
++ ){
136 struct cache_board
*cache_ptr
= &global_skateshop
.cache
[i
];
138 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
139 if( cache_ptr
->state
== k_cache_board_state_load_request
){
140 if( cache_ptr
->reg_index
>= addon_count(k_workshop_file_type_board
) ){
141 /* should maybe have a different value for this case */
142 cache_ptr
->state
= k_cache_board_state_none
;
143 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
147 /* continue with the request */
148 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
150 cache_ptr
->reg_ptr
= get_addon_from_index( k_workshop_file_type_board
,
151 cache_ptr
->reg_index
);
153 if( cache_ptr
->reg_ptr
->workshop_id
){
154 vg_async_item
*call
=
155 vg_async_alloc( sizeof(struct async_workshop_filepath_info
) );
157 struct async_workshop_filepath_info
*info
= call
->payload
;
158 info
->buf
= path_buf
;
159 info
->id
= cache_ptr
->reg_ptr
->workshop_id
;
160 info
->len
= vg_list_size(path_buf
);
161 vg_async_dispatch( call
, async_workshop_get_filepath
);
162 vg_async_stall(); /* too bad! */
164 if( path_buf
[0] == '\0' ){
165 vg_error( "Failed SteamAPI_GetItemInstallInfo(" PRINTF_U64
")\n",
166 cache_ptr
->reg_ptr
->workshop_id
);
170 folder
.buffer
= path_buf
;
171 folder
.i
= strlen(path_buf
);
175 vg_strnull( &folder
, path_buf
, 4096 );
176 vg_strcat( &folder
, "boards/" );
177 vg_strcat( &folder
, cache_ptr
->reg_ptr
->foldername
);
180 /* load content files
181 * --------------------------------- */
183 vg_str content_path
= folder
;
186 vg_msg_init( &root
, cache_ptr
->reg_ptr
->metadata
,
187 cache_ptr
->reg_ptr
->metadata_len
);
189 const char *kv_content
= vg_msg_seekkvstr( &root
, "content", 0 );
191 vg_strcat( &content_path
, "/" );
192 vg_strcat( &content_path
, kv_content
);
195 vg_error( "No content paths in metadata\n" );
199 if( !vg_strgood( &content_path
) ) {
200 vg_error( "Metadata path too long\n" );
204 vg_info( "Load content: %s\n", content_path
.buffer
);
205 player_board_load( &cache_ptr
->board
, content_path
.buffer
);
206 vg_async_call( skateshop_async_board_loaded
, cache_ptr
, 0 );
210 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
211 cache_ptr
->state
= k_cache_board_state_none
;
212 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
215 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
220 VG_STATIC
void world_scan_thread( void *_args
){
221 addon_mount_content_folder( k_workshop_file_type_world
, "maps", ".mdl" );
222 addon_mount_workshop_items();
223 vg_async_call( async_addon_reg_update
, NULL
, 0 );
228 * Asynchronous scan of local disk for worlds
230 VG_STATIC
void skateshop_op_world_scan(void){
231 skaterift_begin_op( k_async_op_world_scan
);
232 vg_loader_start( world_scan_thread
, NULL
);
235 VG_STATIC
void board_scan_thread( void *_args
){
236 addon_mount_content_folder( k_workshop_file_type_board
, "boards", ".mdl" );
237 addon_mount_workshop_items();
238 vg_async_call( async_addon_reg_update
, NULL
, 0 );
240 workshop_visibile_load_loop();
244 VG_STATIC
void skateshop_op_board_scan(void){
245 skaterift_begin_op( k_async_op_board_scan
);
246 vg_loader_start( board_scan_thread
, NULL
);
251 * -----------------------------------------------------------------------------
254 /* we can only keep using a viewslot pointer for multiple frames if we watch it
255 * using this function */
256 VG_STATIC
void watch_cache_board( struct cache_board
*ptr
){
257 if( ptr
->ref_count
>= 32 ){
258 vg_fatal_error( "dynamic board watch missmatch (limit is 32)\n" );
261 ptr
->last_use_time
= vg
.time
;
265 /* after this is called, the calling code only has access to the pointer for the
266 * duration of the rest of the frame */
267 VG_STATIC
void unwatch_cache_board( struct cache_board
*ptr
){
268 if( ptr
->ref_count
== 0 ){
269 vg_fatal_error( "dynamic board unwatch missmatch (no watchers)\n" );
279 VG_STATIC
void skateshop_init(void){
280 u32 cache_size
= sizeof(struct cache_board
)*SKATESHOP_BOARD_CACHE_MAX
;
281 global_skateshop
.cache
= vg_linear_alloc( vg_mem
.rtmemory
, cache_size
);
282 memset( global_skateshop
.cache
, 0, cache_size
);
284 for( u32 i
=0; i
<SKATESHOP_BOARD_CACHE_MAX
; i
++ ){
285 struct cache_board
*board
= &global_skateshop
.cache
[i
];
286 board
->state
= k_cache_board_state_none
;
287 board
->reg_ptr
= NULL
;
288 board
->reg_index
= 0xffffffff;
289 board
->last_use_time
= -99999.9;
290 board
->ref_count
= 0;
294 VG_STATIC
struct cache_board
*skateshop_selected_cache_if_loaded(void)
296 if( addon_count(k_workshop_file_type_board
) ){
297 addon_reg
*reg
= get_addon_from_index(k_workshop_file_type_board
,
298 global_skateshop
.selected_board_id
);
300 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
302 struct cache_board
*cache_ptr
= reg
->userdata
;
303 if( cache_ptr
->state
== k_cache_board_state_loaded
){
304 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
308 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
314 VG_STATIC
void pointcloud_async_end(void *_
, u32 __
)
316 pointcloud_animate( k_pointcloud_anim_opening
);
320 VG_STATIC
void pointcloud_clear_async(void *_
, u32 __
)
322 pointcloud
.count
= 0;
323 pointcloud_animate( k_pointcloud_anim_opening
);
327 VG_STATIC
void skateshop_preview_loader_thread( void *_data
)
329 addon_reg
*reg
= _data
;
331 if( reg
->workshop_id
){
332 vg_error( "Workshop files unsupported\n" );
333 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
338 vg_strnull( &path
, path_buf
, 4096 );
339 vg_strcat( &path
, "maps/" );
340 vg_strcat( &path
, reg
->foldername
);
341 vg_strcat( &path
, "/preview.bin" );
343 vg_linear_clear(vg_mem
.scratch
);
346 void *data
= vg_file_read( vg_mem
.scratch
, path_buf
, &size
);
348 if( size
< sizeof(pointcloud_buffer
) ){
349 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
353 vg_async_item
*call
= vg_async_alloc(size
);
354 pointcloud_buffer
*pcbuf
= call
->payload
;
355 memcpy( pcbuf
, data
, size
);
357 u32 point_count
= (size
-sizeof(pointcloud_buffer
)) /
358 sizeof(struct pointcloud_vert
);
359 pcbuf
->max
= point_count
;
360 pcbuf
->count
= point_count
;
361 pcbuf
->op
= k_pointcloud_op_clear
;
363 vg_async_dispatch( call
, async_pointcloud_sub
);
364 vg_async_call( pointcloud_async_end
, NULL
, 0 );
367 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
372 VG_STATIC
void skateshop_load_world_preview( addon_reg
*reg
)
374 skaterift_begin_op( k_async_op_world_load_preview
);
375 vg_loader_start( skateshop_preview_loader_thread
, reg
);
381 void temp_update_playermodel(void);
382 VG_STATIC
void global_skateshop_preupdate(void)
384 float rate
= vg_minf( 1.0f
, vg
.time_frame_delta
* 2.0f
);
385 global_skateshop
.factive
= vg_lerpf( global_skateshop
.factive
,
386 global_skateshop
.active
, rate
);
388 if( !global_skateshop
.active
) return;
390 world_instance
*world
= world_current_instance();
391 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
393 /* camera positioning */
394 ent_camera
*ref
= mdl_arritm( &world
->ent_camera
,
395 mdl_entity_id_id(shop
->id_camera
) );
397 v3f dir
= {0.0f
,-1.0f
,0.0f
};
398 mdl_transform_vector( &ref
->transform
, dir
, dir
);
399 m3x3_mulv( localplayer
.invbasis
, dir
, dir
);
400 player_vector_angles( localplayer
.cam_override_angles
, dir
, 1.0f
, 0.0f
);
403 if( shop
->type
== k_skateshop_type_boardshop
||
404 shop
->type
== k_skateshop_type_worldshop
){
405 ent_marker
*display
= mdl_arritm( &world
->ent_marker
,
406 mdl_entity_id_id(shop
->boards
.id_display
) );
408 v3_sub( display
->transform
.co
, localplayer
.rb
.co
, lookat
);
411 else if( shop
->type
== k_skateshop_type_charshop
){
412 v3_sub( ref
->transform
.co
, localplayer
.rb
.co
, lookat
);
415 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
418 q_axis_angle( localplayer
.rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
},
419 atan2f(lookat
[0],lookat
[2]) );
421 v3_copy( ref
->transform
.co
, localplayer
.cam_override_pos
);
422 localplayer
.cam_override_fov
= ref
->fov
;
423 localplayer
.cam_override_strength
= global_skateshop
.factive
;
426 if( shop
->type
== k_skateshop_type_boardshop
){
427 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
428 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
430 struct cache_board
*selected_cache
= skateshop_selected_cache_if_loaded();
432 if( selected_cache
){
433 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
438 * ----------------------
441 if( button_down( k_srbind_mleft
) ){
442 if( global_skateshop
.selected_board_id
> 0 ){
443 global_skateshop
.selected_board_id
--;
447 if( button_down( k_srbind_mright
) ){
448 if( global_skateshop
.selected_board_id
+1 <
449 addon_count(k_workshop_file_type_board
) )
451 global_skateshop
.selected_board_id
++;
455 if( selected_cache
&& button_down( k_srbind_maccept
) ){
456 vg_info( "chose board from skateshop (%u)\n",
457 global_skateshop
.selected_board_id
);
459 if( localplayer
.board_view_slot
){
460 unwatch_cache_board( localplayer
.board_view_slot
);
463 localplayer
.board_view_slot
= selected_cache
;
464 watch_cache_board( localplayer
.board_view_slot
);
465 global_skateshop_exit();
469 else if( shop
->type
== k_skateshop_type_charshop
){
470 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
471 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
472 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
474 if( button_down( k_srbind_mleft
) ){
475 if( cl_playermdl_id
> 0 ){
479 cl_playermdl_id
= 2; /* HACK */
481 temp_update_playermodel(); /* HACK */
484 if( button_down( k_srbind_mright
) ){
485 if( cl_playermdl_id
+1 < 3 ){
489 cl_playermdl_id
= 0; /* HACK */
491 temp_update_playermodel(); /* HACK */
495 if( button_down( k_srbind_maccept
) ){
496 global_skateshop_exit();
499 else if( shop
->type
== k_skateshop_type_worldshop
){
503 if( addon_count(k_workshop_file_type_world
) &&
504 ((skaterift
.async_op
== k_async_op_none
)||
505 (skaterift
.async_op
== k_async_op_world_load_preview
))){
506 gui_helper_action( axis_display_string(k_sraxis_mbrowse_h
), "browse" );
510 if( (skaterift
.async_op
== k_async_op_none
) &&
511 global_skateshop
.selected_world_id
> 0 ){
512 gui_helper_action( button_display_string(k_srbind_maccept
),
520 if( button_down( k_srbind_mleft
) ){
521 if( global_skateshop
.selected_world_id
> 0 )
523 global_skateshop
.selected_world_id
--;
528 if( button_down( k_srbind_mright
) ){
529 if( global_skateshop
.selected_world_id
+1 <
530 addon_count(k_workshop_file_type_world
) )
532 global_skateshop
.selected_world_id
++;
538 if( change
&& pointcloud_idle() ){
539 pointcloud_animate( k_pointcloud_anim_hiding
);
542 if( skaterift
.async_op
== k_async_op_none
){
543 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
544 global_skateshop
.selected_world_id
);
546 /* automatically load in clouds */
547 if( loadable
&& button_down( k_srbind_maccept
) ){
548 vg_info( "Select rift (%u)\n",
549 global_skateshop
.selected_world_id
);
550 skaterift_change_world( reg
->foldername
);
554 if( pointcloud
.anim
== k_pointcloud_anim_idle_closed
){
555 if( global_skateshop
.pointcloud_world_id
!=
556 global_skateshop
.selected_world_id
)
558 global_skateshop
.pointcloud_world_id
=
559 global_skateshop
.selected_world_id
;
560 skateshop_load_world_preview( reg
);
563 pointcloud_animate( k_pointcloud_anim_opening
);
566 else if( pointcloud
.anim
== k_pointcloud_anim_idle_open
){
567 if( global_skateshop
.pointcloud_world_id
!=
568 global_skateshop
.selected_world_id
)
570 pointcloud_animate( k_pointcloud_anim_hiding
);
577 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
580 if( button_down( k_srbind_mback
) ){
581 global_skateshop_exit();
586 VG_STATIC
void skateshop_render_boardshop(void)
588 world_instance
*world
= world_current_instance();
589 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
591 u32 slot_count
= vg_list_size(global_skateshop
.shop_view_slots
);
593 ent_marker
*mark_rack
= mdl_arritm( &world
->ent_marker
,
594 mdl_entity_id_id(shop
->boards
.id_rack
)),
595 *mark_display
= mdl_arritm( &world
->ent_marker
,
596 mdl_entity_id_id(shop
->boards
.id_display
));
598 int visibility
[ SKATESHOP_VIEW_SLOT_MAX
];
599 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
600 for( u32 i
=0; i
<SKATESHOP_VIEW_SLOT_MAX
; i
++ ){
601 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
605 if( slot
->cache_ptr
== NULL
) visibility
[i
] = 0;
606 else if( slot
->cache_ptr
->state
!= k_cache_board_state_loaded
)
609 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
611 /* Render loaded boards in the view slots */
612 for( u32 i
=0; i
<slot_count
; i
++ ){
613 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
614 float selected
= 0.0f
;
616 if( !visibility
[i
] ) goto fade_out
;
619 transform_identity( &xform
);
621 xform
.co
[0] = -((float)i
- ((float)slot_count
)*0.5f
)*0.45f
;
622 mdl_transform_mul( &mark_rack
->transform
, &xform
, &xform
);
624 if( slot
->cache_ptr
->reg_index
== global_skateshop
.selected_board_id
){
628 float t
= slot
->view_blend
;
629 v3_lerp( xform
.co
, mark_display
->transform
.co
, t
, xform
.co
);
630 q_nlerp( xform
.q
, mark_display
->transform
.q
, t
, xform
.q
);
631 v3_lerp( xform
.s
, mark_display
->transform
.s
, t
, xform
.s
);
634 mdl_transform_m4x3( &xform
, mmdl
);
635 render_board( &main_camera
, world
, &slot
->cache_ptr
->board
, mmdl
,
636 k_board_shader_entity
);
639 float rate
= 5.0f
*vg
.time_delta
;
640 slot
->view_blend
= vg_lerpf( slot
->view_blend
, selected
, rate
);
643 ent_marker
*mark_info
= mdl_arritm( &world
->ent_marker
,
644 mdl_entity_id_id(shop
->boards
.id_info
));
646 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
647 mdl_transform_m4x3( &mark_rack
->transform
, mrack
);
650 const char *text_title
= "Fish - Title";
651 const char *text_author
= "by Shaniqua";
655 m4x3_identity( mlocal
);
660 font3d_bind( &gui
.font
, &main_camera
);
661 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
664 * ------------------------------------------------------------------ */
666 v3_zero( mlocal
[3] );
667 mlocal
[0][0] = -scale
*2.0f
;
668 mlocal
[1][2] = -scale
*2.0f
;
669 mlocal
[2][1] = -thickness
;
670 mlocal
[3][2] = -0.7f
;
671 m4x3_mul( mrack
, mlocal
, mmdl
);
673 if( addon_count(k_workshop_file_type_board
) ){
676 i
+=highscore_intl( buf
+i
, global_skateshop
.selected_board_id
+1, 3 );
678 i
+=highscore_intl( buf
+i
, addon_count(k_workshop_file_type_board
), 3 );
681 font3d_simple_draw( &gui
.font
, 0, buf
, &main_camera
, mmdl
);
684 font3d_simple_draw( &gui
.font
, 0,
685 "Nothing installed", &main_camera
, mmdl
);
688 struct cache_board
*cache_ptr
= skateshop_selected_cache_if_loaded();
691 global_skateshop
.render
.item_title
= "";
692 global_skateshop
.render
.item_desc
= "";
696 if( global_skateshop
.render
.reg_id
!= global_skateshop
.selected_board_id
){
697 addon_reg
*reg
= cache_ptr
->reg_ptr
;
699 vg_msg_init( &root
, reg
->metadata
, reg
->metadata_len
);
701 vg_msg workshop
= root
;
702 if( vg_msg_seekframe( &workshop
, "workshop", 0 ) ){
703 const char *title
= vg_msg_seekkvstr( &workshop
, "title", 0 );
704 if( title
) global_skateshop
.render
.item_title
= title
;
707 global_skateshop
.render
.reg_id
= global_skateshop
.selected_board_id
;
710 addon_reg
*reg
= cache_ptr
->reg_ptr
;
713 * ----------------------------------------------------------------- */
715 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
716 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0,
717 global_skateshop
.render
.item_title
);
718 mlocal
[3][0] *= scale
*0.5f
;
721 m4x3_mul( mtext
, mlocal
, mmdl
);
722 font3d_simple_draw( &gui
.font
, 0, global_skateshop
.render
.item_title
,
723 &main_camera
, mmdl
);
726 * ----------------------------------------------------------------- */
728 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
729 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, "JA" );
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 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
754 global_skateshop
.selected_world_id
);
756 vg_msg_init( &root
, reg
->metadata
, reg
->metadata_len
);
757 vg_msg workshop
= root
;
758 if( vg_msg_seekframe( &workshop
, "workshop", 0 ) ){
759 global_skateshop
.render
.world_title
= vg_msg_seekkvstr( &workshop
,
762 global_skateshop
.render
.world_loc
= vg_msg_seekkvstr(&root
,"location",0);
763 global_skateshop
.render
.world_reg
= global_skateshop
.selected_world_id
;
767 char buftext
[128], bufsubtext
[128];
768 vg_str info
, subtext
;
769 vg_strnull( &info
, buftext
, 128 );
770 vg_strnull( &subtext
, bufsubtext
, 128 );
772 if( addon_count(k_workshop_file_type_world
) ){
773 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
774 global_skateshop
.selected_world_id
);
776 info
.i
+=highscore_intl( info
.buffer
+info
.i
,
777 global_skateshop
.selected_world_id
+1, 3 );
778 info
.buffer
[info
.i
++] = '/';
779 info
.i
+=highscore_intl( info
.buffer
+info
.i
,
780 addon_count(k_workshop_file_type_world
), 3 );
781 info
.buffer
[info
.i
++] = ' ';
782 info
.buffer
[info
.i
] = '\0';
784 vg_strcat( &info
, global_skateshop
.render
.world_title
);
785 if( skaterift
.async_op
== k_async_op_world_loading
||
786 skaterift
.async_op
== k_async_op_world_preloading
){
787 vg_strcat( &subtext
, "Loading..." );
790 vg_strcat( &subtext
, global_skateshop
.render
.world_loc
);
794 vg_strcat( &info
, "No worlds installed" );
798 m4x3f mtext
,mlocal
,mtextmdl
;
799 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
801 font3d_bind( &gui
.font
, &main_camera
);
802 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
804 float scale
= 0.2f
, thickness
= 0.015f
, scale1
= 0.08f
;
806 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
807 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, buftext
);
808 mlocal
[3][0] *= scale
*0.5f
;
811 m4x3_mul( mtext
, mlocal
, mtextmdl
);
812 font3d_simple_draw( &gui
.font
, 0, buftext
, &main_camera
, mtextmdl
);
814 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale1
, scale1
, thickness
} );
815 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, bufsubtext
);
816 mlocal
[3][0] *= scale1
*0.5f
;
817 mlocal
[3][1] = -scale1
*0.3f
;
818 m4x3_mul( mtext
, mlocal
, mtextmdl
);
819 font3d_simple_draw( &gui
.font
, 0, bufsubtext
, &main_camera
, mtextmdl
);
823 mdl_transform_m4x3( &mark_display
->transform
, mmdl
);
824 m4x3_rotate_y( mmdl
, vg
.time
* 0.2 );
827 glBlendFunc(GL_ONE
, GL_ONE
);
828 glDisable(GL_DEPTH_TEST
);
829 pointcloud_render( world
, &main_camera
, mmdl
);
831 glEnable(GL_DEPTH_TEST
);
835 * World: render event
837 VG_STATIC
void skateshop_render(void)
839 if( !global_skateshop
.active
) return;
841 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
843 if( shop
->type
== k_skateshop_type_boardshop
){
844 skateshop_render_boardshop();
846 else if( shop
->type
== k_skateshop_type_charshop
){
847 skateshop_render_charshop();
849 else if( shop
->type
== k_skateshop_type_worldshop
){
850 skateshop_render_worldshop();
853 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
858 * Entity logic: entrance event
860 VG_STATIC
void ent_skateshop_call( world_instance
*world
, ent_call
*call
)
862 u32 index
= mdl_entity_id_id( call
->id
);
863 ent_skateshop
*shop
= mdl_arritm( &world
->ent_skateshop
, index
);
864 vg_info( "skateshop_call\n" );
866 if( menu
.active
) return;
867 if( skaterift
.async_op
!= k_async_op_none
) return;
869 if( call
->function
== k_ent_function_trigger
){
870 if( localplayer
.subsystem
!= k_player_subsystem_walk
){
874 vg_info( "Entering skateshop\n" );
876 localplayer
.immobile
= 1;
877 menu
.disable_open
= 1;
878 global_skateshop
.active
= 1;
880 v3_zero( localplayer
.rb
.v
);
881 v3_zero( localplayer
.rb
.w
);
882 localplayer
._walk
.move_speed
= 0.0f
;
883 global_skateshop
.ptr_ent
= shop
;
885 if( shop
->type
== k_skateshop_type_boardshop
){
886 skateshop_update_viewpage();
887 skateshop_op_board_scan();
889 else if( shop
->type
== k_skateshop_type_worldshop
){
890 pointcloud_animate( k_pointcloud_anim_opening
);
891 skateshop_op_world_scan();
897 * Entity logic: exit event
899 VG_STATIC
void global_skateshop_exit(void)
901 vg_info( "exit skateshop\n" );
902 localplayer
.immobile
= 0;
903 global_skateshop
.active
= 0;
904 menu
.disable_open
= 0;
905 srinput
.ignore_input_frames
= 2;
908 #endif /* ENT_SKATESHOP_C */