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