1 #ifndef ENT_SKATESHOP_C
2 #define ENT_SKATESHOP_C
8 #define MAX_LOCAL_BOARDS 64
9 #define BILL_TIN_BOARDS 1
10 #define MAX_DYNAMIC_BOARDS 9
14 ent_skateshop
*ptr_ent
;
20 k_skateshop_loc_page__viewing
,
22 k_skateshop_loc_select_use
,
23 k_skateshop_loc_select_cancel
,
24 k_skateshop_loc_select_upload
,
25 k_skateshop_loc_page__selected
,
27 k_skateshop_loc_page__upload
,
33 enum dynamic_board_state
{
34 k_dynamic_board_state_none
,
35 k_dynamic_board_state_loaded
,
36 k_dynamic_board_state_loading
,
42 struct player_board board
;
53 struct dynamic_board
*db
;
63 struct dynamic_board
*dynamic
;
65 char filename
[64]; /* if workshop, string version of uid. */
74 float interaction_cooldown
;
76 u32 selected_registry_id
;
78 static global_skateshop
;
80 static inline int const_str_eq( u32 hash
, const char *str
, const char *cmp
)
82 if( hash
== vg_strdjb2(cmp
) )
83 if( !strcmp( str
, cmp
) )
88 static int skateshop_workshop_name_blacklisted( u32 hash
, const char *name
)
90 if( const_str_eq( hash
, name
, "skaterift_fish.mdl" ) ) return 1;
94 VG_STATIC
void skateshop_loader_start( void (*pfn
)(void) )
96 if( global_skateshop
.loading
)
97 vg_fatal_error( "skateshop thread sync failure\n" );
99 global_skateshop
.loading
= 1;
100 vg_loader_start( pfn
);
103 VG_STATIC
void skateshop_async_post( void *payload
, u32 size
)
105 global_skateshop
.loading
= 0;
109 struct dynamic_board
*skateshop_lru_alloc( u32 id
)
111 double min_time
= 1e300
;
112 struct dynamic_board
*min_board
= NULL
;
114 for( u32 i
=0; i
<MAX_DYNAMIC_BOARDS
; i
++ ){
115 struct dynamic_board
*db
= &global_skateshop
.dynamic_boards
[i
];
117 if( db
->state
== k_dynamic_board_state_loading
)
123 if( db
->last_use_time
< min_time
){
124 min_time
= db
->last_use_time
;
130 localplayer
.board
= NULL
; /* temp */
132 if( min_board
->state
== k_dynamic_board_state_loaded
){
133 struct board_registry
*other
=
134 &global_skateshop
.registry
[min_board
->registry_id
];
136 vg_info( "Deallocating board: '%s'\n", min_board
, other
->filename
);
138 player_board_unload( &min_board
->board
);
139 other
->dynamic
= NULL
;
142 struct board_registry
*reg
= &global_skateshop
.registry
[id
];
144 vg_info( "Allocating board '%s' @%p\n", reg
->filename
, min_board
);
146 min_board
->state
= k_dynamic_board_state_loading
;
147 min_board
->registry_id
= id
;
148 min_board
->last_use_time
= vg
.time
;
149 min_board
->ref_count
= 0;
152 vg_error( "No free boards to load registry!\n" );
159 void skateshop_board_registry_path( struct board_registry
*reg
, char path
[256] )
162 snprintf( path
, 256, "models/boards/workshop/%s/something.mdl",
166 snprintf( path
, 256, "models/boards/%s", reg
->filename
);
170 VG_STATIC
void skateshop_async_board_complete( void *payload
, u32 size
)
172 struct dynamic_board
*db
= payload
;
174 if( db
== global_skateshop
.localplayer_slot
){
175 localplayer
.board
= &db
->board
;
179 for( u32 i
=0; i
<vg_list_size(global_skateshop
.shop_view_slots
); i
++ ){
180 if( global_skateshop
.shop_view_slots
[i
].db
== db
){
186 db
->last_use_time
= vg
.time
;
187 db
->state
= k_dynamic_board_state_loaded
;
189 struct board_registry
*reg
= &global_skateshop
.registry
[ db
->registry_id
];
192 vg_success( "Async board loaded (%s)\n", reg
->filename
);
195 VG_STATIC
void skateshop_load_requested_boards(void)
198 for( u32 i
=0; i
<MAX_DYNAMIC_BOARDS
; i
++ ){
199 struct dynamic_board
*db
= &global_skateshop
.dynamic_boards
[i
];
201 if( db
->state
== k_dynamic_board_state_loading
){
202 struct board_registry
*reg
=
203 &global_skateshop
.registry
[ db
->registry_id
];
205 skateshop_board_registry_path( reg
, path
);
206 player_board_load( &db
->board
, path
);
207 vg_async_call( skateshop_async_board_complete
, db
, 0 );
212 VG_STATIC
void skateshop_thread1_refresh(void)
214 skateshop_load_requested_boards();
215 vg_async_call( skateshop_async_post
, NULL
, 0 );
218 VG_STATIC
int skateshop_use_board( int argc
, const char *argv
[] )
220 if( global_skateshop
.loading
){
221 vg_error( "Cannot use skateshop currently (loader thread missing)\n" );
226 u32 hash
= vg_strdjb2( argv
[0] );
228 for( u32 i
=0; i
<global_skateshop
.registry_count
; i
++ ){
229 struct board_registry
*reg
= &global_skateshop
.registry
[i
];
231 if( const_str_eq( hash
, argv
[0], reg
->filename
) ){
234 struct dynamic_board
*db
= reg
->dynamic
;
236 if( db
->state
== k_dynamic_board_state_loaded
){
237 localplayer
.board
= &db
->board
;
238 db
->last_use_time
= vg
.time
;
241 vg_fatal_error( "Invalid state while loading board\n" );
245 struct dynamic_board
*db
= skateshop_lru_alloc( i
);
246 db
->state
= k_dynamic_board_state_loading
;
247 skateshop_loader_start( skateshop_thread1_refresh
);
256 VG_STATIC
void skateshop_use_board_suggest( int argc
, const char *argv
[] )
259 for( u32 i
=0; i
<global_skateshop
.registry_count
; i
++ ){
260 struct board_registry
*reg
= &global_skateshop
.registry
[i
];
262 if( reg
->ghost
) continue; /* we probably can't load these */
264 console_suggest_score_text( reg
->filename
, argv
[0], 0 );
269 VG_STATIC
void skateshop_scan_for_items(void)
271 vg_linear_clear( vg_mem
.scratch
);
273 for( u32 i
=0; i
<global_skateshop
.registry_count
; i
++ ){
274 struct board_registry
*reg
= &global_skateshop
.registry
[i
];
279 tinydir_open( &dir
, "models/boards" );
281 while( dir
.has_next
){
283 tinydir_readfile( &dir
, &file
);
286 u32 hash
= vg_strdjb2( file
.name
);
288 for( u32 i
=0; i
<global_skateshop
.registry_count
; i
++ ){
289 struct board_registry
*reg
= &global_skateshop
.registry
[i
];
291 if( const_str_eq( hash
, file
.name
, reg
->filename
) ){
297 if( global_skateshop
.registry_count
== MAX_LOCAL_BOARDS
){
298 vg_error( "You have too many boards installed!\n" );
302 vg_info( "new listing!: %s\n", file
.name
);
304 struct board_registry
*reg
= &global_skateshop
.registry
[
305 global_skateshop
.registry_count
++ ];
308 vg_strncpy( file
.name
, reg
->filename
, 64, k_strncpy_always_add_null
);
309 reg
->filename_hash
= hash
;
315 next_file
: tinydir_next( &dir
);
320 skateshop_load_requested_boards();
321 vg_async_call( skateshop_async_post
, NULL
, 0 );
324 VG_STATIC
void global_skateshop_exit(void)
326 vg_info( "exit skateshop\n" );
327 localplayer
.immobile
= 0;
328 global_skateshop
.active
= 0;
331 VG_STATIC
void skateshop_request_viewpage( u32 page
)
333 u32 slot_count
= vg_list_size(global_skateshop
.shop_view_slots
);
334 u32 start
= page
* slot_count
;
336 for( u32 i
=0; i
<slot_count
; i
++ ){
337 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
340 slot
->db
->ref_count
--;
344 u32 reg_index
= start
+i
;
345 if( reg_index
< global_skateshop
.registry_count
){
346 struct board_registry
*reg
= &global_skateshop
.registry
[ reg_index
];
349 struct dynamic_board
*db
= reg
->dynamic
;
351 if( db
->state
== k_dynamic_board_state_loaded
){
352 db
->last_use_time
= vg
.time
;
357 vg_fatal_error( "Invalid state while loading page\n" );
361 struct dynamic_board
*db
= skateshop_lru_alloc( reg_index
);
372 VG_STATIC
void ent_skateshop_call( world_instance
*world
, ent_call
*call
)
374 u32 index
= mdl_entity_id_id( call
->id
);
375 ent_skateshop
*shop
= mdl_arritm( &world
->ent_skateshop
, index
);
376 vg_info( "skateshop_call\n" );
378 if( global_skateshop
.loading
){
379 vg_error( "Cannot enter skateshop currently (loader thread missing)\n" );
383 if( call
->function
== k_ent_function_trigger
){
384 if( localplayer
.subsystem
!= k_player_subsystem_walk
){
388 vg_info( "Entering skateshop\n" );
390 localplayer
.immobile
= 1;
391 global_skateshop
.active
= 1;
392 global_skateshop
.interface_loc
= k_skateshop_loc_page__viewing
;
394 v3_zero( localplayer
.rb
.v
);
395 v3_zero( localplayer
.rb
.w
);
396 localplayer
._walk
.move_speed
= 0.0f
;
398 global_skateshop
.ptr_ent
= shop
;
400 skateshop_request_viewpage(0);
401 skateshop_loader_start( skateshop_scan_for_items
);
405 VG_STATIC
void global_skateshop_preupdate(void)
407 float rate
= vg_minf( 1.0f
, vg
.time_frame_delta
* 2.0f
);
408 global_skateshop
.factive
= vg_lerpf( global_skateshop
.factive
,
409 global_skateshop
.active
, rate
);
411 if( !global_skateshop
.active
) return;
413 world_instance
*world
= get_active_world();
415 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
416 ent_camera
*ref
= mdl_arritm( &world
->ent_camera
,
417 mdl_entity_id_id(shop
->id_camera
) );
418 ent_marker
*display
= mdl_arritm( &world
->ent_marker
,
419 mdl_entity_id_id(shop
->id_display
) );
421 v3f dir
= {0.0f
,-1.0f
,0.0f
};
422 mdl_transform_vector( &ref
->transform
, dir
, dir
);
423 player_vector_angles( localplayer
.cam_override_angles
, dir
, 1.0f
, 0.0f
);
426 v3_sub( display
->transform
.co
, localplayer
.rb
.co
, lookat
);
428 q_axis_angle( localplayer
.rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
},
429 atan2f(lookat
[0],lookat
[2]) );
431 v3_copy( ref
->transform
.co
, localplayer
.cam_override_pos
);
432 localplayer
.cam_override_fov
= ref
->fov
;
434 localplayer
.cam_override_strength
= global_skateshop
.factive
;
436 if( global_skateshop
.interaction_cooldown
> 0.0f
){
437 global_skateshop
.interaction_cooldown
-= vg
.time_delta
;
441 if( global_skateshop
.interface_loc
<= k_skateshop_loc_page__viewing
){
443 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
444 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
445 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
449 if( button_down( k_srbind_mleft
) ){
450 if( global_skateshop
.selected_registry_id
> 0 ){
451 global_skateshop
.selected_registry_id
--;
456 if( button_down( k_srbind_mright
) ){
457 if( global_skateshop
.selected_registry_id
<
458 global_skateshop
.registry_count
-1 )
460 global_skateshop
.selected_registry_id
++;
466 vg_info( "Select registry: %u\n",
467 global_skateshop
.selected_registry_id
);
468 global_skateshop
.interaction_cooldown
= 0.125f
;
472 if( button_down( k_srbind_mback
) ){
473 global_skateshop_exit();
479 VG_STATIC
void skateshop_init(void)
481 global_skateshop
.registry
=
482 vg_linear_alloc( vg_mem
.rtmemory
,
483 sizeof(struct board_registry
)*MAX_LOCAL_BOARDS
);
484 global_skateshop
.registry_count
= 0;
485 global_skateshop
.dynamic_boards
=
486 vg_linear_alloc( vg_mem
.rtmemory
,
487 sizeof(struct dynamic_board
)*MAX_DYNAMIC_BOARDS
);
489 memset( global_skateshop
.dynamic_boards
, 0,
490 sizeof(struct dynamic_board
)*MAX_DYNAMIC_BOARDS
);
492 for( u32 i
=0; i
<MAX_DYNAMIC_BOARDS
; i
++ ){
493 struct dynamic_board
*board
= &global_skateshop
.dynamic_boards
[i
];
494 board
->state
= k_dynamic_board_state_none
;
495 board
->registry_id
= 0xffffffff;
496 board
->last_use_time
= -99999.9;
497 board
->ref_count
= 0;
500 vg_console_reg_cmd( "use_board",
501 skateshop_use_board
, skateshop_use_board_suggest
);
503 skateshop_scan_for_items();
506 VG_STATIC
void skateshop_render(void)
508 if( !global_skateshop
.active
) return;
510 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
511 world_instance
*world
= get_active_world();
513 u32 slot_count
= vg_list_size(global_skateshop
.shop_view_slots
);
515 for( u32 i
=0; i
<slot_count
; i
++ ){
516 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
518 float selected
= 0.0f
;
523 if( slot
->db
->state
!= k_dynamic_board_state_loaded
)
527 transform_identity( &xform
);
529 xform
.co
[0] = -((float)i
- ((float)slot_count
)*0.5f
)*0.45f
;
531 ent_marker
*rack
= mdl_arritm( &world
->ent_marker
,
532 mdl_entity_id_id(shop
->id_rack
)),
533 *display
= mdl_arritm( &world
->ent_marker
,
534 mdl_entity_id_id(shop
->id_display
));
536 mdl_transform_mul( &rack
->transform
, &xform
, &xform
);
538 if( slot
->db
->registry_id
== global_skateshop
.selected_registry_id
){
542 float t
= slot
->view_blend
;
543 v3_lerp( xform
.co
, display
->transform
.co
, t
, xform
.co
);
544 q_nlerp( xform
.q
, display
->transform
.q
, t
, xform
.q
);
545 v3_lerp( xform
.s
, display
->transform
.s
, t
, xform
.s
);
548 mdl_transform_m4x3( &xform
, mmdl
);
549 render_board( &main_camera
, world
, &slot
->db
->board
, mmdl
,
550 k_board_shader_entity
);
553 float rate
= 5.0f
*vg
.time_delta
;
554 slot
->view_blend
= vg_lerpf( slot
->view_blend
, selected
, rate
);
557 ent_marker
*info
= mdl_arritm( &world
->ent_marker
,
558 mdl_entity_id_id(shop
->id_info
));
560 mdl_transform_m4x3( &info
->transform
, mtext
);
562 const char *text_title
= "Fish - Title";
563 const char *text_author
= "by Shaniqua";
566 m4x3_identity( mlocal
);
570 mlocal
[0][0] = scale
; mlocal
[1][1] = scale
;
571 mlocal
[2][2] = 0.03f
;
572 mlocal
[3][0] = -font3d_string_width(&world_global
.font
,0,text_title
);
573 mlocal
[3][0] *= scale
*0.5f
;
575 m4x3_mul( mtext
, mlocal
, mmdl
);
577 font3d_bind( &world_global
.font
, &main_camera
);
579 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
580 font3d_simple_draw( &world_global
.font
, 0, text_title
, &main_camera
, mmdl
);
583 mlocal
[0][0] = scale
; mlocal
[1][1] = scale
;
584 mlocal
[3][0] = -font3d_string_width(&world_global
.font
,0,text_author
);
585 mlocal
[3][0] *= scale
*0.5f
;
587 m4x3_mul( mtext
, mlocal
, mmdl
);
588 font3d_simple_draw( &world_global
.font
, 0, text_author
, &main_camera
, mmdl
);
591 #endif /* ENT_SKATESHOP_C */