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
;
187 vg_msg_init( &msg
, cache_ptr
->reg_ptr
->metadata
,
188 cache_ptr
->reg_ptr
->metadata_len
);
190 while( vg_msg_next( &msg
, &cmd
) ){
191 if( (msg
.depth
== 0) && (cmd
.code
== k_vg_msg_code_kvstring
) ){
192 if( VG_STRDJB2_EQ( "content", cmd
.key
, cmd
.key_djb2
) ){
193 vg_strcat( &content_path
, "/" );
194 vg_strcat( &content_path
, cmd
.value
._buf
);
201 if( !vg_strgood( &content_path
) ) {
202 vg_error( "Metadata path too long\n" );
207 vg_error( "No content paths in metadata\n" );
211 vg_info( "Load content: %s\n", content_path
.buffer
);
212 player_board_load( &cache_ptr
->board
, content_path
.buffer
);
213 vg_async_call( skateshop_async_board_loaded
, cache_ptr
, 0 );
217 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
218 cache_ptr
->state
= k_cache_board_state_none
;
219 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
222 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
227 VG_STATIC
void world_scan_thread( void *_args
){
228 addon_mount_local_folder( k_workshop_file_type_world
, "maps", ".mdl" );
229 addon_mount_workshop_items();
230 vg_async_call( async_addon_reg_update
, NULL
, 0 );
235 * Asynchronous scan of local disk for worlds
237 VG_STATIC
void skateshop_op_world_scan(void){
238 skaterift_begin_op( k_async_op_world_scan
);
239 vg_loader_start( world_scan_thread
, NULL
);
242 VG_STATIC
void board_scan_thread( void *_args
){
243 addon_mount_local_folder( k_workshop_file_type_board
, "boards", ".mdl" );
244 addon_mount_workshop_items();
245 vg_async_call( async_addon_reg_update
, NULL
, 0 );
247 workshop_visibile_load_loop();
251 VG_STATIC
void skateshop_op_board_scan(void){
252 skaterift_begin_op( k_async_op_board_scan
);
253 vg_loader_start( board_scan_thread
, NULL
);
258 * -----------------------------------------------------------------------------
261 /* we can only keep using a viewslot pointer for multiple frames if we watch it
262 * using this function */
263 VG_STATIC
void watch_cache_board( struct cache_board
*ptr
){
264 if( ptr
->ref_count
>= 32 ){
265 vg_fatal_error( "dynamic board watch missmatch (limit is 32)\n" );
268 ptr
->last_use_time
= vg
.time
;
272 /* after this is called, the calling code only has access to the pointer for the
273 * duration of the rest of the frame */
274 VG_STATIC
void unwatch_cache_board( struct cache_board
*ptr
){
275 if( ptr
->ref_count
== 0 ){
276 vg_fatal_error( "dynamic board unwatch missmatch (no watchers)\n" );
286 VG_STATIC
void skateshop_init(void){
287 u32 cache_size
= sizeof(struct cache_board
)*SKATESHOP_BOARD_CACHE_MAX
;
288 global_skateshop
.cache
= vg_linear_alloc( vg_mem
.rtmemory
, cache_size
);
289 memset( global_skateshop
.cache
, 0, cache_size
);
291 for( u32 i
=0; i
<SKATESHOP_BOARD_CACHE_MAX
; i
++ ){
292 struct cache_board
*board
= &global_skateshop
.cache
[i
];
293 board
->state
= k_cache_board_state_none
;
294 board
->reg_ptr
= NULL
;
295 board
->reg_index
= 0xffffffff;
296 board
->last_use_time
= -99999.9;
297 board
->ref_count
= 0;
301 VG_STATIC
struct cache_board
*skateshop_selected_cache_if_loaded(void)
303 if( addon_count(k_workshop_file_type_board
) ){
304 addon_reg
*reg
= get_addon_from_index(k_workshop_file_type_board
,
305 global_skateshop
.selected_board_id
);
307 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
309 struct cache_board
*cache_ptr
= reg
->userdata
;
310 if( cache_ptr
->state
== k_cache_board_state_loaded
){
311 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
315 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
321 VG_STATIC
void pointcloud_async_end(void *_
, u32 __
)
323 pointcloud_animate( k_pointcloud_anim_opening
);
327 VG_STATIC
void pointcloud_clear_async(void *_
, u32 __
)
329 pointcloud
.count
= 0;
330 pointcloud_animate( k_pointcloud_anim_opening
);
334 VG_STATIC
void skateshop_preview_loader_thread( void *_data
)
336 addon_reg
*reg
= _data
;
338 if( reg
->workshop_id
){
339 vg_error( "Workshop files unsupported\n" );
340 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
345 vg_strnull( &path
, path_buf
, 4096 );
346 vg_strcat( &path
, "maps/" );
347 vg_strcat( &path
, reg
->foldername
);
348 vg_strcat( &path
, "/preview.bin" );
350 vg_linear_clear(vg_mem
.scratch
);
353 void *data
= vg_file_read( vg_mem
.scratch
, path_buf
, &size
);
355 if( size
< sizeof(pointcloud_buffer
) ){
356 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
360 vg_async_item
*call
= vg_async_alloc(size
);
361 pointcloud_buffer
*pcbuf
= call
->payload
;
362 memcpy( pcbuf
, data
, size
);
364 u32 point_count
= (size
-sizeof(pointcloud_buffer
)) /
365 sizeof(struct pointcloud_vert
);
366 pcbuf
->max
= point_count
;
367 pcbuf
->count
= point_count
;
368 pcbuf
->op
= k_pointcloud_op_clear
;
370 vg_async_dispatch( call
, async_pointcloud_sub
);
371 vg_async_call( pointcloud_async_end
, NULL
, 0 );
374 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
379 VG_STATIC
void skateshop_load_world_preview( addon_reg
*reg
)
381 skaterift_begin_op( k_async_op_world_load_preview
);
382 vg_loader_start( skateshop_preview_loader_thread
, reg
);
388 void temp_update_playermodel(void);
389 VG_STATIC
void global_skateshop_preupdate(void)
391 float rate
= vg_minf( 1.0f
, vg
.time_frame_delta
* 2.0f
);
392 global_skateshop
.factive
= vg_lerpf( global_skateshop
.factive
,
393 global_skateshop
.active
, rate
);
395 if( !global_skateshop
.active
) return;
397 world_instance
*world
= world_current_instance();
398 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
400 /* camera positioning */
401 ent_camera
*ref
= mdl_arritm( &world
->ent_camera
,
402 mdl_entity_id_id(shop
->id_camera
) );
404 v3f dir
= {0.0f
,-1.0f
,0.0f
};
405 mdl_transform_vector( &ref
->transform
, dir
, dir
);
406 m3x3_mulv( localplayer
.invbasis
, dir
, dir
);
407 player_vector_angles( localplayer
.cam_override_angles
, dir
, 1.0f
, 0.0f
);
410 if( shop
->type
== k_skateshop_type_boardshop
||
411 shop
->type
== k_skateshop_type_worldshop
){
412 ent_marker
*display
= mdl_arritm( &world
->ent_marker
,
413 mdl_entity_id_id(shop
->boards
.id_display
) );
415 v3_sub( display
->transform
.co
, localplayer
.rb
.co
, lookat
);
418 else if( shop
->type
== k_skateshop_type_charshop
){
419 v3_sub( ref
->transform
.co
, localplayer
.rb
.co
, lookat
);
422 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
425 q_axis_angle( localplayer
.rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
},
426 atan2f(lookat
[0],lookat
[2]) );
428 v3_copy( ref
->transform
.co
, localplayer
.cam_override_pos
);
429 localplayer
.cam_override_fov
= ref
->fov
;
430 localplayer
.cam_override_strength
= global_skateshop
.factive
;
433 if( shop
->type
== k_skateshop_type_boardshop
){
434 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
435 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
437 struct cache_board
*selected_cache
= skateshop_selected_cache_if_loaded();
439 if( selected_cache
){
440 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
445 * ----------------------
448 if( button_down( k_srbind_mleft
) ){
449 if( global_skateshop
.selected_board_id
> 0 ){
450 global_skateshop
.selected_board_id
--;
454 if( button_down( k_srbind_mright
) ){
455 if( global_skateshop
.selected_board_id
+1 <
456 addon_count(k_workshop_file_type_board
) )
458 global_skateshop
.selected_board_id
++;
462 if( selected_cache
&& button_down( k_srbind_maccept
) ){
463 vg_info( "chose board from skateshop (%u)\n",
464 global_skateshop
.selected_board_id
);
466 if( localplayer
.board_view_slot
){
467 unwatch_cache_board( localplayer
.board_view_slot
);
470 localplayer
.board_view_slot
= selected_cache
;
471 watch_cache_board( localplayer
.board_view_slot
);
472 global_skateshop_exit();
476 else if( shop
->type
== k_skateshop_type_charshop
){
477 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
478 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
479 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
481 if( button_down( k_srbind_mleft
) ){
482 if( cl_playermdl_id
> 0 ){
486 cl_playermdl_id
= 2; /* HACK */
488 temp_update_playermodel(); /* HACK */
491 if( button_down( k_srbind_mright
) ){
492 if( cl_playermdl_id
+1 < 3 ){
496 cl_playermdl_id
= 0; /* HACK */
498 temp_update_playermodel(); /* HACK */
502 if( button_down( k_srbind_maccept
) ){
503 global_skateshop_exit();
506 else if( shop
->type
== k_skateshop_type_worldshop
){
510 if( addon_count(k_workshop_file_type_world
) &&
511 ((skaterift
.async_op
== k_async_op_none
)||
512 (skaterift
.async_op
== k_async_op_world_load_preview
))){
513 gui_helper_action( axis_display_string(k_sraxis_mbrowse_h
), "browse" );
517 if( skaterift
.async_op
== k_async_op_none
){
518 gui_helper_action( button_display_string(k_srbind_maccept
), "load" );
525 if( button_down( k_srbind_mleft
) ){
526 if( global_skateshop
.selected_world_id
> 0 )
528 global_skateshop
.selected_world_id
--;
533 if( button_down( k_srbind_mright
) ){
534 if( global_skateshop
.selected_world_id
+1 <
535 addon_count(k_workshop_file_type_world
) )
537 global_skateshop
.selected_world_id
++;
543 if( change
&& pointcloud_idle() ){
544 pointcloud_animate( k_pointcloud_anim_hiding
);
547 if( skaterift
.async_op
== k_async_op_none
){
548 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
549 global_skateshop
.selected_world_id
);
551 /* automatically load in clouds */
552 if( loadable
&& button_down( k_srbind_maccept
) ){
553 vg_info( "Select world (%u)\n",
554 global_skateshop
.selected_world_id
);
555 skaterift_change_world( reg
->foldername
);
559 if( pointcloud
.anim
== k_pointcloud_anim_idle_closed
){
560 if( global_skateshop
.pointcloud_world_id
!=
561 global_skateshop
.selected_world_id
)
563 global_skateshop
.pointcloud_world_id
=
564 global_skateshop
.selected_world_id
;
565 skateshop_load_world_preview( reg
);
568 pointcloud_animate( k_pointcloud_anim_opening
);
571 else if( pointcloud
.anim
== k_pointcloud_anim_idle_open
){
572 if( global_skateshop
.pointcloud_world_id
!=
573 global_skateshop
.selected_world_id
)
575 pointcloud_animate( k_pointcloud_anim_hiding
);
582 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
585 if( button_down( k_srbind_mback
) ){
586 global_skateshop_exit();
591 VG_STATIC
void skateshop_render_boardshop(void)
593 world_instance
*world
= world_current_instance();
594 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
596 u32 slot_count
= vg_list_size(global_skateshop
.shop_view_slots
);
598 ent_marker
*mark_rack
= mdl_arritm( &world
->ent_marker
,
599 mdl_entity_id_id(shop
->boards
.id_rack
)),
600 *mark_display
= mdl_arritm( &world
->ent_marker
,
601 mdl_entity_id_id(shop
->boards
.id_display
));
603 int visibility
[ SKATESHOP_VIEW_SLOT_MAX
];
604 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
605 for( u32 i
=0; i
<SKATESHOP_VIEW_SLOT_MAX
; i
++ ){
606 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
610 if( slot
->cache_ptr
== NULL
) visibility
[i
] = 0;
611 else if( slot
->cache_ptr
->state
!= k_cache_board_state_loaded
)
614 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
616 /* Render loaded boards in the view slots */
617 for( u32 i
=0; i
<slot_count
; i
++ ){
618 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
619 float selected
= 0.0f
;
621 if( !visibility
[i
] ) goto fade_out
;
624 transform_identity( &xform
);
626 xform
.co
[0] = -((float)i
- ((float)slot_count
)*0.5f
)*0.45f
;
627 mdl_transform_mul( &mark_rack
->transform
, &xform
, &xform
);
629 if( slot
->cache_ptr
->reg_index
== global_skateshop
.selected_board_id
){
633 float t
= slot
->view_blend
;
634 v3_lerp( xform
.co
, mark_display
->transform
.co
, t
, xform
.co
);
635 q_nlerp( xform
.q
, mark_display
->transform
.q
, t
, xform
.q
);
636 v3_lerp( xform
.s
, mark_display
->transform
.s
, t
, xform
.s
);
639 mdl_transform_m4x3( &xform
, mmdl
);
640 render_board( &main_camera
, world
, &slot
->cache_ptr
->board
, mmdl
,
641 k_board_shader_entity
);
644 float rate
= 5.0f
*vg
.time_delta
;
645 slot
->view_blend
= vg_lerpf( slot
->view_blend
, selected
, rate
);
648 ent_marker
*mark_info
= mdl_arritm( &world
->ent_marker
,
649 mdl_entity_id_id(shop
->boards
.id_info
));
651 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
652 mdl_transform_m4x3( &mark_rack
->transform
, mrack
);
655 const char *text_title
= "Fish - Title";
656 const char *text_author
= "by Shaniqua";
660 m4x3_identity( mlocal
);
665 font3d_bind( &gui
.font
, &main_camera
);
666 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
669 * ------------------------------------------------------------------ */
671 v3_zero( mlocal
[3] );
672 mlocal
[0][0] = -scale
*2.0f
;
673 mlocal
[1][2] = -scale
*2.0f
;
674 mlocal
[2][1] = -thickness
;
675 mlocal
[3][2] = -0.7f
;
676 m4x3_mul( mrack
, mlocal
, mmdl
);
678 if( addon_count(k_workshop_file_type_board
) ){
681 i
+=highscore_intl( buf
+i
, global_skateshop
.selected_board_id
+1, 3 );
683 i
+=highscore_intl( buf
+i
, addon_count(k_workshop_file_type_board
), 3 );
686 font3d_simple_draw( &gui
.font
, 0, buf
, &main_camera
, mmdl
);
689 font3d_simple_draw( &gui
.font
, 0,
690 "Nothing installed", &main_camera
, mmdl
);
693 struct cache_board
*cache_ptr
= skateshop_selected_cache_if_loaded();
695 global_skateshop
.render
.item_title
= "";
696 global_skateshop
.render
.item_desc
= "";
700 if( global_skateshop
.render
.reg_id
!= global_skateshop
.selected_board_id
){
701 addon_reg
*reg
= cache_ptr
->reg_ptr
;
703 vg_msg_init( &msg
, reg
->metadata
, reg
->metadata_len
);
706 while( vg_msg_next( &msg
, &cmd
) ){
707 if( (msg
.depth
== 1) && (cmd
.code
== k_vg_msg_code_frame
) ){
708 if( VG_STRDJB2_EQ( "workshop", cmd
.key
, cmd
.key_djb2
) ){
709 u32 depth
= msg
.depth
;
710 while( (msg
.depth
== depth
) && vg_msg_next( &msg
, &cmd
) ){
711 if( cmd
.code
== k_vg_msg_code_kvstring
){
712 if( VG_STRDJB2_EQ( "title", cmd
.key
, cmd
.key_djb2
) ){
713 global_skateshop
.render
.item_title
= cmd
.value
._buf
;
721 global_skateshop
.render
.reg_id
= global_skateshop
.selected_board_id
;
724 addon_reg
*reg
= cache_ptr
->reg_ptr
;
727 * ----------------------------------------------------------------- */
729 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
730 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0,
731 global_skateshop
.render
.item_title
);
732 mlocal
[3][0] *= scale
*0.5f
;
735 m4x3_mul( mtext
, mlocal
, mmdl
);
736 font3d_simple_draw( &gui
.font
, 0, global_skateshop
.render
.item_title
,
737 &main_camera
, mmdl
);
740 * ----------------------------------------------------------------- */
742 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
743 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, "JA" );
744 mlocal
[3][0] *= scale
*0.5f
;
747 m4x3_mul( mtext
, mlocal
, mmdl
);
748 font3d_simple_draw( &gui
.font
, 0, "JA", &main_camera
, mmdl
);
751 VG_STATIC
void skateshop_render_charshop(void)
755 VG_STATIC
void skateshop_render_worldshop(void)
757 world_instance
*world
= world_current_instance();
759 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
760 ent_marker
*mark_display
= mdl_arritm( &world
->ent_marker
,
761 mdl_entity_id_id(shop
->worlds
.id_display
)),
762 *mark_info
= mdl_arritm( &world
->ent_marker
,
763 mdl_entity_id_id(shop
->boards
.id_info
));
766 char buftext
[128], bufsubtext
[128];
767 vg_str info
, subtext
;
768 vg_strnull( &info
, buftext
, 128 );
769 vg_strnull( &subtext
, bufsubtext
, 128 );
771 if( addon_count(k_workshop_file_type_world
) ){
772 addon_reg
*reg
= get_addon_from_index( k_workshop_file_type_world
,
773 global_skateshop
.selected_world_id
);
775 info
.i
+=highscore_intl( info
.buffer
+info
.i
,
776 global_skateshop
.selected_world_id
+1, 3 );
777 info
.buffer
[info
.i
++] = '/';
778 info
.i
+=highscore_intl( info
.buffer
+info
.i
,
779 addon_count(k_workshop_file_type_world
), 3 );
780 info
.buffer
[info
.i
++] = ' ';
781 info
.buffer
[info
.i
] = '\0';
783 vg_strcat( &info
, "AFAWJFKAW" );
784 if( skaterift
.async_op
== k_async_op_world_loading
||
785 skaterift
.async_op
== k_async_op_world_preloading
){
786 vg_strcat( &subtext
, "Loading..." );
789 vg_strcat( &subtext
, "No information" );
793 vg_strcat( &info
, "No worlds installed" );
797 m4x3f mtext
,mlocal
,mtextmdl
;
798 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
800 font3d_bind( &gui
.font
, &main_camera
);
801 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
803 float scale
= 0.2f
, thickness
= 0.015f
, scale1
= 0.08f
;
805 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
806 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, buftext
);
807 mlocal
[3][0] *= scale
*0.5f
;
810 m4x3_mul( mtext
, mlocal
, mtextmdl
);
811 font3d_simple_draw( &gui
.font
, 0, buftext
, &main_camera
, mtextmdl
);
813 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale1
, scale1
, thickness
} );
814 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, bufsubtext
);
815 mlocal
[3][0] *= scale1
*0.5f
;
816 mlocal
[3][1] = -scale1
*0.3f
;
817 m4x3_mul( mtext
, mlocal
, mtextmdl
);
818 font3d_simple_draw( &gui
.font
, 0, bufsubtext
, &main_camera
, mtextmdl
);
822 mdl_transform_m4x3( &mark_display
->transform
, mmdl
);
823 m4x3_rotate_y( mmdl
, vg
.time
* 0.2 );
826 glBlendFunc(GL_ONE
, GL_ONE
);
827 glDisable(GL_DEPTH_TEST
);
828 pointcloud_render( world
, &main_camera
, mmdl
);
830 glEnable(GL_DEPTH_TEST
);
834 * World: render event
836 VG_STATIC
void skateshop_render(void)
838 if( !global_skateshop
.active
) return;
840 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
842 if( shop
->type
== k_skateshop_type_boardshop
){
843 skateshop_render_boardshop();
845 else if( shop
->type
== k_skateshop_type_charshop
){
846 skateshop_render_charshop();
848 else if( shop
->type
== k_skateshop_type_worldshop
){
849 skateshop_render_worldshop();
852 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
857 * Entity logic: entrance event
859 VG_STATIC
void ent_skateshop_call( world_instance
*world
, ent_call
*call
)
861 u32 index
= mdl_entity_id_id( call
->id
);
862 ent_skateshop
*shop
= mdl_arritm( &world
->ent_skateshop
, index
);
863 vg_info( "skateshop_call\n" );
865 if( menu
.active
) return;
866 if( skaterift
.async_op
!= k_async_op_none
) return;
868 if( call
->function
== k_ent_function_trigger
){
869 if( localplayer
.subsystem
!= k_player_subsystem_walk
){
873 vg_info( "Entering skateshop\n" );
875 localplayer
.immobile
= 1;
876 menu
.disable_open
= 1;
877 global_skateshop
.active
= 1;
879 v3_zero( localplayer
.rb
.v
);
880 v3_zero( localplayer
.rb
.w
);
881 localplayer
._walk
.move_speed
= 0.0f
;
882 global_skateshop
.ptr_ent
= shop
;
884 if( shop
->type
== k_skateshop_type_boardshop
){
885 skateshop_update_viewpage();
886 skateshop_op_board_scan();
888 else if( shop
->type
== k_skateshop_type_worldshop
){
889 pointcloud_animate( k_pointcloud_anim_opening
);
890 skateshop_op_world_scan();
896 * Entity logic: exit event
898 VG_STATIC
void global_skateshop_exit(void)
900 vg_info( "exit skateshop\n" );
901 localplayer
.immobile
= 0;
902 global_skateshop
.active
= 0;
903 menu
.disable_open
= 0;
904 srinput
.ignore_input_frames
= 2;
907 #endif /* ENT_SKATESHOP_C */