1 #include "vg/vg_steam.h"
2 #include "vg/vg_steam_user_stats.h"
6 #include "world_load.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"
18 #include "player_walk.h"
20 bh_system bh_system_entity_list
=
22 .expand_bound
= entity_bh_expand_bound
,
23 .item_centroid
= entity_bh_centroid
,
24 .item_closest
= entity_bh_closest
,
25 .item_swap
= entity_bh_swap
,
26 .item_debug
= entity_bh_debug
,
30 void world_entity_set_focus( u32 entity_id
)
32 if( world_static
.focused_entity
)
34 vg_warn( "Entity %u#%u tried to take focus from %u#%u\n",
35 mdl_entity_id_type( entity_id
),
36 mdl_entity_id_id( entity_id
),
37 mdl_entity_id_type( world_static
.focused_entity
),
38 mdl_entity_id_id( world_static
.focused_entity
) );
42 world_static
.focused_entity
= entity_id
;
45 void world_entity_focus_modal(void)
47 localplayer
.immobile
= 1;
48 menu
.disable_open
= 1;
49 srinput
.state
= k_input_state_resume
;
51 v3_zero( localplayer
.rb
.v
);
52 v3_zero( localplayer
.rb
.w
);
53 player_walk
.move_speed
= 0.0f
;
54 skaterift
.activity
= k_skaterift_ent_focus
;
57 void world_entity_exit_modal(void)
59 if( skaterift
.activity
!= k_skaterift_ent_focus
)
61 vg_warn( "Entity %u#%u tried to exit modal when we weren't in one\n",
62 mdl_entity_id_type( world_static
.focused_entity
),
63 mdl_entity_id_id( world_static
.focused_entity
) );
67 localplayer
.immobile
= 0;
68 menu
.disable_open
= 0;
69 srinput
.state
= k_input_state_resume
;
70 skaterift
.activity
= k_skaterift_default
;
73 void world_entity_clear_focus(void)
75 if( skaterift
.activity
== k_skaterift_ent_focus
)
77 vg_warn( "Entity %u#%u tried to clear focus before exiting modal\n",
78 mdl_entity_id_type( world_static
.focused_entity
),
79 mdl_entity_id_id( world_static
.focused_entity
) );
83 world_static
.focused_entity
= 0;
86 void world_entity_focus_camera( world_instance
*world
, u32 uid
)
88 if( mdl_entity_id_type( uid
) == k_ent_camera
){
89 u32 index
= mdl_entity_id_id( uid
);
90 ent_camera
*cam
= mdl_arritm( &world
->ent_camera
, index
);
92 v3f dir
= {0.0f
,-1.0f
,0.0f
};
93 mdl_transform_vector( &cam
->transform
, dir
, dir
);
94 v3_angles( dir
, world_static
.focus_cam
.angles
);
95 v3_copy( cam
->transform
.co
, world_static
.focus_cam
.pos
);
96 world_static
.focus_cam
.fov
= cam
->fov
;
99 vg_camera_copy( &localplayer
.cam
, &world_static
.focus_cam
);
102 world_static
.focus_cam
.nearz
= localplayer
.cam
.nearz
;
103 world_static
.focus_cam
.farz
= localplayer
.cam
.farz
;
107 /* logic preupdate */
108 void world_entity_focus_preupdate(void)
110 f32 rate
= vg_minf( 1.0f
, vg
.time_frame_delta
* 2.0f
);
112 if( skaterift
.activity
== k_skaterift_ent_focus
)
115 vg_slewf( &world_static
.focus_strength
, active
,
116 vg
.time_frame_delta
* (1.0f
/0.5f
) );
118 u32 type
= mdl_entity_id_type( world_static
.focused_entity
),
119 index
= mdl_entity_id_id( world_static
.focused_entity
);
120 world_instance
*world
= world_current_instance();
123 if( type
== k_ent_skateshop
)
125 ent_skateshop
*skateshop
= mdl_arritm( &world
->ent_skateshop
, index
);
126 ent_skateshop_preupdate( skateshop
, active
);
128 else if( type
== k_ent_challenge
)
130 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, index
);
131 ent_challenge_preupdate( challenge
, active
);
133 else if( type
== k_ent_route
)
135 ent_route
*route
= mdl_arritm( &world
->ent_route
, index
);
136 ent_route_preupdate( route
, active
);
138 else if( type
== k_ent_npc
)
140 ent_npc
*npc
= mdl_arritm( &world
->ent_npc
, index
);
141 ent_npc_preupdate( npc
, active
);
145 /* additional renderings like text etc.. */
146 void world_entity_focus_render(void)
148 world_instance
*world
= world_current_instance();
149 if( skaterift
.activity
!= k_skaterift_ent_focus
){
150 skateshop_render_nonfocused( world
, &skaterift
.cam
);
154 u32 type
= mdl_entity_id_type( world_static
.focused_entity
),
155 index
= mdl_entity_id_id( world_static
.focused_entity
);
157 if( type
== k_ent_skateshop
){
158 ent_skateshop
*skateshop
= mdl_arritm( &world
->ent_skateshop
, index
);
159 skateshop_render( skateshop
);
161 else if( type
== k_ent_challenge
){}
162 else if( type
== k_ent_route
){}
163 else if( type
== k_ent_miniworld
){}
164 else if( type
== k_ent_npc
){}
166 vg_fatal_error( "Programming error\n" );
170 void world_gen_entities_init( world_instance
*world
)
173 for( u32 j
=0; j
<mdl_arrcount(&world
->ent_light
); j
++ ){
174 ent_light
*light
= mdl_arritm( &world
->ent_light
, j
);
177 q_m3x3( light
->transform
.q
, to_world
);
178 v3_copy( light
->transform
.co
, to_world
[3] );
179 m4x3_invert_affine( to_world
, light
->inverse_world
);
181 light
->angle_sin_cos
[0] = sinf( light
->angle
* 0.5f
);
182 light
->angle_sin_cos
[1] = cosf( light
->angle
* 0.5f
);
185 vg_async_call( world_link_gates_async
, world
, 0 );
189 for( u32 j
=0; j
<mdl_arrcount(&world
->ent_water
); j
++ ){
190 ent_water
*water
= mdl_arritm( &world
->ent_water
, j
);
191 if( world
->water
.enabled
){
192 vg_warn( "Multiple water surfaces in level!\n" );
196 world
->water
.enabled
= 1;
197 water_set_surface( world
, water
->transform
.co
[1] );
201 for( u32 j
=0; j
<mdl_arrcount(&world
->ent_volume
); j
++ ){
202 ent_volume
*volume
= mdl_arritm( &world
->ent_volume
, j
);
203 mdl_transform_m4x3( &volume
->transform
, volume
->to_world
);
204 m4x3_invert_full( volume
->to_world
, volume
->to_local
);
208 for( u32 j
=0; j
<mdl_arrcount(&world
->ent_audio
); j
++ ){
209 ent_audio
*audio
= mdl_arritm( &world
->ent_audio
, j
);
211 for( u32 k
=0; k
<audio
->clip_count
; k
++ ){
212 ent_audio_clip
*clip
= mdl_arritm( &world
->ent_audio_clip
,
213 audio
->clip_start
+k
);
215 if( clip
->_
.file
.pack_size
){
216 u32 size
= clip
->_
.file
.pack_size
,
217 offset
= clip
->_
.file
.pack_offset
;
219 /* embedded files are fine to clear the scratch buffer, only
220 * external audio uses it */
222 vg_linear_clear( vg_mem
.scratch
);
223 void *data
= vg_linear_alloc( vg_mem
.scratch
,
224 clip
->_
.file
.pack_size
);
226 mdl_fread_pack_file( &world
->meta
, &clip
->_
.file
, data
);
228 clip
->_
.clip
.path
= NULL
;
229 clip
->_
.clip
.flags
= audio
->flags
;
230 clip
->_
.clip
.data
= data
;
231 clip
->_
.clip
.size
= size
;
234 clip
->_
.clip
.path
= mdl_pstr(&world
->meta
,clip
->_
.file
.pstr_path
);
235 clip
->_
.clip
.flags
= audio
->flags
;
236 clip
->_
.clip
.data
= NULL
;
237 clip
->_
.clip
.size
= 0;
240 audio_clip_load( &clip
->_
.clip
, world
->heap
);
244 /* create generic entity hierachy for those who need it */
245 u32 indexed_count
= 0;
248 mdl_array_ptr
*array
;
251 { k_ent_gate
, &world
->ent_gate
},
252 { k_ent_objective
, &world
->ent_objective
},
253 { k_ent_volume
, &world
->ent_volume
},
254 { k_ent_challenge
, &world
->ent_challenge
},
255 { k_ent_glider
, &world
->ent_glider
},
256 { k_ent_npc
, &world
->ent_npc
}
259 for( u32 i
=0; i
<vg_list_size(indexables
); i
++ )
260 indexed_count
+= mdl_arrcount( indexables
[i
].array
);
261 vg_info( "indexing %u entities\n", indexed_count
);
263 world
->entity_list
= vg_linear_alloc( world
->heap
,
264 vg_align8(indexed_count
*sizeof(u32
)));
267 for( u32 i
=0; i
<vg_list_size(indexables
); i
++ ){
268 u32 type
= indexables
[i
].type
,
269 count
= mdl_arrcount( indexables
[i
].array
);
271 for( u32 j
=0; j
<count
; j
++ )
272 world
->entity_list
[index
++] = mdl_entity_id( type
, j
);
275 world
->entity_bh
= bh_create( world
->heap
, &bh_system_entity_list
, world
,
278 world
->tar_min
= world
->entity_bh
->nodes
[0].bbx
[0][1];
279 world
->tar_max
= world
->entity_bh
->nodes
[0].bbx
[1][1] + 20.0f
;
281 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_marker
); i
++ ){
282 ent_marker
*marker
= mdl_arritm( &world
->ent_marker
, i
);
284 if( MDL_CONST_PSTREQ( &world
->meta
, marker
->pstr_alias
, "tar_min" ) )
285 world
->tar_min
= marker
->transform
.co
[1];
287 if( MDL_CONST_PSTREQ( &world
->meta
, marker
->pstr_alias
, "tar_max" ) )
288 world
->tar_max
= marker
->transform
.co
[1];
292 ent_spawn
*world_find_closest_spawn( world_instance
*world
, v3f position
)
294 ent_spawn
*rp
= NULL
, *r
;
295 float min_dist
= INFINITY
;
297 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_spawn
); i
++ ){
298 r
= mdl_arritm( &world
->ent_spawn
, i
);
299 float d
= v3_dist2( r
->transform
.co
, position
);
308 if( mdl_arrcount(&world
->ent_spawn
) ){
309 vg_warn( "Invalid distances to spawns.. defaulting to first one.\n" );
310 return mdl_arritm( &world
->ent_spawn
, 0 );
313 vg_error( "There are no spawns in the level!\n" );
320 ent_spawn
*world_find_spawn_by_name( world_instance
*world
, const char *name
)
322 ent_spawn
*rp
= NULL
, *r
;
323 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_spawn
); i
++ ){
324 r
= mdl_arritm( &world
->ent_spawn
, i
);
325 if( !strcmp( mdl_pstr(&world
->meta
, r
->pstr_name
), name
) ){
332 vg_warn( "No spawn named '%s'\n", name
);
337 void world_default_spawn_pos( world_instance
*world
, v3f pos
)
339 ent_spawn
*rp
= world_find_spawn_by_name( world
, "start" );
340 if( !rp
) rp
= world_find_closest_spawn( world
, (v3f
){0,0,0} );
342 v3_copy( rp
->transform
.co
, pos
);
345 vg_error( "There are no valid spawns in the world\n" );
350 entity_call_result
ent_volume_call( world_instance
*world
, ent_call
*call
)
352 u32 index
= mdl_entity_id_id( call
->id
);
353 ent_volume
*volume
= mdl_arritm( &world
->ent_volume
, index
);
355 if( !volume
->target
)
356 return k_entity_call_result_OK
;
358 if( call
->function
== k_ent_function_trigger
)
360 call
->id
= volume
->target
;
362 if( volume
->flags
& k_ent_volume_flag_particles
)
364 float *co
= alloca( sizeof(float)*3 );
365 co
[0] = vg_randf64(&vg
.rand
)*2.0f
-1.0f
;
366 co
[1] = vg_randf64(&vg
.rand
)*2.0f
-1.0f
;
367 co
[2] = vg_randf64(&vg
.rand
)*2.0f
-1.0f
;
368 m4x3_mulv( volume
->to_world
, co
, co
);
370 call
->function
= k_ent_function_particle_spawn
;
372 entity_call( world
, call
);
376 call
->function
= volume
->trigger
.event
;
377 entity_call( world
, call
);
380 return k_entity_call_result_OK
;
382 else if( call
->function
== k_ent_function_trigger_leave
)
384 call
->id
= volume
->target
;
386 if( volume
->flags
& k_ent_volume_flag_particles
)
388 vg_warn( "Invalid condition; calling leave on particle volume.\n" );
392 call
->function
= volume
->trigger
.event_leave
;
393 entity_call( world
, call
);
396 return k_entity_call_result_OK
;
399 return k_entity_call_result_unhandled
;
402 entity_call_result
ent_audio_call( world_instance
*world
, ent_call
*call
)
404 if( world
->status
== k_world_status_unloading
)
406 vg_warn( "cannot modify audio while unloading world\n" );
407 return k_entity_call_result_invalid
;
410 u8 world_id
= (world
- world_static
.instances
) + 1;
411 u32 index
= mdl_entity_id_id( call
->id
);
412 ent_audio
*audio
= mdl_arritm( &world
->ent_audio
, index
);
416 if( call
->function
== k_ent_function_particle_spawn
)
418 v3_copy( call
->data
, sound_co
);
420 else if( call
->function
== k_ent_function_trigger
)
422 v3_copy( audio
->transform
.co
, sound_co
);
425 return k_entity_call_result_unhandled
;
427 float chance
= vg_randf64(&vg
.rand
)*100.0f
,
430 for( u32 i
=0; i
<audio
->clip_count
; i
++ ){
431 ent_audio_clip
*clip
= mdl_arritm( &world
->ent_audio_clip
,
432 audio
->clip_start
+i
);
434 float mod
= world
->probabilities
[ audio
->probability_curve
],
435 p
= clip
->probability
* mod
;
442 if( audio
->behaviour
== k_channel_behaviour_unlimited
)
444 audio_oneshot_3d( &clip
->_
.clip
, sound_co
,
445 audio
->transform
.s
[0],
448 else if( audio
->behaviour
== k_channel_behaviour_discard_if_full
)
451 audio_get_group_idle_channel( audio
->group
,
452 audio
->max_channels
);
456 audio_channel_init( ch
, &clip
->_
.clip
, audio
->flags
);
457 audio_channel_group( ch
, audio
->group
);
458 audio_channel_world( ch
, world_id
);
459 audio_channel_set_spacial( ch
, sound_co
, audio
->transform
.s
[0] );
460 audio_channel_edit_volume( ch
, audio
->volume
, 1 );
461 ch
= audio_relinquish_channel( ch
);
464 else if( audio
->behaviour
== k_channel_behaviour_crossfade_if_full
)
467 audio_get_group_idle_channel( audio
->group
,
468 audio
->max_channels
);
472 audio_channel
*existing
=
473 audio_get_group_first_active_channel( audio
->group
);
476 if( existing
->source
== &clip
->_
.clip
){
478 return k_entity_call_result_OK
;
482 existing
= audio_channel_fadeout(existing
, audio
->crossfade
);
485 ch
= audio_get_first_idle_channel();
490 audio_channel_init( ch
, &clip
->_
.clip
, audio
->flags
);
491 audio_channel_group( ch
, audio
->group
);
492 audio_channel_world( ch
, world_id
);
493 audio_channel_fadein( ch
, audio
->crossfade
);
494 ch
= audio_relinquish_channel( ch
);
499 return k_entity_call_result_OK
;
502 return k_entity_call_result_OK
;
506 entity_call_result
ent_ccmd_call( world_instance
*world
, ent_call
*call
)
508 if( call
->function
== k_ent_function_trigger
)
510 u32 index
= mdl_entity_id_id( call
->id
);
511 ent_ccmd
*ccmd
= mdl_arritm( &world
->ent_ccmd
, index
);
512 vg_execute_console_input( mdl_pstr(&world
->meta
, ccmd
->pstr_command
), 0 );
513 return k_entity_call_result_OK
;
516 return k_entity_call_result_unhandled
;
521 * ----------------------------------------------------------------------------
524 void entity_bh_expand_bound( void *user
, boxf bound
, u32 item_index
)
526 world_instance
*world
= user
;
528 u32 id
= world
->entity_list
[ item_index
],
529 type
= mdl_entity_id_type( id
),
530 index
= mdl_entity_id_id( id
);
532 if( type
== k_ent_gate
){
533 ent_gate
*gate
= mdl_arritm( &world
->ent_gate
, index
);
534 boxf box
= {{ -gate
->dimensions
[0], -gate
->dimensions
[1], -0.1f
},
535 { gate
->dimensions
[0], gate
->dimensions
[1], 0.1f
}};
537 m4x3_expand_aabb_aabb( gate
->to_world
, bound
, box
);
539 else if( type
== k_ent_objective
){
540 ent_objective
*objective
= mdl_arritm( &world
->ent_objective
, index
);
542 /* TODO: This might be more work than necessary. could maybe just get
543 * away with representing them as points */
548 for( u32 i
=0; i
<objective
->submesh_count
; i
++ ){
549 mdl_submesh
*sm
= mdl_arritm( &world
->meta
.submeshs
,
550 objective
->submesh_start
+i
);
551 box_concat( box
, sm
->bbx
);
555 mdl_transform_m4x3( &objective
->transform
, transform
);
556 m4x3_expand_aabb_aabb( transform
, bound
, box
);
558 else if( type
== k_ent_volume
){
559 ent_volume
*volume
= mdl_arritm( &world
->ent_volume
, index
);
560 m4x3_expand_aabb_aabb( volume
->to_world
, bound
,
561 (boxf
){{-1.0f
,-1.0f
,-1.0f
},{ 1.0f
, 1.0f
, 1.0f
}} );
563 else if( type
== k_ent_challenge
){
564 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, index
);
566 boxf box
= {{-1.2f
*0.5f
,-0.72f
*0.5f
,-0.01f
*0.5f
},
567 { 1.2f
*0.5f
, 0.72f
*0.5f
, 0.01f
*0.5f
}};
569 mdl_transform_m4x3( &challenge
->transform
, transform
);
570 m4x3_expand_aabb_aabb( transform
, bound
, box
);
572 else if( type
== k_ent_glider
){
573 ent_glider
*glider
= mdl_arritm( &world
->ent_glider
, index
);
575 mdl_transform_m4x3( &glider
->transform
, transform
);
576 m4x3_expand_aabb_aabb( transform
, bound
,
577 (boxf
){{-1.0f
,-1.0f
,-1.0f
},{ 1.0f
, 1.0f
, 1.0f
}} );
579 else if( type
== k_ent_npc
)
581 ent_npc
*npc
= mdl_arritm( &world
->ent_npc
, index
);
582 box_addpt( bound
, npc
->transform
.co
);
585 vg_fatal_error( "Programming error\n" );
589 float entity_bh_centroid( void *user
, u32 item_index
, int axis
)
591 world_instance
*world
= user
;
593 u32 id
= world
->entity_list
[ item_index
],
594 type
= mdl_entity_id_type( id
),
595 index
= mdl_entity_id_id( id
);
597 if( type
== k_ent_gate
){
598 ent_gate
*gate
= mdl_arritm( &world
->ent_gate
, index
);
599 return gate
->to_world
[3][axis
];
601 else if( type
== k_ent_objective
){
602 ent_objective
*objective
= mdl_arritm( &world
->ent_objective
, index
);
603 return objective
->transform
.co
[axis
];
605 else if( type
== k_ent_volume
){
606 ent_volume
*volume
= mdl_arritm( &world
->ent_volume
, index
);
607 return volume
->transform
.co
[axis
];
609 else if( type
== k_ent_challenge
)
611 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, index
);
612 return challenge
->transform
.co
[axis
];
614 else if( type
== k_ent_glider
)
616 ent_glider
*glider
= mdl_arritm( &world
->ent_glider
, index
);
617 return glider
->transform
.co
[axis
];
619 else if( type
== k_ent_npc
)
621 ent_npc
*npc
= mdl_arritm( &world
->ent_npc
, index
);
622 return npc
->transform
.co
[axis
];
626 vg_fatal_error( "Programming error\n" );
631 void entity_bh_swap( void *user
, u32 ia
, u32 ib
)
633 world_instance
*world
= user
;
635 u32 a
= world
->entity_list
[ ia
],
636 b
= world
->entity_list
[ ib
];
638 world
->entity_list
[ ia
] = b
;
639 world
->entity_list
[ ib
] = a
;
642 void entity_bh_debug( void *user
, u32 item_index
){
643 world_instance
*world
= user
;
645 u32 id
= world
->entity_list
[ item_index
],
646 type
= mdl_entity_id_type( id
),
647 index
= mdl_entity_id_id( id
);
649 if( type
== k_ent_gate
){
650 ent_gate
*gate
= mdl_arritm( &world
->ent_gate
, index
);
651 boxf box
= {{ -gate
->dimensions
[0], -gate
->dimensions
[1], -0.1f
},
652 { gate
->dimensions
[0], gate
->dimensions
[1], 0.1f
}};
653 vg_line_boxf_transformed( gate
->to_world
, box
, 0xf000ff00 );
655 else if( type
== k_ent_objective
){
656 ent_objective
*objective
= mdl_arritm( &world
->ent_objective
, index
);
660 for( u32 i
=0; i
<objective
->submesh_count
; i
++ ){
661 mdl_submesh
*sm
= mdl_arritm( &world
->meta
.submeshs
,
662 objective
->submesh_start
+i
);
663 box_concat( box
, sm
->bbx
);
667 mdl_transform_m4x3( &objective
->transform
, transform
);
668 vg_line_boxf_transformed( transform
, box
, 0xf000ff00 );
670 else if( type
== k_ent_volume
){
671 ent_volume
*volume
= mdl_arritm( &world
->ent_volume
, index
);
672 vg_line_boxf_transformed( volume
->to_world
,
673 (boxf
){{-1.0f
,-1.0f
,-1.0f
},{ 1.0f
, 1.0f
, 1.0f
}},
676 else if( type
== k_ent_challenge
){
677 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, index
);
679 boxf box
= {{-1.2f
*0.5f
,-0.72f
*0.5f
,-0.01f
*0.5f
},
680 { 1.2f
*0.5f
, 0.72f
*0.5f
, 0.01f
*0.5f
}};
682 mdl_transform_m4x3( &challenge
->transform
, transform
);
683 vg_line_boxf_transformed( transform
, box
, 0xf0ff0000 );
686 vg_fatal_error( "Programming error\n" );
690 void update_ach_models(void)
692 world_instance
*hub
= &world_static
.instances
[k_world_purpose_hub
];
693 if( hub
->status
!= k_world_status_loaded
) return;
695 for( u32 i
=0; i
<mdl_arrcount( &hub
->ent_prop
); i
++ ){
696 ent_prop
*prop
= mdl_arritm( &hub
->ent_prop
, i
);
697 if( prop
->flags
& 0x2 ){
698 if( MDL_CONST_PSTREQ( &hub
->meta
, prop
->pstr_alias
, "MARC" ) )
699 if( skaterift
.achievements
& 0x1 )
701 if( MDL_CONST_PSTREQ( &hub
->meta
, prop
->pstr_alias
, "ALBERT" ) )
702 if( skaterift
.achievements
& 0x2 )
704 if( MDL_CONST_PSTREQ( &hub
->meta
, prop
->pstr_alias
, "JANET" ) )
705 if( skaterift
.achievements
& 0x4 )
707 if( MDL_CONST_PSTREQ( &hub
->meta
, prop
->pstr_alias
, "BERNADETTA" ) )
708 if( skaterift
.achievements
& 0x8 )
714 void entity_bh_closest( void *user
, u32 item_index
, v3f point
, v3f closest
)
716 world_instance
*world
= user
;
718 u32 id
= world
->entity_list
[ item_index
],
719 type
= mdl_entity_id_type( id
),
720 index
= mdl_entity_id_id( id
);
722 if( type
== k_ent_gate
){
723 ent_gate
*gate
= mdl_arritm( &world
->ent_gate
, index
);
724 v3_copy( gate
->to_world
[3], closest
);
726 else if( type
== k_ent_objective
){
727 ent_objective
*challenge
= mdl_arritm( &world
->ent_objective
, index
);
728 v3_copy( challenge
->transform
.co
, closest
);
730 else if( type
== k_ent_volume
){
731 ent_volume
*volume
= mdl_arritm( &world
->ent_volume
, index
);
732 v3_copy( volume
->to_world
[3], closest
);
734 else if( type
== k_ent_challenge
){
735 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, index
);
736 v3_copy( challenge
->transform
.co
, closest
);
739 vg_fatal_error( "Programming error\n" );
743 void world_entity_start( world_instance
*world
, vg_msg
*sav
)
745 vg_info( "Start instance %p\n", world
);
747 world
->probabilities
[ k_probability_curve_constant
] = 1.0f
;
748 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_audio
); i
++ )
750 ent_audio
*audio
= mdl_arritm(&world
->ent_audio
,i
);
751 if( audio
->flags
& AUDIO_FLAG_AUTO_START
)
755 call
.function
= k_ent_function_trigger
;
756 call
.id
= mdl_entity_id( k_ent_audio
, i
);
757 entity_call( world
, &call
);
762 * ----------------------------------------------------------------------- */
764 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_challenge
); i
++ ){
765 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, i
);
766 const char *alias
= mdl_pstr( &world
->meta
, challenge
->pstr_alias
);
769 vg_msg_getkvintg( sav
, alias
, k_vg_msg_u32
, &result
);
775 call
.id
= mdl_entity_id( k_ent_challenge
, i
);
776 entity_call( world
, &call
);
780 vg_msg routes_block
= *sav
;
781 if( vg_msg_seekframe( &routes_block
, "routes" ) ){
782 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_route
); i
++ ){
783 ent_route
*route
= mdl_arritm( &world
->ent_route
, i
);
785 vg_msg route_info
= routes_block
;
786 if( vg_msg_seekframe( &route_info
,
787 mdl_pstr(&world
->meta
,route
->pstr_name
) ) ){
790 vg_msg_getkvintg( &route_info
, "flags", k_vg_msg_u32
, &flags
);
791 route
->flags
|= flags
;
793 vg_msg_getkvintg( &route_info
, "best_laptime", k_vg_msg_f64
,
794 &route
->best_laptime
);
796 f32 sections
[ route
->checkpoints_count
];
798 if( vg_msg_getkvcmd( &route_info
, "sections", &cmd
) ){
799 vg_msg_cast( cmd
.value
, cmd
.code
, sections
,
801 vg_msg_count_bits(route
->checkpoints_count
) );
804 for( u32 j
=0; j
<route
->checkpoints_count
; j
++ )
808 for( u32 j
=0; j
<route
->checkpoints_count
; j
++ ){
809 ent_checkpoint
*cp
= mdl_arritm( &world
->ent_checkpoint
,
810 route
->checkpoints_start
+ j
);
812 cp
->best_time
= sections
[j
];
815 /* LEGACY: check if steam achievements can give us a medal */
816 if( steam_ready
&& steam_stats_ready
){
817 for( u32 j
=0; j
<vg_list_size(track_infos
); j
++ ){
818 struct track_info
*inf
= &track_infos
[j
];
819 if( !strcmp(inf
->name
,
820 mdl_pstr(&world
->meta
,route
->pstr_name
))){
822 steamapi_bool set
= 0;
823 if( SteamAPI_ISteamUserStats_GetAchievement(
824 hSteamUserStats
, inf
->achievement_id
, &set
) )
827 route
->flags
|= k_ent_route_flag_achieve_silver
;
837 ent_region_re_eval( world
);
840 void world_entity_serialize( world_instance
*world
, vg_msg
*sav
)
842 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_challenge
); i
++ ){
843 ent_challenge
*challenge
= mdl_arritm(&world
->ent_challenge
,i
);
845 const char *alias
= mdl_pstr(&world
->meta
,challenge
->pstr_alias
);
846 vg_msg_wkvnum( sav
, alias
, k_vg_msg_u32
, 1, &challenge
->status
);
849 if( mdl_arrcount(&world
->ent_route
) ){
850 vg_msg_frame( sav
, "routes" );
851 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_route
); i
++ ){
852 ent_route
*route
= mdl_arritm( &world
->ent_route
, i
);
854 vg_msg_frame( sav
, mdl_pstr( &world
->meta
, route
->pstr_name
) );
856 vg_msg_wkvnum( sav
, "flags", k_vg_msg_u32
, 1, &route
->flags
);
857 vg_msg_wkvnum( sav
, "best_laptime",
858 k_vg_msg_f64
, 1, &route
->best_laptime
);
860 f32 sections
[ route
->checkpoints_count
];
862 for( u32 j
=0; j
<route
->checkpoints_count
; j
++ ){
863 ent_checkpoint
*cp
= mdl_arritm( &world
->ent_checkpoint
,
864 route
->checkpoints_start
+ j
);
866 sections
[j
] = cp
->best_time
;
869 vg_msg_wkvnum( sav
, "sections", k_vg_msg_f32
,
870 route
->checkpoints_count
, sections
);
872 vg_msg_end_frame( sav
);
874 vg_msg_end_frame( sav
);