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