revision 2
[carveJwlIkooP6JGAAIwe30JlM.git] / world_entity.c
1 #include "vg/vg_steam.h"
2 #include "vg/vg_steam_user_stats.h"
3 #include "model.h"
4 #include "entity.h"
5 #include "world.h"
6 #include "world_load.h"
7 #include "save.h"
8 #include "vg/vg_msg.h"
9 #include "menu.h"
10 #include "ent_challenge.h"
11 #include "ent_skateshop.h"
12 #include "ent_route.h"
13 #include "ent_traffic.h"
14 #include "ent_glider.h"
15 #include "ent_region.h"
16 #include "input.h"
17 #include "player_walk.h"
18
19 bh_system bh_system_entity_list =
20 {
21 .expand_bound = entity_bh_expand_bound,
22 .item_centroid = entity_bh_centroid,
23 .item_closest = entity_bh_closest,
24 .item_swap = entity_bh_swap,
25 .item_debug = entity_bh_debug,
26 .cast_ray = NULL
27 };
28
29 void world_entity_focus( u32 entity_id )
30 {
31 localplayer.immobile = 1;
32 menu.disable_open = 1;
33
34 v3_zero( localplayer.rb.v );
35 v3_zero( localplayer.rb.w );
36 player_walk.move_speed = 0.0f;
37 world_static.focused_entity = entity_id;
38 skaterift.activity = k_skaterift_ent_focus;
39 }
40
41 void world_entity_unfocus(void)
42 {
43 localplayer.immobile = 0;
44 skaterift.activity = k_skaterift_default;
45 menu.disable_open = 0;
46 srinput.state = k_input_state_resume;
47 }
48
49 void world_entity_focus_camera( world_instance *world, u32 uid )
50 {
51 if( mdl_entity_id_type( uid ) == k_ent_camera ){
52 u32 index = mdl_entity_id_id( uid );
53 ent_camera *cam = mdl_arritm( &world->ent_camera, index );
54
55 v3f dir = {0.0f,-1.0f,0.0f};
56 mdl_transform_vector( &cam->transform, dir, dir );
57 v3_angles( dir, world_static.focus_cam.angles );
58 v3_copy( cam->transform.co, world_static.focus_cam.pos );
59 world_static.focus_cam.fov = cam->fov;
60 }
61 else {
62 vg_camera_copy( &localplayer.cam, &world_static.focus_cam );
63
64 /* TODO ? */
65 world_static.focus_cam.nearz = localplayer.cam.nearz;
66 world_static.focus_cam.farz = localplayer.cam.farz;
67 }
68 }
69
70 /* logic preupdate */
71 void world_entity_focus_preupdate(void)
72 {
73 f32 rate = vg_minf( 1.0f, vg.time_frame_delta * 2.0f );
74 int active = 0;
75 if( skaterift.activity == k_skaterift_ent_focus )
76 active = 1;
77
78 vg_slewf( &world_static.focus_strength, active,
79 vg.time_frame_delta * (1.0f/0.5f) );
80
81 u32 type = mdl_entity_id_type( world_static.focused_entity ),
82 index = mdl_entity_id_id( world_static.focused_entity );
83 world_instance *world = world_current_instance();
84
85 /* TODO: Table. */
86 if( type == k_ent_skateshop ){
87 ent_skateshop *skateshop = mdl_arritm( &world->ent_skateshop, index );
88 ent_skateshop_preupdate( skateshop, active );
89 }
90 else if( type == k_ent_challenge ){
91 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
92 ent_challenge_preupdate( challenge, active );
93 }
94 else if( type == k_ent_route ){
95 ent_route *route = mdl_arritm( &world->ent_route, index );
96 ent_route_preupdate( route, active );
97 }
98 }
99
100 /* additional renderings like text etc.. */
101 void world_entity_focus_render(void)
102 {
103 world_instance *world = world_current_instance();
104 if( skaterift.activity != k_skaterift_ent_focus ){
105 skateshop_render_nonfocused( world, &skaterift.cam );
106 return;
107 }
108
109 u32 type = mdl_entity_id_type( world_static.focused_entity ),
110 index = mdl_entity_id_id( world_static.focused_entity );
111
112 if( type == k_ent_skateshop ){
113 ent_skateshop *skateshop = mdl_arritm( &world->ent_skateshop, index );
114 skateshop_render( skateshop );
115 }
116 else if( type == k_ent_challenge ){}
117 else if( type == k_ent_route ){}
118 else if( type == k_ent_miniworld ){}
119 else {
120 vg_fatal_error( "Programming error\n" );
121 }
122 }
123
124 void world_gen_entities_init( world_instance *world )
125 {
126 /* lights */
127 for( u32 j=0; j<mdl_arrcount(&world->ent_light); j ++ ){
128 ent_light *light = mdl_arritm( &world->ent_light, j );
129
130 m4x3f to_world;
131 q_m3x3( light->transform.q, to_world );
132 v3_copy( light->transform.co, to_world[3] );
133 m4x3_invert_affine( to_world, light->inverse_world );
134
135 light->angle_sin_cos[0] = sinf( light->angle * 0.5f );
136 light->angle_sin_cos[1] = cosf( light->angle * 0.5f );
137 }
138
139 /* gates */
140 for( u32 j=0; j<mdl_arrcount(&world->ent_gate); j ++ ){
141 ent_gate *gate = mdl_arritm( &world->ent_gate, j );
142
143 if( !(gate->flags & k_ent_gate_nonlocal) ) {
144 gate_transform_update( gate );
145 }
146 }
147
148 vg_async_call( world_link_nonlocal_async, world, 0 );
149
150 /* water */
151 for( u32 j=0; j<mdl_arrcount(&world->ent_water); j++ ){
152 ent_water *water = mdl_arritm( &world->ent_water, j );
153 if( world->water.enabled ){
154 vg_warn( "Multiple water surfaces in level!\n" );
155 break;
156 }
157
158 world->water.enabled = 1;
159 water_set_surface( world, water->transform.co[1] );
160 }
161
162 /* volumes */
163 for( u32 j=0; j<mdl_arrcount(&world->ent_volume); j++ ){
164 ent_volume *volume = mdl_arritm( &world->ent_volume, j );
165 mdl_transform_m4x3( &volume->transform, volume->to_world );
166 m4x3_invert_full( volume->to_world, volume->to_local );
167 }
168
169 /* audio packs */
170 for( u32 j=0; j<mdl_arrcount(&world->ent_audio); j++ ){
171 ent_audio *audio = mdl_arritm( &world->ent_audio, j );
172
173 for( u32 k=0; k<audio->clip_count; k++ ){
174 ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip,
175 audio->clip_start+k );
176
177 if( clip->_.file.pack_size ){
178 u32 size = clip->_.file.pack_size,
179 offset = clip->_.file.pack_offset;
180
181 /* embedded files are fine to clear the scratch buffer, only
182 * external audio uses it */
183
184 vg_linear_clear( vg_mem.scratch );
185 void *data = vg_linear_alloc( vg_mem.scratch,
186 clip->_.file.pack_size );
187
188 mdl_fread_pack_file( &world->meta, &clip->_.file, data );
189
190 clip->_.clip.path = NULL;
191 clip->_.clip.flags = audio->flags;
192 clip->_.clip.data = data;
193 clip->_.clip.size = size;
194 }
195 else{
196 clip->_.clip.path = mdl_pstr(&world->meta,clip->_.file.pstr_path);
197 clip->_.clip.flags = audio->flags;
198 clip->_.clip.data = NULL;
199 clip->_.clip.size = 0;
200 }
201
202 audio_clip_load( &clip->_.clip, world->heap );
203 }
204 }
205
206 /* create generic entity hierachy for those who need it */
207 u32 indexed_count = 0;
208 struct {
209 u32 type;
210 mdl_array_ptr *array;
211 }
212 indexables[] = {
213 { k_ent_gate, &world->ent_gate },
214 { k_ent_objective, &world->ent_objective },
215 { k_ent_volume, &world->ent_volume },
216 { k_ent_challenge, &world->ent_challenge },
217 { k_ent_glider, &world->ent_glider }
218 };
219
220 for( u32 i=0; i<vg_list_size(indexables); i++ )
221 indexed_count += mdl_arrcount( indexables[i].array );
222 vg_info( "indexing %u entities\n", indexed_count );
223
224 world->entity_list = vg_linear_alloc( world->heap,
225 vg_align8(indexed_count*sizeof(u32)));
226
227 u32 index=0;
228 for( u32 i=0; i<vg_list_size(indexables); i++ ){
229 u32 type = indexables[i].type,
230 count = mdl_arrcount( indexables[i].array );
231
232 for( u32 j=0; j<count; j ++ )
233 world->entity_list[index ++] = mdl_entity_id( type, j );
234 }
235
236 world->entity_bh = bh_create( world->heap, &bh_system_entity_list, world,
237 indexed_count, 2 );
238
239 world->tar_min = world->entity_bh->nodes[0].bbx[0][1];
240 world->tar_max = world->entity_bh->nodes[0].bbx[1][1] + 20.0f;
241
242 for( u32 i=0; i<mdl_arrcount(&world->ent_marker); i++ ){
243 ent_marker *marker = mdl_arritm( &world->ent_marker, i );
244
245 if( MDL_CONST_PSTREQ( &world->meta, marker->pstr_alias, "tar_min" ) )
246 world->tar_min = marker->transform.co[1];
247
248 if( MDL_CONST_PSTREQ( &world->meta, marker->pstr_alias, "tar_max" ) )
249 world->tar_max = marker->transform.co[1];
250 }
251 }
252
253 ent_spawn *world_find_closest_spawn( world_instance *world, v3f position )
254 {
255 ent_spawn *rp = NULL, *r;
256 float min_dist = INFINITY;
257
258 for( u32 i=0; i<mdl_arrcount(&world->ent_spawn); i++ ){
259 r = mdl_arritm( &world->ent_spawn, i );
260 float d = v3_dist2( r->transform.co, position );
261
262 if( d < min_dist ){
263 min_dist = d;
264 rp = r;
265 }
266 }
267
268 if( !rp ){
269 if( mdl_arrcount(&world->ent_spawn) ){
270 vg_warn( "Invalid distances to spawns.. defaulting to first one.\n" );
271 return mdl_arritm( &world->ent_spawn, 0 );
272 }
273 else{
274 vg_error( "There are no spawns in the level!\n" );
275 }
276 }
277
278 return rp;
279 }
280
281 ent_spawn *world_find_spawn_by_name( world_instance *world, const char *name )
282 {
283 ent_spawn *rp = NULL, *r;
284 for( u32 i=0; i<mdl_arrcount(&world->ent_spawn); i++ ){
285 r = mdl_arritm( &world->ent_spawn, i );
286 if( !strcmp( mdl_pstr(&world->meta, r->pstr_name), name ) ){
287 rp = r;
288 break;
289 }
290 }
291
292 if( !rp )
293 vg_warn( "No spawn named '%s'\n", name );
294
295 return rp;
296 }
297
298 void world_default_spawn_pos( world_instance *world, v3f pos )
299 {
300 ent_spawn *rp = world_find_spawn_by_name( world, "start" );
301 if( !rp ) rp = world_find_closest_spawn( world, (v3f){0,0,0} );
302 if( rp )
303 v3_copy( rp->transform.co, pos );
304 else
305 {
306 vg_error( "There are no valid spawns in the world\n" );
307 v3_zero( pos );
308 }
309 }
310
311 void ent_volume_call( world_instance *world, ent_call *call )
312 {
313 u32 index = mdl_entity_id_id( call->id );
314 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
315 if( !volume->target ) return;
316
317 if( call->function == k_ent_function_trigger ){
318 call->id = volume->target;
319
320 if( volume->flags & k_ent_volume_flag_particles ){
321 float *co = alloca( sizeof(float)*3 );
322 co[0] = vg_randf64(&vg.rand)*2.0f-1.0f;
323 co[1] = vg_randf64(&vg.rand)*2.0f-1.0f;
324 co[2] = vg_randf64(&vg.rand)*2.0f-1.0f;
325 m4x3_mulv( volume->to_world, co, co );
326
327 call->function = k_ent_function_particle_spawn;
328 call->data = co;
329 entity_call( world, call );
330 }
331 else{
332 call->function = volume->trigger.event;
333 entity_call( world, call );
334 }
335 }
336 else if( call->function == k_ent_function_trigger_leave ){
337 call->id = volume->target;
338
339 if( volume->flags & k_ent_volume_flag_particles ){
340 vg_warn( "Invalid condition; calling leave on particle volume.\n" );
341 }
342 else{
343 call->function = volume->trigger.event_leave;
344 entity_call( world, call );
345 }
346 }
347 }
348
349 void ent_audio_call( world_instance *world, ent_call *call )
350 {
351 if( world->status == k_world_status_unloading ){
352 vg_warn( "cannot modify audio while unloading world\n" );
353 return;
354 }
355
356 u8 world_id = (world - world_static.instances) + 1;
357 u32 index = mdl_entity_id_id( call->id );
358 ent_audio *audio = mdl_arritm( &world->ent_audio, index );
359
360 v3f sound_co;
361
362 if( call->function == k_ent_function_particle_spawn ){
363 v3_copy( call->data, sound_co );
364 }
365 else if( call->function == k_ent_function_trigger ){
366 v3_copy( audio->transform.co, sound_co );
367 }
368 else
369 return;
370
371 float chance = vg_randf64(&vg.rand)*100.0f,
372 bar = 0.0f;
373
374 for( u32 i=0; i<audio->clip_count; i++ ){
375 ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip,
376 audio->clip_start+i );
377
378 float mod = world->probabilities[ audio->probability_curve ],
379 p = clip->probability * mod;
380
381 bar += p;
382 if( chance < bar ){
383 audio_lock();
384
385 if( audio->behaviour == k_channel_behaviour_unlimited ){
386 audio_oneshot_3d( &clip->_.clip, sound_co,
387 audio->transform.s[0],
388 audio->volume );
389 }
390 else if( audio->behaviour == k_channel_behaviour_discard_if_full ){
391 audio_channel *ch =
392 audio_get_group_idle_channel( audio->group,
393 audio->max_channels );
394
395 if( ch ){
396 audio_channel_init( ch, &clip->_.clip, audio->flags );
397 audio_channel_group( ch, audio->group );
398 audio_channel_world( ch, world_id );
399 audio_channel_set_spacial( ch, sound_co, audio->transform.s[0] );
400 audio_channel_edit_volume( ch, audio->volume, 1 );
401 ch = audio_relinquish_channel( ch );
402 }
403 }
404 else if( audio->behaviour == k_channel_behaviour_crossfade_if_full){
405 audio_channel *ch =
406 audio_get_group_idle_channel( audio->group,
407 audio->max_channels );
408
409 /* group is full */
410 if( !ch ){
411 audio_channel *existing =
412 audio_get_group_first_active_channel( audio->group );
413
414 if( existing ){
415 if( existing->source == &clip->_.clip ){
416 audio_unlock();
417 return;
418 }
419
420 existing->group = 0;
421 existing = audio_channel_fadeout(existing, audio->crossfade);
422 }
423
424 ch = audio_get_first_idle_channel();
425 }
426
427 if( ch ){
428 audio_channel_init( ch, &clip->_.clip, audio->flags );
429 audio_channel_group( ch, audio->group );
430 audio_channel_world( ch, world_id );
431 audio_channel_fadein( ch, audio->crossfade );
432 ch = audio_relinquish_channel( ch );
433 }
434 }
435
436 audio_unlock();
437 return;
438 }
439 }
440 }
441
442
443 void ent_ccmd_call( world_instance *world, ent_call *call )
444 {
445 if( call->function == k_ent_function_trigger ){
446 u32 index = mdl_entity_id_id( call->id );
447 ent_ccmd *ccmd = mdl_arritm( &world->ent_ccmd, index );
448 vg_execute_console_input( mdl_pstr(&world->meta, ccmd->pstr_command),
449 0 );
450 }
451 }
452
453 /*
454 * BVH implementation
455 * ----------------------------------------------------------------------------
456 */
457
458 void entity_bh_expand_bound( void *user, boxf bound, u32 item_index )
459 {
460 world_instance *world = user;
461
462 u32 id = world->entity_list[ item_index ],
463 type = mdl_entity_id_type( id ),
464 index = mdl_entity_id_id( id );
465
466 if( type == k_ent_gate ){
467 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
468 boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
469 { gate->dimensions[0], gate->dimensions[1], 0.1f }};
470
471 m4x3_expand_aabb_aabb( gate->to_world, bound, box );
472 }
473 else if( type == k_ent_objective ){
474 ent_objective *objective = mdl_arritm( &world->ent_objective, index );
475
476 /* TODO: This might be more work than necessary. could maybe just get
477 * away with representing them as points */
478
479 boxf box;
480 box_init_inf( box );
481
482 for( u32 i=0; i<objective->submesh_count; i++ ){
483 mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
484 objective->submesh_start+i );
485 box_concat( box, sm->bbx );
486 }
487
488 m4x3f transform;
489 mdl_transform_m4x3( &objective->transform, transform );
490 m4x3_expand_aabb_aabb( transform, bound, box );
491 }
492 else if( type == k_ent_volume ){
493 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
494 m4x3_expand_aabb_aabb( volume->to_world, bound,
495 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}} );
496 }
497 else if( type == k_ent_challenge ){
498 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
499
500 boxf box = {{-1.2f*0.5f,-0.72f*0.5f,-0.01f*0.5f},
501 { 1.2f*0.5f, 0.72f*0.5f, 0.01f*0.5f}};
502 m4x3f transform;
503 mdl_transform_m4x3( &challenge->transform, transform );
504 m4x3_expand_aabb_aabb( transform, bound, box );
505 }
506 else if( type == k_ent_glider ){
507 ent_glider *glider = mdl_arritm( &world->ent_glider, index );
508 m4x3f transform;
509 mdl_transform_m4x3( &glider->transform, transform );
510 m4x3_expand_aabb_aabb( transform, bound,
511 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}} );
512 }
513 else{
514 vg_fatal_error( "Programming error\n" );
515 }
516 }
517
518 float entity_bh_centroid( void *user, u32 item_index, int axis )
519 {
520 world_instance *world = user;
521
522 u32 id = world->entity_list[ item_index ],
523 type = mdl_entity_id_type( id ),
524 index = mdl_entity_id_id( id );
525
526 if( type == k_ent_gate ){
527 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
528 return gate->to_world[3][axis];
529 }
530 else if( type == k_ent_objective ){
531 ent_objective *objective = mdl_arritm( &world->ent_objective, index );
532 return objective->transform.co[axis];
533 }
534 else if( type == k_ent_volume ){
535 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
536 return volume->transform.co[axis];
537 }
538 else if( type == k_ent_challenge ){
539 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
540 return challenge->transform.co[axis];
541 }
542 else if( type == k_ent_glider ){
543 ent_glider *glider = mdl_arritm( &world->ent_glider, index );
544 return glider->transform.co[axis];
545 }
546 else {
547 vg_fatal_error( "Programming error\n" );
548 return INFINITY;
549 }
550 }
551
552 void entity_bh_swap( void *user, u32 ia, u32 ib )
553 {
554 world_instance *world = user;
555
556 u32 a = world->entity_list[ ia ],
557 b = world->entity_list[ ib ];
558
559 world->entity_list[ ia ] = b;
560 world->entity_list[ ib ] = a;
561 }
562
563 void entity_bh_debug( void *user, u32 item_index ){
564 world_instance *world = user;
565
566 u32 id = world->entity_list[ item_index ],
567 type = mdl_entity_id_type( id ),
568 index = mdl_entity_id_id( id );
569
570 if( type == k_ent_gate ){
571 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
572 boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
573 { gate->dimensions[0], gate->dimensions[1], 0.1f }};
574 vg_line_boxf_transformed( gate->to_world, box, 0xf000ff00 );
575 }
576 else if( type == k_ent_objective ){
577 ent_objective *objective = mdl_arritm( &world->ent_objective, index );
578 boxf box;
579 box_init_inf( box );
580
581 for( u32 i=0; i<objective->submesh_count; i++ ){
582 mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
583 objective->submesh_start+i );
584 box_concat( box, sm->bbx );
585 }
586
587 m4x3f transform;
588 mdl_transform_m4x3( &objective->transform, transform );
589 vg_line_boxf_transformed( transform, box, 0xf000ff00 );
590 }
591 else if( type == k_ent_volume ){
592 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
593 vg_line_boxf_transformed( volume->to_world,
594 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}},
595 0xf000ff00 );
596 }
597 else if( type == k_ent_challenge ){
598 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
599
600 boxf box = {{-1.2f*0.5f,-0.72f*0.5f,-0.01f*0.5f},
601 { 1.2f*0.5f, 0.72f*0.5f, 0.01f*0.5f}};
602 m4x3f transform;
603 mdl_transform_m4x3( &challenge->transform, transform );
604 vg_line_boxf_transformed( transform, box, 0xf0ff0000 );
605 }
606 else{
607 vg_fatal_error( "Programming error\n" );
608 }
609 }
610
611 void update_ach_models(void)
612 {
613 world_instance *hub = &world_static.instances[k_world_purpose_hub];
614 if( hub->status != k_world_status_loaded ) return;
615
616 for( u32 i=0; i<mdl_arrcount( &hub->ent_prop ); i ++ ){
617 ent_prop *prop = mdl_arritm( &hub->ent_prop, i );
618 if( prop->flags & 0x2 ){
619 if( MDL_CONST_PSTREQ( &hub->meta, prop->pstr_alias, "MARC" ) )
620 if( skaterift.achievements & 0x1 )
621 prop->flags &= ~0x1;
622 if( MDL_CONST_PSTREQ( &hub->meta, prop->pstr_alias, "ALBERT" ) )
623 if( skaterift.achievements & 0x2 )
624 prop->flags &= ~0x1;
625 if( MDL_CONST_PSTREQ( &hub->meta, prop->pstr_alias, "JANET" ) )
626 if( skaterift.achievements & 0x4 )
627 prop->flags &= ~0x1;
628 if( MDL_CONST_PSTREQ( &hub->meta, prop->pstr_alias, "BERNADETTA" ) )
629 if( skaterift.achievements & 0x8 )
630 prop->flags &= ~0x1;
631 }
632 }
633 }
634
635 void entity_bh_closest( void *user, u32 item_index, v3f point, v3f closest )
636 {
637 world_instance *world = user;
638
639 u32 id = world->entity_list[ item_index ],
640 type = mdl_entity_id_type( id ),
641 index = mdl_entity_id_id( id );
642
643 if( type == k_ent_gate ){
644 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
645 v3_copy( gate->to_world[3], closest );
646 }
647 else if( type == k_ent_objective ){
648 ent_objective *challenge = mdl_arritm( &world->ent_objective, index );
649 v3_copy( challenge->transform.co, closest );
650 }
651 else if( type == k_ent_volume ){
652 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
653 v3_copy( volume->to_world[3], closest );
654 }
655 else if( type == k_ent_challenge ){
656 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
657 v3_copy( challenge->transform.co, closest );
658 }
659 else{
660 vg_fatal_error( "Programming error\n" );
661 }
662 }
663
664 void world_entity_start( world_instance *world, vg_msg *sav )
665 {
666 vg_info( "Start instance %p\n", world );
667
668 world->probabilities[ k_probability_curve_constant ] = 1.0f;
669 for( u32 i=0; i<mdl_arrcount(&world->ent_audio); i++ ){
670 ent_audio *audio = mdl_arritm(&world->ent_audio,i);
671 if( audio->flags & AUDIO_FLAG_AUTO_START ){
672 ent_call call;
673 call.data = NULL;
674 call.function = k_ent_function_trigger;
675 call.id = mdl_entity_id( k_ent_audio, i );
676 entity_call( world, &call );
677 }
678 }
679
680 /* read savedata
681 * ----------------------------------------------------------------------- */
682
683 for( u32 i=0; i<mdl_arrcount(&world->ent_challenge); i++ ){
684 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, i );
685 const char *alias = mdl_pstr( &world->meta, challenge->pstr_alias );
686
687 u32 result;
688 vg_msg_getkvintg( sav, alias, k_vg_msg_u32, &result );
689
690 if( result ){
691 ent_call call;
692 call.data = NULL;
693 call.function = 0;
694 call.id = mdl_entity_id( k_ent_challenge, i );
695 entity_call( world, &call );
696 }
697 }
698
699 vg_msg routes_block = *sav;
700 if( vg_msg_seekframe( &routes_block, "routes" ) ){
701 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
702 ent_route *route = mdl_arritm( &world->ent_route, i );
703
704 vg_msg route_info = routes_block;
705 if( vg_msg_seekframe( &route_info,
706 mdl_pstr(&world->meta,route->pstr_name) ) ){
707
708 u32 flags;
709 vg_msg_getkvintg( &route_info, "flags", k_vg_msg_u32, &flags );
710 route->flags |= flags;
711
712 vg_msg_getkvintg( &route_info, "best_laptime", k_vg_msg_f64,
713 &route->best_laptime );
714
715 f32 sections[ route->checkpoints_count ];
716 vg_msg_cmd cmd;
717 if( vg_msg_getkvcmd( &route_info, "sections", &cmd ) ){
718 vg_msg_cast( cmd.value, cmd.code, sections,
719 k_vg_msg_f32 |
720 vg_msg_count_bits(route->checkpoints_count) );
721 }
722 else{
723 for( u32 j=0; j<route->checkpoints_count; j ++ )
724 sections[j] = 0.0f;
725 }
726
727 for( u32 j=0; j<route->checkpoints_count; j ++ ){
728 ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint,
729 route->checkpoints_start + j );
730
731 cp->best_time = sections[j];
732 }
733
734 /* LEGACY: check if steam achievements can give us a medal */
735 if( steam_ready && steam_stats_ready ){
736 for( u32 j=0; j<vg_list_size(track_infos); j ++ ){
737 struct track_info *inf = &track_infos[j];
738 if( !strcmp(inf->name,
739 mdl_pstr(&world->meta,route->pstr_name))){
740
741 steamapi_bool set = 0;
742 if( SteamAPI_ISteamUserStats_GetAchievement(
743 hSteamUserStats, inf->achievement_id, &set ) )
744 {
745 if( set ){
746 route->flags |= k_ent_route_flag_achieve_silver;
747 }
748 }
749 }
750 }
751 }
752 }
753 }
754 }
755
756 ent_region_re_eval( world );
757 }
758
759 void world_entity_serialize( world_instance *world, vg_msg *sav )
760 {
761 for( u32 i=0; i<mdl_arrcount(&world->ent_challenge); i++ ){
762 ent_challenge *challenge = mdl_arritm(&world->ent_challenge,i);
763
764 const char *alias = mdl_pstr(&world->meta,challenge->pstr_alias);
765 vg_msg_wkvnum( sav, alias, k_vg_msg_u32, 1, &challenge->status );
766 }
767
768 if( mdl_arrcount(&world->ent_route) ){
769 vg_msg_frame( sav, "routes" );
770 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
771 ent_route *route = mdl_arritm( &world->ent_route, i );
772
773 vg_msg_frame( sav, mdl_pstr( &world->meta, route->pstr_name ) );
774 {
775 vg_msg_wkvnum( sav, "flags", k_vg_msg_u32, 1, &route->flags );
776 vg_msg_wkvnum( sav, "best_laptime",
777 k_vg_msg_f64, 1, &route->best_laptime );
778
779 f32 sections[ route->checkpoints_count ];
780
781 for( u32 j=0; j<route->checkpoints_count; j ++ ){
782 ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint,
783 route->checkpoints_start + j );
784
785 sections[j] = cp->best_time;
786 }
787
788 vg_msg_wkvnum( sav, "sections", k_vg_msg_f32,
789 route->checkpoints_count, sections );
790 }
791 vg_msg_end_frame( sav );
792 }
793 vg_msg_end_frame( sav );
794 }
795 }