deactivate challenge on max dist
[carveJwlIkooP6JGAAIwe30JlM.git] / world_entity.c
1 #ifndef WORLD_ENTITY_C
2 #define WORLD_ENTITY_C
3
4 #include "model.h"
5 #include "entity.h"
6 #include "world.h"
7 #include "world_load.h"
8 #include "save.h"
9 #include "vg/vg_msg.h"
10 #include "menu.h"
11 #include "ent_challenge.h"
12 #include "ent_skateshop.h"
13
14 VG_STATIC void world_entity_focus( u32 entity_id ){
15 localplayer.immobile = 1;
16 menu.disable_open = 1;
17
18 v3_zero( localplayer.rb.v );
19 v3_zero( localplayer.rb.w );
20 localplayer._walk.move_speed = 0.0f;
21 world_static.focused_entity = entity_id;
22 skaterift.activity = k_skaterift_ent_focus;
23 }
24
25 VG_STATIC void world_entity_unfocus(void){
26 localplayer.immobile = 0;
27 skaterift.activity = k_skaterift_default;
28 menu.disable_open = 0;
29 srinput.enabled = 0;
30 }
31
32 /* logic preupdate */
33 VG_STATIC void world_entity_focus_preupdate(void){
34 f32 rate = vg_minf( 1.0f, vg.time_frame_delta * 2.0f );
35 int active = 0;
36 if( skaterift.activity == k_skaterift_ent_focus )
37 active = 1;
38
39 vg_slewf( &world_static.focus_strength, active,
40 vg.time_frame_delta * (1.0f/0.5f) );
41
42 u32 type = mdl_entity_id_type( world_static.focused_entity ),
43 index = mdl_entity_id_id( world_static.focused_entity );
44 world_instance *world = world_current_instance();
45
46 if( type == k_ent_skateshop ){
47 ent_skateshop *skateshop = mdl_arritm( &world->ent_skateshop, index );
48 ent_skateshop_preupdate( skateshop, active );
49 }
50 else if( type == k_ent_challenge ){
51 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
52 ent_challenge_preupdate( challenge, active );
53 }
54 }
55
56 /* additional renderings like text etc.. */
57 VG_STATIC void world_entity_focus_render(void){
58 if( skaterift.activity != k_skaterift_ent_focus )
59 return;
60
61 u32 type = mdl_entity_id_type( world_static.focused_entity ),
62 index = mdl_entity_id_id( world_static.focused_entity );
63 world_instance *world = world_current_instance();
64
65 if( type == k_ent_skateshop ){
66 ent_skateshop *skateshop = mdl_arritm( &world->ent_skateshop, index );
67 skateshop_render( skateshop );
68 }
69 else if( type == k_ent_challenge ){
70
71 }
72 else {
73 vg_fatal_error( "Programming error\n" );
74 }
75 }
76
77 VG_STATIC void world_gen_entities_init( world_instance *world ){
78 /* lights */
79 for( u32 j=0; j<mdl_arrcount(&world->ent_light); j ++ ){
80 ent_light *light = mdl_arritm( &world->ent_light, j );
81
82 m4x3f to_world;
83 q_m3x3( light->transform.q, to_world );
84 v3_copy( light->transform.co, to_world[3] );
85 m4x3_invert_affine( to_world, light->inverse_world );
86
87 light->angle_sin_cos[0] = sinf( light->angle * 0.5f );
88 light->angle_sin_cos[1] = cosf( light->angle * 0.5f );
89 }
90
91 /* gates */
92 for( u32 j=0; j<mdl_arrcount(&world->ent_gate); j ++ ){
93 ent_gate *gate = mdl_arritm( &world->ent_gate, j );
94
95 if( !(gate->flags & k_ent_gate_nonlocal) ) {
96 gate_transform_update( gate );
97 }
98 }
99 vg_async_call( world_link_nonlocal_async, world, 0 );
100
101 /* water */
102 for( u32 j=0; j<mdl_arrcount(&world->ent_water); j++ ){
103 ent_water *water = mdl_arritm( &world->ent_water, j );
104 if( world->water.enabled ){
105 vg_warn( "Multiple water surfaces in level!\n" );
106 break;
107 }
108
109 world->water.enabled = 1;
110 water_set_surface( world, water->transform.co[1] );
111 }
112
113 /* volumes */
114 for( u32 j=0; j<mdl_arrcount(&world->ent_volume); j++ ){
115 ent_volume *volume = mdl_arritm( &world->ent_volume, j );
116 mdl_transform_m4x3( &volume->transform, volume->to_world );
117 m4x3_invert_full( volume->to_world, volume->to_local );
118 }
119
120 /* audio packs */
121 for( u32 j=0; j<mdl_arrcount(&world->ent_audio); j++ ){
122 ent_audio *audio = mdl_arritm( &world->ent_audio, j );
123
124 for( u32 k=0; k<audio->clip_count; k++ ){
125 ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip,
126 audio->clip_start+k );
127
128 if( clip->_.file.pack_size ){
129 u32 size = clip->_.file.pack_size,
130 offset = clip->_.file.pack_offset;
131
132 /* embedded files are fine to clear the scratch buffer, only
133 * external audio uses it */
134
135 vg_linear_clear( vg_mem.scratch );
136 void *data = vg_linear_alloc( vg_mem.scratch,
137 clip->_.file.pack_size );
138
139 mdl_fread_pack_file( &world->meta, &clip->_.file, data );
140
141 clip->_.clip.path = NULL;
142 clip->_.clip.flags = audio->flags;
143 clip->_.clip.data = data;
144 clip->_.clip.size = size;
145 }
146 else{
147 clip->_.clip.path = mdl_pstr(&world->meta,clip->_.file.pstr_path);
148 clip->_.clip.flags = audio->flags;
149 clip->_.clip.data = NULL;
150 clip->_.clip.size = 0;
151 }
152
153 audio_clip_load( &clip->_.clip, world->heap );
154 }
155 }
156
157 /* create generic entity hierachy for those who need it */
158 u32 indexed_count = 0;
159 struct {
160 u32 type;
161 mdl_array_ptr *array;
162 }
163 indexables[] = {
164 { k_ent_gate, &world->ent_gate },
165 { k_ent_objective, &world->ent_objective },
166 { k_ent_volume, &world->ent_volume },
167 { k_ent_challenge, &world->ent_challenge }
168 };
169
170 for( u32 i=0; i<vg_list_size(indexables); i++ )
171 indexed_count += mdl_arrcount( indexables[i].array );
172 vg_info( "indexing %u entities\n", indexed_count );
173
174 world->entity_list = vg_linear_alloc( world->heap,
175 vg_align8(indexed_count*sizeof(u32)));
176
177 u32 index=0;
178 for( u32 i=0; i<vg_list_size(indexables); i++ ){
179 u32 type = indexables[i].type,
180 count = mdl_arrcount( indexables[i].array );
181
182 for( u32 j=0; j<count; j ++ )
183 world->entity_list[index ++] = mdl_entity_id( type, j );
184 }
185
186 world->entity_bh = bh_create( world->heap, &bh_system_entity_list, world,
187 indexed_count, 2 );
188 }
189
190 VG_STATIC
191 ent_spawn *world_find_closest_spawn( world_instance *world, v3f position )
192 {
193 ent_spawn *rp = NULL, *r;
194 float min_dist = INFINITY;
195
196 for( u32 i=0; i<mdl_arrcount(&world->ent_spawn); i++ ){
197 r = mdl_arritm( &world->ent_spawn, i );
198 float d = v3_dist2( r->transform.co, position );
199
200 if( d < min_dist ){
201 min_dist = d;
202 rp = r;
203 }
204 }
205
206 if( !rp ){
207 if( mdl_arrcount(&world->ent_spawn) ){
208 vg_warn( "Invalid distances to spawns.. defaulting to first one.\n" );
209 return mdl_arritm( &world->ent_spawn, 0 );
210 }
211 else{
212 vg_error( "There are no spawns in the level!\n" );
213 }
214 }
215
216 return rp;
217 }
218
219 VG_STATIC
220 ent_spawn *world_find_spawn_by_name( world_instance *world, const char *name )
221 {
222 ent_spawn *rp = NULL, *r;
223 for( u32 i=0; i<mdl_arrcount(&world->ent_spawn); i++ ){
224 r = mdl_arritm( &world->ent_spawn, i );
225 if( !strcmp( mdl_pstr(&world->meta, r->pstr_name), name ) ){
226 rp = r;
227 break;
228 }
229 }
230
231 if( !rp )
232 vg_warn( "No spawn named '%s'\n", name );
233
234 return rp;
235 }
236
237 VG_STATIC void ent_volume_call( world_instance *world, ent_call *call )
238 {
239 u32 index = mdl_entity_id_id( call->id );
240 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
241 if( !volume->target ) return;
242
243 if( call->function == k_ent_function_trigger ){
244 call->id = volume->target;
245
246 if( volume->flags & k_ent_volume_flag_particles ){
247 float *co = alloca( sizeof(float)*3 );
248 co[0] = vg_randf64()*2.0f-1.0f;
249 co[1] = vg_randf64()*2.0f-1.0f;
250 co[2] = vg_randf64()*2.0f-1.0f;
251 m4x3_mulv( volume->to_world, co, co );
252
253 call->function = k_ent_function_particle_spawn;
254 call->data = co;
255 entity_call( world, call );
256 }
257 else{
258 call->function = volume->trigger.event;
259 entity_call( world, call );
260 }
261 }
262 }
263
264 VG_STATIC void ent_audio_call( world_instance *world, ent_call *call ){
265 if( world->status == k_world_status_unloading ){
266 vg_warn( "cannot modify audio while unloading world\n" );
267 return;
268 }
269
270 u8 world_id = (world - world_static.instances) + 1;
271 u32 index = mdl_entity_id_id( call->id );
272 ent_audio *audio = mdl_arritm( &world->ent_audio, index );
273
274 v3f sound_co;
275
276 if( call->function == k_ent_function_particle_spawn ){
277 v3_copy( call->data, sound_co );
278 }
279 else if( call->function == k_ent_function_trigger ){
280 v3_copy( audio->transform.co, sound_co );
281 }
282 else
283 vg_fatal_error( "ent_audio_call (invalid function id)" );
284
285 float chance = vg_randf64()*100.0f,
286 bar = 0.0f;
287
288 for( u32 i=0; i<audio->clip_count; i++ ){
289 ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip,
290 audio->clip_start+i );
291
292 float mod = world->probabilities[ audio->probability_curve ],
293 p = clip->probability * mod;
294
295 bar += p;
296 if( chance < bar ){
297 audio_lock();
298
299 if( audio->behaviour == k_channel_behaviour_unlimited ){
300 audio_oneshot_3d( &clip->_.clip, sound_co,
301 audio->transform.s[0],
302 audio->volume );
303 }
304 else if( audio->behaviour == k_channel_behaviour_discard_if_full ){
305 audio_channel *ch =
306 audio_get_group_idle_channel( audio->group,
307 audio->max_channels );
308
309 if( ch ){
310 audio_channel_init( ch, &clip->_.clip, audio->flags );
311 audio_channel_group( ch, audio->group );
312 audio_channel_world( ch, world_id );
313 audio_channel_set_spacial( ch, sound_co, audio->transform.s[0] );
314 audio_channel_edit_volume( ch, audio->volume, 1 );
315 ch = audio_relinquish_channel( ch );
316 }
317 }
318 else if( audio->behaviour == k_channel_behaviour_crossfade_if_full){
319 audio_channel *ch =
320 audio_get_group_idle_channel( audio->group,
321 audio->max_channels );
322
323 /* group is full */
324 if( !ch ){
325 audio_channel *existing =
326 audio_get_group_first_active_channel( audio->group );
327
328 if( existing ){
329 if( existing->source == &clip->_.clip ){
330 audio_unlock();
331 return;
332 }
333
334 existing->group = 0;
335 existing = audio_channel_fadeout(existing, audio->crossfade);
336 }
337
338 ch = audio_get_first_idle_channel();
339 }
340
341 if( ch ){
342 audio_channel_init( ch, &clip->_.clip, audio->flags );
343 audio_channel_group( ch, audio->group );
344 audio_channel_world( ch, world_id );
345 audio_channel_fadein( ch, audio->crossfade );
346 ch = audio_relinquish_channel( ch );
347 }
348 }
349
350 audio_unlock();
351 return;
352 }
353 }
354 }
355
356
357 VG_STATIC void ent_ccmd_call( world_instance *world, ent_call *call ){
358 if( call->function == k_ent_function_trigger ){
359 u32 index = mdl_entity_id_id( call->id );
360 ent_ccmd *ccmd = mdl_arritm( &world->ent_ccmd, index );
361 vg_execute_console_input( mdl_pstr(&world->meta, ccmd->pstr_command) );
362 }
363 }
364
365 /*
366 * BVH implementation
367 * ----------------------------------------------------------------------------
368 */
369
370 VG_STATIC void
371 entity_bh_expand_bound( void *user, boxf bound, u32 item_index ){
372 world_instance *world = user;
373
374 u32 id = world->entity_list[ item_index ],
375 type = mdl_entity_id_type( id ),
376 index = mdl_entity_id_id( id );
377
378 if( type == k_ent_gate ){
379 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
380 boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
381 { gate->dimensions[0], gate->dimensions[1], 0.1f }};
382
383 m4x3_expand_aabb_aabb( gate->to_world, bound, box );
384 }
385 else if( type == k_ent_objective ){
386 ent_objective *objective = mdl_arritm( &world->ent_objective, index );
387
388 /* TODO: This might be more work than necessary. could maybe just get
389 * away with representing them as points */
390
391 boxf box;
392 box_init_inf( box );
393
394 for( u32 i=0; i<objective->submesh_count; i++ ){
395 mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
396 objective->submesh_start+i );
397 box_concat( box, sm->bbx );
398 }
399
400 m4x3f transform;
401 mdl_transform_m4x3( &objective->transform, transform );
402 m4x3_expand_aabb_aabb( transform, bound, box );
403 }
404 else if( type == k_ent_volume ){
405 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
406 m4x3_expand_aabb_aabb( volume->to_world, bound,
407 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}} );
408 }
409 else if( type == k_ent_challenge ){
410 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
411
412 boxf box = {{-1.2f*0.5f,-0.72f*0.5f,-0.01f*0.5f},
413 { 1.2f*0.5f, 0.72f*0.5f, 0.01f*0.5f}};
414 m4x3f transform;
415 mdl_transform_m4x3( &challenge->transform, transform );
416 m4x3_expand_aabb_aabb( transform, bound, box );
417 }
418 else{
419 vg_fatal_error( "Programming error\n" );
420 }
421 }
422
423 VG_STATIC float entity_bh_centroid( void *user, u32 item_index, int axis ){
424 world_instance *world = user;
425
426 u32 id = world->entity_list[ item_index ],
427 type = mdl_entity_id_type( id ),
428 index = mdl_entity_id_id( id );
429
430 if( type == k_ent_gate ){
431 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
432 return gate->to_world[3][axis];
433 }
434 else if( type == k_ent_objective ){
435 ent_objective *objective = mdl_arritm( &world->ent_objective, index );
436 return objective->transform.co[axis];
437 }
438 else if( type == k_ent_volume ){
439 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
440 return volume->transform.co[axis];
441 }
442 else if( type == k_ent_challenge ){
443 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
444 return challenge->transform.co[axis];
445 }
446 else {
447 vg_fatal_error( "Programming error\n" );
448 return INFINITY;
449 }
450 }
451
452 VG_STATIC void entity_bh_swap( void *user, u32 ia, u32 ib ){
453 world_instance *world = user;
454
455 u32 a = world->entity_list[ ia ],
456 b = world->entity_list[ ib ];
457
458 world->entity_list[ ia ] = b;
459 world->entity_list[ ib ] = a;
460 }
461
462 VG_STATIC void entity_bh_debug( void *user, u32 item_index ){
463 world_instance *world = user;
464
465 u32 id = world->entity_list[ item_index ],
466 type = mdl_entity_id_type( id ),
467 index = mdl_entity_id_id( id );
468
469 if( type == k_ent_gate ){
470 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
471 boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
472 { gate->dimensions[0], gate->dimensions[1], 0.1f }};
473 vg_line_boxf_transformed( gate->to_world, box, 0xf000ff00 );
474 }
475 else if( type == k_ent_objective ){
476 ent_objective *objective = mdl_arritm( &world->ent_objective, index );
477 boxf box;
478 box_init_inf( box );
479
480 for( u32 i=0; i<objective->submesh_count; i++ ){
481 mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
482 objective->submesh_start+i );
483 box_concat( box, sm->bbx );
484 }
485
486 m4x3f transform;
487 mdl_transform_m4x3( &objective->transform, transform );
488 vg_line_boxf_transformed( transform, box, 0xf000ff00 );
489 }
490 else if( type == k_ent_volume ){
491 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
492 vg_line_boxf_transformed( volume->to_world,
493 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}},
494 0xf000ff00 );
495 }
496 else if( type == k_ent_challenge ){
497 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
498
499 boxf box = {{-1.2f*0.5f,-0.72f*0.5f,-0.01f*0.5f},
500 { 1.2f*0.5f, 0.72f*0.5f, 0.01f*0.5f}};
501 m4x3f transform;
502 mdl_transform_m4x3( &challenge->transform, transform );
503 vg_line_boxf_transformed( transform, box, 0xf0ff0000 );
504 }
505 else{
506 vg_fatal_error( "Programming error\n" );
507 }
508 }
509
510 VG_STATIC void entity_bh_closest( void *user, u32 item_index, v3f point,
511 v3f closest ){
512 world_instance *world = user;
513
514 u32 id = world->entity_list[ item_index ],
515 type = mdl_entity_id_type( id ),
516 index = mdl_entity_id_id( id );
517
518 if( type == k_ent_gate ){
519 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
520 v3_copy( gate->to_world[3], closest );
521 }
522 else if( type == k_ent_objective ){
523 ent_objective *challenge = mdl_arritm( &world->ent_objective, index );
524 v3_copy( challenge->transform.co, closest );
525 }
526 else if( type == k_ent_volume ){
527 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
528 v3_copy( volume->to_world[3], closest );
529 }
530 else if( type == k_ent_challenge ){
531 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
532 v3_copy( challenge->transform.co, closest );
533 }
534 else{
535 vg_fatal_error( "Programming error\n" );
536 }
537 }
538
539 VG_STATIC void world_entity_start( world_instance *world, vg_msg *sav ){
540 vg_info( "Start instance %p\n", world );
541
542 world->probabilities[ k_probability_curve_constant ] = 1.0f;
543 for( u32 i=0; i<mdl_arrcount(&world->ent_audio); i++ ){
544 ent_audio *audio = mdl_arritm(&world->ent_audio,i);
545 if( audio->flags & AUDIO_FLAG_AUTO_START ){
546 ent_call call;
547 call.data = NULL;
548 call.function = k_ent_function_trigger;
549 call.id = mdl_entity_id( k_ent_audio, i );
550 entity_call( world, &call );
551 }
552 }
553
554 /* read savedata
555 * ----------------------------------------------------------------------- */
556
557 for( u32 i=0; i<mdl_arrcount(&world->ent_challenge); i++ ){
558 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, i );
559 const char *alias = mdl_pstr( &world->meta, challenge->pstr_alias );
560
561 if( vg_msg_seekkvu32( sav, alias, k_vg_msg_first ) ){
562 ent_call call;
563 call.data = NULL;
564 call.function = 0;
565 call.id = mdl_entity_id( k_ent_challenge, i );
566 entity_call( world, &call );
567 }
568 }
569 }
570
571 VG_STATIC void world_entity_serialize( world_instance *world, vg_msg *sav ){
572 for( u32 i=0; i<mdl_arrcount(&world->ent_challenge); i++ ){
573 ent_challenge *challenge = mdl_arritm(&world->ent_challenge,i);
574
575 const char *alias = mdl_pstr(&world->meta,challenge->pstr_alias);
576 vg_msg_wkvu32( sav, alias, challenge->status );
577 }
578 }
579
580 #endif /* WORLD_ENTITY_C */