basic npc
[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 { k_ent_npc, &world->ent_npc }
219 };
220
221 for( u32 i=0; i<vg_list_size(indexables); i++ )
222 indexed_count += mdl_arrcount( indexables[i].array );
223 vg_info( "indexing %u entities\n", indexed_count );
224
225 world->entity_list = vg_linear_alloc( world->heap,
226 vg_align8(indexed_count*sizeof(u32)));
227
228 u32 index=0;
229 for( u32 i=0; i<vg_list_size(indexables); i++ ){
230 u32 type = indexables[i].type,
231 count = mdl_arrcount( indexables[i].array );
232
233 for( u32 j=0; j<count; j ++ )
234 world->entity_list[index ++] = mdl_entity_id( type, j );
235 }
236
237 world->entity_bh = bh_create( world->heap, &bh_system_entity_list, world,
238 indexed_count, 2 );
239
240 world->tar_min = world->entity_bh->nodes[0].bbx[0][1];
241 world->tar_max = world->entity_bh->nodes[0].bbx[1][1] + 20.0f;
242
243 for( u32 i=0; i<mdl_arrcount(&world->ent_marker); i++ ){
244 ent_marker *marker = mdl_arritm( &world->ent_marker, i );
245
246 if( MDL_CONST_PSTREQ( &world->meta, marker->pstr_alias, "tar_min" ) )
247 world->tar_min = marker->transform.co[1];
248
249 if( MDL_CONST_PSTREQ( &world->meta, marker->pstr_alias, "tar_max" ) )
250 world->tar_max = marker->transform.co[1];
251 }
252 }
253
254 ent_spawn *world_find_closest_spawn( world_instance *world, v3f position )
255 {
256 ent_spawn *rp = NULL, *r;
257 float min_dist = INFINITY;
258
259 for( u32 i=0; i<mdl_arrcount(&world->ent_spawn); i++ ){
260 r = mdl_arritm( &world->ent_spawn, i );
261 float d = v3_dist2( r->transform.co, position );
262
263 if( d < min_dist ){
264 min_dist = d;
265 rp = r;
266 }
267 }
268
269 if( !rp ){
270 if( mdl_arrcount(&world->ent_spawn) ){
271 vg_warn( "Invalid distances to spawns.. defaulting to first one.\n" );
272 return mdl_arritm( &world->ent_spawn, 0 );
273 }
274 else{
275 vg_error( "There are no spawns in the level!\n" );
276 }
277 }
278
279 return rp;
280 }
281
282 ent_spawn *world_find_spawn_by_name( world_instance *world, const char *name )
283 {
284 ent_spawn *rp = NULL, *r;
285 for( u32 i=0; i<mdl_arrcount(&world->ent_spawn); i++ ){
286 r = mdl_arritm( &world->ent_spawn, i );
287 if( !strcmp( mdl_pstr(&world->meta, r->pstr_name), name ) ){
288 rp = r;
289 break;
290 }
291 }
292
293 if( !rp )
294 vg_warn( "No spawn named '%s'\n", name );
295
296 return rp;
297 }
298
299 void world_default_spawn_pos( world_instance *world, v3f pos )
300 {
301 ent_spawn *rp = world_find_spawn_by_name( world, "start" );
302 if( !rp ) rp = world_find_closest_spawn( world, (v3f){0,0,0} );
303 if( rp )
304 v3_copy( rp->transform.co, pos );
305 else
306 {
307 vg_error( "There are no valid spawns in the world\n" );
308 v3_zero( pos );
309 }
310 }
311
312 void ent_volume_call( world_instance *world, ent_call *call )
313 {
314 u32 index = mdl_entity_id_id( call->id );
315 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
316 if( !volume->target ) return;
317
318 if( call->function == k_ent_function_trigger ){
319 call->id = volume->target;
320
321 if( volume->flags & k_ent_volume_flag_particles ){
322 float *co = alloca( sizeof(float)*3 );
323 co[0] = vg_randf64(&vg.rand)*2.0f-1.0f;
324 co[1] = vg_randf64(&vg.rand)*2.0f-1.0f;
325 co[2] = vg_randf64(&vg.rand)*2.0f-1.0f;
326 m4x3_mulv( volume->to_world, co, co );
327
328 call->function = k_ent_function_particle_spawn;
329 call->data = co;
330 entity_call( world, call );
331 }
332 else{
333 call->function = volume->trigger.event;
334 entity_call( world, call );
335 }
336 }
337 else if( call->function == k_ent_function_trigger_leave ){
338 call->id = volume->target;
339
340 if( volume->flags & k_ent_volume_flag_particles ){
341 vg_warn( "Invalid condition; calling leave on particle volume.\n" );
342 }
343 else{
344 call->function = volume->trigger.event_leave;
345 entity_call( world, call );
346 }
347 }
348 }
349
350 void ent_audio_call( world_instance *world, ent_call *call )
351 {
352 if( world->status == k_world_status_unloading ){
353 vg_warn( "cannot modify audio while unloading world\n" );
354 return;
355 }
356
357 u8 world_id = (world - world_static.instances) + 1;
358 u32 index = mdl_entity_id_id( call->id );
359 ent_audio *audio = mdl_arritm( &world->ent_audio, index );
360
361 v3f sound_co;
362
363 if( call->function == k_ent_function_particle_spawn ){
364 v3_copy( call->data, sound_co );
365 }
366 else if( call->function == k_ent_function_trigger ){
367 v3_copy( audio->transform.co, sound_co );
368 }
369 else
370 return;
371
372 float chance = vg_randf64(&vg.rand)*100.0f,
373 bar = 0.0f;
374
375 for( u32 i=0; i<audio->clip_count; i++ ){
376 ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip,
377 audio->clip_start+i );
378
379 float mod = world->probabilities[ audio->probability_curve ],
380 p = clip->probability * mod;
381
382 bar += p;
383 if( chance < bar ){
384 audio_lock();
385
386 if( audio->behaviour == k_channel_behaviour_unlimited ){
387 audio_oneshot_3d( &clip->_.clip, sound_co,
388 audio->transform.s[0],
389 audio->volume );
390 }
391 else if( audio->behaviour == k_channel_behaviour_discard_if_full ){
392 audio_channel *ch =
393 audio_get_group_idle_channel( audio->group,
394 audio->max_channels );
395
396 if( ch ){
397 audio_channel_init( ch, &clip->_.clip, audio->flags );
398 audio_channel_group( ch, audio->group );
399 audio_channel_world( ch, world_id );
400 audio_channel_set_spacial( ch, sound_co, audio->transform.s[0] );
401 audio_channel_edit_volume( ch, audio->volume, 1 );
402 ch = audio_relinquish_channel( ch );
403 }
404 }
405 else if( audio->behaviour == k_channel_behaviour_crossfade_if_full){
406 audio_channel *ch =
407 audio_get_group_idle_channel( audio->group,
408 audio->max_channels );
409
410 /* group is full */
411 if( !ch ){
412 audio_channel *existing =
413 audio_get_group_first_active_channel( audio->group );
414
415 if( existing ){
416 if( existing->source == &clip->_.clip ){
417 audio_unlock();
418 return;
419 }
420
421 existing->group = 0;
422 existing = audio_channel_fadeout(existing, audio->crossfade);
423 }
424
425 ch = audio_get_first_idle_channel();
426 }
427
428 if( ch ){
429 audio_channel_init( ch, &clip->_.clip, audio->flags );
430 audio_channel_group( ch, audio->group );
431 audio_channel_world( ch, world_id );
432 audio_channel_fadein( ch, audio->crossfade );
433 ch = audio_relinquish_channel( ch );
434 }
435 }
436
437 audio_unlock();
438 return;
439 }
440 }
441 }
442
443
444 void ent_ccmd_call( world_instance *world, ent_call *call )
445 {
446 if( call->function == k_ent_function_trigger ){
447 u32 index = mdl_entity_id_id( call->id );
448 ent_ccmd *ccmd = mdl_arritm( &world->ent_ccmd, index );
449 vg_execute_console_input( mdl_pstr(&world->meta, ccmd->pstr_command),
450 0 );
451 }
452 }
453
454 /*
455 * BVH implementation
456 * ----------------------------------------------------------------------------
457 */
458
459 void entity_bh_expand_bound( void *user, boxf bound, u32 item_index )
460 {
461 world_instance *world = user;
462
463 u32 id = world->entity_list[ item_index ],
464 type = mdl_entity_id_type( id ),
465 index = mdl_entity_id_id( id );
466
467 if( type == k_ent_gate ){
468 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
469 boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
470 { gate->dimensions[0], gate->dimensions[1], 0.1f }};
471
472 m4x3_expand_aabb_aabb( gate->to_world, bound, box );
473 }
474 else if( type == k_ent_objective ){
475 ent_objective *objective = mdl_arritm( &world->ent_objective, index );
476
477 /* TODO: This might be more work than necessary. could maybe just get
478 * away with representing them as points */
479
480 boxf box;
481 box_init_inf( box );
482
483 for( u32 i=0; i<objective->submesh_count; i++ ){
484 mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
485 objective->submesh_start+i );
486 box_concat( box, sm->bbx );
487 }
488
489 m4x3f transform;
490 mdl_transform_m4x3( &objective->transform, transform );
491 m4x3_expand_aabb_aabb( transform, bound, box );
492 }
493 else if( type == k_ent_volume ){
494 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
495 m4x3_expand_aabb_aabb( volume->to_world, bound,
496 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}} );
497 }
498 else if( type == k_ent_challenge ){
499 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
500
501 boxf box = {{-1.2f*0.5f,-0.72f*0.5f,-0.01f*0.5f},
502 { 1.2f*0.5f, 0.72f*0.5f, 0.01f*0.5f}};
503 m4x3f transform;
504 mdl_transform_m4x3( &challenge->transform, transform );
505 m4x3_expand_aabb_aabb( transform, bound, box );
506 }
507 else if( type == k_ent_glider ){
508 ent_glider *glider = mdl_arritm( &world->ent_glider, index );
509 m4x3f transform;
510 mdl_transform_m4x3( &glider->transform, transform );
511 m4x3_expand_aabb_aabb( transform, bound,
512 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}} );
513 }
514 else if( type == k_ent_npc )
515 {
516 ent_npc *npc = mdl_arritm( &world->ent_npc, index );
517 box_addpt( bound, npc->transform.co );
518 }
519 else{
520 vg_fatal_error( "Programming error\n" );
521 }
522 }
523
524 float entity_bh_centroid( void *user, u32 item_index, int axis )
525 {
526 world_instance *world = user;
527
528 u32 id = world->entity_list[ item_index ],
529 type = mdl_entity_id_type( id ),
530 index = mdl_entity_id_id( id );
531
532 if( type == k_ent_gate ){
533 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
534 return gate->to_world[3][axis];
535 }
536 else if( type == k_ent_objective ){
537 ent_objective *objective = mdl_arritm( &world->ent_objective, index );
538 return objective->transform.co[axis];
539 }
540 else if( type == k_ent_volume ){
541 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
542 return volume->transform.co[axis];
543 }
544 else if( type == k_ent_challenge )
545 {
546 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
547 return challenge->transform.co[axis];
548 }
549 else if( type == k_ent_glider )
550 {
551 ent_glider *glider = mdl_arritm( &world->ent_glider, index );
552 return glider->transform.co[axis];
553 }
554 else if( type == k_ent_npc )
555 {
556 ent_npc *npc = mdl_arritm( &world->ent_npc, index );
557 return npc->transform.co[axis];
558 }
559 else
560 {
561 vg_fatal_error( "Programming error\n" );
562 return INFINITY;
563 }
564 }
565
566 void entity_bh_swap( void *user, u32 ia, u32 ib )
567 {
568 world_instance *world = user;
569
570 u32 a = world->entity_list[ ia ],
571 b = world->entity_list[ ib ];
572
573 world->entity_list[ ia ] = b;
574 world->entity_list[ ib ] = a;
575 }
576
577 void entity_bh_debug( void *user, u32 item_index ){
578 world_instance *world = user;
579
580 u32 id = world->entity_list[ item_index ],
581 type = mdl_entity_id_type( id ),
582 index = mdl_entity_id_id( id );
583
584 if( type == k_ent_gate ){
585 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
586 boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
587 { gate->dimensions[0], gate->dimensions[1], 0.1f }};
588 vg_line_boxf_transformed( gate->to_world, box, 0xf000ff00 );
589 }
590 else if( type == k_ent_objective ){
591 ent_objective *objective = mdl_arritm( &world->ent_objective, index );
592 boxf box;
593 box_init_inf( box );
594
595 for( u32 i=0; i<objective->submesh_count; i++ ){
596 mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
597 objective->submesh_start+i );
598 box_concat( box, sm->bbx );
599 }
600
601 m4x3f transform;
602 mdl_transform_m4x3( &objective->transform, transform );
603 vg_line_boxf_transformed( transform, box, 0xf000ff00 );
604 }
605 else if( type == k_ent_volume ){
606 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
607 vg_line_boxf_transformed( volume->to_world,
608 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}},
609 0xf000ff00 );
610 }
611 else if( type == k_ent_challenge ){
612 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
613
614 boxf box = {{-1.2f*0.5f,-0.72f*0.5f,-0.01f*0.5f},
615 { 1.2f*0.5f, 0.72f*0.5f, 0.01f*0.5f}};
616 m4x3f transform;
617 mdl_transform_m4x3( &challenge->transform, transform );
618 vg_line_boxf_transformed( transform, box, 0xf0ff0000 );
619 }
620 else{
621 vg_fatal_error( "Programming error\n" );
622 }
623 }
624
625 void update_ach_models(void)
626 {
627 world_instance *hub = &world_static.instances[k_world_purpose_hub];
628 if( hub->status != k_world_status_loaded ) return;
629
630 for( u32 i=0; i<mdl_arrcount( &hub->ent_prop ); i ++ ){
631 ent_prop *prop = mdl_arritm( &hub->ent_prop, i );
632 if( prop->flags & 0x2 ){
633 if( MDL_CONST_PSTREQ( &hub->meta, prop->pstr_alias, "MARC" ) )
634 if( skaterift.achievements & 0x1 )
635 prop->flags &= ~0x1;
636 if( MDL_CONST_PSTREQ( &hub->meta, prop->pstr_alias, "ALBERT" ) )
637 if( skaterift.achievements & 0x2 )
638 prop->flags &= ~0x1;
639 if( MDL_CONST_PSTREQ( &hub->meta, prop->pstr_alias, "JANET" ) )
640 if( skaterift.achievements & 0x4 )
641 prop->flags &= ~0x1;
642 if( MDL_CONST_PSTREQ( &hub->meta, prop->pstr_alias, "BERNADETTA" ) )
643 if( skaterift.achievements & 0x8 )
644 prop->flags &= ~0x1;
645 }
646 }
647 }
648
649 void entity_bh_closest( void *user, u32 item_index, v3f point, v3f closest )
650 {
651 world_instance *world = user;
652
653 u32 id = world->entity_list[ item_index ],
654 type = mdl_entity_id_type( id ),
655 index = mdl_entity_id_id( id );
656
657 if( type == k_ent_gate ){
658 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
659 v3_copy( gate->to_world[3], closest );
660 }
661 else if( type == k_ent_objective ){
662 ent_objective *challenge = mdl_arritm( &world->ent_objective, index );
663 v3_copy( challenge->transform.co, closest );
664 }
665 else if( type == k_ent_volume ){
666 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
667 v3_copy( volume->to_world[3], closest );
668 }
669 else if( type == k_ent_challenge ){
670 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
671 v3_copy( challenge->transform.co, closest );
672 }
673 else{
674 vg_fatal_error( "Programming error\n" );
675 }
676 }
677
678 void world_entity_start( world_instance *world, vg_msg *sav )
679 {
680 vg_info( "Start instance %p\n", world );
681
682 world->probabilities[ k_probability_curve_constant ] = 1.0f;
683 for( u32 i=0; i<mdl_arrcount(&world->ent_audio); i++ ){
684 ent_audio *audio = mdl_arritm(&world->ent_audio,i);
685 if( audio->flags & AUDIO_FLAG_AUTO_START ){
686 ent_call call;
687 call.data = NULL;
688 call.function = k_ent_function_trigger;
689 call.id = mdl_entity_id( k_ent_audio, i );
690 entity_call( world, &call );
691 }
692 }
693
694 /* read savedata
695 * ----------------------------------------------------------------------- */
696
697 for( u32 i=0; i<mdl_arrcount(&world->ent_challenge); i++ ){
698 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, i );
699 const char *alias = mdl_pstr( &world->meta, challenge->pstr_alias );
700
701 u32 result;
702 vg_msg_getkvintg( sav, alias, k_vg_msg_u32, &result );
703
704 if( result ){
705 ent_call call;
706 call.data = NULL;
707 call.function = 0;
708 call.id = mdl_entity_id( k_ent_challenge, i );
709 entity_call( world, &call );
710 }
711 }
712
713 vg_msg routes_block = *sav;
714 if( vg_msg_seekframe( &routes_block, "routes" ) ){
715 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
716 ent_route *route = mdl_arritm( &world->ent_route, i );
717
718 vg_msg route_info = routes_block;
719 if( vg_msg_seekframe( &route_info,
720 mdl_pstr(&world->meta,route->pstr_name) ) ){
721
722 u32 flags;
723 vg_msg_getkvintg( &route_info, "flags", k_vg_msg_u32, &flags );
724 route->flags |= flags;
725
726 vg_msg_getkvintg( &route_info, "best_laptime", k_vg_msg_f64,
727 &route->best_laptime );
728
729 f32 sections[ route->checkpoints_count ];
730 vg_msg_cmd cmd;
731 if( vg_msg_getkvcmd( &route_info, "sections", &cmd ) ){
732 vg_msg_cast( cmd.value, cmd.code, sections,
733 k_vg_msg_f32 |
734 vg_msg_count_bits(route->checkpoints_count) );
735 }
736 else{
737 for( u32 j=0; j<route->checkpoints_count; j ++ )
738 sections[j] = 0.0f;
739 }
740
741 for( u32 j=0; j<route->checkpoints_count; j ++ ){
742 ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint,
743 route->checkpoints_start + j );
744
745 cp->best_time = sections[j];
746 }
747
748 /* LEGACY: check if steam achievements can give us a medal */
749 if( steam_ready && steam_stats_ready ){
750 for( u32 j=0; j<vg_list_size(track_infos); j ++ ){
751 struct track_info *inf = &track_infos[j];
752 if( !strcmp(inf->name,
753 mdl_pstr(&world->meta,route->pstr_name))){
754
755 steamapi_bool set = 0;
756 if( SteamAPI_ISteamUserStats_GetAchievement(
757 hSteamUserStats, inf->achievement_id, &set ) )
758 {
759 if( set ){
760 route->flags |= k_ent_route_flag_achieve_silver;
761 }
762 }
763 }
764 }
765 }
766 }
767 }
768 }
769
770 ent_region_re_eval( world );
771 }
772
773 void world_entity_serialize( world_instance *world, vg_msg *sav )
774 {
775 for( u32 i=0; i<mdl_arrcount(&world->ent_challenge); i++ ){
776 ent_challenge *challenge = mdl_arritm(&world->ent_challenge,i);
777
778 const char *alias = mdl_pstr(&world->meta,challenge->pstr_alias);
779 vg_msg_wkvnum( sav, alias, k_vg_msg_u32, 1, &challenge->status );
780 }
781
782 if( mdl_arrcount(&world->ent_route) ){
783 vg_msg_frame( sav, "routes" );
784 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
785 ent_route *route = mdl_arritm( &world->ent_route, i );
786
787 vg_msg_frame( sav, mdl_pstr( &world->meta, route->pstr_name ) );
788 {
789 vg_msg_wkvnum( sav, "flags", k_vg_msg_u32, 1, &route->flags );
790 vg_msg_wkvnum( sav, "best_laptime",
791 k_vg_msg_f64, 1, &route->best_laptime );
792
793 f32 sections[ route->checkpoints_count ];
794
795 for( u32 j=0; j<route->checkpoints_count; j ++ ){
796 ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint,
797 route->checkpoints_start + j );
798
799 sections[j] = cp->best_time;
800 }
801
802 vg_msg_wkvnum( sav, "sections", k_vg_msg_f32,
803 route->checkpoints_count, sections );
804 }
805 vg_msg_end_frame( sav );
806 }
807 vg_msg_end_frame( sav );
808 }
809 }