1 #ifndef ENT_SKATESHOP_C
2 #define ENT_SKATESHOP_C
4 #include "ent_skateshop.h"
9 static inline int const_str_eq( u32 hash
, const char *str
, const char *cmp
)
11 if( hash
== vg_strdjb2(cmp
) )
12 if( !strcmp( str
, cmp
) )
17 static int skateshop_workshop_name_blacklisted( u32 hash
, const char *name
)
19 if( const_str_eq( hash
, name
, "skaterift_fish.mdl" ) ) return 1;
23 VG_STATIC
void skateshop_loader_start( void (*pfn
)(void) )
25 if( global_skateshop
.loading
)
26 vg_fatal_error( "skateshop thread sync failure\n" );
28 global_skateshop
.loading
= 1;
29 vg_loader_start( pfn
);
32 VG_STATIC
void skateshop_async_post( void *payload
, u32 size
)
34 global_skateshop
.loading
= 0;
38 struct dynamic_board
*skateshop_lru_alloc( u32 id
)
40 double min_time
= 1e300
;
41 struct dynamic_board
*min_board
= NULL
;
43 for( u32 i
=0; i
<MAX_DYNAMIC_BOARDS
; i
++ ){
44 struct dynamic_board
*db
= &global_skateshop
.dynamic_boards
[i
];
46 if( db
->state
== k_dynamic_board_state_loading
)
52 if( db
->last_use_time
< min_time
){
53 min_time
= db
->last_use_time
;
59 if( min_board
->state
== k_dynamic_board_state_loaded
){
60 struct board_registry
*other
=
61 &global_skateshop
.registry
[min_board
->registry_id
];
63 vg_info( "Deallocating board: '%s'\n", min_board
, other
->filename
);
65 player_board_unload( &min_board
->board
);
66 other
->dynamic
= NULL
;
69 struct board_registry
*reg
= &global_skateshop
.registry
[id
];
71 vg_info( "Allocating board '%s' @%p\n", reg
->filename
, min_board
);
73 min_board
->state
= k_dynamic_board_state_loading
;
74 min_board
->registry_id
= id
;
75 min_board
->last_use_time
= vg
.time
;
76 min_board
->ref_count
= 0;
79 vg_error( "No free boards to load registry!\n" );
86 void skateshop_board_registry_path( struct board_registry
*reg
, char path
[256] )
89 snprintf( path
, 256, "models/boards/workshop/%s/something.mdl",
93 snprintf( path
, 256, "models/boards/%s", reg
->filename
);
97 /* we can only keep using a viewslot pointer for multiple frames if we watch it
98 * using this function */
99 VG_STATIC
void watch_dynamic_board( struct dynamic_board
*db
)
101 if( db
->ref_count
>= 32 ){
102 vg_fatal_error( "dynamic board watch missmatch (limit is 32)\n" );
105 db
->last_use_time
= vg
.time
;
109 /* after this is called, the calling code only has access to the pointer for the
110 * duration of a frame */
111 VG_STATIC
void unwatch_dynamic_board( struct dynamic_board
*db
)
113 if( db
->ref_count
== 0 ){
114 vg_fatal_error( "dynamic board unwatch missmatch (no watchers)\n" );
120 VG_STATIC
void skateshop_async_board_complete( void *payload
, u32 size
)
122 struct dynamic_board
*db
= payload
;
124 /* all possible view slots are 'listening' for this event,
125 * which must be checked here */
126 for( u32 i
=0; i
<vg_list_size(global_skateshop
.shop_view_slots
); i
++ ){
127 if( global_skateshop
.shop_view_slots
[i
].db
== db
){
128 watch_dynamic_board( db
);
132 if( localplayer
.board_view_slot
== db
){
133 watch_dynamic_board( db
);
136 db
->last_use_time
= vg
.time
;
137 db
->state
= k_dynamic_board_state_loaded
;
139 struct board_registry
*reg
= &global_skateshop
.registry
[ db
->registry_id
];
142 vg_success( "Async board loaded (%s)\n", reg
->filename
);
145 VG_STATIC
void skateshop_load_requested_boards(void)
148 for( u32 i
=0; i
<MAX_DYNAMIC_BOARDS
; i
++ ){
149 struct dynamic_board
*db
= &global_skateshop
.dynamic_boards
[i
];
151 if( db
->state
== k_dynamic_board_state_loading
){
152 struct board_registry
*reg
=
153 &global_skateshop
.registry
[ db
->registry_id
];
155 skateshop_board_registry_path( reg
, path
);
156 player_board_load( &db
->board
, path
);
157 vg_async_call( skateshop_async_board_complete
, db
, 0 );
162 VG_STATIC
void skateshop_thread1_refresh(void)
164 skateshop_load_requested_boards();
165 vg_async_call( skateshop_async_post
, NULL
, 0 );
168 VG_STATIC
int skateshop_use_board( int argc
, const char *argv
[] )
170 if( global_skateshop
.loading
){
171 vg_error( "Cannot use skateshop currently (loader thread missing)\n" );
176 u32 hash
= vg_strdjb2( argv
[0] );
178 for( u32 i
=0; i
<global_skateshop
.registry_count
; i
++ ){
179 struct board_registry
*reg
= &global_skateshop
.registry
[i
];
181 if( const_str_eq( hash
, argv
[0], reg
->filename
) ){
183 struct dynamic_board
*db
= reg
->dynamic
;
185 if( db
->state
== k_dynamic_board_state_loaded
){
186 localplayer
.board_view_slot
= db
;
187 watch_dynamic_board( db
);
190 vg_fatal_error( "Invalid state while loading board\n" );
194 if( localplayer
.board_view_slot
){
195 unwatch_dynamic_board( localplayer
.board_view_slot
);
196 localplayer
.board_view_slot
= NULL
;
199 struct dynamic_board
*db
= skateshop_lru_alloc( i
);
200 localplayer
.board_view_slot
= db
;
201 db
->state
= k_dynamic_board_state_loading
;
202 skateshop_loader_start( skateshop_thread1_refresh
);
211 VG_STATIC
void skateshop_use_board_suggest( int argc
, const char *argv
[] )
214 for( u32 i
=0; i
<global_skateshop
.registry_count
; i
++ ){
215 struct board_registry
*reg
= &global_skateshop
.registry
[i
];
217 if( reg
->ghost
) continue; /* we probably can't load these */
219 console_suggest_score_text( reg
->filename
, argv
[0], 0 );
224 VG_STATIC
void skateshop_scan_for_items(void)
226 vg_linear_clear( vg_mem
.scratch
);
228 for( u32 i
=0; i
<global_skateshop
.registry_count
; i
++ ){
229 struct board_registry
*reg
= &global_skateshop
.registry
[i
];
234 tinydir_open( &dir
, "models/boards" );
236 while( dir
.has_next
){
238 tinydir_readfile( &dir
, &file
);
241 u32 hash
= vg_strdjb2( file
.name
);
243 for( u32 i
=0; i
<global_skateshop
.registry_count
; i
++ ){
244 struct board_registry
*reg
= &global_skateshop
.registry
[i
];
246 if( const_str_eq( hash
, file
.name
, reg
->filename
) ){
252 if( global_skateshop
.registry_count
== MAX_LOCAL_BOARDS
){
253 vg_error( "You have too many boards installed!\n" );
257 vg_info( "new listing!: %s\n", file
.name
);
259 struct board_registry
*reg
= &global_skateshop
.registry
[
260 global_skateshop
.registry_count
++ ];
263 vg_strncpy( file
.name
, reg
->filename
, 64, k_strncpy_always_add_null
);
264 reg
->filename_hash
= hash
;
270 next_file
: tinydir_next( &dir
);
275 skateshop_load_requested_boards();
276 vg_async_call( skateshop_async_post
, NULL
, 0 );
279 VG_STATIC
void global_skateshop_exit(void)
281 vg_info( "exit skateshop\n" );
282 localplayer
.immobile
= 0;
283 global_skateshop
.active
= 0;
284 srinput
.ignore_input_frames
= 2;
287 VG_STATIC
void skateshop_request_viewpage( u32 page
)
289 u32 slot_count
= vg_list_size(global_skateshop
.shop_view_slots
);
290 u32 start
= page
* slot_count
;
292 for( u32 i
=0; i
<slot_count
; i
++ ){
293 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
296 slot
->db
->ref_count
--;
300 u32 reg_index
= start
+i
;
301 if( reg_index
< global_skateshop
.registry_count
){
302 struct board_registry
*reg
= &global_skateshop
.registry
[ reg_index
];
305 struct dynamic_board
*db
= reg
->dynamic
;
307 if( db
->state
== k_dynamic_board_state_loaded
){
308 db
->last_use_time
= vg
.time
;
313 vg_fatal_error( "Invalid state while loading page\n" );
317 struct dynamic_board
*db
= skateshop_lru_alloc( reg_index
);
328 VG_STATIC
void ent_skateshop_call( world_instance
*world
, ent_call
*call
)
330 u32 index
= mdl_entity_id_id( call
->id
);
331 ent_skateshop
*shop
= mdl_arritm( &world
->ent_skateshop
, index
);
332 vg_info( "skateshop_call\n" );
334 if( global_skateshop
.loading
){
335 vg_error( "Cannot enter skateshop currently (loader thread missing)\n" );
339 if( call
->function
== k_ent_function_trigger
){
340 if( localplayer
.subsystem
!= k_player_subsystem_walk
){
344 vg_info( "Entering skateshop\n" );
346 localplayer
.immobile
= 1;
347 global_skateshop
.active
= 1;
348 global_skateshop
.interface_loc
= k_skateshop_loc_page__viewing
;
350 v3_zero( localplayer
.rb
.v
);
351 v3_zero( localplayer
.rb
.w
);
352 localplayer
._walk
.move_speed
= 0.0f
;
354 global_skateshop
.ptr_ent
= shop
;
356 skateshop_request_viewpage(0);
357 skateshop_loader_start( skateshop_scan_for_items
);
361 VG_STATIC
void global_skateshop_preupdate(void)
363 float rate
= vg_minf( 1.0f
, vg
.time_frame_delta
* 2.0f
);
364 global_skateshop
.factive
= vg_lerpf( global_skateshop
.factive
,
365 global_skateshop
.active
, rate
);
367 if( !global_skateshop
.active
) return;
369 world_instance
*world
= get_active_world();
371 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
372 ent_camera
*ref
= mdl_arritm( &world
->ent_camera
,
373 mdl_entity_id_id(shop
->id_camera
) );
374 ent_marker
*display
= mdl_arritm( &world
->ent_marker
,
375 mdl_entity_id_id(shop
->id_display
) );
377 v3f dir
= {0.0f
,-1.0f
,0.0f
};
378 mdl_transform_vector( &ref
->transform
, dir
, dir
);
379 player_vector_angles( localplayer
.cam_override_angles
, dir
, 1.0f
, 0.0f
);
382 v3_sub( display
->transform
.co
, localplayer
.rb
.co
, lookat
);
384 q_axis_angle( localplayer
.rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
},
385 atan2f(lookat
[0],lookat
[2]) );
387 v3_copy( ref
->transform
.co
, localplayer
.cam_override_pos
);
388 localplayer
.cam_override_fov
= ref
->fov
;
390 localplayer
.cam_override_strength
= global_skateshop
.factive
;
392 if( global_skateshop
.interaction_cooldown
> 0.0f
){
393 global_skateshop
.interaction_cooldown
-= vg
.time_delta
;
397 if( global_skateshop
.interface_loc
<= k_skateshop_loc_page__viewing
){
399 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h
), "browse" );
400 gui_helper_action( button_display_string( k_srbind_mback
), "exit" );
402 u32 reg_id
= global_skateshop
.selected_registry_id
;
403 struct board_registry
*picker
= &global_skateshop
.registry
[ reg_id
];
405 int pick_availible
= 0;
406 if( picker
->dynamic
&&
407 (picker
->dynamic
->state
== k_dynamic_board_state_loaded
) )
410 gui_helper_action( button_display_string( k_srbind_maccept
), "pick" );
415 if( button_down( k_srbind_mleft
) ){
416 if( global_skateshop
.selected_registry_id
> 0 ){
417 global_skateshop
.selected_registry_id
--;
422 if( button_down( k_srbind_mright
) ){
423 if( global_skateshop
.selected_registry_id
<
424 global_skateshop
.registry_count
-1 )
426 global_skateshop
.selected_registry_id
++;
432 vg_info( "Select registry: %u\n",
433 global_skateshop
.selected_registry_id
);
434 global_skateshop
.interaction_cooldown
= 0.125f
;
438 if( pick_availible
&& button_down( k_srbind_maccept
) ){
439 vg_info( "chose board from skateshop (%u)\n", reg_id
);
441 if( localplayer
.board_view_slot
){
442 unwatch_dynamic_board( localplayer
.board_view_slot
);
443 localplayer
.board_view_slot
= NULL
;
446 localplayer
.board_view_slot
= picker
->dynamic
;
447 watch_dynamic_board( localplayer
.board_view_slot
);
449 global_skateshop_exit();
453 if( button_down( k_srbind_mback
) ){
454 global_skateshop_exit();
460 VG_STATIC
void skateshop_init(void)
462 global_skateshop
.registry
=
463 vg_linear_alloc( vg_mem
.rtmemory
,
464 sizeof(struct board_registry
)*MAX_LOCAL_BOARDS
);
465 global_skateshop
.registry_count
= 0;
466 global_skateshop
.dynamic_boards
=
467 vg_linear_alloc( vg_mem
.rtmemory
,
468 sizeof(struct dynamic_board
)*MAX_DYNAMIC_BOARDS
);
470 memset( global_skateshop
.dynamic_boards
, 0,
471 sizeof(struct dynamic_board
)*MAX_DYNAMIC_BOARDS
);
473 for( u32 i
=0; i
<MAX_DYNAMIC_BOARDS
; i
++ ){
474 struct dynamic_board
*board
= &global_skateshop
.dynamic_boards
[i
];
475 board
->state
= k_dynamic_board_state_none
;
476 board
->registry_id
= 0xffffffff;
477 board
->last_use_time
= -99999.9;
478 board
->ref_count
= 0;
481 vg_console_reg_cmd( "use_board",
482 skateshop_use_board
, skateshop_use_board_suggest
);
484 skateshop_scan_for_items();
487 VG_STATIC
void skateshop_render(void)
489 if( !global_skateshop
.active
) return;
491 ent_skateshop
*shop
= global_skateshop
.ptr_ent
;
492 world_instance
*world
= get_active_world();
494 u32 slot_count
= vg_list_size(global_skateshop
.shop_view_slots
);
496 ent_marker
*mark_rack
= mdl_arritm( &world
->ent_marker
,
497 mdl_entity_id_id(shop
->id_rack
)),
498 *mark_display
= mdl_arritm( &world
->ent_marker
,
499 mdl_entity_id_id(shop
->id_display
));
501 for( u32 i
=0; i
<slot_count
; i
++ ){
502 struct shop_view_slot
*slot
= &global_skateshop
.shop_view_slots
[i
];
504 float selected
= 0.0f
;
509 if( slot
->db
->state
!= k_dynamic_board_state_loaded
)
513 transform_identity( &xform
);
515 xform
.co
[0] = -((float)i
- ((float)slot_count
)*0.5f
)*0.45f
;
517 mdl_transform_mul( &mark_rack
->transform
, &xform
, &xform
);
519 if( slot
->db
->registry_id
== global_skateshop
.selected_registry_id
){
523 float t
= slot
->view_blend
;
524 v3_lerp( xform
.co
, mark_display
->transform
.co
, t
, xform
.co
);
525 q_nlerp( xform
.q
, mark_display
->transform
.q
, t
, xform
.q
);
526 v3_lerp( xform
.s
, mark_display
->transform
.s
, t
, xform
.s
);
529 mdl_transform_m4x3( &xform
, mmdl
);
530 render_board( &main_camera
, world
, &slot
->db
->board
, mmdl
,
531 k_board_shader_entity
);
534 float rate
= 5.0f
*vg
.time_delta
;
535 slot
->view_blend
= vg_lerpf( slot
->view_blend
, selected
, rate
);
538 ent_marker
*mark_info
= mdl_arritm( &world
->ent_marker
,
539 mdl_entity_id_id(shop
->id_info
));
541 mdl_transform_m4x3( &mark_info
->transform
, mtext
);
542 mdl_transform_m4x3( &mark_rack
->transform
, mrack
);
544 const char *text_title
= "Fish - Title";
545 const char *text_author
= "by Shaniqua";
548 m4x3_identity( mlocal
);
551 * ----------------------------------------------------------------- */
555 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
556 mlocal
[3][0] = -font3d_string_width(&world_global
.font
,0,text_title
);
557 mlocal
[3][0] *= scale
*0.5f
;
559 m4x3_mul( mtext
, mlocal
, mmdl
);
561 font3d_bind( &world_global
.font
, &main_camera
);
563 shader_model_font_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
564 font3d_simple_draw( &world_global
.font
, 0, text_title
, &main_camera
, mmdl
);
567 * ----------------------------------------------------------------- */
569 m3x3_setdiagonalv3( mlocal
, (v3f
){ scale
, scale
, thickness
} );
570 mlocal
[3][0] = -font3d_string_width(&world_global
.font
,0,text_author
);
571 mlocal
[3][0] *= scale
*0.5f
;
573 m4x3_mul( mtext
, mlocal
, mmdl
);
574 font3d_simple_draw( &world_global
.font
, 0, text_author
, &main_camera
, mmdl
);
577 * ------------------------------------------------------------------ */
580 v3_zero( mlocal
[3] );
581 mlocal
[0][0] = -scale
;
582 mlocal
[1][2] = -scale
;
583 mlocal
[2][1] = -thickness
;
584 mlocal
[3][2] = -0.7f
;
585 m4x3_mul( mrack
, mlocal
, mmdl
);
589 i
+=highscore_intl( buf
+i
, global_skateshop
.selected_registry_id
+1, 3 );
591 i
+=highscore_intl( buf
+i
, global_skateshop
.registry_count
, 3 );
594 font3d_simple_draw( &world_global
.font
, 0, buf
, &main_camera
, mmdl
);
597 #endif /* ENT_SKATESHOP_C */