basic npc
[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_unfocus();
321 gui_helper_clear();
322 skaterift_autosave(1);
323 return;
324 }
325 }
326 else if( shop->type == k_skateshop_type_charshop ){
327 if( !vg_loader_availible() ) return;
328
329 int changed = 0;
330 u32 valid_count = addon_count( k_addon_type_player, ADDON_REG_HIDDEN );
331
332 if( button_down( k_srbind_mleft ) ){
333 if( global_skateshop.selected_player_id > 0 ){
334 global_skateshop.selected_player_id --;
335 }
336 else{
337 global_skateshop.selected_player_id = valid_count-1;
338 }
339
340 changed = 1;
341 }
342
343 if( button_down( k_srbind_mright ) ){
344 if( global_skateshop.selected_player_id+1 < valid_count ){
345 global_skateshop.selected_player_id ++;
346 }
347 else{
348 global_skateshop.selected_player_id = 0;
349 }
350
351 changed = 1;
352 }
353
354 if( changed ){
355 addon_reg *addon = get_addon_from_index(
356 k_addon_type_player, global_skateshop.selected_player_id,
357 ADDON_REG_HIDDEN );
358
359 u32 real_id = get_index_from_addon(
360 k_addon_type_player, addon );
361
362 player__use_model( real_id );
363 }
364
365 if( button_down( k_srbind_maccept ) ){
366 network_send_item( k_netmsg_playeritem_player );
367 world_entity_unfocus();
368 gui_helper_clear();
369 }
370 }
371 else if( shop->type == k_skateshop_type_worldshop ){
372 int browseable = 0,
373 loadable = 0;
374
375 u32 valid_count = addon_count( k_addon_type_world, ADDON_REG_HIDDEN );
376
377 if( valid_count && vg_loader_availible() )
378 browseable = 1;
379
380 if( valid_count && vg_loader_availible() )
381 loadable = 1;
382
383 global_skateshop.helper_browse->greyed = !browseable;
384 global_skateshop.helper_pick->greyed = !loadable;
385
386 addon_reg *selected_world = NULL;
387
388 int change = 0;
389 if( browseable ){
390 if( button_down( k_srbind_mleft ) ){
391 if( global_skateshop.selected_world_id > 0 ){
392 global_skateshop.selected_world_id --;
393 change = 1;
394 }
395 }
396
397 if( button_down( k_srbind_mright ) ){
398 if( global_skateshop.selected_world_id+1 < valid_count ){
399 global_skateshop.selected_world_id ++;
400 change = 1;
401 }
402 }
403
404 selected_world = get_addon_from_index( k_addon_type_world,
405 global_skateshop.selected_world_id, ADDON_REG_HIDDEN );
406
407 if( change || (global_skateshop.reg_preview == NULL) ){
408 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
409 global_skateshop.reg_preview = selected_world;
410 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
411 }
412 }
413
414 if( loadable ){
415 if( button_down( k_srbind_maccept ) ){
416 skaterift_change_world_start( selected_world );
417 }
418 }
419 }
420 else if( shop->type == k_skateshop_type_server ){
421 f64 delta = vg.time_real - network_client.last_intent_change;
422
423 if( (delta > 5.0) && (!skaterift.demo_mode) ){
424 global_skateshop.helper_pick->greyed = 0;
425 if( button_down( k_srbind_maccept ) ){
426 network_client.user_intent = !network_client.user_intent;
427 network_client.last_intent_change = vg.time_real;
428 skateshop_server_helper_update();
429 render_server_status_gui();
430 }
431 }
432 else {
433 global_skateshop.helper_pick->greyed = 1;
434 }
435 }
436 else{
437 vg_fatal_error( "Unknown store (%u)\n", shop->type );
438 }
439
440 if( button_down( k_srbind_mback ) ){
441 if( shop->type == k_skateshop_type_charshop )
442 network_send_item( k_netmsg_playeritem_player );
443
444 world_entity_unfocus();
445 gui_helper_clear();
446 return;
447 }
448 }
449
450 void skateshop_world_preupdate( world_instance *world )
451 {
452 for( u32 i=0; i<mdl_arrcount(&world->ent_skateshop); i++ ){
453 ent_skateshop *shop = mdl_arritm( &world->ent_skateshop, i );
454
455 if( shop->type == k_skateshop_type_server ){
456 f32 a = network_client.user_intent;
457
458 vg_slewf( &network_client.fintent, a, vg.time_frame_delta );
459 a = (vg_smoothstepf( network_client.fintent ) - 0.5f) * (VG_PIf/2.0f);
460
461 ent_prop *lever = mdl_arritm( &world->ent_prop,
462 mdl_entity_id_id(shop->server.id_lever) );
463
464 /* we need parent transforms now? */
465 q_axis_angle( lever->transform.q, (v3f){0,0,1}, a );
466 }
467 }
468 }
469
470 static void skateshop_render_boardshop( ent_skateshop *shop ){
471 world_instance *world = world_current_instance();
472 u32 slot_count = vg_list_size(global_skateshop.shop_view_slots);
473
474 ent_marker *mark_rack = mdl_arritm( &world->ent_marker,
475 mdl_entity_id_id(shop->boards.id_rack)),
476 *mark_display = mdl_arritm( &world->ent_marker,
477 mdl_entity_id_id(shop->boards.id_display));
478
479 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
480 struct addon_cache *cache = &addon_system.cache[k_addon_type_board];
481
482 /* Render loaded boards in the view slots */
483 for( u32 i=0; i<slot_count; i++ ){
484 struct shop_view_slot *slot = &global_skateshop.shop_view_slots[i];
485 float selected = 0.0f;
486
487 if( !slot->cache_id )
488 goto fade_out;
489
490 addon_cache_entry *entry = vg_pool_item( &cache->pool, slot->cache_id );
491
492 if( entry->state != k_addon_cache_state_loaded )
493 goto fade_out;
494
495 struct player_board *board =
496 addon_cache_item( k_addon_type_board, slot->cache_id );
497
498 mdl_transform xform;
499 transform_identity( &xform );
500
501 xform.co[0] = -((float)i - ((float)slot_count)*0.5f)*0.45f;
502 mdl_transform_mul( &mark_rack->transform, &xform, &xform );
503
504
505 if( entry->reg_index == global_skateshop.selected_board_id ){
506 selected = 1.0f;
507 }
508
509 float t = slot->view_blend;
510 v3_lerp( xform.co, mark_display->transform.co, t, xform.co );
511 q_nlerp( xform.q, mark_display->transform.q, t, xform.q );
512 v3_lerp( xform.s, mark_display->transform.s, t, xform.s );
513
514 struct player_board_pose pose = {0};
515 m4x3f mmdl;
516 mdl_transform_m4x3( &xform, mmdl );
517 render_board( &skaterift.cam, world, board, mmdl,
518 &pose, k_board_shader_entity );
519
520 fade_out:;
521 float rate = 5.0f*vg.time_delta;
522 slot->view_blend = vg_lerpf( slot->view_blend, selected, rate );
523 }
524
525 ent_marker *mark_info = mdl_arritm( &world->ent_marker,
526 mdl_entity_id_id(shop->boards.id_info));
527 m4x3f mtext, mrack;
528 mdl_transform_m4x3( &mark_info->transform, mtext );
529 mdl_transform_m4x3( &mark_rack->transform, mrack );
530
531 m4x3f mlocal, mmdl;
532 m4x3_identity( mlocal );
533
534 float scale = 0.2f,
535 thickness = 0.03f;
536
537 font3d_bind( &gui.font, k_font_shader_default, 0, world, &skaterift.cam );
538 shader_model_font_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} );
539
540 /* Selection counter
541 * ------------------------------------------------------------------ */
542 m3x3_zero( mlocal );
543 v3_zero( mlocal[3] );
544 mlocal[0][0] = -scale*2.0f;
545 mlocal[1][2] = -scale*2.0f;
546 mlocal[2][1] = -thickness;
547 mlocal[3][2] = -0.7f;
548 m4x3_mul( mrack, mlocal, mmdl );
549
550 u32 valid_count = addon_count(k_addon_type_board,0);
551 if( valid_count ){
552 char buf[16];
553 vg_str str;
554 vg_strnull( &str, buf, sizeof(buf) );
555 vg_strcati32( &str, global_skateshop.selected_board_id+1 );
556 vg_strcatch( &str, '/' );
557 vg_strcati32( &str, valid_count );
558 font3d_simple_draw( 0, buf, &skaterift.cam, mmdl );
559 }
560 else{
561 font3d_simple_draw( 0, "Nothing installed", &skaterift.cam, mmdl );
562 }
563
564 u16 cache_id = skateshop_selected_cache_id();
565 struct addon_cache_entry *entry = vg_pool_item( &cache->pool, cache_id );
566 addon_reg *reg = NULL;
567
568 if( entry ) reg = entry->reg_ptr;
569
570 if( !reg ){
571 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
572 global_skateshop.render.item_title = "";
573 global_skateshop.render.item_desc = "";
574 return;
575 }
576
577 if( global_skateshop.render.reg_id != global_skateshop.selected_board_id ){
578 global_skateshop.render.item_title = "";
579 global_skateshop.render.item_desc = "";
580 vg_msg msg;
581 vg_msg_init( &msg, reg->metadata, reg->metadata_len );
582
583 if( vg_msg_seekframe( &msg, "workshop" ) ){
584 const char *title = vg_msg_getkvstr( &msg, "title" );
585 if( title ) global_skateshop.render.item_title = title;
586
587 const char *dsc = vg_msg_getkvstr( &msg, "author" );
588 if( dsc ) global_skateshop.render.item_desc = dsc;
589 vg_msg_skip_frame( &msg );
590 }
591
592 global_skateshop.render.reg_id = global_skateshop.selected_board_id;
593 }
594
595 /* Skin title
596 * ----------------------------------------------------------------- */
597 m3x3_zero( mlocal );
598 m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
599 mlocal[3][0] = -font3d_string_width( 0, global_skateshop.render.item_title );
600 mlocal[3][0] *= scale*0.5f;
601 mlocal[3][1] = 0.1f;
602 mlocal[3][2] = 0.0f;
603 m4x3_mul( mtext, mlocal, mmdl );
604 font3d_simple_draw( 0, global_skateshop.render.item_title,
605 &skaterift.cam, mmdl );
606
607 /* Author name
608 * ----------------------------------------------------------------- */
609 scale *= 0.4f;
610 m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
611 mlocal[3][0] = -font3d_string_width( 0, global_skateshop.render.item_desc );
612 mlocal[3][0] *= scale*0.5f;
613 mlocal[3][1] = 0.0f;
614 mlocal[3][2] = 0.0f;
615 m4x3_mul( mtext, mlocal, mmdl );
616 font3d_simple_draw( 0, global_skateshop.render.item_desc,
617 &skaterift.cam, mmdl );
618
619 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
620 }
621
622 static void skateshop_render_charshop( ent_skateshop *shop ){
623 }
624
625 static void skateshop_render_worldshop( ent_skateshop *shop ){
626 world_instance *world = world_current_instance();
627
628 ent_marker *mark_display = mdl_arritm( &world->ent_marker,
629 mdl_entity_id_id(shop->worlds.id_display)),
630 *mark_info = mdl_arritm( &world->ent_marker,
631 mdl_entity_id_id(shop->boards.id_info));
632
633 if( global_skateshop.render.world_reg != global_skateshop.selected_world_id){
634 global_skateshop.render.world_title = "missing: workshop.title";
635
636 addon_reg *reg = get_addon_from_index( k_addon_type_world,
637 global_skateshop.selected_world_id, ADDON_REG_HIDDEN );
638
639 if( !reg )
640 goto none;
641
642 if( reg->alias.workshop_id ){
643 vg_msg msg;
644 vg_msg_init( &msg, reg->metadata, reg->metadata_len );
645
646 global_skateshop.render.world_loc = vg_msg_getkvstr(&msg,"location");
647 global_skateshop.render.world_reg = global_skateshop.selected_world_id;
648
649 if( vg_msg_seekframe( &msg, "workshop" ) ){
650 global_skateshop.render.world_title = vg_msg_getkvstr(&msg,"title");
651 vg_msg_skip_frame( &msg );
652 }
653 else {
654 vg_warn( "No workshop body\n" );
655 }
656 }
657 else {
658 global_skateshop.render.world_title = reg->alias.foldername;
659 }
660 }
661
662 none:;
663
664 /* Text */
665 char buftext[128], bufsubtext[128];
666 vg_str info, subtext;
667 vg_strnull( &info, buftext, 128 );
668 vg_strnull( &subtext, bufsubtext, 128 );
669
670 u32 valid_count = addon_count(k_addon_type_world,ADDON_REG_HIDDEN);
671 if( valid_count ){
672 vg_strcati32( &info, global_skateshop.selected_world_id+1 );
673 vg_strcatch( &info, '/' );
674 vg_strcati32( &info, valid_count );
675 vg_strcatch( &info, ' ' );
676 vg_strcat( &info, global_skateshop.render.world_title );
677
678 if( !vg_loader_availible() ){
679 vg_strcat( &subtext, "Loading..." );
680 }
681 else{
682 addon_reg *reg = get_addon_from_index( k_addon_type_world,
683 global_skateshop.selected_world_id, ADDON_REG_HIDDEN );
684
685 if( reg->alias.workshop_id )
686 vg_strcat( &subtext, "(Workshop) " );
687
688 vg_strcat( &subtext, global_skateshop.render.world_loc );
689 }
690 }
691 else{
692 vg_strcat( &info, "No workshop worlds installed" );
693 }
694
695 m4x3f mtext,mlocal,mtextmdl;
696 mdl_transform_m4x3( &mark_info->transform, mtext );
697
698 font3d_bind( &gui.font, k_font_shader_default, 0, NULL, &skaterift.cam );
699 shader_model_font_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} );
700
701 float scale = 0.2f, thickness = 0.015f, scale1 = 0.08f;
702 m3x3_zero( mlocal );
703 m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
704 mlocal[3][0] = -font3d_string_width( 0, buftext );
705 mlocal[3][0] *= scale*0.5f;
706 mlocal[3][1] = 0.1f;
707 mlocal[3][2] = 0.0f;
708 m4x3_mul( mtext, mlocal, mtextmdl );
709 font3d_simple_draw( 0, buftext, &skaterift.cam, mtextmdl );
710
711 m3x3_setdiagonalv3( mlocal, (v3f){ scale1, scale1, thickness } );
712 mlocal[3][0] = -font3d_string_width( 0, bufsubtext );
713 mlocal[3][0] *= scale1*0.5f;
714 mlocal[3][1] = -scale1*0.3f;
715 m4x3_mul( mtext, mlocal, mtextmdl );
716 font3d_simple_draw( 0, bufsubtext, &skaterift.cam, mtextmdl );
717 }
718
719 /*
720 * World: render event
721 */
722 void skateshop_render( ent_skateshop *shop )
723 {
724 if( shop->type == k_skateshop_type_boardshop )
725 skateshop_render_boardshop( shop );
726 else if( shop->type == k_skateshop_type_charshop )
727 skateshop_render_charshop( shop );
728 else if( shop->type == k_skateshop_type_worldshop )
729 skateshop_render_worldshop( shop );
730 else if( shop->type == k_skateshop_type_server ){
731 }
732 else
733 vg_fatal_error( "Unknown store (%u)\n", shop->type );
734 }
735
736 void skateshop_render_nonfocused( world_instance *world, vg_camera *cam )
737 {
738 for( u32 j=0; j<mdl_arrcount( &world->ent_skateshop ); j ++ ){
739 ent_skateshop *shop = mdl_arritm(&world->ent_skateshop, j );
740
741 if( shop->type != k_skateshop_type_boardshop ) continue;
742
743 f32 dist2 = v3_dist2( cam->pos, shop->transform.co ),
744 maxdist = 50.0f;
745
746 if( dist2 > maxdist*maxdist ) continue;
747 ent_marker *mark_rack = mdl_arritm( &world->ent_marker,
748 mdl_entity_id_id(shop->boards.id_rack));
749
750 if( !mark_rack )
751 continue;
752
753 u32 slot_count = vg_list_size(global_skateshop.shop_view_slots);
754 for( u32 i=0; i<slot_count; i++ ){
755 struct player_board *board = &localplayer.fallback_board;
756
757 mdl_transform xform;
758 transform_identity( &xform );
759
760 xform.co[0] = -((float)i - ((float)slot_count)*0.5f)*0.45f;
761 mdl_transform_mul( &mark_rack->transform, &xform, &xform );
762
763 struct player_board_pose pose = {0};
764 m4x3f mmdl;
765 mdl_transform_m4x3( &xform, mmdl );
766 render_board( cam, world, board, mmdl, &pose, k_board_shader_entity );
767 }
768 }
769 }
770
771 static void ent_skateshop_helpers_pickable( const char *acceptance ){
772 vg_str text;
773
774 if( gui_new_helper( input_button_list[k_srbind_mback], &text ))
775 vg_strcat( &text, "exit" );
776
777 if( (global_skateshop.helper_pick = gui_new_helper(
778 input_button_list[k_srbind_maccept], &text))){
779 vg_strcat( &text, acceptance );
780 }
781
782 if( (global_skateshop.helper_browse = gui_new_helper(
783 input_axis_list[k_sraxis_mbrowse_h], &text ))){
784 vg_strcat( &text, "browse" );
785 }
786 }
787
788 /*
789 * Entity logic: entrance event
790 */
791 void ent_skateshop_call( world_instance *world, ent_call *call )
792 {
793 u32 index = mdl_entity_id_id( call->id );
794 ent_skateshop *shop = mdl_arritm( &world->ent_skateshop, index );
795 vg_info( "skateshop_call\n" );
796
797 if( skaterift.activity != k_skaterift_default ) return;
798 if( !vg_loader_availible() ) return;
799
800 if( call->function == k_ent_function_trigger ){
801 if( localplayer.subsystem != k_player_subsystem_walk ) return;
802
803 vg_info( "Entering skateshop\n" );
804
805 world_entity_focus( call->id );
806 gui_helper_clear();
807
808 if( shop->type == k_skateshop_type_boardshop ){
809 skateshop_update_viewpage();
810 skateshop_op_board_scan();
811 ent_skateshop_helpers_pickable( "pick" );
812 }
813 else if( shop->type == k_skateshop_type_charshop ){
814 ent_skateshop_helpers_pickable( "pick" );
815 }
816 else if( shop->type == k_skateshop_type_worldshop ){
817 ent_skateshop_helpers_pickable( "open rift" );
818 skateshop_op_world_scan();
819 }
820 else if( shop->type == k_skateshop_type_server ){
821 vg_str text;
822 global_skateshop.helper_pick = gui_new_helper(
823 input_button_list[k_srbind_maccept], &text);
824 if( gui_new_helper( input_button_list[k_srbind_mback], &text ))
825 vg_strcat( &text, "exit" );
826 skateshop_server_helper_update();
827 }
828 }
829 }