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;
398 reg
->type
= k_world_load_type_local
;
402 * Async thread which scans local files for boards, as well as scheduling
403 * synchronous calls to the workshop
405 VG_STATIC
void world_scan_thread( void *_args
)
407 vg_linear_clear( vg_mem
.scratch
);
409 for( u32 i
=0; i
<global_skateshop
.t1_world_registry_count
; i
++ ){
410 struct registry_world
*reg
= &global_skateshop
.world_registry
[i
];
411 reg
->state
= k_registry_board_state_indexed_absent
;
417 vg_info( "Scanning maps/*.mdl\n" );
421 vg_strnull( &path
, path_buf
, 4096 );
422 vg_strcat( &path
, "maps/" );
424 DIR *dir
= opendir( path
.buffer
);
426 vg_error( "opendir('maps') failed\n" );
427 vg_async_call( workshop_async_any_complete
, NULL
, 0 );
431 struct dirent
*entry
;
432 while( (entry
= readdir(dir
)) ){
433 if( entry
->d_type
== DT_DIR
){
434 if( entry
->d_name
[0] == '.' ) continue;
436 vg_str folder
= path
;
437 char *folder_name
= folder
.buffer
+folder
.i
;
439 if( strlen( entry
->d_name
) >
440 vg_list_size(global_skateshop
.world_registry
[0].foldername
)){
441 vg_warn( "Map folder too long: %s\n", entry
->d_name
);
445 vg_strcat( &folder
, entry
->d_name
);
446 if( !vg_strgood( &folder
) ) break;
448 DIR *subdir
= opendir( folder
.buffer
);
449 while( (entry
= readdir(subdir
)) ){
450 if( entry
->d_type
== DT_REG
){
451 if( entry
->d_name
[0] == '.' ) continue;
453 vg_str file
= folder
;
454 vg_strcat( &file
, "/" );
455 vg_strcat( &file
, entry
->d_name
);
456 if( !vg_strgood( &file
) ) continue;
458 char *ext
= vg_strch( &file
, '.' );
460 if( strcmp(ext
,".mdl") ) continue;
462 vg_strcat( &folder
, "" );
463 world_scan_register_local( folder_name
);
471 vg_async_call( workshop_async_world_reg_update
, NULL
, 0 );
476 if( steam_ready
) workshop_steam_scan();
478 vg_async_call( workshop_async_reg_update
, NULL
, 0 );
480 workshop_visibile_load_loop_thread(NULL
);
485 * Asynchronous scan of local disk for worlds
487 VG_STATIC
void skateshop_op_world_scan(void)
489 skaterift_begin_op( k_async_op_world_scan
);
490 vg_loader_start( world_scan_thread
, NULL
);
495 * -----------------------------------------------------------------------------
498 /* we can only keep using a viewslot pointer for multiple frames if we watch it
499 * using this function */
500 VG_STATIC
void watch_cache_board( struct cache_board
*ptr
)
502 if( ptr
->ref_count
>= 32 ){
503 vg_fatal_error( "dynamic board watch missmatch (limit is 32)\n" );
506 ptr
->last_use_time
= vg
.time
;
510 /* after this is called, the calling code only has access to the pointer for the
511 * duration of the rest of the frame */
512 VG_STATIC
void unwatch_cache_board( struct cache_board
*ptr
)
514 if( ptr
->ref_count
== 0 ){
515 vg_fatal_error( "dynamic board unwatch missmatch (no watchers)\n" );
522 * Callback handler for persona state changes,
523 * it sets the author names on the registries
525 VG_STATIC
void callback_persona_statechange( CallbackMsg_t
*msg
)
527 PersonaStateChange_t
*info
= (PersonaStateChange_t
*)msg
->m_pubParam
;
528 ISteamFriends
*hSteamFriends
= SteamAPI_SteamFriends();
530 if( info
->m_nChangeFlags
& k_EPersonaChangeName
){
531 for( u32 i
=0; i
<global_skateshop
.registry_count
; i
++ ){
532 struct registry_board
*reg
= &global_skateshop
.registry
[i
];
533 if( reg
->workshop
.author
== info
->m_ulSteamID
){
534 const char *name
= SteamAPI_ISteamFriends_GetFriendPersonaName(
535 hSteamFriends
, info
->m_ulSteamID
);
536 str_utf8_collapse( name
, reg
->workshop
.author_name
, 32 );
545 VG_STATIC
void skateshop_init(void)
547 u32 reg_size
= sizeof(struct registry_board
)*SKATESHOP_REGISTRY_MAX
,
548 wreg_size
= sizeof(struct registry_world
)*SKATESHOP_WORLDS_MAX
,
549 cache_size
= sizeof(struct cache_board
)*SKATESHOP_BOARD_CACHE_MAX
;
551 global_skateshop
.registry
= vg_linear_alloc( vg_mem
.rtmemory
, reg_size
);
552 global_skateshop
.world_registry
=
553 vg_linear_alloc( vg_mem
.rtmemory
, wreg_size
);
554 global_skateshop
.cache
= vg_linear_alloc( vg_mem
.rtmemory
, cache_size
);
556 memset( global_skateshop
.cache
, 0, cache_size
);
558 for( u32 i
=0; i
<SKATESHOP_BOARD_CACHE_MAX
; i
++ ){
559 struct cache_board
*board
= &global_skateshop
.cache
[i
];
560 board
->state
= k_cache_board_state_none
;
561 board
->registry_id
= 0xffffffff;
562 board
->last_use_time
= -99999.9;
563 board
->ref_count
= 0;
567 steam_register_callback( k_iPersonaStateChange
,
568 callback_persona_statechange
);
572 VG_STATIC
struct cache_board
*skateshop_selected_cache_if_loaded(void)
574 if( global_skateshop
.registry_count
> 0 ){
575 u32 reg_id
= global_skateshop
.selected_registry_id
;
576 struct registry_board
*reg
= &global_skateshop
.registry
[ reg_id
];
578 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
579 if( reg
->cache_ptr
&&
580 (reg
->cache_ptr
->state
== k_cache_board_state_loaded
) )
582 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
583 return reg
->cache_ptr
;
585 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
591 VG_STATIC
void pointcloud_async_end(void *_
, u32 __
)
593 pointcloud_animate( k_pointcloud_anim_opening
);
597 VG_STATIC
void pointcloud_clear_async(void *_
, u32 __
)
599 pointcloud
.count
= 0;
600 pointcloud_animate( k_pointcloud_anim_opening
);
604 VG_STATIC
void skateshop_preview_loader_thread( void *_data
)
606 struct registry_world
*reg
= _data
;
608 if( reg
->type
== k_world_load_type_local
){
611 vg_strnull( &path
, path_buf
, 4096 );
612 vg_strcat( &path
, "maps/" );
613 vg_strcat( &path
, reg
->foldername
);
614 vg_strcat( &path
, "/preview.bin" );
616 vg_linear_clear(vg_mem
.scratch
);
619 void *data
= vg_file_read( vg_mem
.scratch
, path_buf
, &size
);
621 if( size
< sizeof(pointcloud_buffer
) ){
622 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
626 vg_async_item
*call
= vg_async_alloc(size
);
627 pointcloud_buffer
*pcbuf
= call
->payload
;
628 memcpy( pcbuf
, data
, size
);
630 u32 point_count
= (size
-sizeof(pointcloud_buffer
)) /
631 sizeof(struct pointcloud_vert
);
632 pcbuf
->max
= point_count
;
633 pcbuf
->count
= point_count
;
634 pcbuf
->op
= k_pointcloud_op_clear
;
636 vg_async_dispatch( call
, async_pointcloud_sub
);
637 vg_async_call( pointcloud_async_end
, NULL
, 0 );
640 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
644 vg_async_call( pointcloud_clear_async
, NULL
, 0 );
648 VG_STATIC
void skateshop_load_world_preview( struct registry_world
*reg
)
650 skaterift_begin_op( k_async_op_world_load_preview
);
651 vg_loader_start( skateshop_preview_loader_thread
, reg
);
657 void temp_update_playermodel(void);
658 VG_STATIC
void global_skateshop_preupdate(void)
660 float rate
= vg_minf( 1.0f
, vg
.time_frame_delta
* 2.0f
);
661 global_skateshop
.factive
= vg_lerpf( global_skateshop
.factive
,
662 global_skateshop
.active
, rate
);
664 if( !global_skateshop
.active
) return;
666 world_instance
*world
= world_current_instance();
667 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
669 /* camera positioning */
670 ent_camera
*ref
= mdl_arritm( &world
->ent_camera
,
671 mdl_entity_id_id(shop
->id_camera
) );
673 v3f dir
= {0.0f
,-1.0f
,0.0f
};
674 mdl_transform_vector( &ref
->transform
, dir
, dir
);
675 m3x3_mulv( localplayer
.invbasis
, dir
, dir
);
676 player_vector_angles( localplayer
.cam_override_angles
, dir
, 1.0f
, 0.0f
);
679 if( shop
->type
== k_skateshop_type_boardshop
||
680 shop
->type
== k_skateshop_type_worldshop
){
681 ent_marker
*display
= mdl_arritm( &world
->ent_marker
,
682 mdl_entity_id_id(shop
->boards
.id_display
) );
684 v3_sub( display
->transform
.co
, localplayer
.rb
.co
, lookat
);
687 else if( shop
->type
== k_skateshop_type_charshop
){
688 v3_sub( ref
->transform
.co
, localplayer
.rb
.co
, lookat
);
691 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
694 q_axis_angle( localplayer
.rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
},
695 atan2f(lookat
[0],lookat
[2]) );
697 v3_copy( ref
->transform
.co
, localplayer
.cam_override_pos
);
698 localplayer
.cam_override_fov
= ref
->fov
;
699 localplayer
.cam_override_strength
= global_skateshop
.factive
;
702 if( shop
->type
== k_skateshop_type_boardshop
){
703 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
704 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
706 struct cache_board
*selected_cache
= skateshop_selected_cache_if_loaded();
708 if( selected_cache
){
709 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
714 * ----------------------
717 if( button_down( k_srbind_mleft
) ){
718 if( global_skateshop
.selected_registry_id
> 0 ){
719 global_skateshop
.selected_registry_id
--;
723 if( button_down( k_srbind_mright
) ){
724 if( global_skateshop
.selected_registry_id
+1 <
725 global_skateshop
.registry_count
)
727 global_skateshop
.selected_registry_id
++;
731 if( selected_cache
&& button_down( k_srbind_maccept
) ){
732 vg_info( "chose board from skateshop (%u)\n",
733 global_skateshop
.selected_registry_id
);
735 if( localplayer
.board_view_slot
){
736 unwatch_cache_board( localplayer
.board_view_slot
);
739 localplayer
.board_view_slot
= selected_cache
;
740 watch_cache_board( localplayer
.board_view_slot
);
742 global_skateshop_exit();
746 else if( shop
->type
== k_skateshop_type_charshop
){
747 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
748 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
749 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
751 if( button_down( k_srbind_mleft
) ){
752 if( cl_playermdl_id
> 0 ){
756 cl_playermdl_id
= 2; /* HACK */
758 temp_update_playermodel(); /* HACK */
761 if( button_down( k_srbind_mright
) ){
762 if( cl_playermdl_id
+1 < 3 ){
766 cl_playermdl_id
= 0; /* HACK */
768 temp_update_playermodel(); /* HACK */
772 if( button_down( k_srbind_maccept
) ){
773 global_skateshop_exit();
776 else if( shop
->type
== k_skateshop_type_worldshop
){
780 if( global_skateshop
.world_registry_count
&&
781 ((skaterift
.async_op
== k_async_op_none
)||
782 (skaterift
.async_op
== k_async_op_world_load_preview
))){
783 gui_helper_action( axis_display_string(k_sraxis_mbrowse_h
), "browse" );
787 if( skaterift
.async_op
== k_async_op_none
){
788 gui_helper_action( button_display_string(k_srbind_maccept
), "load" );
795 if( button_down( k_srbind_mleft
) ){
796 if( global_skateshop
.selected_world_id
> 0 )
798 global_skateshop
.selected_world_id
--;
803 if( button_down( k_srbind_mright
) ){
804 if( global_skateshop
.selected_world_id
+1 <
805 global_skateshop
.world_registry_count
)
807 global_skateshop
.selected_world_id
++;
813 if( change
&& pointcloud_idle() ){
814 pointcloud_animate( k_pointcloud_anim_hiding
);
817 if( skaterift
.async_op
== k_async_op_none
){
818 struct registry_world
*rw
= &global_skateshop
.world_registry
[
819 global_skateshop
.selected_world_id
];
821 /* automatically load in clouds */
822 if( loadable
&& button_down( k_srbind_maccept
) ){
823 vg_info( "Select world (%u)\n",
824 global_skateshop
.selected_world_id
);
825 skaterift_change_world( rw
->foldername
);
829 if( pointcloud
.anim
== k_pointcloud_anim_idle_closed
){
830 if( global_skateshop
.pointcloud_world_id
!=
831 global_skateshop
.selected_world_id
)
833 global_skateshop
.pointcloud_world_id
=
834 global_skateshop
.selected_world_id
;
835 skateshop_load_world_preview( rw
);
838 pointcloud_animate( k_pointcloud_anim_opening
);
841 else if( pointcloud
.anim
== k_pointcloud_anim_idle_open
){
842 if( global_skateshop
.pointcloud_world_id
!=
843 global_skateshop
.selected_world_id
)
845 pointcloud_animate( k_pointcloud_anim_hiding
);
852 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
855 if( button_down( k_srbind_mback
) ){
856 global_skateshop_exit();
861 VG_STATIC
void skateshop_render_boardshop(void)
863 world_instance
*world
= world_current_instance();
864 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
866 u32 slot_count
= vg_list_size(global_skateshop
.shop_view_slots
);
868 ent_marker
*mark_rack
= mdl_arritm( &world
->ent_marker
,
869 mdl_entity_id_id(shop
->boards
.id_rack
)),
870 *mark_display
= mdl_arritm( &world
->ent_marker
,
871 mdl_entity_id_id(shop
->boards
.id_display
));
873 int visibility
[ SKATESHOP_VIEW_SLOT_MAX
];
874 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
875 for( u32 i
=0; i
<SKATESHOP_VIEW_SLOT_MAX
; i
++ ){
876 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
880 if( slot
->cache_ptr
== NULL
) visibility
[i
] = 0;
881 else if( slot
->cache_ptr
->state
!= k_cache_board_state_loaded
)
884 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
886 /* Render loaded boards in the view slots */
887 for( u32 i
=0; i
<slot_count
; i
++ ){
888 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
889 float selected
= 0.0f
;
891 if( !visibility
[i
] ) goto fade_out
;
894 transform_identity( &xform
);
896 xform
.co
[0] = -((float)i
- ((float)slot_count
)*0.5f
)*0.45f
;
897 mdl_transform_mul( &mark_rack
->transform
, &xform
, &xform
);
899 if( slot
->cache_ptr
->registry_id
==
900 global_skateshop
.selected_registry_id
){
904 float t
= slot
->view_blend
;
905 v3_lerp( xform
.co
, mark_display
->transform
.co
, t
, xform
.co
);
906 q_nlerp( xform
.q
, mark_display
->transform
.q
, t
, xform
.q
);
907 v3_lerp( xform
.s
, mark_display
->transform
.s
, t
, xform
.s
);
910 mdl_transform_m4x3( &xform
, mmdl
);
911 render_board( &main_camera
, world
, &slot
->cache_ptr
->board
, mmdl
,
912 k_board_shader_entity
);
915 float rate
= 5.0f
*vg
.time_delta
;
916 slot
->view_blend
= vg_lerpf( slot
->view_blend
, selected
, rate
);
919 ent_marker
*mark_info
= mdl_arritm( &world
->ent_marker
,
920 mdl_entity_id_id(shop
->boards
.id_info
));
922 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
923 mdl_transform_m4x3( &mark_rack
->transform
, mrack
);
926 const char *text_title
= "Fish - Title";
927 const char *text_author
= "by Shaniqua";
931 m4x3_identity( mlocal
);
936 font3d_bind( &gui
.font
, &main_camera
);
937 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
940 * ------------------------------------------------------------------ */
942 v3_zero( mlocal
[3] );
943 mlocal
[0][0] = -scale
*2.0f
;
944 mlocal
[1][2] = -scale
*2.0f
;
945 mlocal
[2][1] = -thickness
;
946 mlocal
[3][2] = -0.7f
;
947 m4x3_mul( mrack
, mlocal
, mmdl
);
949 if( global_skateshop
.registry_count
== 0 ){
950 font3d_simple_draw( &gui
.font
, 0,
951 "Nothing installed", &main_camera
, mmdl
);
956 i
+=highscore_intl( buf
+i
, global_skateshop
.selected_registry_id
+1, 3 );
958 i
+=highscore_intl( buf
+i
, global_skateshop
.registry_count
, 3 );
961 font3d_simple_draw( &gui
.font
, 0, buf
, &main_camera
, mmdl
);
964 struct cache_board
*cache_ptr
= skateshop_selected_cache_if_loaded();
965 if( !cache_ptr
) return;
967 struct registry_board
*reg
=
968 &global_skateshop
.registry
[cache_ptr
->registry_id
];
969 struct workshop_file_info
*info
= ®
->workshop
;
972 * ----------------------------------------------------------------- */
974 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
975 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, info
->title
);
976 mlocal
[3][0] *= scale
*0.5f
;
979 m4x3_mul( mtext
, mlocal
, mmdl
);
980 font3d_simple_draw( &gui
.font
, 0, info
->title
, &main_camera
, mmdl
);
983 * ----------------------------------------------------------------- */
985 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
986 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, info
->author_name
);
987 mlocal
[3][0] *= scale
*0.5f
;
990 m4x3_mul( mtext
, mlocal
, mmdl
);
991 font3d_simple_draw( &gui
.font
, 0, info
->author_name
, &main_camera
, mmdl
);
994 VG_STATIC
void skateshop_render_charshop(void)
998 VG_STATIC
void skateshop_render_worldshop(void)
1000 world_instance
*world
= world_current_instance();
1002 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
1003 ent_marker
*mark_display
= mdl_arritm( &world
->ent_marker
,
1004 mdl_entity_id_id(shop
->worlds
.id_display
)),
1005 *mark_info
= mdl_arritm( &world
->ent_marker
,
1006 mdl_entity_id_id(shop
->boards
.id_info
));
1009 char buftext
[128], bufsubtext
[128];
1010 vg_str info
, subtext
;
1011 vg_strnull( &info
, buftext
, 128 );
1012 vg_strnull( &subtext
, bufsubtext
, 128 );
1014 if( global_skateshop
.world_registry_count
){
1015 struct registry_world
*rw
= &global_skateshop
.world_registry
[
1016 global_skateshop
.selected_world_id
];
1018 info
.i
+=highscore_intl( info
.buffer
+info
.i
,
1019 global_skateshop
.selected_world_id
+1, 3 );
1020 info
.buffer
[info
.i
++] = '/';
1021 info
.i
+=highscore_intl( info
.buffer
+info
.i
,
1022 global_skateshop
.world_registry_count
, 3 );
1023 info
.buffer
[info
.i
++] = ' ';
1024 info
.buffer
[info
.i
] = '\0';
1026 vg_strcat( &info
, rw
->foldername
);
1027 if( skaterift
.async_op
== k_async_op_world_loading
||
1028 skaterift
.async_op
== k_async_op_world_preloading
){
1029 vg_strcat( &subtext
, "Loading..." );
1032 vg_strcat( &subtext
, "No information" );
1036 vg_strcat( &info
, "No worlds installed" );
1040 m4x3f mtext
,mlocal
,mtextmdl
;
1041 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
1043 font3d_bind( &gui
.font
, &main_camera
);
1044 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
1046 float scale
= 0.2f
, thickness
= 0.015f
, scale1
= 0.08f
;
1047 m3x3_zero( mlocal
);
1048 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
1049 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, buftext
);
1050 mlocal
[3][0] *= scale
*0.5f
;
1051 mlocal
[3][1] = 0.1f
;
1052 mlocal
[3][2] = 0.0f
;
1053 m4x3_mul( mtext
, mlocal
, mtextmdl
);
1054 font3d_simple_draw( &gui
.font
, 0, buftext
, &main_camera
, mtextmdl
);
1056 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale1
, scale1
, thickness
} );
1057 mlocal
[3][0] = -font3d_string_width( &gui
.font
, 0, bufsubtext
);
1058 mlocal
[3][0] *= scale1
*0.5f
;
1059 mlocal
[3][1] = -scale1
*0.3f
;
1060 m4x3_mul( mtext
, mlocal
, mtextmdl
);
1061 font3d_simple_draw( &gui
.font
, 0, bufsubtext
, &main_camera
, mtextmdl
);
1065 mdl_transform_m4x3( &mark_display
->transform
, mmdl
);
1066 m4x3_rotate_y( mmdl
, vg
.time
* 0.2 );
1069 glBlendFunc(GL_ONE
, GL_ONE
);
1070 glDisable(GL_DEPTH_TEST
);
1071 pointcloud_render( world
, &main_camera
, mmdl
);
1072 glDisable(GL_BLEND
);
1073 glEnable(GL_DEPTH_TEST
);
1077 * World: render event
1079 VG_STATIC
void skateshop_render(void)
1081 if( !global_skateshop
.active
) return;
1083 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
1085 if( shop
->type
== k_skateshop_type_boardshop
){
1086 skateshop_render_boardshop();
1088 else if( shop
->type
== k_skateshop_type_charshop
){
1089 skateshop_render_charshop();
1091 else if( shop
->type
== k_skateshop_type_worldshop
){
1092 skateshop_render_worldshop();
1095 vg_fatal_error( "Unknown store (%u)\n", shop
->type
);
1100 * Entity logic: entrance event
1102 VG_STATIC
void ent_skateshop_call( world_instance
*world
, ent_call
*call
)
1104 u32 index
= mdl_entity_id_id( call
->id
);
1105 ent_skateshop
*shop
= mdl_arritm( &world
->ent_skateshop
, index
);
1106 vg_info( "skateshop_call\n" );
1108 if( menu
.active
) return;
1109 if( skaterift
.async_op
!= k_async_op_none
) return;
1111 if( call
->function
== k_ent_function_trigger
){
1112 if( localplayer
.subsystem
!= k_player_subsystem_walk
){
1116 vg_info( "Entering skateshop\n" );
1118 localplayer
.immobile
= 1;
1119 menu
.disable_open
= 1;
1120 global_skateshop
.active
= 1;
1122 v3_zero( localplayer
.rb
.v
);
1123 v3_zero( localplayer
.rb
.w
);
1124 localplayer
._walk
.move_speed
= 0.0f
;
1125 global_skateshop
.ptr_ent
= shop
;
1127 if( shop
->type
== k_skateshop_type_boardshop
){
1128 skateshop_update_viewpage();
1129 workshop_op_item_scan();
1131 else if( shop
->type
== k_skateshop_type_worldshop
){
1132 pointcloud_animate( k_pointcloud_anim_opening
);
1133 skateshop_op_world_scan();
1139 * Entity logic: exit event
1141 VG_STATIC
void global_skateshop_exit(void)
1143 vg_info( "exit skateshop\n" );
1144 localplayer
.immobile
= 0;
1145 global_skateshop
.active
= 0;
1146 menu
.disable_open
= 0;
1147 srinput
.ignore_input_frames
= 2;
1150 #endif /* ENT_SKATESHOP_C */