7 #include "world_load.h"
11 #include "ent_challenge.h"
12 #include "ent_skateshop.h"
13 #include "ent_route.h"
14 #include "ent_traffic.h"
15 #include "ent_glider.h"
17 static void world_entity_focus( u32 entity_id
){
18 localplayer
.immobile
= 1;
19 menu
.disable_open
= 1;
21 v3_zero( localplayer
.rb
.v
);
22 v3_zero( localplayer
.rb
.w
);
23 player_walk
.move_speed
= 0.0f
;
24 world_static
.focused_entity
= entity_id
;
25 skaterift
.activity
= k_skaterift_ent_focus
;
28 static void world_entity_unfocus(void){
29 localplayer
.immobile
= 0;
30 skaterift
.activity
= k_skaterift_default
;
31 menu
.disable_open
= 0;
32 srinput
.state
= k_input_state_resume
;
35 static void world_entity_focus_camera( world_instance
*world
, u32 uid
){
36 if( mdl_entity_id_type( uid
) == k_ent_camera
){
37 u32 index
= mdl_entity_id_id( uid
);
38 ent_camera
*cam
= mdl_arritm( &world
->ent_camera
, index
);
40 v3f dir
= {0.0f
,-1.0f
,0.0f
};
41 mdl_transform_vector( &cam
->transform
, dir
, dir
);
42 v3_angles( dir
, world_static
.focus_cam
.angles
);
43 v3_copy( cam
->transform
.co
, world_static
.focus_cam
.pos
);
44 world_static
.focus_cam
.fov
= cam
->fov
;
47 camera_copy( &localplayer
.cam
, &world_static
.focus_cam
);
50 world_static
.focus_cam
.nearz
= localplayer
.cam
.nearz
;
51 world_static
.focus_cam
.farz
= localplayer
.cam
.farz
;
56 static void world_entity_focus_preupdate(void){
57 f32 rate
= vg_minf( 1.0f
, vg
.time_frame_delta
* 2.0f
);
59 if( skaterift
.activity
== k_skaterift_ent_focus
)
62 vg_slewf( &world_static
.focus_strength
, active
,
63 vg
.time_frame_delta
* (1.0f
/0.5f
) );
65 u32 type
= mdl_entity_id_type( world_static
.focused_entity
),
66 index
= mdl_entity_id_id( world_static
.focused_entity
);
67 world_instance
*world
= world_current_instance();
70 if( type
== k_ent_skateshop
){
71 ent_skateshop
*skateshop
= mdl_arritm( &world
->ent_skateshop
, index
);
72 ent_skateshop_preupdate( skateshop
, active
);
74 else if( type
== k_ent_challenge
){
75 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, index
);
76 ent_challenge_preupdate( challenge
, active
);
78 else if( type
== k_ent_route
){
79 ent_route
*route
= mdl_arritm( &world
->ent_route
, index
);
80 ent_route_preupdate( route
, active
);
84 /* additional renderings like text etc.. */
85 static void world_entity_focus_render(void){
86 world_instance
*world
= world_current_instance();
87 if( skaterift
.activity
!= k_skaterift_ent_focus
){
88 skateshop_render_nonfocused( world
, &skaterift
.cam
);
92 u32 type
= mdl_entity_id_type( world_static
.focused_entity
),
93 index
= mdl_entity_id_id( world_static
.focused_entity
);
95 if( type
== k_ent_skateshop
){
96 ent_skateshop
*skateshop
= mdl_arritm( &world
->ent_skateshop
, index
);
97 skateshop_render( skateshop
);
99 else if( type
== k_ent_challenge
){}
100 else if( type
== k_ent_route
){}
101 else if( type
== k_ent_miniworld
){}
103 vg_fatal_error( "Programming error\n" );
107 static void world_gen_entities_init( world_instance
*world
){
109 for( u32 j
=0; j
<mdl_arrcount(&world
->ent_light
); j
++ ){
110 ent_light
*light
= mdl_arritm( &world
->ent_light
, j
);
113 q_m3x3( light
->transform
.q
, to_world
);
114 v3_copy( light
->transform
.co
, to_world
[3] );
115 m4x3_invert_affine( to_world
, light
->inverse_world
);
117 light
->angle_sin_cos
[0] = sinf( light
->angle
* 0.5f
);
118 light
->angle_sin_cos
[1] = cosf( light
->angle
* 0.5f
);
122 for( u32 j
=0; j
<mdl_arrcount(&world
->ent_gate
); j
++ ){
123 ent_gate
*gate
= mdl_arritm( &world
->ent_gate
, j
);
125 if( !(gate
->flags
& k_ent_gate_nonlocal
) ) {
126 gate_transform_update( gate
);
130 vg_async_call( world_link_nonlocal_async
, world
, 0 );
133 for( u32 j
=0; j
<mdl_arrcount(&world
->ent_water
); j
++ ){
134 ent_water
*water
= mdl_arritm( &world
->ent_water
, j
);
135 if( world
->water
.enabled
){
136 vg_warn( "Multiple water surfaces in level!\n" );
140 world
->water
.enabled
= 1;
141 water_set_surface( world
, water
->transform
.co
[1] );
145 for( u32 j
=0; j
<mdl_arrcount(&world
->ent_volume
); j
++ ){
146 ent_volume
*volume
= mdl_arritm( &world
->ent_volume
, j
);
147 mdl_transform_m4x3( &volume
->transform
, volume
->to_world
);
148 m4x3_invert_full( volume
->to_world
, volume
->to_local
);
152 for( u32 j
=0; j
<mdl_arrcount(&world
->ent_audio
); j
++ ){
153 ent_audio
*audio
= mdl_arritm( &world
->ent_audio
, j
);
155 for( u32 k
=0; k
<audio
->clip_count
; k
++ ){
156 ent_audio_clip
*clip
= mdl_arritm( &world
->ent_audio_clip
,
157 audio
->clip_start
+k
);
159 if( clip
->_
.file
.pack_size
){
160 u32 size
= clip
->_
.file
.pack_size
,
161 offset
= clip
->_
.file
.pack_offset
;
163 /* embedded files are fine to clear the scratch buffer, only
164 * external audio uses it */
166 vg_linear_clear( vg_mem
.scratch
);
167 void *data
= vg_linear_alloc( vg_mem
.scratch
,
168 clip
->_
.file
.pack_size
);
170 mdl_fread_pack_file( &world
->meta
, &clip
->_
.file
, data
);
172 clip
->_
.clip
.path
= NULL
;
173 clip
->_
.clip
.flags
= audio
->flags
;
174 clip
->_
.clip
.data
= data
;
175 clip
->_
.clip
.size
= size
;
178 clip
->_
.clip
.path
= mdl_pstr(&world
->meta
,clip
->_
.file
.pstr_path
);
179 clip
->_
.clip
.flags
= audio
->flags
;
180 clip
->_
.clip
.data
= NULL
;
181 clip
->_
.clip
.size
= 0;
184 audio_clip_load( &clip
->_
.clip
, world
->heap
);
188 /* create generic entity hierachy for those who need it */
189 u32 indexed_count
= 0;
192 mdl_array_ptr
*array
;
195 { k_ent_gate
, &world
->ent_gate
},
196 { k_ent_objective
, &world
->ent_objective
},
197 { k_ent_volume
, &world
->ent_volume
},
198 { k_ent_challenge
, &world
->ent_challenge
},
199 { k_ent_glider
, &world
->ent_glider
}
202 for( u32 i
=0; i
<vg_list_size(indexables
); i
++ )
203 indexed_count
+= mdl_arrcount( indexables
[i
].array
);
204 vg_info( "indexing %u entities\n", indexed_count
);
206 world
->entity_list
= vg_linear_alloc( world
->heap
,
207 vg_align8(indexed_count
*sizeof(u32
)));
210 for( u32 i
=0; i
<vg_list_size(indexables
); i
++ ){
211 u32 type
= indexables
[i
].type
,
212 count
= mdl_arrcount( indexables
[i
].array
);
214 for( u32 j
=0; j
<count
; j
++ )
215 world
->entity_list
[index
++] = mdl_entity_id( type
, j
);
218 world
->entity_bh
= bh_create( world
->heap
, &bh_system_entity_list
, world
,
221 world
->tar_min
= world
->entity_bh
->nodes
[0].bbx
[0][1];
222 world
->tar_max
= world
->entity_bh
->nodes
[0].bbx
[1][1] + 20.0f
;
224 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_marker
); i
++ ){
225 ent_marker
*marker
= mdl_arritm( &world
->ent_marker
, i
);
227 if( MDL_CONST_PSTREQ( &world
->meta
, marker
->pstr_alias
, "tar_min" ) )
228 world
->tar_min
= marker
->transform
.co
[1];
230 if( MDL_CONST_PSTREQ( &world
->meta
, marker
->pstr_alias
, "tar_max" ) )
231 world
->tar_max
= marker
->transform
.co
[1];
236 ent_spawn
*world_find_closest_spawn( world_instance
*world
, v3f position
)
238 ent_spawn
*rp
= NULL
, *r
;
239 float min_dist
= INFINITY
;
241 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_spawn
); i
++ ){
242 r
= mdl_arritm( &world
->ent_spawn
, i
);
243 float d
= v3_dist2( r
->transform
.co
, position
);
252 if( mdl_arrcount(&world
->ent_spawn
) ){
253 vg_warn( "Invalid distances to spawns.. defaulting to first one.\n" );
254 return mdl_arritm( &world
->ent_spawn
, 0 );
257 vg_error( "There are no spawns in the level!\n" );
265 ent_spawn
*world_find_spawn_by_name( world_instance
*world
, const char *name
)
267 ent_spawn
*rp
= NULL
, *r
;
268 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_spawn
); i
++ ){
269 r
= mdl_arritm( &world
->ent_spawn
, i
);
270 if( !strcmp( mdl_pstr(&world
->meta
, r
->pstr_name
), name
) ){
277 vg_warn( "No spawn named '%s'\n", name
);
282 static void world_default_spawn_pos( world_instance
*world
, v3f pos
)
284 ent_spawn
*rp
= world_find_spawn_by_name( world
, "start" );
285 if( !rp
) rp
= world_find_closest_spawn( world
, (v3f
){0,0,0} );
287 v3_copy( rp
->transform
.co
, pos
);
290 vg_error( "There are no valid spawns in the world\n" );
295 static void ent_volume_call( world_instance
*world
, ent_call
*call
){
296 u32 index
= mdl_entity_id_id( call
->id
);
297 ent_volume
*volume
= mdl_arritm( &world
->ent_volume
, index
);
298 if( !volume
->target
) return;
300 if( call
->function
== k_ent_function_trigger
){
301 call
->id
= volume
->target
;
303 if( volume
->flags
& k_ent_volume_flag_particles
){
304 float *co
= alloca( sizeof(float)*3 );
305 co
[0] = vg_randf64(&vg
.rand
)*2.0f
-1.0f
;
306 co
[1] = vg_randf64(&vg
.rand
)*2.0f
-1.0f
;
307 co
[2] = vg_randf64(&vg
.rand
)*2.0f
-1.0f
;
308 m4x3_mulv( volume
->to_world
, co
, co
);
310 call
->function
= k_ent_function_particle_spawn
;
312 entity_call( world
, call
);
315 call
->function
= volume
->trigger
.event
;
316 entity_call( world
, call
);
319 else if( call
->function
== k_ent_function_trigger_leave
){
320 call
->id
= volume
->target
;
322 if( volume
->flags
& k_ent_volume_flag_particles
){
326 call
->function
= volume
->trigger
.event_leave
;
327 entity_call( world
, call
);
332 static void ent_audio_call( world_instance
*world
, ent_call
*call
){
333 if( world
->status
== k_world_status_unloading
){
334 vg_warn( "cannot modify audio while unloading world\n" );
338 u8 world_id
= (world
- world_static
.instances
) + 1;
339 u32 index
= mdl_entity_id_id( call
->id
);
340 ent_audio
*audio
= mdl_arritm( &world
->ent_audio
, index
);
344 if( call
->function
== k_ent_function_particle_spawn
){
345 v3_copy( call
->data
, sound_co
);
347 else if( call
->function
== k_ent_function_trigger
){
348 v3_copy( audio
->transform
.co
, sound_co
);
353 float chance
= vg_randf64(&vg
.rand
)*100.0f
,
356 for( u32 i
=0; i
<audio
->clip_count
; i
++ ){
357 ent_audio_clip
*clip
= mdl_arritm( &world
->ent_audio_clip
,
358 audio
->clip_start
+i
);
360 float mod
= world
->probabilities
[ audio
->probability_curve
],
361 p
= clip
->probability
* mod
;
367 if( audio
->behaviour
== k_channel_behaviour_unlimited
){
368 audio_oneshot_3d( &clip
->_
.clip
, sound_co
,
369 audio
->transform
.s
[0],
372 else if( audio
->behaviour
== k_channel_behaviour_discard_if_full
){
374 audio_get_group_idle_channel( audio
->group
,
375 audio
->max_channels
);
378 audio_channel_init( ch
, &clip
->_
.clip
, audio
->flags
);
379 audio_channel_group( ch
, audio
->group
);
380 audio_channel_world( ch
, world_id
);
381 audio_channel_set_spacial( ch
, sound_co
, audio
->transform
.s
[0] );
382 audio_channel_edit_volume( ch
, audio
->volume
, 1 );
383 ch
= audio_relinquish_channel( ch
);
386 else if( audio
->behaviour
== k_channel_behaviour_crossfade_if_full
){
388 audio_get_group_idle_channel( audio
->group
,
389 audio
->max_channels
);
393 audio_channel
*existing
=
394 audio_get_group_first_active_channel( audio
->group
);
397 if( existing
->source
== &clip
->_
.clip
){
403 existing
= audio_channel_fadeout(existing
, audio
->crossfade
);
406 ch
= audio_get_first_idle_channel();
410 audio_channel_init( ch
, &clip
->_
.clip
, audio
->flags
);
411 audio_channel_group( ch
, audio
->group
);
412 audio_channel_world( ch
, world_id
);
413 audio_channel_fadein( ch
, audio
->crossfade
);
414 ch
= audio_relinquish_channel( ch
);
425 static void ent_ccmd_call( world_instance
*world
, ent_call
*call
){
426 if( call
->function
== k_ent_function_trigger
){
427 u32 index
= mdl_entity_id_id( call
->id
);
428 ent_ccmd
*ccmd
= mdl_arritm( &world
->ent_ccmd
, index
);
429 vg_execute_console_input( mdl_pstr(&world
->meta
, ccmd
->pstr_command
),
436 * ----------------------------------------------------------------------------
440 entity_bh_expand_bound( void *user
, boxf bound
, u32 item_index
){
441 world_instance
*world
= user
;
443 u32 id
= world
->entity_list
[ item_index
],
444 type
= mdl_entity_id_type( id
),
445 index
= mdl_entity_id_id( id
);
447 if( type
== k_ent_gate
){
448 ent_gate
*gate
= mdl_arritm( &world
->ent_gate
, index
);
449 boxf box
= {{ -gate
->dimensions
[0], -gate
->dimensions
[1], -0.1f
},
450 { gate
->dimensions
[0], gate
->dimensions
[1], 0.1f
}};
452 m4x3_expand_aabb_aabb( gate
->to_world
, bound
, box
);
454 else if( type
== k_ent_objective
){
455 ent_objective
*objective
= mdl_arritm( &world
->ent_objective
, index
);
457 /* TODO: This might be more work than necessary. could maybe just get
458 * away with representing them as points */
463 for( u32 i
=0; i
<objective
->submesh_count
; i
++ ){
464 mdl_submesh
*sm
= mdl_arritm( &world
->meta
.submeshs
,
465 objective
->submesh_start
+i
);
466 box_concat( box
, sm
->bbx
);
470 mdl_transform_m4x3( &objective
->transform
, transform
);
471 m4x3_expand_aabb_aabb( transform
, bound
, box
);
473 else if( type
== k_ent_volume
){
474 ent_volume
*volume
= mdl_arritm( &world
->ent_volume
, index
);
475 m4x3_expand_aabb_aabb( volume
->to_world
, bound
,
476 (boxf
){{-1.0f
,-1.0f
,-1.0f
},{ 1.0f
, 1.0f
, 1.0f
}} );
478 else if( type
== k_ent_challenge
){
479 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, index
);
481 boxf box
= {{-1.2f
*0.5f
,-0.72f
*0.5f
,-0.01f
*0.5f
},
482 { 1.2f
*0.5f
, 0.72f
*0.5f
, 0.01f
*0.5f
}};
484 mdl_transform_m4x3( &challenge
->transform
, transform
);
485 m4x3_expand_aabb_aabb( transform
, bound
, box
);
487 else if( type
== k_ent_glider
){
488 ent_glider
*glider
= mdl_arritm( &world
->ent_glider
, index
);
490 mdl_transform_m4x3( &glider
->transform
, transform
);
491 m4x3_expand_aabb_aabb( transform
, bound
,
492 (boxf
){{-1.0f
,-1.0f
,-1.0f
},{ 1.0f
, 1.0f
, 1.0f
}} );
495 vg_fatal_error( "Programming error\n" );
499 static float entity_bh_centroid( void *user
, u32 item_index
, int axis
){
500 world_instance
*world
= user
;
502 u32 id
= world
->entity_list
[ item_index
],
503 type
= mdl_entity_id_type( id
),
504 index
= mdl_entity_id_id( id
);
506 if( type
== k_ent_gate
){
507 ent_gate
*gate
= mdl_arritm( &world
->ent_gate
, index
);
508 return gate
->to_world
[3][axis
];
510 else if( type
== k_ent_objective
){
511 ent_objective
*objective
= mdl_arritm( &world
->ent_objective
, index
);
512 return objective
->transform
.co
[axis
];
514 else if( type
== k_ent_volume
){
515 ent_volume
*volume
= mdl_arritm( &world
->ent_volume
, index
);
516 return volume
->transform
.co
[axis
];
518 else if( type
== k_ent_challenge
){
519 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, index
);
520 return challenge
->transform
.co
[axis
];
522 else if( type
== k_ent_glider
){
523 ent_glider
*glider
= mdl_arritm( &world
->ent_glider
, index
);
524 return glider
->transform
.co
[axis
];
527 vg_fatal_error( "Programming error\n" );
532 static void entity_bh_swap( void *user
, u32 ia
, u32 ib
){
533 world_instance
*world
= user
;
535 u32 a
= world
->entity_list
[ ia
],
536 b
= world
->entity_list
[ ib
];
538 world
->entity_list
[ ia
] = b
;
539 world
->entity_list
[ ib
] = a
;
542 static void entity_bh_debug( void *user
, u32 item_index
){
543 world_instance
*world
= user
;
545 u32 id
= world
->entity_list
[ item_index
],
546 type
= mdl_entity_id_type( id
),
547 index
= mdl_entity_id_id( id
);
549 if( type
== k_ent_gate
){
550 ent_gate
*gate
= mdl_arritm( &world
->ent_gate
, index
);
551 boxf box
= {{ -gate
->dimensions
[0], -gate
->dimensions
[1], -0.1f
},
552 { gate
->dimensions
[0], gate
->dimensions
[1], 0.1f
}};
553 vg_line_boxf_transformed( gate
->to_world
, box
, 0xf000ff00 );
555 else if( type
== k_ent_objective
){
556 ent_objective
*objective
= mdl_arritm( &world
->ent_objective
, index
);
560 for( u32 i
=0; i
<objective
->submesh_count
; i
++ ){
561 mdl_submesh
*sm
= mdl_arritm( &world
->meta
.submeshs
,
562 objective
->submesh_start
+i
);
563 box_concat( box
, sm
->bbx
);
567 mdl_transform_m4x3( &objective
->transform
, transform
);
568 vg_line_boxf_transformed( transform
, box
, 0xf000ff00 );
570 else if( type
== k_ent_volume
){
571 ent_volume
*volume
= mdl_arritm( &world
->ent_volume
, index
);
572 vg_line_boxf_transformed( volume
->to_world
,
573 (boxf
){{-1.0f
,-1.0f
,-1.0f
},{ 1.0f
, 1.0f
, 1.0f
}},
576 else if( type
== k_ent_challenge
){
577 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, index
);
579 boxf box
= {{-1.2f
*0.5f
,-0.72f
*0.5f
,-0.01f
*0.5f
},
580 { 1.2f
*0.5f
, 0.72f
*0.5f
, 0.01f
*0.5f
}};
582 mdl_transform_m4x3( &challenge
->transform
, transform
);
583 vg_line_boxf_transformed( transform
, box
, 0xf0ff0000 );
586 vg_fatal_error( "Programming error\n" );
590 static void update_ach_models(void){
591 world_instance
*hub
= &world_static
.instances
[k_world_purpose_hub
];
592 if( hub
->status
!= k_world_status_loaded
) return;
594 for( u32 i
=0; i
<mdl_arrcount( &hub
->ent_prop
); i
++ ){
595 ent_prop
*prop
= mdl_arritm( &hub
->ent_prop
, i
);
596 if( prop
->flags
& 0x2 ){
597 if( MDL_CONST_PSTREQ( &hub
->meta
, prop
->pstr_alias
, "MARC" ) )
598 if( skaterift
.achievements
& 0x1 )
600 if( MDL_CONST_PSTREQ( &hub
->meta
, prop
->pstr_alias
, "ALBERT" ) )
601 if( skaterift
.achievements
& 0x2 )
603 if( MDL_CONST_PSTREQ( &hub
->meta
, prop
->pstr_alias
, "JANET" ) )
604 if( skaterift
.achievements
& 0x4 )
606 if( MDL_CONST_PSTREQ( &hub
->meta
, prop
->pstr_alias
, "BERNADETTA" ) )
607 if( skaterift
.achievements
& 0x8 )
613 static void entity_bh_closest( void *user
, u32 item_index
, v3f point
,
615 world_instance
*world
= user
;
617 u32 id
= world
->entity_list
[ item_index
],
618 type
= mdl_entity_id_type( id
),
619 index
= mdl_entity_id_id( id
);
621 if( type
== k_ent_gate
){
622 ent_gate
*gate
= mdl_arritm( &world
->ent_gate
, index
);
623 v3_copy( gate
->to_world
[3], closest
);
625 else if( type
== k_ent_objective
){
626 ent_objective
*challenge
= mdl_arritm( &world
->ent_objective
, index
);
627 v3_copy( challenge
->transform
.co
, closest
);
629 else if( type
== k_ent_volume
){
630 ent_volume
*volume
= mdl_arritm( &world
->ent_volume
, index
);
631 v3_copy( volume
->to_world
[3], closest
);
633 else if( type
== k_ent_challenge
){
634 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, index
);
635 v3_copy( challenge
->transform
.co
, closest
);
638 vg_fatal_error( "Programming error\n" );
642 static void world_entity_start( world_instance
*world
, vg_msg
*sav
){
643 vg_info( "Start instance %p\n", world
);
645 world
->probabilities
[ k_probability_curve_constant
] = 1.0f
;
646 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_audio
); i
++ ){
647 ent_audio
*audio
= mdl_arritm(&world
->ent_audio
,i
);
648 if( audio
->flags
& AUDIO_FLAG_AUTO_START
){
651 call
.function
= k_ent_function_trigger
;
652 call
.id
= mdl_entity_id( k_ent_audio
, i
);
653 entity_call( world
, &call
);
658 * ----------------------------------------------------------------------- */
660 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_challenge
); i
++ ){
661 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, i
);
662 const char *alias
= mdl_pstr( &world
->meta
, challenge
->pstr_alias
);
664 if( vg_msg_getkvu32( sav
, alias
, 0 ) ){
668 call
.id
= mdl_entity_id( k_ent_challenge
, i
);
669 entity_call( world
, &call
);
673 vg_msg routes_block
= *sav
;
674 if( vg_msg_seekframe( &routes_block
, "routes" ) ){
675 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_route
); i
++ ){
676 ent_route
*route
= mdl_arritm( &world
->ent_route
, i
);
678 vg_msg route_info
= routes_block
;
679 if( vg_msg_seekframe( &route_info
,
680 mdl_pstr(&world
->meta
,route
->pstr_name
) ) ){
681 route
->flags
|= vg_msg_getkvu32( &route_info
, "flags", 0 );
682 route
->best_laptime
=
683 vg_msg_getkvf64( &route_info
, "best_laptime", 0.0 );
685 f32 sections
[ route
->checkpoints_count
];
687 if( vg_msg_getkvcmd( &route_info
, "sections", &cmd
) ){
688 vg_msg_cast( cmd
.value
, cmd
.code
, sections
,
690 vg_msg_count_bits(route
->checkpoints_count
) );
693 for( u32 j
=0; j
<route
->checkpoints_count
; j
++ )
697 for( u32 j
=0; j
<route
->checkpoints_count
; j
++ ){
698 ent_checkpoint
*cp
= mdl_arritm( &world
->ent_checkpoint
,
699 route
->checkpoints_start
+ j
);
701 cp
->best_time
= sections
[j
];
704 /* LEGACY: check if steam achievements can give us a medal */
705 if( steam_ready
&& steam_stats_ready
){
706 for( u32 j
=0; j
<vg_list_size(track_infos
); j
++ ){
707 struct track_info
*inf
= &track_infos
[j
];
708 if( !strcmp(inf
->name
,
709 mdl_pstr(&world
->meta
,route
->pstr_name
))){
711 steamapi_bool set
= 0;
712 if( SteamAPI_ISteamUserStats_GetAchievement(
713 hSteamUserStats
, inf
->achievement_id
, &set
) )
716 route
->flags
|= k_ent_route_flag_achieve_silver
;
726 ent_region_re_eval( world
);
729 static void world_entity_serialize( world_instance
*world
, vg_msg
*sav
){
730 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_challenge
); i
++ ){
731 ent_challenge
*challenge
= mdl_arritm(&world
->ent_challenge
,i
);
733 const char *alias
= mdl_pstr(&world
->meta
,challenge
->pstr_alias
);
734 vg_msg_wkvu32( sav
, alias
, challenge
->status
);
737 if( mdl_arrcount(&world
->ent_route
) ){
738 vg_msg_frame( sav
, "routes" );
739 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_route
); i
++ ){
740 ent_route
*route
= mdl_arritm( &world
->ent_route
, i
);
742 vg_msg_frame( sav
, mdl_pstr( &world
->meta
, route
->pstr_name
) );
744 vg_msg_wkvu32( sav
, "flags", route
->flags
);
745 vg_msg_wkvf64( sav
, "best_laptime", route
->best_laptime
);
747 f32 sections
[ route
->checkpoints_count
];
749 for( u32 j
=0; j
<route
->checkpoints_count
; j
++ ){
750 ent_checkpoint
*cp
= mdl_arritm( &world
->ent_checkpoint
,
751 route
->checkpoints_start
+ j
);
753 sections
[j
] = cp
->best_time
;
756 vg_msg_wkvnum( sav
, "sections", k_vg_msg_f32
,
757 route
->checkpoints_count
, sections
);
759 vg_msg_end_frame( sav
);
761 vg_msg_end_frame( sav
);
765 #endif /* WORLD_ENTITY_C */