loader stuff
[carveJwlIkooP6JGAAIwe30JlM.git] / ent_skateshop.c
1 #ifndef ENT_SKATESHOP_C
2 #define ENT_SKATESHOP_C
3
4 #define VG_GAME
5 #include "vg/vg.h"
6 #include "vg/vg_steam_ugc.h"
7 #include "ent_skateshop.h"
8 #include "world.h"
9 #include "player.h"
10 #include "gui.h"
11 #include "menu.h"
12 #include "pointcloud.h"
13
14 /*
15 * Checks string equality but does a hash check first
16 */
17 static inline int const_str_eq( u32 hash, const char *str, const char *cmp )
18 {
19 if( hash == vg_strdjb2(cmp) )
20 if( !strcmp( str, cmp ) )
21 return 1;
22 return 0;
23 }
24
25 /*
26 * Get an existing cache instance, allocate a new one to be loaded, or NULL if
27 * there is no space
28 */
29 VG_STATIC struct cache_board *skateshop_cache_fetch( u32 registry_index )
30 {
31 struct registry_board *reg = NULL;
32
33 if( registry_index < global_skateshop.registry_count ){
34 reg = &global_skateshop.registry[ registry_index ];
35
36 if( reg->cache_ptr ){
37 return reg->cache_ptr;
38 }
39 }
40
41 /* lru eviction. should be a linked list maybe... */
42 double min_time = 1e300;
43 struct cache_board *min_board = NULL;
44
45 SDL_AtomicLock( &global_skateshop.sl_cache_access );
46 for( u32 i=0; i<SKATESHOP_BOARD_CACHE_MAX; i++ ){
47 struct cache_board *cache_ptr = &global_skateshop.cache[i];
48
49 if( cache_ptr->state == k_cache_board_state_load_request ) continue;
50 if( cache_ptr->ref_count ) continue;
51
52 if( cache_ptr->last_use_time < min_time ){
53 min_time = cache_ptr->last_use_time;
54 min_board = cache_ptr;
55 }
56 }
57
58 if( min_board ){
59 if( min_board->state == k_cache_board_state_loaded ){
60 struct registry_board *other =
61 &global_skateshop.registry[ min_board->registry_id ];
62
63 vg_info( "Deallocating board: '%s'\n", min_board, other->filename );
64
65 player_board_unload( &min_board->board );
66 other->cache_ptr = NULL;
67 }
68
69 if( reg ){
70 vg_info( "Allocating board (reg:%u) '%s'\n",
71 registry_index, reg->filename );
72 }
73 else{
74 vg_info( "Pre-allocating board (reg:%u) 'null'\n", registry_index );
75 }
76
77 min_board->registry_id = registry_index;
78 min_board->last_use_time = vg.time;
79 min_board->ref_count = 0;
80 min_board->state = k_cache_board_state_load_request;
81 }
82 else{
83 vg_error( "No free boards to load registry!\n" );
84 }
85
86 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
87 return min_board;
88 }
89
90 VG_STATIC void skateshop_update_viewpage(void)
91 {
92 u32 page = global_skateshop.selected_registry_id/SKATESHOP_VIEW_SLOT_MAX;
93
94 for( u32 i=0; i<SKATESHOP_VIEW_SLOT_MAX; i++ ){
95 struct shop_view_slot *slot = &global_skateshop.shop_view_slots[i];
96 u32 request_id = page*SKATESHOP_VIEW_SLOT_MAX + i;
97
98 if( slot->cache_ptr ) unwatch_cache_board( slot->cache_ptr );
99
100 slot->cache_ptr = skateshop_cache_fetch( request_id );
101 if( slot->cache_ptr ) watch_cache_board( slot->cache_ptr );
102 }
103 }
104
105 /* generic reciever */
106 VG_STATIC void workshop_async_any_complete( void *data, u32 size )
107 {
108 skaterift_end_op();
109 }
110
111 /*
112 * op/subroutine: k_workshop_op_item_load
113 * -----------------------------------------------------------------------------
114 */
115
116 /*
117 * Reciever for board completion; only promotes the status in the main thread
118 */
119 VG_STATIC void skateshop_async_board_loaded( void *payload, u32 size )
120 {
121 SDL_AtomicLock( &global_skateshop.sl_cache_access );
122 struct cache_board *cache_ptr = payload;
123 cache_ptr->last_use_time = vg.time;
124 cache_ptr->state = k_cache_board_state_loaded;
125
126 struct registry_board *reg =
127 &global_skateshop.registry[ cache_ptr->registry_id ];
128 reg->cache_ptr = cache_ptr;
129 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
130
131 vg_success( "Async board loaded (%s)\n", reg->filename );
132 }
133
134 /*
135 * Thread(or subroutine of thread), for checking view slots that weve installed.
136 * Load the model if a view slot wants it
137 */
138 VG_STATIC void workshop_visibile_load_loop_thread( void *_args )
139 {
140 char path[1024];
141 for( u32 i=0; i<SKATESHOP_BOARD_CACHE_MAX; i++ ){
142 struct cache_board *cache_ptr = &global_skateshop.cache[i];
143
144 SDL_AtomicLock( &global_skateshop.sl_cache_access );
145 if( cache_ptr->state == k_cache_board_state_load_request ){
146 if( cache_ptr->registry_id >= global_skateshop.registry_count ){
147 /* should maybe have a different value for this case */
148 cache_ptr->state = k_cache_board_state_none;
149 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
150 continue;
151 }
152
153 /* continue with the request */
154 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
155
156 struct registry_board *reg =
157 &global_skateshop.registry[ cache_ptr->registry_id ];
158
159 if( reg->workshop_id ){
160 vg_async_item *call =
161 vg_async_alloc( sizeof(struct async_workshop_filepath_info) );
162
163 struct async_workshop_filepath_info *info = call->payload;
164 info->buf = path;
165 info->id = reg->workshop_id;
166 info->len = vg_list_size(path) - strlen("/board.mdl")-1;
167 vg_async_dispatch( call, async_workshop_get_filepath );
168 vg_async_stall(); /* too bad! */
169
170 if( path[0] == '\0' ){
171 SDL_AtomicLock( &global_skateshop.sl_cache_access );
172 cache_ptr->state = k_cache_board_state_none;
173 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
174
175 vg_error( "Failed SteamAPI_GetItemInstallInfo(" PRINTF_U64 ")\n",
176 reg->workshop_id );
177 continue;
178 }
179 else{
180 strcat( path, "/board.mdl" );
181 }
182 }
183 else{
184 snprintf( path, 256, "models/boards/%s", reg->filename );
185 }
186
187 player_board_load( &cache_ptr->board, path );
188 vg_async_call( skateshop_async_board_loaded, cache_ptr, 0 );
189 }
190 else
191 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
192 }
193 vg_async_call( workshop_async_any_complete, NULL, 0 );
194 }
195
196 /*
197 * op: k_workshop_op_item_scan
198 * -----------------------------------------------------------------------------
199 */
200
201 /*
202 * Reciever for scan completion. copies the registry_count back into t0
203 */
204 VG_STATIC void workshop_async_reg_update( void *data, u32 size )
205 {
206 vg_info( "Registry update notify\n" );
207 global_skateshop.registry_count = global_skateshop.t1_registry_count;
208 }
209
210 VG_STATIC void workshop_steam_scan(void)
211 {
212 /*
213 * Steam workshop scan
214 */
215 vg_info( "Scanning steam workshop for boards\n" );
216 PublishedFileId_t workshop_ids[ SKATESHOP_REGISTRY_MAX ];
217 u32 workshop_count = SKATESHOP_REGISTRY_MAX;
218
219 vg_async_item *call = vg_async_alloc(
220 sizeof(struct async_workshop_installed_files_info));
221 struct async_workshop_installed_files_info *info = call->payload;
222 info->buffer = workshop_ids;
223 info->len = &workshop_count;
224 vg_async_dispatch( call, async_workshop_get_installed_files );
225 vg_async_stall();
226
227 for( u32 j=0; j<workshop_count; j++ ){
228 PublishedFileId_t id = workshop_ids[j];
229
230 for( u32 i=0; i<global_skateshop.t1_registry_count; i++ ){
231 struct registry_board *reg = &global_skateshop.registry[i];
232
233 if( reg->workshop_id == id ){
234 reg->state = k_registry_board_state_indexed;
235 goto next_file_workshop;
236 }
237 }
238
239 if( global_skateshop.t1_registry_count == SKATESHOP_REGISTRY_MAX ){
240 vg_error( "You have too many boards installed!\n" );
241 break;
242 }
243
244 vg_info( "new listing from the steam workshop!: "PRINTF_U64"\n", id );
245
246 struct registry_board *reg = &global_skateshop.registry[
247 global_skateshop.t1_registry_count ++ ];
248
249 reg->cache_ptr = NULL;
250 snprintf( reg->filename, 64, PRINTF_U64, id );
251 reg->filename_hash = vg_strdjb2( reg->filename );
252 reg->workshop_id = id;
253 reg->state = k_registry_board_state_indexed;
254
255 workshop_file_info_clear( &reg->workshop );
256 strcpy( reg->workshop.title, "Workshop file" );
257
258 /* load the metadata off the disk */
259 vg_async_item *call =
260 vg_async_alloc( sizeof(struct async_workshop_filepath_info) );
261
262 const char *meta_file = "/board.mdl.inf";
263 char path[ 1024 ];
264 struct async_workshop_filepath_info *info = call->payload;
265 info->buf = path;
266 info->id = reg->workshop_id;
267 info->len = vg_list_size(path) - strlen(meta_file)-1;
268 vg_async_dispatch( call, async_workshop_get_filepath );
269 vg_async_stall(); /* too bad! */
270
271 strcat( path, meta_file );
272 workshop_load_metadata( path, &reg->workshop );
273
274 next_file_workshop:;
275 }
276 }
277
278 /*
279 * Async thread which scans local files for boards, as well as scheduling
280 * synchronous calls to the workshop
281 */
282 VG_STATIC void workshop_scan_thread( void *_args )
283 {
284 vg_linear_clear( vg_mem.scratch );
285
286 for( u32 i=0; i<global_skateshop.t1_registry_count; i++ ){
287 struct registry_board *reg = &global_skateshop.registry[i];
288 reg->state = k_registry_board_state_indexed_absent;
289 }
290
291 /*
292 * Local disk scan
293 */
294 vg_info( "Scanning models/boards/*.mdl\n" );
295 tinydir_dir dir;
296 tinydir_open( &dir, "models/boards" );
297
298 while( dir.has_next ){
299 tinydir_file file;
300 tinydir_readfile( &dir, &file );
301
302 if( file.is_reg ){
303 u32 hash = vg_strdjb2( file.name );
304
305 for( u32 i=0; i<global_skateshop.t1_registry_count; i++ ){
306 struct registry_board *reg = &global_skateshop.registry[i];
307
308 if( const_str_eq( hash, file.name, reg->filename ) ){
309 reg->state = k_registry_board_state_indexed;
310 goto next_file;
311 }
312 }
313
314 if( global_skateshop.t1_registry_count == SKATESHOP_REGISTRY_MAX ){
315 vg_error( "You have too many boards installed!\n" );
316 break;
317 }
318
319 vg_info( "new listing!: %s\n", file.name );
320
321 struct registry_board *reg =
322 &global_skateshop.registry[global_skateshop.t1_registry_count ++];
323
324 reg->cache_ptr = NULL;
325 vg_strncpy( file.name, reg->filename, 64, k_strncpy_always_add_null );
326 vg_strncpy( file.name, reg->workshop.title,
327 64, k_strncpy_always_add_null );
328 reg->filename_hash = hash;
329 reg->workshop_id = 0;
330 reg->state = k_registry_board_state_indexed;
331 reg->workshop.author = 0;
332 strcpy( reg->workshop.author_name, "custom" );
333 }
334
335 next_file: tinydir_next( &dir );
336 }
337
338 tinydir_close(&dir);
339
340 if( steam_ready ) workshop_steam_scan();
341
342 vg_async_call( workshop_async_reg_update, NULL, 0 );
343 vg_async_stall();
344 workshop_visibile_load_loop_thread(NULL);
345 }
346
347 /*
348 * Asynchronous scan of local disk for items and add them to the registry
349 */
350 VG_STATIC void workshop_op_item_scan(void)
351 {
352 skaterift_begin_op( k_workshop_op_item_scan );
353 vg_loader_start( workshop_scan_thread, NULL );
354 }
355
356 /*
357 * op: k_async_op_world_scan
358 * -----------------------------------------------------------------------------
359 */
360
361 /*
362 * Reciever for scan completion. copies the registry_count back into t0
363 */
364 VG_STATIC void workshop_async_world_reg_update( void *data, u32 size )
365 {
366 vg_info( "World registry update notify\n" );
367 global_skateshop.world_registry_count =
368 global_skateshop.t1_world_registry_count;
369 skaterift_end_op();
370 }
371
372 /*
373 * Add a local world folder to the registry, it will verify existing ones are
374 * still there too.
375 */
376 VG_STATIC void world_scan_register_local( const char *folder_name )
377 {
378 u32 hash = vg_strdjb2( folder_name );
379 for( u32 i=0; i<global_skateshop.t1_world_registry_count; i++ ){
380 struct registry_world *reg =
381 &global_skateshop.world_registry[i];
382
383 if( const_str_eq( hash, folder_name, reg->foldername ) ){
384 reg->state = k_registry_board_state_indexed;
385 return;
386 }
387 }
388
389 if( global_skateshop.t1_world_registry_count == SKATESHOP_WORLDS_MAX ){
390 vg_error( "You have too many worlds installed!\n" );
391 return;
392 }
393
394 vg_info( "new listing!: %s\n", folder_name );
395
396 struct registry_world *reg = &global_skateshop.world_registry[
397 global_skateshop.t1_world_registry_count ++ ];
398
399 vg_strncpy( folder_name, reg->foldername, 64, k_strncpy_overflow_fatal );
400 reg->foldername_hash = hash;
401 reg->state = k_registry_board_state_indexed;
402 reg->meta_present = 0;
403 }
404
405 /*
406 * Async thread which scans local files for boards, as well as scheduling
407 * synchronous calls to the workshop
408 */
409 VG_STATIC void world_scan_thread( void *_args )
410 {
411 vg_linear_clear( vg_mem.scratch );
412
413 for( u32 i=0; i<global_skateshop.t1_world_registry_count; i++ ){
414 struct registry_world *reg = &global_skateshop.world_registry[i];
415 reg->state = k_registry_board_state_indexed_absent;
416 }
417
418 /*
419 * Local disk scan
420 */
421 vg_info( "Scanning maps/*.mdl\n" );
422
423 char path_buf[4096];
424 vg_str path;
425 vg_strnull( &path, path_buf, 4096 );
426 vg_strcat( &path, "maps/" );
427
428 DIR *dir = opendir( path.buffer );
429 if( !dir ){
430 vg_error( "opendir('maps') failed\n" );
431 vg_async_call( workshop_async_any_complete, NULL, 0 );
432 return;
433 }
434
435 struct dirent *entry;
436 while( (entry = readdir(dir)) ){
437 if( entry->d_type == DT_DIR ){
438 if( entry->d_name[0] == '.' ) continue;
439
440 vg_str folder = path;
441 char *folder_name = folder.buffer+folder.i;
442
443 if( strlen( entry->d_name ) >
444 vg_list_size(global_skateshop.world_registry[0].foldername)){
445 vg_warn( "Map folder too long: %s\n", entry->d_name );
446 continue;
447 }
448
449 vg_strcat( &folder, entry->d_name );
450 if( !vg_strgood( &folder ) ) break;
451
452 DIR *subdir = opendir( folder.buffer );
453 while( (entry = readdir(subdir)) ){
454 if( entry->d_type == DT_REG ){
455 if( entry->d_name[0] == '.' ) continue;
456
457 vg_str file = folder;
458 vg_strcat( &file, "/" );
459 vg_strcat( &file, entry->d_name );
460 if( !vg_strgood( &file ) ) break;
461
462 char *ext = vg_strch( &file, '.' );
463 if( !ext ) continue;
464 if( strcmp(ext,".mdl") ) continue;
465
466 vg_strcat( &folder, "" );
467 world_scan_register_local( folder_name );
468 }
469 }
470 closedir(subdir);
471 }
472 }
473 closedir(dir);
474
475 vg_async_call( workshop_async_world_reg_update, NULL, 0 );
476
477 #if 0
478 tinydir_close(&dir);
479
480 if( steam_ready ) workshop_steam_scan();
481
482 vg_async_call( workshop_async_reg_update, NULL, 0 );
483 vg_async_stall();
484 workshop_visibile_load_loop_thread(NULL);
485 #endif
486 }
487
488 /*
489 * Asynchronous scan of local disk for worlds
490 */
491 VG_STATIC void skateshop_op_world_scan(void)
492 {
493 skaterift_begin_op( k_async_op_world_scan );
494 vg_loader_start( world_scan_thread, NULL );
495 }
496
497 /*
498 * Regular stuff
499 * -----------------------------------------------------------------------------
500 */
501
502 /* we can only keep using a viewslot pointer for multiple frames if we watch it
503 * using this function */
504 VG_STATIC void watch_cache_board( struct cache_board *ptr )
505 {
506 if( ptr->ref_count >= 32 ){
507 vg_fatal_error( "dynamic board watch missmatch (limit is 32)\n" );
508 }
509
510 ptr->last_use_time = vg.time;
511 ptr->ref_count ++;
512 }
513
514 /* after this is called, the calling code only has access to the pointer for the
515 * duration of the rest of the frame */
516 VG_STATIC void unwatch_cache_board( struct cache_board *ptr )
517 {
518 if( ptr->ref_count == 0 ){
519 vg_fatal_error( "dynamic board unwatch missmatch (no watchers)\n" );
520 }
521
522 ptr->ref_count --;
523 }
524
525 /*
526 * Callback handler for persona state changes,
527 * it sets the author names on the registries
528 */
529 VG_STATIC void callback_persona_statechange( CallbackMsg_t *msg )
530 {
531 PersonaStateChange_t *info = (PersonaStateChange_t *)msg->m_pubParam;
532 ISteamFriends *hSteamFriends = SteamAPI_SteamFriends();
533
534 if( info->m_nChangeFlags & k_EPersonaChangeName ){
535 for( u32 i=0; i<global_skateshop.registry_count; i++ ){
536 struct registry_board *reg = &global_skateshop.registry[i];
537 if( reg->workshop.author == info->m_ulSteamID ){
538 const char *name = SteamAPI_ISteamFriends_GetFriendPersonaName(
539 hSteamFriends, info->m_ulSteamID );
540 str_utf8_collapse( name, reg->workshop.author_name, 32 );
541 }
542 }
543 }
544 }
545
546 /*
547 * VG event init
548 */
549 VG_STATIC void skateshop_init(void)
550 {
551 u32 reg_size = sizeof(struct registry_board)*SKATESHOP_REGISTRY_MAX,
552 wreg_size = sizeof(struct registry_world)*SKATESHOP_WORLDS_MAX,
553 cache_size = sizeof(struct cache_board)*SKATESHOP_BOARD_CACHE_MAX;
554
555 global_skateshop.registry = vg_linear_alloc( vg_mem.rtmemory, reg_size );
556 global_skateshop.world_registry =
557 vg_linear_alloc( vg_mem.rtmemory, wreg_size );
558 global_skateshop.cache = vg_linear_alloc( vg_mem.rtmemory, cache_size );
559
560 memset( global_skateshop.cache, 0, cache_size );
561
562 for( u32 i=0; i<SKATESHOP_BOARD_CACHE_MAX; i++ ){
563 struct cache_board *board = &global_skateshop.cache[i];
564 board->state = k_cache_board_state_none;
565 board->registry_id = 0xffffffff;
566 board->last_use_time = -99999.9;
567 board->ref_count = 0;
568 }
569
570 if( steam_ready ){
571 steam_register_callback( k_iPersonaStateChange,
572 callback_persona_statechange );
573 }
574 }
575
576 VG_STATIC struct cache_board *skateshop_selected_cache_if_loaded(void)
577 {
578 if( global_skateshop.registry_count > 0 ){
579 u32 reg_id = global_skateshop.selected_registry_id;
580 struct registry_board *reg = &global_skateshop.registry[ reg_id ];
581
582 SDL_AtomicLock( &global_skateshop.sl_cache_access );
583 if( reg->cache_ptr &&
584 (reg->cache_ptr->state == k_cache_board_state_loaded ) )
585 {
586 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
587 return reg->cache_ptr;
588 }
589 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
590 }
591
592 return NULL;
593 }
594
595 /*
596 * VG event preupdate
597 */
598 void temp_update_playermodel(void);
599 VG_STATIC void global_skateshop_preupdate(void)
600 {
601 float rate = vg_minf( 1.0f, vg.time_frame_delta * 2.0f );
602 global_skateshop.factive = vg_lerpf( global_skateshop.factive,
603 global_skateshop.active, rate );
604
605 if( !global_skateshop.active ) return;
606
607 world_instance *world = get_active_world();
608
609 ent_skateshop *shop = global_skateshop.ptr_ent;
610
611 /* camera positioning */
612 ent_camera *ref = mdl_arritm( &world->ent_camera,
613 mdl_entity_id_id(shop->id_camera) );
614
615 v3f dir = {0.0f,-1.0f,0.0f};
616 mdl_transform_vector( &ref->transform, dir, dir );
617 m3x3_mulv( localplayer.invbasis, dir, dir );
618 player_vector_angles( localplayer.cam_override_angles, dir, 1.0f, 0.0f );
619
620 v3f lookat;
621 if( shop->type == k_skateshop_type_boardshop ||
622 shop->type == k_skateshop_type_worldshop ){
623 ent_marker *display = mdl_arritm( &world->ent_marker,
624 mdl_entity_id_id(shop->boards.id_display) );
625
626 v3_sub( display->transform.co, localplayer.rb.co, lookat );
627
628 }
629 else if( shop->type == k_skateshop_type_charshop ){
630 v3_sub( ref->transform.co, localplayer.rb.co, lookat );
631 }
632 else{
633 vg_fatal_error( "Unknown store (%u)\n", shop->type );
634 }
635
636 q_axis_angle( localplayer.rb.q, (v3f){0.0f,1.0f,0.0f},
637 atan2f(lookat[0],lookat[2]) );
638
639 v3_copy( ref->transform.co, localplayer.cam_override_pos );
640 localplayer.cam_override_fov = ref->fov;
641 localplayer.cam_override_strength = global_skateshop.factive;
642
643 /* input */
644 if( shop->type == k_skateshop_type_boardshop ){
645 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h ), "browse" );
646 gui_helper_action( button_display_string( k_srbind_mback ), "exit" );
647
648 struct cache_board *selected_cache = skateshop_selected_cache_if_loaded();
649
650 if( selected_cache ){
651 gui_helper_action( button_display_string( k_srbind_maccept ), "pick" );
652 }
653
654 /*
655 * Controls
656 * ----------------------
657 */
658
659 if( button_down( k_srbind_mleft ) ){
660 if( global_skateshop.selected_registry_id > 0 ){
661 global_skateshop.selected_registry_id --;
662 }
663 }
664
665 if( button_down( k_srbind_mright ) ){
666 if( global_skateshop.selected_registry_id+1 <
667 global_skateshop.registry_count )
668 {
669 global_skateshop.selected_registry_id ++;
670 }
671 }
672
673 if( selected_cache && button_down( k_srbind_maccept ) ){
674 vg_info( "chose board from skateshop (%u)\n",
675 global_skateshop.selected_registry_id );
676
677 if( localplayer.board_view_slot ){
678 unwatch_cache_board( localplayer.board_view_slot );
679 }
680
681 localplayer.board_view_slot = selected_cache;
682 watch_cache_board( localplayer.board_view_slot );
683
684 global_skateshop_exit();
685 return;
686 }
687 }
688 else if( shop->type == k_skateshop_type_charshop ){
689 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h ), "browse" );
690 gui_helper_action( button_display_string( k_srbind_mback ), "exit" );
691 gui_helper_action( button_display_string( k_srbind_maccept ), "pick" );
692
693 if( button_down( k_srbind_mleft ) ){
694 if( cl_playermdl_id > 0 ){
695 cl_playermdl_id --;
696 }
697 else{
698 cl_playermdl_id = 2; /* HACK */
699 }
700 temp_update_playermodel(); /* HACK */
701 }
702
703 if( button_down( k_srbind_mright ) ){
704 if( cl_playermdl_id+1 < 3 ){
705 cl_playermdl_id ++;
706 }
707 else{
708 cl_playermdl_id = 0; /* HACK */
709 }
710 temp_update_playermodel(); /* HACK */
711 /*lol*/
712 }
713
714 if( button_down( k_srbind_maccept ) ){
715 global_skateshop_exit();
716 }
717 }
718 else if( shop->type == k_skateshop_type_worldshop ){
719 if( global_skateshop.world_registry_count ){
720 gui_helper_action( axis_display_string(k_sraxis_mbrowse_h), "browse" );
721 }
722
723 int change = 0;
724 if( button_down( k_srbind_mleft ) ){
725 if( global_skateshop.selected_world_id > 0 ){
726 global_skateshop.selected_world_id --;
727 change = 1;
728 }
729 }
730
731 if( button_down( k_srbind_mright ) ){
732 if( global_skateshop.selected_world_id+1 <
733 global_skateshop.world_registry_count ){
734 global_skateshop.selected_world_id ++;
735 change = 1;
736 }
737 }
738
739 if( change && (pointcloud.anim == k_pointcloud_anim_idle) ){
740 pointcloud.anim = k_pointcloud_anim_hiding;
741 pointcloud.anim_start = vg.time;
742 }
743
744 if( button_down( k_srbind_maccept ) ){
745 vg_info( "Select world (%u)\n", global_skateshop.selected_world_id );
746 global_skateshop_exit();
747 return;
748 }
749 }
750 else{
751 vg_fatal_error( "Unknown store (%u)\n", shop->type );
752 }
753
754 if( button_down( k_srbind_mback ) ){
755 global_skateshop_exit();
756 return;
757 }
758 }
759
760 VG_STATIC void skateshop_render_boardshop(void)
761 {
762 world_instance *world = get_active_world();
763 ent_skateshop *shop = global_skateshop.ptr_ent;
764
765 u32 slot_count = vg_list_size(global_skateshop.shop_view_slots);
766
767 ent_marker *mark_rack = mdl_arritm( &world->ent_marker,
768 mdl_entity_id_id(shop->boards.id_rack)),
769 *mark_display = mdl_arritm( &world->ent_marker,
770 mdl_entity_id_id(shop->boards.id_display));
771
772 int visibility[ SKATESHOP_VIEW_SLOT_MAX ];
773 SDL_AtomicLock( &global_skateshop.sl_cache_access );
774 for( u32 i=0; i<SKATESHOP_VIEW_SLOT_MAX; i++ ){
775 struct shop_view_slot *slot = &global_skateshop.shop_view_slots[i];
776
777 visibility[i] = 1;
778
779 if( slot->cache_ptr == NULL ) visibility[i] = 0;
780 else if( slot->cache_ptr->state != k_cache_board_state_loaded )
781 visibility[i] = 0;
782 }
783 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
784
785 /* Render loaded boards in the view slots */
786 for( u32 i=0; i<slot_count; i++ ){
787 struct shop_view_slot *slot = &global_skateshop.shop_view_slots[i];
788 float selected = 0.0f;
789
790 if( !visibility[i] ) goto fade_out;
791
792 mdl_transform xform;
793 transform_identity( &xform );
794
795 xform.co[0] = -((float)i - ((float)slot_count)*0.5f)*0.45f;
796 mdl_transform_mul( &mark_rack->transform, &xform, &xform );
797
798 if( slot->cache_ptr->registry_id ==
799 global_skateshop.selected_registry_id ){
800 selected = 1.0f;
801 }
802
803 float t = slot->view_blend;
804 v3_lerp( xform.co, mark_display->transform.co, t, xform.co );
805 q_nlerp( xform.q, mark_display->transform.q, t, xform.q );
806 v3_lerp( xform.s, mark_display->transform.s, t, xform.s );
807
808 m4x3f mmdl;
809 mdl_transform_m4x3( &xform, mmdl );
810 render_board( &main_camera, world, &slot->cache_ptr->board, mmdl,
811 k_board_shader_entity );
812
813 fade_out:;
814 float rate = 5.0f*vg.time_delta;
815 slot->view_blend = vg_lerpf( slot->view_blend, selected, rate );
816 }
817
818 ent_marker *mark_info = mdl_arritm( &world->ent_marker,
819 mdl_entity_id_id(shop->boards.id_info));
820 m4x3f mtext, mrack;
821 mdl_transform_m4x3( &mark_info->transform, mtext );
822 mdl_transform_m4x3( &mark_rack->transform, mrack );
823
824 #if 0
825 const char *text_title = "Fish - Title";
826 const char *text_author = "by Shaniqua";
827 #endif
828
829 m4x3f mlocal, mmdl;
830 m4x3_identity( mlocal );
831
832 float scale = 0.2f,
833 thickness = 0.03f;
834
835 font3d_bind( &world_global.font, &main_camera );
836 shader_model_font_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} );
837
838 /* Selection counter
839 * ------------------------------------------------------------------ */
840 m3x3_zero( mlocal );
841 v3_zero( mlocal[3] );
842 mlocal[0][0] = -scale*2.0f;
843 mlocal[1][2] = -scale*2.0f;
844 mlocal[2][1] = -thickness;
845 mlocal[3][2] = -0.7f;
846 m4x3_mul( mrack, mlocal, mmdl );
847
848 if( global_skateshop.registry_count == 0 ){
849 font3d_simple_draw( &world_global.font, 0,
850 "Nothing installed", &main_camera, mmdl );
851 }
852 else{
853 char buf[16];
854 int i=0;
855 i+=highscore_intl( buf+i, global_skateshop.selected_registry_id+1, 3 );
856 buf[i++] = '/';
857 i+=highscore_intl( buf+i, global_skateshop.registry_count, 3 );
858 buf[i++] = '\0';
859
860 font3d_simple_draw( &world_global.font, 0, buf, &main_camera, mmdl );
861 }
862
863 struct cache_board *cache_ptr = skateshop_selected_cache_if_loaded();
864 if( !cache_ptr ) return;
865
866 struct registry_board *reg =
867 &global_skateshop.registry[cache_ptr->registry_id];
868 struct workshop_file_info *info = &reg->workshop;
869
870 /* Skin title
871 * ----------------------------------------------------------------- */
872 m3x3_zero( mlocal );
873 m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
874 mlocal[3][0] = -font3d_string_width( &world_global.font, 0, info->title );
875 mlocal[3][0] *= scale*0.5f;
876 mlocal[3][1] = 0.1f;
877 mlocal[3][2] = 0.0f;
878 m4x3_mul( mtext, mlocal, mmdl );
879 font3d_simple_draw( &world_global.font, 0, info->title, &main_camera, mmdl );
880
881 /* Author name
882 * ----------------------------------------------------------------- */
883 scale *= 0.4f;
884 m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
885 mlocal[3][0] = -font3d_string_width( &world_global.font, 0,
886 info->author_name );
887 mlocal[3][0] *= scale*0.5f;
888 mlocal[3][1] = 0.0f;
889 mlocal[3][2] = 0.0f;
890 m4x3_mul( mtext, mlocal, mmdl );
891 font3d_simple_draw( &world_global.font, 0,
892 info->author_name, &main_camera, mmdl );
893 }
894
895 VG_STATIC void skateshop_render_charshop(void)
896 {
897 }
898
899 VG_STATIC void skateshop_render_worldshop(void)
900 {
901 world_instance *world = get_active_world();
902
903 ent_skateshop *shop = global_skateshop.ptr_ent;
904 ent_marker *mark_display = mdl_arritm( &world->ent_marker,
905 mdl_entity_id_id(shop->worlds.id_display)),
906 *mark_info = mdl_arritm( &world->ent_marker,
907 mdl_entity_id_id(shop->boards.id_info));
908
909 /* Text */
910 char buftext[128], bufsubtext[128];
911 vg_str info, subtext;
912 vg_strnull( &info, buftext, 128 );
913 vg_strnull( &subtext, bufsubtext, 128 );
914
915 if( global_skateshop.world_registry_count ){
916 struct registry_world *rw = &global_skateshop.world_registry[
917 global_skateshop.selected_world_id ];
918
919 info.i+=highscore_intl( info.buffer+info.i,
920 global_skateshop.selected_world_id+1, 3 );
921 info.buffer[info.i++] = '/';
922 info.i+=highscore_intl( info.buffer+info.i,
923 global_skateshop.world_registry_count, 3 );
924 info.buffer[info.i++] = ' ';
925 info.buffer[info.i] = '\0';
926
927 if( rw->meta_present ){
928 vg_fatal_error("");
929 }
930 else{
931 vg_strcat( &info, rw->foldername );
932 vg_strcat( &subtext, "No information" );
933 }
934 }
935 else{
936 vg_strcat( &info, "No worlds installed" );
937 }
938
939 m4x3f mtext,mlocal,mtextmdl;
940 mdl_transform_m4x3( &mark_info->transform, mtext );
941
942 font3d_bind( &world_global.font, &main_camera );
943 shader_model_font_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} );
944
945 float scale = 0.2f, thickness = 0.015f, scale1 = 0.08f;
946 m3x3_zero( mlocal );
947 m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
948 mlocal[3][0] = -font3d_string_width( &world_global.font, 0, buftext );
949 mlocal[3][0] *= scale*0.5f;
950 mlocal[3][1] = 0.1f;
951 mlocal[3][2] = 0.0f;
952 m4x3_mul( mtext, mlocal, mtextmdl );
953 font3d_simple_draw( &world_global.font, 0, buftext, &main_camera, mtextmdl );
954
955 m3x3_setdiagonalv3( mlocal, (v3f){ scale1, scale1, thickness } );
956 mlocal[3][0] = -font3d_string_width( &world_global.font, 0, bufsubtext );
957 mlocal[3][0] *= scale1*0.5f;
958 mlocal[3][1] = -scale1*0.3f;
959 m4x3_mul( mtext, mlocal, mtextmdl );
960 font3d_simple_draw( &world_global.font, 0, bufsubtext,
961 &main_camera, mtextmdl );
962
963 /* pointcloud */
964 m4x3f mmdl;
965 mdl_transform_m4x3( &mark_display->transform, mmdl );
966 m4x3_rotate_y( mmdl, vg.time * 0.2 );
967
968 glEnable(GL_BLEND);
969 glBlendFunc(GL_ONE, GL_ONE);
970 glDisable(GL_DEPTH_TEST);
971 pointcloud_render( world, &main_camera, mmdl );
972 glDisable(GL_BLEND);
973 glEnable(GL_DEPTH_TEST);
974 }
975
976 /*
977 * World: render event
978 */
979 VG_STATIC void skateshop_render(void)
980 {
981 if( !global_skateshop.active ) return;
982
983 ent_skateshop *shop = global_skateshop.ptr_ent;
984
985 if( shop->type == k_skateshop_type_boardshop ){
986 skateshop_render_boardshop();
987 }
988 else if( shop->type == k_skateshop_type_charshop ){
989 skateshop_render_charshop();
990 }
991 else if( shop->type == k_skateshop_type_worldshop ){
992 skateshop_render_worldshop();
993 }
994 else{
995 vg_fatal_error( "Unknown store (%u)\n", shop->type );
996 }
997 }
998
999 /*
1000 * Entity logic: entrance event
1001 */
1002 VG_STATIC void ent_skateshop_call( world_instance *world, ent_call *call )
1003 {
1004 u32 index = mdl_entity_id_id( call->id );
1005 ent_skateshop *shop = mdl_arritm( &world->ent_skateshop, index );
1006 vg_info( "skateshop_call\n" );
1007
1008 if( menu.active ) return;
1009 if( skaterift.async_op != k_async_op_none ) return;
1010
1011 if( call->function == k_ent_function_trigger ){
1012 if( localplayer.subsystem != k_player_subsystem_walk ){
1013 return;
1014 }
1015
1016 vg_info( "Entering skateshop\n" );
1017
1018 localplayer.immobile = 1;
1019 menu.disable_open = 1;
1020 global_skateshop.active = 1;
1021
1022 v3_zero( localplayer.rb.v );
1023 v3_zero( localplayer.rb.w );
1024 localplayer._walk.move_speed = 0.0f;
1025 global_skateshop.ptr_ent = shop;
1026
1027 if( shop->type == k_skateshop_type_boardshop ){
1028 skateshop_update_viewpage();
1029 workshop_op_item_scan();
1030 }
1031 else if( shop->type == k_skateshop_type_worldshop ){
1032 pointcloud.anim = k_pointcloud_anim_opening;
1033 pointcloud.anim_start = vg.time;
1034 skateshop_op_world_scan();
1035 }
1036 }
1037 }
1038
1039 /*
1040 * Entity logic: exit event
1041 */
1042 VG_STATIC void global_skateshop_exit(void)
1043 {
1044 vg_info( "exit skateshop\n" );
1045 localplayer.immobile = 0;
1046 global_skateshop.active = 0;
1047 menu.disable_open = 0;
1048 srinput.ignore_input_frames = 2;
1049 }
1050
1051 #endif /* ENT_SKATESHOP_C */