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