fd59f3eb047cf652cdca82f3e0c065ac34e34f79
[carveJwlIkooP6JGAAIwe30JlM.git] / ent_skateshop.c
1 #include "vg/vg_steam_ugc.h"
2 #include "vg/vg_msg.h"
3 #include "vg/vg_tex.h"
4 #include "vg/vg_image.h"
5 #include "vg/vg_loader.h"
6 #include "ent_skateshop.h"
7 #include "world.h"
8 #include "player.h"
9 #include "gui.h"
10 #include "menu.h"
11 #include "steam.h"
12 #include "addon.h"
13 #include "save.h"
14 #include "network.h"
15
16 struct global_skateshop global_skateshop =
17 {
18 .render={.reg_id=0xffffffff,.world_reg=0xffffffff}
19 };
20
21 /*
22 * Checks string equality but does a hash check first
23 */
24 static inline int const_str_eq( u32 hash, const char *str, const char *cmp )
25 {
26 if( hash == vg_strdjb2(cmp) )
27 if( !strcmp( str, cmp ) )
28 return 1;
29 return 0;
30 }
31
32 static void skateshop_update_viewpage(void){
33 u32 page = global_skateshop.selected_board_id/SKATESHOP_VIEW_SLOT_MAX;
34
35 for( u32 i=0; i<SKATESHOP_VIEW_SLOT_MAX; i++ ){
36 u32 j = SKATESHOP_VIEW_SLOT_MAX-1-i;
37 struct shop_view_slot *slot = &global_skateshop.shop_view_slots[j];
38 addon_cache_unwatch( k_addon_type_board, slot->cache_id );
39 }
40
41 for( u32 i=0; i<SKATESHOP_VIEW_SLOT_MAX; i++ ){
42 struct shop_view_slot *slot = &global_skateshop.shop_view_slots[i];
43 u32 request_id = page*SKATESHOP_VIEW_SLOT_MAX + i;
44 slot->cache_id = addon_cache_create_viewer( k_addon_type_board,
45 request_id );
46 }
47 }
48
49 struct async_preview_load_thread_data{
50 void *data;
51 addon_reg *reg;
52 };
53
54 static void skateshop_async_preview_imageload( void *data, u32 len ){
55 struct async_preview_load_thread_data *inf = data;
56
57 if( inf->data ){
58 glBindTexture( GL_TEXTURE_2D, global_skateshop.tex_preview );
59 glTexSubImage2D( GL_TEXTURE_2D, 0,0,0,
60 WORKSHOP_PREVIEW_WIDTH, WORKSHOP_PREVIEW_HEIGHT,
61 GL_RGB, GL_UNSIGNED_BYTE, inf->data );
62 glGenerateMipmap( GL_TEXTURE_2D );
63 stbi_image_free( inf->data );
64
65 skaterift.rt_textures[k_skaterift_rt_workshop_preview] =
66 global_skateshop.tex_preview;
67 }
68 else {
69 skaterift.rt_textures[k_skaterift_rt_workshop_preview] = vg.tex_missing;
70 }
71
72 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
73 global_skateshop.reg_loaded_preview = inf->reg;
74 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
75 }
76
77 static void skateshop_update_preview_image_thread(void *_args){
78 char path_buf[4096];
79 vg_str folder;
80 vg_strnull( &folder, path_buf, sizeof(path_buf) );
81
82 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
83 addon_reg *reg_preview = global_skateshop.reg_preview;
84 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
85
86 if( !addon_get_content_folder( reg_preview, &folder, 1 ) ){
87 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
88 global_skateshop.reg_loaded_preview = reg_preview;
89 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
90 return;
91 }
92
93 vg_strcat( &folder, "/preview.jpg" );
94 vg_async_item *call =
95 vg_async_alloc( sizeof(struct async_preview_load_thread_data) );
96 struct async_preview_load_thread_data *inf = call->payload;
97
98 inf->reg = reg_preview;
99
100 if( vg_strgood( &folder ) ){
101 stbi_set_flip_vertically_on_load(1);
102 int x, y, nc;
103 inf->data = stbi_load( folder.buffer, &x, &y, &nc, 3 );
104
105 if( inf->data ){
106 if( (x != WORKSHOP_PREVIEW_WIDTH) || (y != WORKSHOP_PREVIEW_HEIGHT) ){
107 vg_error( "Resolution does not match framebuffer, so we can't"
108 " show it\n" );
109 stbi_image_free( inf->data );
110 inf->data = NULL;
111 }
112 }
113
114 vg_async_dispatch( call, skateshop_async_preview_imageload );
115 }
116 }
117
118 /*
119 * op/subroutine: k_workshop_op_item_load
120 * -----------------------------------------------------------------------------
121 */
122
123 static void world_scan_thread( void *_args ){
124 addon_mount_content_folder( k_addon_type_world, "maps", ".mdl" );
125 addon_mount_workshop_items();
126 vg_async_call( async_addon_reg_update, NULL, 0 );
127 }
128
129 /*
130 * Asynchronous scan of local disk for worlds
131 */
132 static void skateshop_op_world_scan(void){
133 vg_loader_start( world_scan_thread, NULL );
134 }
135
136 static void board_processview_thread( void *_args ){
137 addon_cache_load_loop();
138 }
139
140 static void board_scan_thread( void *_args ){
141 addon_mount_content_folder( k_addon_type_board, "boards", ".mdl" );
142 addon_mount_workshop_items();
143 vg_async_call( async_addon_reg_update, NULL, 0 );
144 vg_async_stall();
145 board_processview_thread(NULL);
146 }
147
148 /* TODO: migrate to addon.c */
149 static void skateshop_op_board_scan(void){
150 vg_loader_start( board_scan_thread, NULL );
151 }
152
153 /* TODO: migrate to addon.c */
154 void skateshop_autostart_loading(void)
155 {
156 if( !vg_loader_availible() ) return;
157
158 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
159 if( global_skateshop.reg_preview != global_skateshop.reg_loaded_preview ){
160 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
161 vg_loader_start( skateshop_update_preview_image_thread, NULL );
162 return;
163 }
164
165 for( u32 type=0; type<k_addon_type_max; type++ ){
166 struct addon_cache *cache = &addon_system.cache[type];
167
168 for( u32 id=1; id<=cache->pool.count; id++ ){
169 addon_cache_entry *entry = vg_pool_item( &cache->pool, id );
170 if( entry->state == k_addon_cache_state_load_request ){
171 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
172 vg_loader_start( board_processview_thread, NULL );
173 return;
174 }
175 }
176 }
177 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
178 }
179
180 /*
181 * Regular stuff
182 * -----------------------------------------------------------------------------
183 */
184
185 static void skateshop_init_async(void *_data,u32 size){
186 glGenTextures( 1, &global_skateshop.tex_preview );
187 glBindTexture( GL_TEXTURE_2D, global_skateshop.tex_preview );
188 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB,
189 WORKSHOP_PREVIEW_WIDTH, WORKSHOP_PREVIEW_HEIGHT,
190 0, GL_RGB, GL_UNSIGNED_BYTE, NULL );
191
192 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
193 GL_LINEAR_MIPMAP_LINEAR );
194 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
195 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
196 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
197
198 skaterift.rt_textures[ k_skaterift_rt_workshop_preview ] = vg.tex_missing;
199 skaterift.rt_textures[ k_skaterift_rt_server_status ] = vg.tex_missing;
200 render_server_status_gui();
201 }
202
203 /*
204 * VG event init
205 */
206 void skateshop_init(void)
207 {
208 vg_async_call( skateshop_init_async, NULL, 0 );
209 }
210
211 static u16 skateshop_selected_cache_id(void){
212 if( addon_count(k_addon_type_board, ADDON_REG_HIDDEN) ){
213 addon_reg *reg = get_addon_from_index(
214 k_addon_type_board, global_skateshop.selected_board_id,
215 ADDON_REG_HIDDEN );
216 return reg->cache_id;
217 }
218 else return 0;
219 }
220
221 static void skateshop_server_helper_update(void){
222 vg_str text;
223 vg_strnull( &text, global_skateshop.helper_toggle->text,
224 sizeof(global_skateshop.helper_toggle->text) );
225
226 if( skaterift.demo_mode ){
227 vg_strcat( &text, "Not availible in demo" );
228 }
229 else {
230 if( network_client.user_intent == k_server_intent_online )
231 vg_strcat( &text, "Disconnect" );
232 else
233 vg_strcat( &text, "Go Online" );
234 }
235 }
236
237 /*
238 * VG event preupdate
239 */
240 void temp_update_playermodel(void);
241 void ent_skateshop_preupdate( ent_skateshop *shop, int active )
242 {
243 if( !active ) return;
244
245 /* input filter */
246 world_instance *world = world_current_instance();
247
248 /* camera positioning */
249 ent_camera *ref = mdl_arritm( &world->ent_camera,
250 mdl_entity_id_id(shop->id_camera) );
251
252 v3f dir = {0.0f,-1.0f,0.0f};
253 mdl_transform_vector( &ref->transform, dir, dir );
254 v3_angles( dir, world_static.focus_cam.angles );
255
256 v3f lookat;
257 if( shop->type == k_skateshop_type_boardshop ||
258 shop->type == k_skateshop_type_worldshop ){
259 ent_marker *display = mdl_arritm( &world->ent_marker,
260 mdl_entity_id_id(shop->boards.id_display) );
261 v3_sub( display->transform.co, localplayer.rb.co, lookat );
262 }
263 else if( shop->type == k_skateshop_type_charshop ){
264 v3_sub( ref->transform.co, localplayer.rb.co, lookat );
265 }
266 else if( shop->type == k_skateshop_type_server ){
267 ent_prop *prop = mdl_arritm( &world->ent_prop,
268 mdl_entity_id_id(shop->server.id_lever) );
269 v3_sub( prop->transform.co, localplayer.rb.co, lookat );
270 }
271 else
272 vg_fatal_error( "Unknown store (%u)\n", shop->type );
273
274 q_axis_angle( localplayer.rb.q, (v3f){0.0f,1.0f,0.0f},
275 atan2f(lookat[0],lookat[2]) );
276
277 v3_copy( ref->transform.co, world_static.focus_cam.pos );
278 world_static.focus_cam.fov = ref->fov;
279
280 /* input */
281 if( shop->type == k_skateshop_type_boardshop ){
282 if( !vg_loader_availible() ) return;
283
284 u16 cache_id = skateshop_selected_cache_id();
285 global_skateshop.helper_pick->greyed = !cache_id;
286
287 /*
288 * Controls
289 * ----------------------
290 */
291 u32 opage = global_skateshop.selected_board_id/SKATESHOP_VIEW_SLOT_MAX;
292
293 if( button_down( k_srbind_mleft ) ){
294 if( global_skateshop.selected_board_id > 0 ){
295 global_skateshop.selected_board_id --;
296 }
297 }
298
299 u32 valid_count = addon_count( k_addon_type_board, 0 );
300 if( button_down( k_srbind_mright ) ){
301 if( global_skateshop.selected_board_id+1 < valid_count ){
302 global_skateshop.selected_board_id ++;
303 }
304 }
305
306 u32 npage = global_skateshop.selected_board_id/SKATESHOP_VIEW_SLOT_MAX;
307
308 if( opage != npage ){
309 skateshop_update_viewpage();
310 }
311 else if( cache_id && button_down( k_srbind_maccept )){
312 vg_info( "chose board from skateshop (%u)\n",
313 global_skateshop.selected_board_id );
314
315 addon_cache_unwatch( k_addon_type_board, localplayer.board_view_slot );
316 addon_cache_watch( k_addon_type_board, cache_id );
317 localplayer.board_view_slot = cache_id;
318 network_send_item( k_netmsg_playeritem_board );
319
320 world_entity_exit_modal();
321 world_entity_clear_focus();
322 gui_helper_clear();
323 skaterift_autosave(1);
324 return;
325 }
326 }
327 else if( shop->type == k_skateshop_type_charshop ){
328 if( !vg_loader_availible() ) return;
329
330 int changed = 0;
331 u32 valid_count = addon_count( k_addon_type_player, ADDON_REG_HIDDEN );
332
333 if( button_down( k_srbind_mleft ) ){
334 if( global_skateshop.selected_player_id > 0 ){
335 global_skateshop.selected_player_id --;
336 }
337 else{
338 global_skateshop.selected_player_id = valid_count-1;
339 }
340
341 changed = 1;
342 }
343
344 if( button_down( k_srbind_mright ) ){
345 if( global_skateshop.selected_player_id+1 < valid_count ){
346 global_skateshop.selected_player_id ++;
347 }
348 else{
349 global_skateshop.selected_player_id = 0;
350 }
351
352 changed = 1;
353 }
354
355 if( changed ){
356 addon_reg *addon = get_addon_from_index(
357 k_addon_type_player, global_skateshop.selected_player_id,
358 ADDON_REG_HIDDEN );
359
360 u32 real_id = get_index_from_addon(
361 k_addon_type_player, addon );
362
363 player__use_model( real_id );
364 }
365
366 if( button_down( k_srbind_maccept ) ){
367 network_send_item( k_netmsg_playeritem_player );
368 world_entity_exit_modal();
369 world_entity_clear_focus();
370 gui_helper_clear();
371 }
372 }
373 else if( shop->type == k_skateshop_type_worldshop ){
374 int browseable = 0,
375 loadable = 0;
376
377 u32 valid_count = addon_count( k_addon_type_world, ADDON_REG_HIDDEN );
378
379 if( valid_count && vg_loader_availible() )
380 browseable = 1;
381
382 if( valid_count && vg_loader_availible() )
383 loadable = 1;
384
385 global_skateshop.helper_browse->greyed = !browseable;
386 global_skateshop.helper_pick->greyed = !loadable;
387
388 addon_reg *selected_world = NULL;
389
390 int change = 0;
391 if( browseable ){
392 if( button_down( k_srbind_mleft ) ){
393 if( global_skateshop.selected_world_id > 0 ){
394 global_skateshop.selected_world_id --;
395 change = 1;
396 }
397 }
398
399 if( button_down( k_srbind_mright ) ){
400 if( global_skateshop.selected_world_id+1 < valid_count ){
401 global_skateshop.selected_world_id ++;
402 change = 1;
403 }
404 }
405
406 selected_world = get_addon_from_index( k_addon_type_world,
407 global_skateshop.selected_world_id, ADDON_REG_HIDDEN );
408
409 if( change || (global_skateshop.reg_preview == NULL) ){
410 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
411 global_skateshop.reg_preview = selected_world;
412 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
413 }
414 }
415
416 if( loadable ){
417 if( button_down( k_srbind_maccept ) ){
418 skaterift_change_world_start( selected_world );
419 }
420 }
421 }
422 else if( shop->type == k_skateshop_type_server ){
423 f64 delta = vg.time_real - network_client.last_intent_change;
424
425 if( (delta > 5.0) && (!skaterift.demo_mode) ){
426 global_skateshop.helper_pick->greyed = 0;
427 if( button_down( k_srbind_maccept ) ){
428 network_client.user_intent = !network_client.user_intent;
429 network_client.last_intent_change = vg.time_real;
430 skateshop_server_helper_update();
431 render_server_status_gui();
432 }
433 }
434 else {
435 global_skateshop.helper_pick->greyed = 1;
436 }
437 }
438 else{
439 vg_fatal_error( "Unknown store (%u)\n", shop->type );
440 }
441
442 if( button_down( k_srbind_mback ) )
443 {
444 if( shop->type == k_skateshop_type_charshop )
445 network_send_item( k_netmsg_playeritem_player );
446
447 world_entity_exit_modal();
448 world_entity_clear_focus();
449 gui_helper_clear();
450 return;
451 }
452 }
453
454 void skateshop_world_preupdate( world_instance *world )
455 {
456 for( u32 i=0; i<mdl_arrcount(&world->ent_skateshop); i++ ){
457 ent_skateshop *shop = mdl_arritm( &world->ent_skateshop, i );
458
459 if( shop->type == k_skateshop_type_server ){
460 f32 a = network_client.user_intent;
461
462 vg_slewf( &network_client.fintent, a, vg.time_frame_delta );
463 a = (vg_smoothstepf( network_client.fintent ) - 0.5f) * (VG_PIf/2.0f);
464
465 ent_prop *lever = mdl_arritm( &world->ent_prop,
466 mdl_entity_id_id(shop->server.id_lever) );
467
468 /* we need parent transforms now? */
469 q_axis_angle( lever->transform.q, (v3f){0,0,1}, a );
470 }
471 }
472 }
473
474 static void skateshop_render_boardshop( ent_skateshop *shop ){
475 world_instance *world = world_current_instance();
476 u32 slot_count = vg_list_size(global_skateshop.shop_view_slots);
477
478 ent_marker *mark_rack = mdl_arritm( &world->ent_marker,
479 mdl_entity_id_id(shop->boards.id_rack)),
480 *mark_display = mdl_arritm( &world->ent_marker,
481 mdl_entity_id_id(shop->boards.id_display));
482
483 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
484 struct addon_cache *cache = &addon_system.cache[k_addon_type_board];
485
486 /* Render loaded boards in the view slots */
487 for( u32 i=0; i<slot_count; i++ ){
488 struct shop_view_slot *slot = &global_skateshop.shop_view_slots[i];
489 float selected = 0.0f;
490
491 if( !slot->cache_id )
492 goto fade_out;
493
494 addon_cache_entry *entry = vg_pool_item( &cache->pool, slot->cache_id );
495
496 if( entry->state != k_addon_cache_state_loaded )
497 goto fade_out;
498
499 struct player_board *board =
500 addon_cache_item( k_addon_type_board, slot->cache_id );
501
502 mdl_transform xform;
503 transform_identity( &xform );
504
505 xform.co[0] = -((float)i - ((float)slot_count)*0.5f)*0.45f;
506 mdl_transform_mul( &mark_rack->transform, &xform, &xform );
507
508
509 if( entry->reg_index == global_skateshop.selected_board_id ){
510 selected = 1.0f;
511 }
512
513 float t = slot->view_blend;
514 v3_lerp( xform.co, mark_display->transform.co, t, xform.co );
515 q_nlerp( xform.q, mark_display->transform.q, t, xform.q );
516 v3_lerp( xform.s, mark_display->transform.s, t, xform.s );
517
518 struct player_board_pose pose = {0};
519 m4x3f mmdl;
520 mdl_transform_m4x3( &xform, mmdl );
521 render_board( &skaterift.cam, world, board, mmdl,
522 &pose, k_board_shader_entity );
523
524 fade_out:;
525 float rate = 5.0f*vg.time_delta;
526 slot->view_blend = vg_lerpf( slot->view_blend, selected, rate );
527 }
528
529 ent_marker *mark_info = mdl_arritm( &world->ent_marker,
530 mdl_entity_id_id(shop->boards.id_info));
531 m4x3f mtext, mrack;
532 mdl_transform_m4x3( &mark_info->transform, mtext );
533 mdl_transform_m4x3( &mark_rack->transform, mrack );
534
535 m4x3f mlocal, mmdl;
536 m4x3_identity( mlocal );
537
538 float scale = 0.2f,
539 thickness = 0.03f;
540
541 font3d_bind( &gui.font, k_font_shader_default, 0, world, &skaterift.cam );
542 shader_model_font_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} );
543
544 /* Selection counter
545 * ------------------------------------------------------------------ */
546 m3x3_zero( mlocal );
547 v3_zero( mlocal[3] );
548 mlocal[0][0] = -scale*2.0f;
549 mlocal[1][2] = -scale*2.0f;
550 mlocal[2][1] = -thickness;
551 mlocal[3][2] = -0.7f;
552 m4x3_mul( mrack, mlocal, mmdl );
553
554 u32 valid_count = addon_count(k_addon_type_board,0);
555 if( valid_count ){
556 char buf[16];
557 vg_str str;
558 vg_strnull( &str, buf, sizeof(buf) );
559 vg_strcati32( &str, global_skateshop.selected_board_id+1 );
560 vg_strcatch( &str, '/' );
561 vg_strcati32( &str, valid_count );
562 font3d_simple_draw( 0, buf, &skaterift.cam, mmdl );
563 }
564 else{
565 font3d_simple_draw( 0, "Nothing installed", &skaterift.cam, mmdl );
566 }
567
568 u16 cache_id = skateshop_selected_cache_id();
569 struct addon_cache_entry *entry = vg_pool_item( &cache->pool, cache_id );
570 addon_reg *reg = NULL;
571
572 if( entry ) reg = entry->reg_ptr;
573
574 if( !reg ){
575 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
576 global_skateshop.render.item_title = "";
577 global_skateshop.render.item_desc = "";
578 return;
579 }
580
581 if( global_skateshop.render.reg_id != global_skateshop.selected_board_id ){
582 global_skateshop.render.item_title = "";
583 global_skateshop.render.item_desc = "";
584 vg_msg msg;
585 vg_msg_init( &msg, reg->metadata, reg->metadata_len );
586
587 if( vg_msg_seekframe( &msg, "workshop" ) ){
588 const char *title = vg_msg_getkvstr( &msg, "title" );
589 if( title ) global_skateshop.render.item_title = title;
590
591 const char *dsc = vg_msg_getkvstr( &msg, "author" );
592 if( dsc ) global_skateshop.render.item_desc = dsc;
593 vg_msg_skip_frame( &msg );
594 }
595
596 global_skateshop.render.reg_id = global_skateshop.selected_board_id;
597 }
598
599 /* Skin title
600 * ----------------------------------------------------------------- */
601 m3x3_zero( mlocal );
602 m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
603 mlocal[3][0] = -font3d_string_width( 0, global_skateshop.render.item_title );
604 mlocal[3][0] *= scale*0.5f;
605 mlocal[3][1] = 0.1f;
606 mlocal[3][2] = 0.0f;
607 m4x3_mul( mtext, mlocal, mmdl );
608 font3d_simple_draw( 0, global_skateshop.render.item_title,
609 &skaterift.cam, mmdl );
610
611 /* Author name
612 * ----------------------------------------------------------------- */
613 scale *= 0.4f;
614 m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
615 mlocal[3][0] = -font3d_string_width( 0, global_skateshop.render.item_desc );
616 mlocal[3][0] *= scale*0.5f;
617 mlocal[3][1] = 0.0f;
618 mlocal[3][2] = 0.0f;
619 m4x3_mul( mtext, mlocal, mmdl );
620 font3d_simple_draw( 0, global_skateshop.render.item_desc,
621 &skaterift.cam, mmdl );
622
623 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
624 }
625
626 static void skateshop_render_charshop( ent_skateshop *shop ){
627 }
628
629 static void skateshop_render_worldshop( ent_skateshop *shop ){
630 world_instance *world = world_current_instance();
631
632 ent_marker *mark_display = mdl_arritm( &world->ent_marker,
633 mdl_entity_id_id(shop->worlds.id_display)),
634 *mark_info = mdl_arritm( &world->ent_marker,
635 mdl_entity_id_id(shop->boards.id_info));
636
637 if( global_skateshop.render.world_reg != global_skateshop.selected_world_id){
638 global_skateshop.render.world_title = "missing: workshop.title";
639
640 addon_reg *reg = get_addon_from_index( k_addon_type_world,
641 global_skateshop.selected_world_id, ADDON_REG_HIDDEN );
642
643 if( !reg )
644 goto none;
645
646 if( reg->alias.workshop_id ){
647 vg_msg msg;
648 vg_msg_init( &msg, reg->metadata, reg->metadata_len );
649
650 global_skateshop.render.world_loc = vg_msg_getkvstr(&msg,"location");
651 global_skateshop.render.world_reg = global_skateshop.selected_world_id;
652
653 if( vg_msg_seekframe( &msg, "workshop" ) ){
654 global_skateshop.render.world_title = vg_msg_getkvstr(&msg,"title");
655 vg_msg_skip_frame( &msg );
656 }
657 else {
658 vg_warn( "No workshop body\n" );
659 }
660 }
661 else {
662 global_skateshop.render.world_title = reg->alias.foldername;
663 }
664 }
665
666 none:;
667
668 /* Text */
669 char buftext[128], bufsubtext[128];
670 vg_str info, subtext;
671 vg_strnull( &info, buftext, 128 );
672 vg_strnull( &subtext, bufsubtext, 128 );
673
674 u32 valid_count = addon_count(k_addon_type_world,ADDON_REG_HIDDEN);
675 if( valid_count ){
676 vg_strcati32( &info, global_skateshop.selected_world_id+1 );
677 vg_strcatch( &info, '/' );
678 vg_strcati32( &info, valid_count );
679 vg_strcatch( &info, ' ' );
680 vg_strcat( &info, global_skateshop.render.world_title );
681
682 if( !vg_loader_availible() ){
683 vg_strcat( &subtext, "Loading..." );
684 }
685 else{
686 addon_reg *reg = get_addon_from_index( k_addon_type_world,
687 global_skateshop.selected_world_id, ADDON_REG_HIDDEN );
688
689 if( reg->alias.workshop_id )
690 vg_strcat( &subtext, "(Workshop) " );
691
692 vg_strcat( &subtext, global_skateshop.render.world_loc );
693 }
694 }
695 else{
696 vg_strcat( &info, "No workshop worlds installed" );
697 }
698
699 m4x3f mtext,mlocal,mtextmdl;
700 mdl_transform_m4x3( &mark_info->transform, mtext );
701
702 font3d_bind( &gui.font, k_font_shader_default, 0, NULL, &skaterift.cam );
703 shader_model_font_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} );
704
705 float scale = 0.2f, thickness = 0.015f, scale1 = 0.08f;
706 m3x3_zero( mlocal );
707 m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
708 mlocal[3][0] = -font3d_string_width( 0, buftext );
709 mlocal[3][0] *= scale*0.5f;
710 mlocal[3][1] = 0.1f;
711 mlocal[3][2] = 0.0f;
712 m4x3_mul( mtext, mlocal, mtextmdl );
713 font3d_simple_draw( 0, buftext, &skaterift.cam, mtextmdl );
714
715 m3x3_setdiagonalv3( mlocal, (v3f){ scale1, scale1, thickness } );
716 mlocal[3][0] = -font3d_string_width( 0, bufsubtext );
717 mlocal[3][0] *= scale1*0.5f;
718 mlocal[3][1] = -scale1*0.3f;
719 m4x3_mul( mtext, mlocal, mtextmdl );
720 font3d_simple_draw( 0, bufsubtext, &skaterift.cam, mtextmdl );
721 }
722
723 /*
724 * World: render event
725 */
726 void skateshop_render( ent_skateshop *shop )
727 {
728 if( shop->type == k_skateshop_type_boardshop )
729 skateshop_render_boardshop( shop );
730 else if( shop->type == k_skateshop_type_charshop )
731 skateshop_render_charshop( shop );
732 else if( shop->type == k_skateshop_type_worldshop )
733 skateshop_render_worldshop( shop );
734 else if( shop->type == k_skateshop_type_server ){
735 }
736 else
737 vg_fatal_error( "Unknown store (%u)\n", shop->type );
738 }
739
740 void skateshop_render_nonfocused( world_instance *world, vg_camera *cam )
741 {
742 for( u32 j=0; j<mdl_arrcount( &world->ent_skateshop ); j ++ ){
743 ent_skateshop *shop = mdl_arritm(&world->ent_skateshop, j );
744
745 if( shop->type != k_skateshop_type_boardshop ) continue;
746
747 f32 dist2 = v3_dist2( cam->pos, shop->transform.co ),
748 maxdist = 50.0f;
749
750 if( dist2 > maxdist*maxdist ) continue;
751 ent_marker *mark_rack = mdl_arritm( &world->ent_marker,
752 mdl_entity_id_id(shop->boards.id_rack));
753
754 if( !mark_rack )
755 continue;
756
757 u32 slot_count = vg_list_size(global_skateshop.shop_view_slots);
758 for( u32 i=0; i<slot_count; i++ ){
759 struct player_board *board = &localplayer.fallback_board;
760
761 mdl_transform xform;
762 transform_identity( &xform );
763
764 xform.co[0] = -((float)i - ((float)slot_count)*0.5f)*0.45f;
765 mdl_transform_mul( &mark_rack->transform, &xform, &xform );
766
767 struct player_board_pose pose = {0};
768 m4x3f mmdl;
769 mdl_transform_m4x3( &xform, mmdl );
770 render_board( cam, world, board, mmdl, &pose, k_board_shader_entity );
771 }
772 }
773 }
774
775 static void ent_skateshop_helpers_pickable( const char *acceptance ){
776 vg_str text;
777
778 if( gui_new_helper( input_button_list[k_srbind_mback], &text ))
779 vg_strcat( &text, "exit" );
780
781 if( (global_skateshop.helper_pick = gui_new_helper(
782 input_button_list[k_srbind_maccept], &text))){
783 vg_strcat( &text, acceptance );
784 }
785
786 if( (global_skateshop.helper_browse = gui_new_helper(
787 input_axis_list[k_sraxis_mbrowse_h], &text ))){
788 vg_strcat( &text, "browse" );
789 }
790 }
791
792 /*
793 * Entity logic: entrance event
794 */
795 void ent_skateshop_call( world_instance *world, ent_call *call )
796 {
797 u32 index = mdl_entity_id_id( call->id );
798 ent_skateshop *shop = mdl_arritm( &world->ent_skateshop, index );
799 vg_info( "skateshop_call\n" );
800
801 if( skaterift.activity != k_skaterift_default ) return;
802 if( !vg_loader_availible() ) return;
803
804 if( call->function == k_ent_function_trigger ){
805 if( localplayer.subsystem != k_player_subsystem_walk ) return;
806
807 vg_info( "Entering skateshop\n" );
808
809 world_entity_set_focus( call->id );
810 world_entity_focus_modal();
811 gui_helper_clear();
812
813 if( shop->type == k_skateshop_type_boardshop ){
814 skateshop_update_viewpage();
815 skateshop_op_board_scan();
816 ent_skateshop_helpers_pickable( "pick" );
817 }
818 else if( shop->type == k_skateshop_type_charshop ){
819 ent_skateshop_helpers_pickable( "pick" );
820 }
821 else if( shop->type == k_skateshop_type_worldshop ){
822 ent_skateshop_helpers_pickable( "open rift" );
823 skateshop_op_world_scan();
824 }
825 else if( shop->type == k_skateshop_type_server ){
826 vg_str text;
827 global_skateshop.helper_pick = gui_new_helper(
828 input_button_list[k_srbind_maccept], &text);
829 if( gui_new_helper( input_button_list[k_srbind_mback], &text ))
830 vg_strcat( &text, "exit" );
831 skateshop_server_helper_update();
832 }
833 }
834 }