1 #ifndef ENT_SKATESHOP_C
2 #define ENT_SKATESHOP_C
4 #include "ent_skateshop.h"
10 * Checks string equality but does a hash check first
12 static inline int const_str_eq( u32 hash
, const char *str
, const char *cmp
)
14 if( hash
== vg_strdjb2(cmp
) )
15 if( !strcmp( str
, cmp
) )
21 * Get an existing cache instance, allocate a new one to be loaded, or NULL if
24 VG_STATIC
struct cache_board
*skateshop_cache_fetch( u32 registry_index
)
26 struct registry_board
*reg
= NULL
;
28 if( registry_index
< global_skateshop
.registry_count
){
29 reg
= &global_skateshop
.registry
[ registry_index
];
32 return reg
->cache_ptr
;
36 /* lru eviction. should be a linked list maybe... */
37 double min_time
= 1e300
;
38 struct cache_board
*min_board
= NULL
;
40 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
41 for( u32 i
=0; i
<SKATESHOP_BOARD_CACHE_MAX
; i
++ ){
42 struct cache_board
*cache_ptr
= &global_skateshop
.cache
[i
];
44 if( cache_ptr
->state
== k_cache_board_state_load_request
) continue;
45 if( cache_ptr
->ref_count
) continue;
47 if( cache_ptr
->last_use_time
< min_time
){
48 min_time
= cache_ptr
->last_use_time
;
49 min_board
= cache_ptr
;
54 if( min_board
->state
== k_cache_board_state_loaded
){
55 struct registry_board
*other
=
56 &global_skateshop
.registry
[ min_board
->registry_id
];
58 vg_info( "Deallocating board: '%s'\n", min_board
, other
->filename
);
60 player_board_unload( &min_board
->board
);
61 other
->cache_ptr
= NULL
;
65 vg_info( "Allocating board (reg:%u) '%s'\n",
66 registry_index
, reg
->filename
);
69 vg_info( "Pre-allocating board (reg:%u) 'null'\n", registry_index
);
72 min_board
->registry_id
= registry_index
;
73 min_board
->last_use_time
= vg
.time
;
74 min_board
->ref_count
= 0;
75 min_board
->state
= k_cache_board_state_load_request
;
78 vg_error( "No free boards to load registry!\n" );
81 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
85 VG_STATIC
void skateshop_update_viewpage(void)
87 u32 page
= global_skateshop
.selected_registry_id
/SKATESHOP_VIEW_SLOT_MAX
;
89 for( u32 i
=0; i
<SKATESHOP_VIEW_SLOT_MAX
; i
++ ){
90 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
91 u32 request_id
= page
*SKATESHOP_VIEW_SLOT_MAX
+ i
;
93 if( slot
->cache_ptr
) unwatch_cache_board( slot
->cache_ptr
);
95 slot
->cache_ptr
= skateshop_cache_fetch( request_id
);
96 if( slot
->cache_ptr
) watch_cache_board( slot
->cache_ptr
);
100 /* generic reciever */
101 VG_STATIC
void workshop_async_any_complete( void *data
, u32 size
)
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
;
206 * Async thread which scans local files for boards, as well as scheduling
207 * synchronous calls to the workshop
209 VG_STATIC
void workshop_scan_thread( void *_args
)
211 vg_linear_clear( vg_mem
.scratch
);
213 for( u32 i
=0; i
<global_skateshop
.t1_registry_count
; i
++ ){
214 struct registry_board
*reg
= &global_skateshop
.registry
[i
];
215 reg
->state
= k_registry_board_state_indexed_absent
;
221 vg_info( "Scanning models/boards/*.mdl\n" );
223 tinydir_open( &dir
, "models/boards" );
225 while( dir
.has_next
){
227 tinydir_readfile( &dir
, &file
);
230 u32 hash
= vg_strdjb2( file
.name
);
232 for( u32 i
=0; i
<global_skateshop
.t1_registry_count
; i
++ ){
233 struct registry_board
*reg
= &global_skateshop
.registry
[i
];
235 if( const_str_eq( hash
, file
.name
, reg
->filename
) ){
236 reg
->state
= k_registry_board_state_indexed
;
241 if( global_skateshop
.t1_registry_count
== SKATESHOP_REGISTRY_MAX
){
242 vg_error( "You have too many boards installed!\n" );
246 vg_info( "new listing!: %s\n", file
.name
);
248 struct registry_board
*reg
=
249 &global_skateshop
.registry
[global_skateshop
.t1_registry_count
++];
251 reg
->cache_ptr
= NULL
;
252 vg_strncpy( file
.name
, reg
->filename
, 64, k_strncpy_always_add_null
);
253 reg
->filename_hash
= hash
;
254 reg
->workshop_id
= 0;
255 reg
->state
= k_registry_board_state_indexed
;
258 next_file
: tinydir_next( &dir
);
264 * Steam workshop scan
266 vg_info( "Scanning steam workshop for boards\n" );
267 PublishedFileId_t workshop_ids
[ SKATESHOP_REGISTRY_MAX
];
268 u32 workshop_count
= SKATESHOP_REGISTRY_MAX
;
270 vg_async_item
*call
= vg_async_alloc(
271 sizeof(struct async_workshop_installed_files_info
));
272 struct async_workshop_installed_files_info
*info
= call
->payload
;
273 info
->buffer
= workshop_ids
;
274 info
->len
= &workshop_count
;
275 vg_async_dispatch( call
, async_workshop_get_installed_files
);
278 for( u32 j
=0; j
<workshop_count
; j
++ ){
279 PublishedFileId_t id
= workshop_ids
[j
];
281 for( u32 i
=0; i
<global_skateshop
.t1_registry_count
; i
++ ){
282 struct registry_board
*reg
= &global_skateshop
.registry
[i
];
284 if( reg
->workshop_id
== id
){
285 reg
->state
= k_registry_board_state_indexed
;
286 goto next_file_workshop
;
290 if( global_skateshop
.t1_registry_count
== SKATESHOP_REGISTRY_MAX
){
291 vg_error( "You have too many boards installed!\n" );
295 vg_info( "new listing from the steam workshop!: "PRINTF_U64
"\n", id
);
297 struct registry_board
*reg
= &global_skateshop
.registry
[
298 global_skateshop
.t1_registry_count
++ ];
300 reg
->cache_ptr
= NULL
;
301 snprintf( reg
->filename
, 64, PRINTF_U64
, id
);
302 reg
->filename_hash
= vg_strdjb2( reg
->filename
);
303 reg
->workshop_id
= id
;
304 reg
->state
= k_registry_board_state_indexed
;
309 vg_async_call( workshop_async_reg_update
, NULL
, 0 );
311 workshop_visibile_load_loop_thread(NULL
);
315 * Asynchronous scan of local disk for items and add them to the registry
317 VG_STATIC
void workshop_op_item_scan(void)
319 workshop_begin_op( k_workshop_op_item_scan
);
320 vg_loader_start( workshop_scan_thread
, NULL
);
325 * -----------------------------------------------------------------------------
328 /* we can only keep using a viewslot pointer for multiple frames if we watch it
329 * using this function */
330 VG_STATIC
void watch_cache_board( struct cache_board
*ptr
)
332 if( ptr
->ref_count
>= 32 ){
333 vg_fatal_error( "dynamic board watch missmatch (limit is 32)\n" );
336 ptr
->last_use_time
= vg
.time
;
340 /* after this is called, the calling code only has access to the pointer for the
341 * duration of the rest of the frame */
342 VG_STATIC
void unwatch_cache_board( struct cache_board
*ptr
)
344 if( ptr
->ref_count
== 0 ){
345 vg_fatal_error( "dynamic board unwatch missmatch (no watchers)\n" );
354 VG_STATIC
void skateshop_init(void)
356 u32 reg_size
= sizeof(struct registry_board
)*SKATESHOP_REGISTRY_MAX
,
357 cache_size
= sizeof(struct cache_board
)*SKATESHOP_BOARD_CACHE_MAX
;
359 global_skateshop
.registry
= vg_linear_alloc( vg_mem
.rtmemory
, reg_size
);
360 global_skateshop
.registry_count
= 0;
361 global_skateshop
.cache
= vg_linear_alloc( vg_mem
.rtmemory
, cache_size
);
363 memset( global_skateshop
.cache
, 0, cache_size
);
365 for( u32 i
=0; i
<SKATESHOP_BOARD_CACHE_MAX
; i
++ ){
366 struct cache_board
*board
= &global_skateshop
.cache
[i
];
367 board
->state
= k_cache_board_state_none
;
368 board
->registry_id
= 0xffffffff;
369 board
->last_use_time
= -99999.9;
370 board
->ref_count
= 0;
374 VG_STATIC
struct cache_board
*skateshop_selected_cache_if_loaded(void)
376 if( global_skateshop
.registry_count
> 0 ){
377 u32 reg_id
= global_skateshop
.selected_registry_id
;
378 struct registry_board
*reg
= &global_skateshop
.registry
[ reg_id
];
380 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
381 if( reg
->cache_ptr
&&
382 (reg
->cache_ptr
->state
== k_cache_board_state_loaded
) )
384 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
385 return reg
->cache_ptr
;
387 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
396 VG_STATIC
void global_skateshop_preupdate(void)
398 float rate
= vg_minf( 1.0f
, vg
.time_frame_delta
* 2.0f
);
399 global_skateshop
.factive
= vg_lerpf( global_skateshop
.factive
,
400 global_skateshop
.active
, rate
);
402 if( !global_skateshop
.active
) return;
404 world_instance
*world
= get_active_world();
406 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
407 ent_camera
*ref
= mdl_arritm( &world
->ent_camera
,
408 mdl_entity_id_id(shop
->id_camera
) );
409 ent_marker
*display
= mdl_arritm( &world
->ent_marker
,
410 mdl_entity_id_id(shop
->id_display
) );
412 v3f dir
= {0.0f
,-1.0f
,0.0f
};
413 mdl_transform_vector( &ref
->transform
, dir
, dir
);
414 player_vector_angles( localplayer
.cam_override_angles
, dir
, 1.0f
, 0.0f
);
417 v3_sub( display
->transform
.co
, localplayer
.rb
.co
, lookat
);
419 q_axis_angle( localplayer
.rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
},
420 atan2f(lookat
[0],lookat
[2]) );
422 v3_copy( ref
->transform
.co
, localplayer
.cam_override_pos
);
423 localplayer
.cam_override_fov
= ref
->fov
;
424 localplayer
.cam_override_strength
= global_skateshop
.factive
;
426 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
427 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( global_skateshop
.interaction_cooldown
> 0.0f
){
442 global_skateshop
.interaction_cooldown
-= vg
.time_delta
;
446 if( button_down( k_srbind_mleft
) ){
447 if( global_skateshop
.selected_registry_id
> 0 ){
448 global_skateshop
.selected_registry_id
--;
453 if( button_down( k_srbind_mright
) ){
454 if( global_skateshop
.selected_registry_id
+1 <
455 global_skateshop
.registry_count
)
457 global_skateshop
.selected_registry_id
++;
463 vg_info( "Select registry: %u\n",
464 global_skateshop
.selected_registry_id
);
465 global_skateshop
.interaction_cooldown
= 0.125f
;
469 if( selected_cache
&& button_down( k_srbind_maccept
) ){
470 vg_info( "chose board from skateshop (%u)\n",
471 global_skateshop
.selected_registry_id
);
473 if( localplayer
.board_view_slot
){
474 unwatch_cache_board( localplayer
.board_view_slot
);
477 localplayer
.board_view_slot
= selected_cache
;
478 watch_cache_board( localplayer
.board_view_slot
);
480 global_skateshop_exit();
484 if( button_down( k_srbind_mback
) ){
485 global_skateshop_exit();
491 * World: render event
493 VG_STATIC
void skateshop_render(void)
495 if( !global_skateshop
.active
) return;
497 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
498 world_instance
*world
= get_active_world();
500 u32 slot_count
= vg_list_size(global_skateshop
.shop_view_slots
);
502 ent_marker
*mark_rack
= mdl_arritm( &world
->ent_marker
,
503 mdl_entity_id_id(shop
->id_rack
)),
504 *mark_display
= mdl_arritm( &world
->ent_marker
,
505 mdl_entity_id_id(shop
->id_display
));
507 int visibility
[ SKATESHOP_VIEW_SLOT_MAX
];
508 SDL_AtomicLock( &global_skateshop
.sl_cache_access
);
509 for( u32 i
=0; i
<SKATESHOP_VIEW_SLOT_MAX
; i
++ ){
510 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
514 if( slot
->cache_ptr
== NULL
) visibility
[i
] = 0;
515 else if( slot
->cache_ptr
->state
!= k_cache_board_state_loaded
)
518 SDL_AtomicUnlock( &global_skateshop
.sl_cache_access
);
520 /* Render loaded boards in the view slots */
521 for( u32 i
=0; i
<slot_count
; i
++ ){
522 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
523 float selected
= 0.0f
;
525 if( !visibility
[i
] ) goto fade_out
;
528 transform_identity( &xform
);
530 xform
.co
[0] = -((float)i
- ((float)slot_count
)*0.5f
)*0.45f
;
531 mdl_transform_mul( &mark_rack
->transform
, &xform
, &xform
);
533 if( slot
->cache_ptr
->registry_id
==
534 global_skateshop
.selected_registry_id
){
538 float t
= slot
->view_blend
;
539 v3_lerp( xform
.co
, mark_display
->transform
.co
, t
, xform
.co
);
540 q_nlerp( xform
.q
, mark_display
->transform
.q
, t
, xform
.q
);
541 v3_lerp( xform
.s
, mark_display
->transform
.s
, t
, xform
.s
);
544 mdl_transform_m4x3( &xform
, mmdl
);
545 render_board( &main_camera
, world
, &slot
->cache_ptr
->board
, mmdl
,
546 k_board_shader_entity
);
549 float rate
= 5.0f
*vg
.time_delta
;
550 slot
->view_blend
= vg_lerpf( slot
->view_blend
, selected
, rate
);
553 ent_marker
*mark_info
= mdl_arritm( &world
->ent_marker
,
554 mdl_entity_id_id(shop
->id_info
));
556 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
557 mdl_transform_m4x3( &mark_rack
->transform
, mrack
);
559 const char *text_title
= "Fish - Title";
560 const char *text_author
= "by Shaniqua";
563 m4x3_identity( mlocal
);
568 font3d_bind( &world_global
.font
, &main_camera
);
569 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
572 * ------------------------------------------------------------------ */
574 v3_zero( mlocal
[3] );
575 mlocal
[0][0] = -scale
*2.0f
;
576 mlocal
[1][2] = -scale
*2.0f
;
577 mlocal
[2][1] = -thickness
;
578 mlocal
[3][2] = -0.7f
;
579 m4x3_mul( mrack
, mlocal
, mmdl
);
581 if( global_skateshop
.registry_count
== 0 ){
582 font3d_simple_draw( &world_global
.font
, 0,
583 "Nothing installed", &main_camera
, mmdl
);
588 i
+=highscore_intl( buf
+i
, global_skateshop
.selected_registry_id
+1, 3 );
590 i
+=highscore_intl( buf
+i
, global_skateshop
.registry_count
, 3 );
593 font3d_simple_draw( &world_global
.font
, 0, buf
, &main_camera
, mmdl
);
596 struct cache_board
*cache_ptr
= skateshop_selected_cache_if_loaded();
597 if( !cache_ptr
) return;
600 * ----------------------------------------------------------------- */
601 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
602 mlocal
[3][0] = -font3d_string_width(&world_global
.font
,0,text_title
);
603 mlocal
[3][0] *= scale
*0.5f
;
605 m4x3_mul( mtext
, mlocal
, mmdl
);
606 font3d_simple_draw( &world_global
.font
, 0, text_title
, &main_camera
, mmdl
);
609 * ----------------------------------------------------------------- */
611 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
612 mlocal
[3][0] = -font3d_string_width(&world_global
.font
,0,text_author
);
613 mlocal
[3][0] *= scale
*0.5f
;
615 m4x3_mul( mtext
, mlocal
, mmdl
);
616 font3d_simple_draw( &world_global
.font
, 0, text_author
, &main_camera
, mmdl
);
620 * Entity logic: entrance event
622 VG_STATIC
void ent_skateshop_call( world_instance
*world
, ent_call
*call
)
624 u32 index
= mdl_entity_id_id( call
->id
);
625 ent_skateshop
*shop
= mdl_arritm( &world
->ent_skateshop
, index
);
626 vg_info( "skateshop_call\n" );
628 if( call
->function
== k_ent_function_trigger
){
629 if( localplayer
.subsystem
!= k_player_subsystem_walk
){
633 vg_info( "Entering skateshop\n" );
635 localplayer
.immobile
= 1;
636 global_skateshop
.active
= 1;
638 v3_zero( localplayer
.rb
.v
);
639 v3_zero( localplayer
.rb
.w
);
640 localplayer
._walk
.move_speed
= 0.0f
;
641 global_skateshop
.ptr_ent
= shop
;
643 skateshop_update_viewpage();
644 workshop_op_item_scan();
649 * Entity logic: exit event
651 VG_STATIC
void global_skateshop_exit(void)
653 vg_info( "exit skateshop\n" );
654 localplayer
.immobile
= 0;
655 global_skateshop
.active
= 0;
656 srinput
.ignore_input_frames
= 2;
659 #endif /* ENT_SKATESHOP_C */