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