1 #ifndef ENT_SKATESHOP_C
2 #define ENT_SKATESHOP_C
6 #include "vg/vg_steam_ugc.h"
7 #include "ent_skateshop.h"
12 #include "pointcloud.h"
13 #include "highscores.h"
16 * Checks string equality but does a hash check first
18 static inline int const_str_eq( u32 hash
, const char *str
, const char *cmp
)
20 if( hash
== vg_strdjb2(cmp
) )
21 if( !strcmp( str
, cmp
) )
27 * Get an existing cache instance, allocate a new one to be loaded, or NULL if
30 VG_STATIC
struct cache_board
*skateshop_cache_fetch( u32 registry_index
)
32 struct registry_board
*reg
= NULL
;
34 if( registry_index
< global_skateshop
.registry_count
){
35 reg
= &global_skateshop
.registry
[ registry_index
];
38 return reg
->cache_ptr
;
42 /* lru eviction. should be a linked list maybe... */
43 double min_time
= 1e300
;
44 struct cache_board
*min_board
= NULL
;
46 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
47 for( u32 i
=0; i
<SKATESHOP_BOARD_CACHE_MAX
; i
++ ){
48 struct cache_board
*cache_ptr
= &global_skateshop
.cache
[i
];
50 if( cache_ptr
->state
== k_cache_board_state_load_request
) continue;
51 if( cache_ptr
->ref_count
) continue;
53 if( cache_ptr
->last_use_time
< min_time
){
54 min_time
= cache_ptr
->last_use_time
;
55 min_board
= cache_ptr
;
60 if( min_board
->state
== k_cache_board_state_loaded
){
61 struct registry_board
*other
=
62 &global_skateshop
.registry
[ min_board
->registry_id
];
64 vg_info( "Deallocating board: '%s'\n", min_board
, other
->filename
);
66 player_board_unload( &min_board
->board
);
67 other
->cache_ptr
= NULL
;
71 vg_info( "Allocating board (reg:%u) '%s'\n",
72 registry_index
, reg
->filename
);
75 vg_info( "Pre-allocating board (reg:%u) 'null'\n", registry_index
);
78 min_board
->registry_id
= registry_index
;
79 min_board
->last_use_time
= vg
.time
;
80 min_board
->ref_count
= 0;
81 min_board
->state
= k_cache_board_state_load_request
;
84 vg_error( "No free boards to load registry!\n" );
87 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
91 VG_STATIC
void skateshop_update_viewpage(void)
93 u32 page
= global_skateshop
.selected_registry_id
/SKATESHOP_VIEW_SLOT_MAX
;
95 for( u32 i
=0; i
<SKATESHOP_VIEW_SLOT_MAX
; i
++ ){
96 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
97 u32 request_id
= page
*SKATESHOP_VIEW_SLOT_MAX
+ i
;
99 if( slot
->cache_ptr
) unwatch_cache_board( slot
->cache_ptr
);
101 slot
->cache_ptr
= skateshop_cache_fetch( request_id
);
102 if( slot
->cache_ptr
) watch_cache_board( slot
->cache_ptr
);
107 * op/subroutine: k_workshop_op_item_load
108 * -----------------------------------------------------------------------------
112 * Reciever for board completion; only promotes the status in the main thread
114 VG_STATIC
void skateshop_async_board_loaded( void *payload
, u32 size
)
116 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
117 struct cache_board
*cache_ptr
= payload
;
118 cache_ptr
->last_use_time
= vg
.time
;
119 cache_ptr
->state
= k_cache_board_state_loaded
;
121 struct registry_board
*reg
=
122 &global_skateshop
.registry
[ cache_ptr
->registry_id
];
123 reg
->cache_ptr
= cache_ptr
;
124 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
126 vg_success( "Async board loaded (%s)\n", reg
->filename
);
130 * Thread(or subroutine of thread), for checking view slots that weve installed.
131 * Load the model if a view slot wants it
133 VG_STATIC
void workshop_visibile_load_loop_thread( void *_args
)
136 for( u32 i
=0; i
<SKATESHOP_BOARD_CACHE_MAX
; i
++ ){
137 struct cache_board
*cache_ptr
= &global_skateshop
.cache
[i
];
139 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
140 if( cache_ptr
->state
== k_cache_board_state_load_request
){
141 if( cache_ptr
->registry_id
>= global_skateshop
.registry_count
){
142 /* should maybe have a different value for this case */
143 cache_ptr
->state
= k_cache_board_state_none
;
144 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
148 /* continue with the request */
149 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
151 struct registry_board
*reg
=
152 &global_skateshop
.registry
[ cache_ptr
->registry_id
];
154 if( reg
->workshop_id
){
155 vg_async_item
*call
=
156 vg_async_alloc( sizeof(struct async_workshop_filepath_info
) );
158 struct async_workshop_filepath_info
*info
= call
->payload
;
160 info
->id
= reg
->workshop_id
;
161 info
->len
= vg_list_size(path
) - strlen("/board.mdl")-1;
162 vg_async_dispatch( call
, async_workshop_get_filepath
);
163 vg_async_stall(); /* too bad! */
165 if( path
[0] == '\0' ){
166 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
167 cache_ptr
->state
= k_cache_board_state_none
;
168 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
170 vg_error( "Failed SteamAPI_GetItemInstallInfo(" PRINTF_U64
")\n",
175 strcat( path
, "/board.mdl" );
179 snprintf( path
, 256, "models/boards/%s", reg
->filename
);
182 player_board_load( &cache_ptr
->board
, path
);
183 vg_async_call( skateshop_async_board_loaded
, cache_ptr
, 0 );
186 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
188 vg_async_call( workshop_async_any_complete
, NULL
, 0 );
192 * op: k_workshop_op_item_scan
193 * -----------------------------------------------------------------------------
197 * Reciever for scan completion. copies the registry_count back into t0
199 VG_STATIC
void workshop_async_reg_update( void *data
, u32 size
)
201 vg_info( "Registry update notify\n" );
202 global_skateshop
.registry_count
= global_skateshop
.t1_registry_count
;
205 VG_STATIC
void workshop_steam_scan(void)
208 * Steam workshop scan
210 vg_info( "Scanning steam workshop for boards\n" );
211 PublishedFileId_t workshop_ids
[ SKATESHOP_REGISTRY_MAX
];
212 u32 workshop_count
= SKATESHOP_REGISTRY_MAX
;
214 vg_async_item
*call
= vg_async_alloc(
215 sizeof(struct async_workshop_installed_files_info
));
216 struct async_workshop_installed_files_info
*info
= call
->payload
;
217 info
->buffer
= workshop_ids
;
218 info
->len
= &workshop_count
;
219 vg_async_dispatch( call
, async_workshop_get_installed_files
);
222 for( u32 j
=0; j
<workshop_count
; j
++ ){
223 PublishedFileId_t id
= workshop_ids
[j
];
225 for( u32 i
=0; i
<global_skateshop
.t1_registry_count
; i
++ ){
226 struct registry_board
*reg
= &global_skateshop
.registry
[i
];
228 if( reg
->workshop_id
== id
){
229 reg
->state
= k_registry_board_state_indexed
;
230 goto next_file_workshop
;
234 if( global_skateshop
.t1_registry_count
== SKATESHOP_REGISTRY_MAX
){
235 vg_error( "You have too many boards installed!\n" );
239 vg_info( "new listing from the steam workshop!: "PRINTF_U64
"\n", id
);
241 struct registry_board
*reg
= &global_skateshop
.registry
[
242 global_skateshop
.t1_registry_count
++ ];
244 reg
->cache_ptr
= NULL
;
245 snprintf( reg
->filename
, 64, PRINTF_U64
, id
);
246 reg
->filename_hash
= vg_strdjb2( reg
->filename
);
247 reg
->workshop_id
= id
;
248 reg
->state
= k_registry_board_state_indexed
;
250 workshop_file_info_clear( ®
->workshop
);
251 strcpy( reg
->workshop
.title
, "Workshop file" );
253 /* load the metadata off the disk */
254 vg_async_item
*call
=
255 vg_async_alloc( sizeof(struct async_workshop_filepath_info
) );
257 const char *meta_file
= "/board.mdl.inf";
259 struct async_workshop_filepath_info
*info
= call
->payload
;
261 info
->id
= reg
->workshop_id
;
262 info
->len
= vg_list_size(path
) - strlen(meta_file
)-1;
263 vg_async_dispatch( call
, async_workshop_get_filepath
);
264 vg_async_stall(); /* too bad! */
266 strcat( path
, meta_file
);
267 workshop_load_metadata( path
, ®
->workshop
);
274 * Async thread which scans local files for boards, as well as scheduling
275 * synchronous calls to the workshop
277 VG_STATIC
void workshop_scan_thread( void *_args
)
279 vg_linear_clear( vg_mem
.scratch
);
281 for( u32 i
=0; i
<global_skateshop
.t1_registry_count
; i
++ ){
282 struct registry_board
*reg
= &global_skateshop
.registry
[i
];
283 reg
->state
= k_registry_board_state_indexed_absent
;
289 vg_info( "Scanning models/boards/*.mdl\n" );
291 tinydir_open( &dir
, "models/boards" );
293 while( dir
.has_next
){
295 tinydir_readfile( &dir
, &file
);
298 u32 hash
= vg_strdjb2( file
.name
);
300 for( u32 i
=0; i
<global_skateshop
.t1_registry_count
; i
++ ){
301 struct registry_board
*reg
= &global_skateshop
.registry
[i
];
303 if( const_str_eq( hash
, file
.name
, reg
->filename
) ){
304 reg
->state
= k_registry_board_state_indexed
;
309 if( global_skateshop
.t1_registry_count
== SKATESHOP_REGISTRY_MAX
){
310 vg_error( "You have too many boards installed!\n" );
314 vg_info( "new listing!: %s\n", file
.name
);
316 struct registry_board
*reg
=
317 &global_skateshop
.registry
[global_skateshop
.t1_registry_count
++];
319 reg
->cache_ptr
= NULL
;
320 vg_strncpy( file
.name
, reg
->filename
, 64, k_strncpy_always_add_null
);
321 vg_strncpy( file
.name
, reg
->workshop
.title
,
322 64, k_strncpy_always_add_null
);
323 reg
->filename_hash
= hash
;
324 reg
->workshop_id
= 0;
325 reg
->state
= k_registry_board_state_indexed
;
326 reg
->workshop
.author
= 0;
327 strcpy( reg
->workshop
.author_name
, "custom" );
330 next_file
: tinydir_next( &dir
);
335 if( steam_ready
) workshop_steam_scan();
337 vg_async_call( workshop_async_reg_update
, NULL
, 0 );
339 workshop_visibile_load_loop_thread(NULL
);
343 * Asynchronous scan of local disk for items and add them to the registry
345 VG_STATIC
void workshop_op_item_scan(void)
347 skaterift_begin_op( k_workshop_op_item_scan
);
348 vg_loader_start( workshop_scan_thread
, NULL
);
352 * op: k_async_op_world_scan
353 * -----------------------------------------------------------------------------
357 * Reciever for scan completion. copies the registry_count back into t0
359 VG_STATIC
void workshop_async_world_reg_update( void *data
, u32 size
)
361 vg_info( "World registry update notify\n" );
362 global_skateshop
.world_registry_count
=
363 global_skateshop
.t1_world_registry_count
;
368 * Add a local world folder to the registry, it will verify existing ones are
371 VG_STATIC
void world_scan_register_local( const char *folder_name
)
373 u32 hash
= vg_strdjb2( folder_name
);
374 for( u32 i
=0; i
<global_skateshop
.t1_world_registry_count
; i
++ ){
375 struct registry_world
*reg
=
376 &global_skateshop
.world_registry
[i
];
378 if( const_str_eq( hash
, folder_name
, reg
->foldername
) ){
379 reg
->state
= k_registry_board_state_indexed
;
384 if( global_skateshop
.t1_world_registry_count
== SKATESHOP_WORLDS_MAX
){
385 vg_error( "You have too many worlds installed!\n" );
389 vg_info( "new listing!: %s\n", folder_name
);
391 struct registry_world
*reg
= &global_skateshop
.world_registry
[
392 global_skateshop
.t1_world_registry_count
++ ];
394 vg_strncpy( folder_name
, reg
->foldername
, 64, k_strncpy_overflow_fatal
);
395 reg
->foldername_hash
= hash
;
396 reg
->state
= k_registry_board_state_indexed
;
397 reg
->meta_present
= 0;
401 * Async thread which scans local files for boards, as well as scheduling
402 * synchronous calls to the workshop
404 VG_STATIC
void world_scan_thread( void *_args
)
406 vg_linear_clear( vg_mem
.scratch
);
408 for( u32 i
=0; i
<global_skateshop
.t1_world_registry_count
; i
++ ){
409 struct registry_world
*reg
= &global_skateshop
.world_registry
[i
];
410 reg
->state
= k_registry_board_state_indexed_absent
;
416 vg_info( "Scanning maps/*.mdl\n" );
420 vg_strnull( &path
, path_buf
, 4096 );
421 vg_strcat( &path
, "maps/" );
423 DIR *dir
= opendir( path
.buffer
);
425 vg_error( "opendir('maps') failed\n" );
426 vg_async_call( workshop_async_any_complete
, NULL
, 0 );
430 struct dirent
*entry
;
431 while( (entry
= readdir(dir
)) ){
432 if( entry
->d_type
== DT_DIR
){
433 if( entry
->d_name
[0] == '.' ) continue;
435 vg_str folder
= path
;
436 char *folder_name
= folder
.buffer
+folder
.i
;
438 if( strlen( entry
->d_name
) >
439 vg_list_size(global_skateshop
.world_registry
[0].foldername
)){
440 vg_warn( "Map folder too long: %s\n", entry
->d_name
);
444 vg_strcat( &folder
, entry
->d_name
);
445 if( !vg_strgood( &folder
) ) break;
447 DIR *subdir
= opendir( folder
.buffer
);
448 while( (entry
= readdir(subdir
)) ){
449 if( entry
->d_type
== DT_REG
){
450 if( entry
->d_name
[0] == '.' ) continue;
452 vg_str file
= folder
;
453 vg_strcat( &file
, "/" );
454 vg_strcat( &file
, entry
->d_name
);
455 if( !vg_strgood( &file
) ) continue;
457 char *ext
= vg_strch( &file
, '.' );
459 if( strcmp(ext
,".mdl") ) continue;
461 vg_strcat( &folder
, "" );
462 world_scan_register_local( folder_name
);
470 vg_async_call( workshop_async_world_reg_update
, NULL
, 0 );
475 if( steam_ready
) workshop_steam_scan();
477 vg_async_call( workshop_async_reg_update
, NULL
, 0 );
479 workshop_visibile_load_loop_thread(NULL
);
484 * Asynchronous scan of local disk for worlds
486 VG_STATIC
void skateshop_op_world_scan(void)
488 skaterift_begin_op( k_async_op_world_scan
);
489 vg_loader_start( world_scan_thread
, NULL
);
494 * -----------------------------------------------------------------------------
497 /* we can only keep using a viewslot pointer for multiple frames if we watch it
498 * using this function */
499 VG_STATIC
void watch_cache_board( struct cache_board
*ptr
)
501 if( ptr
->ref_count
>= 32 ){
502 vg_fatal_error( "dynamic board watch missmatch (limit is 32)\n" );
505 ptr
->last_use_time
= vg
.time
;
509 /* after this is called, the calling code only has access to the pointer for the
510 * duration of the rest of the frame */
511 VG_STATIC
void unwatch_cache_board( struct cache_board
*ptr
)
513 if( ptr
->ref_count
== 0 ){
514 vg_fatal_error( "dynamic board unwatch missmatch (no watchers)\n" );
521 * Callback handler for persona state changes,
522 * it sets the author names on the registries
524 VG_STATIC
void callback_persona_statechange( CallbackMsg_t
*msg
)
526 PersonaStateChange_t
*info
= (PersonaStateChange_t
*)msg
->m_pubParam
;
527 ISteamFriends
*hSteamFriends
= SteamAPI_SteamFriends();
529 if( info
->m_nChangeFlags
& k_EPersonaChangeName
){
530 for( u32 i
=0; i
<global_skateshop
.registry_count
; i
++ ){
531 struct registry_board
*reg
= &global_skateshop
.registry
[i
];
532 if( reg
->workshop
.author
== info
->m_ulSteamID
){
533 const char *name
= SteamAPI_ISteamFriends_GetFriendPersonaName(
534 hSteamFriends
, info
->m_ulSteamID
);
535 str_utf8_collapse( name
, reg
->workshop
.author_name
, 32 );
544 VG_STATIC
void skateshop_init(void)
546 u32 reg_size
= sizeof(struct registry_board
)*SKATESHOP_REGISTRY_MAX
,
547 wreg_size
= sizeof(struct registry_world
)*SKATESHOP_WORLDS_MAX
,
548 cache_size
= sizeof(struct cache_board
)*SKATESHOP_BOARD_CACHE_MAX
;
550 global_skateshop
.registry
= vg_linear_alloc( vg_mem
.rtmemory
, reg_size
);
551 global_skateshop
.world_registry
=
552 vg_linear_alloc( vg_mem
.rtmemory
, wreg_size
);
553 global_skateshop
.cache
= vg_linear_alloc( vg_mem
.rtmemory
, cache_size
);
555 memset( global_skateshop
.cache
, 0, cache_size
);
557 for( u32 i
=0; i
<SKATESHOP_BOARD_CACHE_MAX
; i
++ ){
558 struct cache_board
*board
= &global_skateshop
.cache
[i
];
559 board
->state
= k_cache_board_state_none
;
560 board
->registry_id
= 0xffffffff;
561 board
->last_use_time
= -99999.9;
562 board
->ref_count
= 0;
566 steam_register_callback( k_iPersonaStateChange
,
567 callback_persona_statechange
);
571 VG_STATIC
struct cache_board
*skateshop_selected_cache_if_loaded(void)
573 if( global_skateshop
.registry_count
> 0 ){
574 u32 reg_id
= global_skateshop
.selected_registry_id
;
575 struct registry_board
*reg
= &global_skateshop
.registry
[ reg_id
];
577 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
578 if( reg
->cache_ptr
&&
579 (reg
->cache_ptr
->state
== k_cache_board_state_loaded
) )
581 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
582 return reg
->cache_ptr
;
584 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
593 void temp_update_playermodel(void);
594 VG_STATIC
void global_skateshop_preupdate(void)
596 float rate
= vg_minf( 1.0f
, vg
.time_frame_delta
* 2.0f
);
597 global_skateshop
.factive
= vg_lerpf( global_skateshop
.factive
,
598 global_skateshop
.active
, rate
);
600 if( !global_skateshop
.active
) return;
602 world_instance
*world
= world_current_instance();
604 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
606 /* camera positioning */
607 ent_camera
*ref
= mdl_arritm( &world
->ent_camera
,
608 mdl_entity_id_id(shop
->id_camera
) );
610 v3f dir
= {0.0f
,-1.0f
,0.0f
};
611 mdl_transform_vector( &ref
->transform
, dir
, dir
);
612 m3x3_mulv( localplayer
.invbasis
, dir
, dir
);
613 player_vector_angles( localplayer
.cam_override_angles
, dir
, 1.0f
, 0.0f
);
616 if( shop
->type
== k_skateshop_type_boardshop
||
617 shop
->type
== k_skateshop_type_worldshop
){
618 ent_marker
*display
= mdl_arritm( &world
->ent_marker
,
619 mdl_entity_id_id(shop
->boards
.id_display
) );
621 v3_sub( display
->transform
.co
, localplayer
.rb
.co
, lookat
);
624 else if( shop
->type
== k_skateshop_type_charshop
){
625 v3_sub( ref
->transform
.co
, localplayer
.rb
.co
, lookat
);
628 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
631 q_axis_angle( localplayer
.rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
},
632 atan2f(lookat
[0],lookat
[2]) );
634 v3_copy( ref
->transform
.co
, localplayer
.cam_override_pos
);
635 localplayer
.cam_override_fov
= ref
->fov
;
636 localplayer
.cam_override_strength
= global_skateshop
.factive
;
639 if( shop
->type
== k_skateshop_type_boardshop
){
640 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
641 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
643 struct cache_board
*selected_cache
= skateshop_selected_cache_if_loaded();
645 if( selected_cache
){
646 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
651 * ----------------------
654 if( button_down( k_srbind_mleft
) ){
655 if( global_skateshop
.selected_registry_id
> 0 ){
656 global_skateshop
.selected_registry_id
--;
660 if( button_down( k_srbind_mright
) ){
661 if( global_skateshop
.selected_registry_id
+1 <
662 global_skateshop
.registry_count
)
664 global_skateshop
.selected_registry_id
++;
668 if( selected_cache
&& button_down( k_srbind_maccept
) ){
669 vg_info( "chose board from skateshop (%u)\n",
670 global_skateshop
.selected_registry_id
);
672 if( localplayer
.board_view_slot
){
673 unwatch_cache_board( localplayer
.board_view_slot
);
676 localplayer
.board_view_slot
= selected_cache
;
677 watch_cache_board( localplayer
.board_view_slot
);
679 global_skateshop_exit();
683 else if( shop
->type
== k_skateshop_type_charshop
){
684 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
685 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
686 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
688 if( button_down( k_srbind_mleft
) ){
689 if( cl_playermdl_id
> 0 ){
693 cl_playermdl_id
= 2; /* HACK */
695 temp_update_playermodel(); /* HACK */
698 if( button_down( k_srbind_mright
) ){
699 if( cl_playermdl_id
+1 < 3 ){
703 cl_playermdl_id
= 0; /* HACK */
705 temp_update_playermodel(); /* HACK */
709 if( button_down( k_srbind_maccept
) ){
710 global_skateshop_exit();
713 else if( shop
->type
== k_skateshop_type_worldshop
){
714 if( global_skateshop
.world_registry_count
){
715 gui_helper_action( axis_display_string(k_sraxis_mbrowse_h
), "browse" );
718 if( skaterift
.async_op
== k_async_op_none
){
719 gui_helper_action( button_display_string(k_srbind_maccept
), "load" );
723 if( button_down( k_srbind_mleft
) ){
724 if( global_skateshop
.selected_world_id
> 0 ){
725 global_skateshop
.selected_world_id
--;
730 if( button_down( k_srbind_mright
) ){
731 if( global_skateshop
.selected_world_id
+1 <
732 global_skateshop
.world_registry_count
){
733 global_skateshop
.selected_world_id
++;
738 if( change
&& (pointcloud
.anim
== k_pointcloud_anim_idle
) &&
739 pointcloud
.visibility
== 1.0f
){
740 pointcloud
.anim
= k_pointcloud_anim_hiding
;
741 pointcloud
.anim_start
= vg
.time
;
744 if( (skaterift
.async_op
== k_async_op_none
) &&
745 button_down( k_srbind_maccept
) )
747 struct registry_world
*rw
= &global_skateshop
.world_registry
[
748 global_skateshop
.selected_world_id
];
750 vg_info( "Select world (%u)\n", global_skateshop
.selected_world_id
);
751 skaterift_change_world( rw
->foldername
);
756 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
759 if( button_down( k_srbind_mback
) ){
760 global_skateshop_exit();
765 VG_STATIC
void skateshop_render_boardshop(void)
767 world_instance
*world
= world_current_instance();
768 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
770 u32 slot_count
= vg_list_size(global_skateshop
.shop_view_slots
);
772 ent_marker
*mark_rack
= mdl_arritm( &world
->ent_marker
,
773 mdl_entity_id_id(shop
->boards
.id_rack
)),
774 *mark_display
= mdl_arritm( &world
->ent_marker
,
775 mdl_entity_id_id(shop
->boards
.id_display
));
777 int visibility
[ SKATESHOP_VIEW_SLOT_MAX
];
778 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
779 for( u32 i
=0; i
<SKATESHOP_VIEW_SLOT_MAX
; i
++ ){
780 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
784 if( slot
->cache_ptr
== NULL
) visibility
[i
] = 0;
785 else if( slot
->cache_ptr
->state
!= k_cache_board_state_loaded
)
788 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
790 /* Render loaded boards in the view slots */
791 for( u32 i
=0; i
<slot_count
; i
++ ){
792 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
793 float selected
= 0.0f
;
795 if( !visibility
[i
] ) goto fade_out
;
798 transform_identity( &xform
);
800 xform
.co
[0] = -((float)i
- ((float)slot_count
)*0.5f
)*0.45f
;
801 mdl_transform_mul( &mark_rack
->transform
, &xform
, &xform
);
803 if( slot
->cache_ptr
->registry_id
==
804 global_skateshop
.selected_registry_id
){
808 float t
= slot
->view_blend
;
809 v3_lerp( xform
.co
, mark_display
->transform
.co
, t
, xform
.co
);
810 q_nlerp( xform
.q
, mark_display
->transform
.q
, t
, xform
.q
);
811 v3_lerp( xform
.s
, mark_display
->transform
.s
, t
, xform
.s
);
814 mdl_transform_m4x3( &xform
, mmdl
);
815 render_board( &main_camera
, world
, &slot
->cache_ptr
->board
, mmdl
,
816 k_board_shader_entity
);
819 float rate
= 5.0f
*vg
.time_delta
;
820 slot
->view_blend
= vg_lerpf( slot
->view_blend
, selected
, rate
);
823 ent_marker
*mark_info
= mdl_arritm( &world
->ent_marker
,
824 mdl_entity_id_id(shop
->boards
.id_info
));
826 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
827 mdl_transform_m4x3( &mark_rack
->transform
, mrack
);
830 const char *text_title
= "Fish - Title";
831 const char *text_author
= "by Shaniqua";
835 m4x3_identity( mlocal
);
840 font3d_bind( &gui
.font
, &main_camera
);
841 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
844 * ------------------------------------------------------------------ */
846 v3_zero( mlocal
[3] );
847 mlocal
[0][0] = -scale
*2.0f
;
848 mlocal
[1][2] = -scale
*2.0f
;
849 mlocal
[2][1] = -thickness
;
850 mlocal
[3][2] = -0.7f
;
851 m4x3_mul( mrack
, mlocal
, mmdl
);
853 if( global_skateshop
.registry_count
== 0 ){
854 font3d_simple_draw( &gui
.font
, 0,
855 "Nothing installed", &main_camera
, mmdl
);
860 i
+=highscore_intl( buf
+i
, global_skateshop
.selected_registry_id
+1, 3 );
862 i
+=highscore_intl( buf
+i
, global_skateshop
.registry_count
, 3 );
865 font3d_simple_draw( &gui
.font
, 0, buf
, &main_camera
, mmdl
);
868 struct cache_board
*cache_ptr
= skateshop_selected_cache_if_loaded();
869 if( !cache_ptr
) return;
871 struct registry_board
*reg
=
872 &global_skateshop
.registry
[cache_ptr
->registry_id
];
873 struct workshop_file_info
*info
= ®
->workshop
;
876 * ----------------------------------------------------------------- */
878 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
879 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, info
->title
);
880 mlocal
[3][0] *= scale
*0.5f
;
883 m4x3_mul( mtext
, mlocal
, mmdl
);
884 font3d_simple_draw( &gui
.font
, 0, info
->title
, &main_camera
, mmdl
);
887 * ----------------------------------------------------------------- */
889 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
890 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, info
->author_name
);
891 mlocal
[3][0] *= scale
*0.5f
;
894 m4x3_mul( mtext
, mlocal
, mmdl
);
895 font3d_simple_draw( &gui
.font
, 0, info
->author_name
, &main_camera
, mmdl
);
898 VG_STATIC
void skateshop_render_charshop(void)
902 VG_STATIC
void skateshop_render_worldshop(void)
904 world_instance
*world
= world_current_instance();
906 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
907 ent_marker
*mark_display
= mdl_arritm( &world
->ent_marker
,
908 mdl_entity_id_id(shop
->worlds
.id_display
)),
909 *mark_info
= mdl_arritm( &world
->ent_marker
,
910 mdl_entity_id_id(shop
->boards
.id_info
));
913 char buftext
[128], bufsubtext
[128];
914 vg_str info
, subtext
;
915 vg_strnull( &info
, buftext
, 128 );
916 vg_strnull( &subtext
, bufsubtext
, 128 );
918 if( global_skateshop
.world_registry_count
){
919 struct registry_world
*rw
= &global_skateshop
.world_registry
[
920 global_skateshop
.selected_world_id
];
922 info
.i
+=highscore_intl( info
.buffer
+info
.i
,
923 global_skateshop
.selected_world_id
+1, 3 );
924 info
.buffer
[info
.i
++] = '/';
925 info
.i
+=highscore_intl( info
.buffer
+info
.i
,
926 global_skateshop
.world_registry_count
, 3 );
927 info
.buffer
[info
.i
++] = ' ';
928 info
.buffer
[info
.i
] = '\0';
930 if( rw
->meta_present
){
934 vg_strcat( &info
, rw
->foldername
);
935 vg_strcat( &subtext
, "No information" );
939 vg_strcat( &info
, "No worlds installed" );
942 if( skaterift
.async_op
== k_async_op_world_loading
||
943 skaterift
.async_op
== k_async_op_world_preloading
){
944 vg_strcat( &info
, "Loading..." );
947 m4x3f mtext
,mlocal
,mtextmdl
;
948 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
950 font3d_bind( &gui
.font
, &main_camera
);
951 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
953 float scale
= 0.2f
, thickness
= 0.015f
, scale1
= 0.08f
;
955 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
956 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, buftext
);
957 mlocal
[3][0] *= scale
*0.5f
;
960 m4x3_mul( mtext
, mlocal
, mtextmdl
);
961 font3d_simple_draw( &gui
.font
, 0, buftext
, &main_camera
, mtextmdl
);
963 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale1
, scale1
, thickness
} );
964 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, bufsubtext
);
965 mlocal
[3][0] *= scale1
*0.5f
;
966 mlocal
[3][1] = -scale1
*0.3f
;
967 m4x3_mul( mtext
, mlocal
, mtextmdl
);
968 font3d_simple_draw( &gui
.font
, 0, bufsubtext
, &main_camera
, mtextmdl
);
972 mdl_transform_m4x3( &mark_display
->transform
, mmdl
);
973 m4x3_rotate_y( mmdl
, vg
.time
* 0.2 );
976 glBlendFunc(GL_ONE
, GL_ONE
);
977 glDisable(GL_DEPTH_TEST
);
978 pointcloud_render( world
, &main_camera
, mmdl
);
980 glEnable(GL_DEPTH_TEST
);
984 * World: render event
986 VG_STATIC
void skateshop_render(void)
988 if( !global_skateshop
.active
) return;
990 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
992 if( shop
->type
== k_skateshop_type_boardshop
){
993 skateshop_render_boardshop();
995 else if( shop
->type
== k_skateshop_type_charshop
){
996 skateshop_render_charshop();
998 else if( shop
->type
== k_skateshop_type_worldshop
){
999 skateshop_render_worldshop();
1002 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
1007 * Entity logic: entrance event
1009 VG_STATIC
void ent_skateshop_call( world_instance
*world
, ent_call
*call
)
1011 u32 index
= mdl_entity_id_id( call
->id
);
1012 ent_skateshop
*shop
= mdl_arritm( &world
->ent_skateshop
, index
);
1013 vg_info( "skateshop_call\n" );
1015 if( menu
.active
) return;
1016 if( skaterift
.async_op
!= k_async_op_none
) return;
1018 if( call
->function
== k_ent_function_trigger
){
1019 if( localplayer
.subsystem
!= k_player_subsystem_walk
){
1023 vg_info( "Entering skateshop\n" );
1025 localplayer
.immobile
= 1;
1026 menu
.disable_open
= 1;
1027 global_skateshop
.active
= 1;
1029 v3_zero( localplayer
.rb
.v
);
1030 v3_zero( localplayer
.rb
.w
);
1031 localplayer
._walk
.move_speed
= 0.0f
;
1032 global_skateshop
.ptr_ent
= shop
;
1034 if( shop
->type
== k_skateshop_type_boardshop
){
1035 skateshop_update_viewpage();
1036 workshop_op_item_scan();
1038 else if( shop
->type
== k_skateshop_type_worldshop
){
1039 pointcloud
.anim
= k_pointcloud_anim_opening
;
1040 pointcloud
.anim_start
= vg
.time
;
1041 skateshop_op_world_scan();
1047 * Entity logic: exit event
1049 VG_STATIC
void global_skateshop_exit(void)
1051 vg_info( "exit skateshop\n" );
1052 localplayer
.immobile
= 0;
1053 global_skateshop
.active
= 0;
1054 menu
.disable_open
= 0;
1055 srinput
.ignore_input_frames
= 2;
1058 #endif /* ENT_SKATESHOP_C */