1 #ifndef ENT_SKATESHOP_C
2 #define ENT_SKATESHOP_C
7 #define MAX_LOCAL_BOARDS 64
8 #define BILL_TIN_BOARDS 1
9 #define MAX_DYNAMIC_BOARDS 9
13 ent_skateshop
*ptr_ent
;
19 k_skateshop_loc_page__viewing
,
21 k_skateshop_loc_select_use
,
22 k_skateshop_loc_select_cancel
,
23 k_skateshop_loc_select_upload
,
24 k_skateshop_loc_page__selected
,
26 k_skateshop_loc_page__upload
,
32 enum dynamic_board_state
{
33 k_dynamic_board_state_none
,
34 k_dynamic_board_state_loaded
,
35 k_dynamic_board_state_loading
,
41 struct player_board board
;
52 struct dynamic_board
*db
;
62 struct dynamic_board
*dynamic
;
64 char filename
[64]; /* if workshop, string version of uid. */
73 float interaction_cooldown
;
75 u32 selected_registry_id
;
77 static global_skateshop
;
79 static inline int const_str_eq( u32 hash
, const char *str
, const char *cmp
)
81 if( hash
== vg_strdjb2(cmp
) )
82 if( !strcmp( str
, cmp
) )
87 static int skateshop_workshop_name_blacklisted( u32 hash
, const char *name
)
89 if( const_str_eq( hash
, name
, "skaterift_fish.mdl" ) ) return 1;
93 VG_STATIC
void skateshop_loader_start( void (*pfn
)(void) )
95 if( global_skateshop
.loading
)
96 vg_fatal_error( "skateshop thread sync failure\n" );
98 global_skateshop
.loading
= 1;
99 vg_loader_start( pfn
);
102 VG_STATIC
void skateshop_async_post( void *payload
, u32 size
)
104 global_skateshop
.loading
= 0;
108 struct dynamic_board
*skateshop_lru_alloc( u32 id
)
110 double min_time
= 1e300
;
111 struct dynamic_board
*min_board
= NULL
;
113 for( u32 i
=0; i
<MAX_DYNAMIC_BOARDS
; i
++ ){
114 struct dynamic_board
*db
= &global_skateshop
.dynamic_boards
[i
];
116 if( db
->state
== k_dynamic_board_state_loading
)
122 if( db
->last_use_time
< min_time
){
123 min_time
= db
->last_use_time
;
129 localplayer
.board
= NULL
; /* temp */
131 if( min_board
->state
== k_dynamic_board_state_loaded
){
132 struct board_registry
*other
=
133 &global_skateshop
.registry
[min_board
->registry_id
];
135 vg_info( "Deallocating board: '%s'\n", min_board
, other
->filename
);
137 player_board_unload( &min_board
->board
);
138 other
->dynamic
= NULL
;
141 struct board_registry
*reg
= &global_skateshop
.registry
[id
];
143 vg_info( "Allocating board '%s' @%p\n", reg
->filename
, min_board
);
145 min_board
->state
= k_dynamic_board_state_loading
;
146 min_board
->registry_id
= id
;
147 min_board
->last_use_time
= vg
.time
;
148 min_board
->ref_count
= 0;
151 vg_error( "No free boards to load registry!\n" );
158 void skateshop_board_registry_path( struct board_registry
*reg
, char path
[256] )
161 snprintf( path
, 256, "models/boards/workshop/%s/something.mdl",
165 snprintf( path
, 256, "models/boards/%s", reg
->filename
);
169 VG_STATIC
void skateshop_async_board_complete( void *payload
, u32 size
)
171 struct dynamic_board
*db
= payload
;
173 if( db
== global_skateshop
.localplayer_slot
){
174 localplayer
.board
= &db
->board
;
178 for( u32 i
=0; i
<vg_list_size(global_skateshop
.shop_view_slots
); i
++ ){
179 if( global_skateshop
.shop_view_slots
[i
].db
== db
){
185 db
->last_use_time
= vg
.time
;
186 db
->state
= k_dynamic_board_state_loaded
;
188 struct board_registry
*reg
= &global_skateshop
.registry
[ db
->registry_id
];
191 vg_success( "Async board loaded (%s)\n", reg
->filename
);
194 VG_STATIC
void skateshop_load_requested_boards(void)
197 for( u32 i
=0; i
<MAX_DYNAMIC_BOARDS
; i
++ ){
198 struct dynamic_board
*db
= &global_skateshop
.dynamic_boards
[i
];
200 if( db
->state
== k_dynamic_board_state_loading
){
201 struct board_registry
*reg
=
202 &global_skateshop
.registry
[ db
->registry_id
];
204 skateshop_board_registry_path( reg
, path
);
205 player_board_load( &db
->board
, path
);
206 vg_async_call( skateshop_async_board_complete
, db
, 0 );
211 VG_STATIC
void skateshop_thread1_refresh(void)
213 skateshop_load_requested_boards();
214 vg_async_call( skateshop_async_post
, NULL
, 0 );
217 VG_STATIC
int skateshop_use_board( int argc
, const char *argv
[] )
219 if( global_skateshop
.loading
){
220 vg_error( "Cannot use skateshop currently (loader thread missing)\n" );
225 u32 hash
= vg_strdjb2( argv
[0] );
227 for( u32 i
=0; i
<global_skateshop
.registry_count
; i
++ ){
228 struct board_registry
*reg
= &global_skateshop
.registry
[i
];
230 if( const_str_eq( hash
, argv
[0], reg
->filename
) ){
233 struct dynamic_board
*db
= reg
->dynamic
;
235 if( db
->state
== k_dynamic_board_state_loaded
){
236 localplayer
.board
= &db
->board
;
237 db
->last_use_time
= vg
.time
;
240 vg_fatal_error( "Invalid state while loading board\n" );
244 struct dynamic_board
*db
= skateshop_lru_alloc( i
);
245 db
->state
= k_dynamic_board_state_loading
;
246 skateshop_loader_start( skateshop_thread1_refresh
);
255 VG_STATIC
void skateshop_use_board_suggest( int argc
, const char *argv
[] )
258 for( u32 i
=0; i
<global_skateshop
.registry_count
; i
++ ){
259 struct board_registry
*reg
= &global_skateshop
.registry
[i
];
261 if( reg
->ghost
) continue; /* we probably can't load these */
263 console_suggest_score_text( reg
->filename
, argv
[0], 0 );
268 VG_STATIC
void skateshop_scan_for_items(void)
270 vg_linear_clear( vg_mem
.scratch
);
272 for( u32 i
=0; i
<global_skateshop
.registry_count
; i
++ ){
273 struct board_registry
*reg
= &global_skateshop
.registry
[i
];
278 tinydir_open( &dir
, "models/boards" );
280 while( dir
.has_next
){
282 tinydir_readfile( &dir
, &file
);
285 u32 hash
= vg_strdjb2( file
.name
);
287 for( u32 i
=0; i
<global_skateshop
.registry_count
; i
++ ){
288 struct board_registry
*reg
= &global_skateshop
.registry
[i
];
290 if( const_str_eq( hash
, file
.name
, reg
->filename
) ){
296 if( global_skateshop
.registry_count
== MAX_LOCAL_BOARDS
){
297 vg_error( "You have too many boards installed!\n" );
301 vg_info( "new listing!: %s\n", file
.name
);
303 struct board_registry
*reg
= &global_skateshop
.registry
[
304 global_skateshop
.registry_count
++ ];
307 vg_strncpy( file
.name
, reg
->filename
, 64, k_strncpy_always_add_null
);
308 reg
->filename_hash
= hash
;
314 next_file
: tinydir_next( &dir
);
319 skateshop_load_requested_boards();
320 vg_async_call( skateshop_async_post
, NULL
, 0 );
323 VG_STATIC
void global_skateshop_exit(void)
325 localplayer
.immobile
= 0;
326 global_skateshop
.active
= 0;
329 VG_STATIC
void skateshop_request_viewpage( u32 page
)
331 u32 slot_count
= vg_list_size(global_skateshop
.shop_view_slots
);
332 u32 start
= page
* slot_count
;
334 for( u32 i
=0; i
<slot_count
; i
++ ){
335 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
338 slot
->db
->ref_count
--;
342 u32 reg_index
= start
+i
;
343 if( reg_index
< global_skateshop
.registry_count
){
344 struct board_registry
*reg
= &global_skateshop
.registry
[ reg_index
];
347 struct dynamic_board
*db
= reg
->dynamic
;
349 if( db
->state
== k_dynamic_board_state_loaded
){
350 db
->last_use_time
= vg
.time
;
355 vg_fatal_error( "Invalid state while loading page\n" );
359 struct dynamic_board
*db
= skateshop_lru_alloc( reg_index
);
370 VG_STATIC
void ent_skateshop_call( world_instance
*world
, ent_call
*call
)
372 u32 index
= mdl_entity_id_id( call
->id
);
373 ent_skateshop
*shop
= mdl_arritm( &world
->ent_skateshop
, index
);
374 vg_info( "skateshop_call\n" );
376 if( global_skateshop
.loading
){
377 vg_error( "Cannot enter skateshop currently (loader thread missing)\n" );
381 if( call
->function
== k_ent_function_trigger
){
382 if( localplayer
.subsystem
!= k_player_subsystem_walk
){
386 vg_info( "Entering skateshop\n" );
388 localplayer
.immobile
= 1;
389 global_skateshop
.active
= 1;
390 global_skateshop
.interface_loc
= k_skateshop_loc_page__viewing
;
392 v3_zero( localplayer
.rb
.v
);
393 v3_zero( localplayer
.rb
.w
);
394 localplayer
._walk
.move_speed
= 0.0f
;
396 global_skateshop
.ptr_ent
= shop
;
398 skateshop_request_viewpage(0);
399 skateshop_loader_start( skateshop_scan_for_items
);
403 VG_STATIC
void global_skateshop_preupdate(void)
405 float rate
= vg_minf( 1.0f
, vg
.time_frame_delta
* 2.0f
);
406 global_skateshop
.factive
= vg_lerpf( global_skateshop
.factive
,
407 global_skateshop
.active
, rate
);
409 if( !global_skateshop
.active
) return;
411 world_instance
*world
= get_active_world();
413 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
414 ent_camera
*ref
= mdl_arritm( &world
->ent_camera
,
415 mdl_entity_id_id(shop
->id_camera
) );
416 ent_marker
*display
= mdl_arritm( &world
->ent_marker
,
417 mdl_entity_id_id(shop
->id_display
) );
419 v3f dir
= {0.0f
,-1.0f
,0.0f
};
420 mdl_transform_vector( &ref
->transform
, dir
, dir
);
421 player_vector_angles( localplayer
.cam_override_angles
, dir
, 1.0f
, 0.0f
);
424 v3_sub( display
->transform
.co
, localplayer
.rb
.co
, lookat
);
426 q_axis_angle( localplayer
.rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
},
427 atan2f(lookat
[0],lookat
[2]) );
429 v3_copy( ref
->transform
.co
, localplayer
.cam_override_pos
);
430 localplayer
.cam_override_fov
= ref
->fov
;
432 localplayer
.cam_override_strength
= global_skateshop
.factive
;
434 if( global_skateshop
.interaction_cooldown
> 0.0f
){
435 global_skateshop
.interaction_cooldown
-= vg
.time_delta
;
439 float h
= localplayer
.input_js1h
->axis
.value
;
441 if( global_skateshop
.interface_loc
<= k_skateshop_loc_page__viewing
){
442 if( fabsf(h
) > 0.25f
){
444 if( global_skateshop
.selected_registry_id
> 0 )
445 global_skateshop
.selected_registry_id
--;
448 if( global_skateshop
.selected_registry_id
<
449 global_skateshop
.registry_count
-1 )
451 global_skateshop
.selected_registry_id
++;
455 vg_info( "Select registry: %u\n",
456 global_skateshop
.selected_registry_id
);
457 global_skateshop
.interaction_cooldown
= 0.125f
;
461 if( vg_input_button_down( &input_menu_back
) ){
462 global_skateshop_exit();
468 VG_STATIC
void skateshop_init(void)
470 global_skateshop
.registry
=
471 vg_linear_alloc( vg_mem
.rtmemory
,
472 sizeof(struct board_registry
)*MAX_LOCAL_BOARDS
);
473 global_skateshop
.registry_count
= 0;
474 global_skateshop
.dynamic_boards
=
475 vg_linear_alloc( vg_mem
.rtmemory
,
476 sizeof(struct dynamic_board
)*MAX_DYNAMIC_BOARDS
);
478 memset( global_skateshop
.dynamic_boards
, 0,
479 sizeof(struct dynamic_board
)*MAX_DYNAMIC_BOARDS
);
481 for( u32 i
=0; i
<MAX_DYNAMIC_BOARDS
; i
++ ){
482 struct dynamic_board
*board
= &global_skateshop
.dynamic_boards
[i
];
483 board
->state
= k_dynamic_board_state_none
;
484 board
->registry_id
= 0xffffffff;
485 board
->last_use_time
= -99999.9;
486 board
->ref_count
= 0;
489 vg_console_reg_cmd( "use_board",
490 skateshop_use_board
, skateshop_use_board_suggest
);
492 skateshop_scan_for_items();
495 VG_STATIC
void skateshop_render(void)
497 if( !global_skateshop
.active
) return;
499 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
500 world_instance
*world
= get_active_world();
502 u32 slot_count
= vg_list_size(global_skateshop
.shop_view_slots
);
504 for( u32 i
=0; i
<slot_count
; i
++ ){
505 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
507 float selected
= 0.0f
;
512 if( slot
->db
->state
!= k_dynamic_board_state_loaded
)
516 transform_identity( &xform
);
518 xform
.co
[0] = -((float)i
- ((float)slot_count
)*0.5f
)*0.45f
;
520 ent_marker
*rack
= mdl_arritm( &world
->ent_marker
,
521 mdl_entity_id_id(shop
->id_rack
)),
522 *display
= mdl_arritm( &world
->ent_marker
,
523 mdl_entity_id_id(shop
->id_display
));
525 mdl_transform_mul( &rack
->transform
, &xform
, &xform
);
527 if( slot
->db
->registry_id
== global_skateshop
.selected_registry_id
){
531 float t
= slot
->view_blend
;
532 v3_lerp( xform
.co
, display
->transform
.co
, t
, xform
.co
);
533 q_nlerp( xform
.q
, display
->transform
.q
, t
, xform
.q
);
534 v3_lerp( xform
.s
, display
->transform
.s
, t
, xform
.s
);
537 mdl_transform_m4x3( &xform
, mmdl
);
538 render_board( &main_camera
, world
, &slot
->db
->board
, mmdl
,
539 k_board_shader_entity
);
542 float rate
= 5.0f
*vg
.time_delta
;
543 slot
->view_blend
= vg_lerpf( slot
->view_blend
, selected
, rate
);
546 ent_marker
*info
= mdl_arritm( &world
->ent_marker
,
547 mdl_entity_id_id(shop
->id_info
));
549 mdl_transform_m4x3( &info
->transform
, mtext
);
551 const char *text_title
= "Fish - Title";
552 const char *text_author
= "by Shaniqua";
555 m4x3_identity( mlocal
);
559 mlocal
[0][0] = scale
; mlocal
[1][1] = scale
;
560 mlocal
[2][2] = 0.03f
;
561 mlocal
[3][0] = -font3d_string_width(&world_global
.font
,0,text_title
);
562 mlocal
[3][0] *= scale
*0.5f
;
564 m4x3_mul( mtext
, mlocal
, mmdl
);
566 font3d_bind( &world_global
.font
, &main_camera
);
568 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
569 font3d_simple_draw( &world_global
.font
, 0, text_title
, &main_camera
, mmdl
);
572 mlocal
[0][0] = scale
; mlocal
[1][1] = scale
;
573 mlocal
[3][0] = -font3d_string_width(&world_global
.font
,0,text_author
);
574 mlocal
[3][0] *= scale
*0.5f
;
576 m4x3_mul( mtext
, mlocal
, mmdl
);
577 font3d_simple_draw( &world_global
.font
, 0, text_author
, &main_camera
, mmdl
);
580 #endif /* ENT_SKATESHOP_C */