bugs/map edits
[carveJwlIkooP6JGAAIwe30JlM.git] / world_logic_bricks.h
1 #include "common.h"
2
3 #ifndef WORLD_LOGIC_BRICKS_H
4 #define WORLD_LOGIC_BRICKS_H
5
6 #include "world.h"
7
8 typedef struct logic_packet logic_packet;
9 struct logic_packet
10 {
11 u32 location, function;
12 union mdl_128bit_union data;
13 enum mdl_128bit_datatype type;
14 };
15
16 VG_STATIC void logic_bricks_debug_connection( world_instance *world,
17 mdl_node *from, u32 next,
18 v3f colour )
19 {
20 if( next == 0 )
21 return;
22
23 mdl_node *to = mdl_node_from_id( world->meta, next );
24
25 v3f c;
26 float brightness = 0.8f + world->logic_bricks[ from->sub_uid ].usage * 4.0f;
27 v3_muls( colour, brightness, c );
28
29 u32 clamped = 0xff000000;
30
31 for( int i=0; i<3; i++ )
32 {
33 u8 byte = vg_minf( 1.0f, c[i] ) * 255.0f;
34 clamped |= byte << (i*8);
35 }
36
37 vg_line( from->co, to->co, clamped );
38 }
39
40 VG_STATIC void logic_bricks_debug( world_instance *world, v3f pos )
41 {
42 v3f white = {1.0f,1.0f,1.0f},
43 red = {1.0f,0.2f,0.1f},
44 black = {0.2f,0.2f,0.2f};
45
46 glLineWidth( 2.0f );
47
48 for( int i=0; i<world->logic_brick_count; i++ )
49 {
50 struct logic_brick_ref *ref = &world->logic_bricks[i];
51 mdl_node *node = ref->node;
52
53 if( v3_dist2( node->co, pos ) > 50.0f*50.0f )
54 continue;
55
56 void *entdata = mdl_get_entdata( world->meta, node );
57
58 if( ref->node->classtype == k_classtype_logic_wire )
59 {
60 struct classtype_logic_wire *wire = entdata;
61
62 logic_bricks_debug_connection( world, node, wire->next, white );
63 }
64 else if( ref->node->classtype == k_classtype_logic_chances )
65 {
66 struct classtype_logic_chances *chances = entdata;
67
68 logic_bricks_debug_connection(world, node, chances->targets[0], red );
69 logic_bricks_debug_connection(world, node, chances->targets[1], black);
70 }
71 else if( ref->node->classtype == k_classtype_signal_splitter )
72 {
73 struct classtype_signal_splitter *splitter = entdata;
74 logic_bricks_debug_connection( world, node, splitter->next[0], white );
75 logic_bricks_debug_connection( world, node, splitter->next[1], white );
76 logic_bricks_debug_connection( world, node, splitter->next[2], white );
77 logic_bricks_debug_connection( world, node, splitter->next[3], white );
78 }
79 else if( ref->node->classtype == k_classtype_soundscape )
80 {
81 struct classtype_soundscape *inf = entdata;
82 struct soundscape *s = &world->soundscapes[ ref->internal_id ];
83
84 v3f co;
85 v3_copy( ref->node->co, co );
86
87 boxf box;
88 box[0][0] = co[0]-0.12f;
89 box[0][1] = co[1]-0.12f;
90 box[0][2] = co[2]-0.12f;
91 box[1][0] = co[0]+0.12f;
92 box[1][1] = co[1]+0.12f + 0.1f*(float)inf->max_instances;
93 box[1][2] = co[2]+0.12f;
94
95 vg_line_boxf( box, VG__WHITE );
96
97 for( int i=0; i<s->usage_count; i++ )
98 {
99 vg_line_pt3( co, 0.09f, VG__GREEN );
100 co[1] += 0.2f;
101 }
102 }
103 else if( ref->node->classtype == k_classtype_trigger )
104 {
105 struct classtype_trigger *trigger = entdata;
106 logic_bricks_debug_connection( world, node, trigger->target, white );
107 }
108 else if( ref->node->classtype == k_classtype_particle_box )
109 {
110 struct classtype_particle_box *pb = entdata;
111 logic_bricks_debug_connection( world, node, pb->target, white );
112 }
113
114 ref->usage *= vg_maxf( 0.0f, 1.0f-vg.time_delta*5.0f );
115 }
116 }
117
118 VG_STATIC void logic_packet_terminate( logic_packet *packet )
119 {
120 packet->location = 0xffffffff;
121 }
122
123 VG_STATIC void logic_wire_call( world_instance *world,
124 struct logic_brick_ref *ref,
125 logic_packet *packet )
126 {
127 struct classtype_logic_wire *inf = mdl_get_entdata( world->meta, ref->node );
128
129 if( packet->function == 0 ) /* pass onwards */
130 {
131 if( inf->next )
132 {
133 if( inf->data_type != k_mdl_128bit_datatype_nothing )
134 {
135 packet->data = inf->data;
136 packet->type = inf->data_type;
137 }
138
139 mdl_node *next = mdl_node_from_id( world->meta, inf->next );
140 packet->location = next->sub_uid;
141 packet->function = inf->function;
142 }
143 else
144 logic_packet_terminate( packet );
145 }
146 else if( packet->function == 1 ) /* TODO enable */
147 {
148 logic_packet_terminate( packet );
149 }
150 else if( packet->function == 2 ) /* TODO disable */
151 {
152 logic_packet_terminate( packet );
153 }
154 else
155 {
156 vg_error( "[INVALID FUNCTION] logic_wire:[%u]\n", packet->function );
157 logic_packet_terminate( packet );
158 }
159 }
160
161 VG_STATIC void logic_chances_call( world_instance *world,
162 struct logic_brick_ref *ref,
163 logic_packet *packet )
164 {
165 struct classtype_logic_chances *inf =
166 mdl_get_entdata( world->meta, ref->node );
167
168 if( packet->function == 0 ) /* pass along */
169 {
170 int red = 1;
171
172 if( vg_randf() > inf->p )
173 red = 0;
174
175 if( inf->targets[red] )
176 {
177 mdl_node *pnext = mdl_node_from_id( world->meta, inf->targets[red] );
178
179 if( (pnext->classtype == k_classtype_logic_wire) ||
180 (pnext->classtype == k_classtype_logic_chances) ||
181 (pnext->classtype == k_classtype_signal_splitter) )
182 {
183 packet->location = pnext->sub_uid;
184 }
185 else
186 {
187 vg_error( "[INVALID TARGET] logic_chances:pass( ... )\n" );
188 vg_warn( " target[%d] must be classtype logic_wire\n", red );
189 logic_packet_terminate( packet );
190 }
191 }
192 else
193 {
194 logic_packet_terminate( packet );
195 }
196 }
197 else if( packet->function == 1 ) /* set ratio */
198 {
199 if( packet->type == k_mdl_128bit_datatype_number )
200 {
201 inf->p = packet->data._f32;
202 }
203 else
204 {
205 vg_error( "[INVALID ARGUMENT] logic_chances:set_ratio( f32 p )\n" );
206 }
207
208 logic_packet_terminate( packet );
209 }
210 else
211 {
212 vg_error( "[INVALID FUNCTION] logic_chances:[%u]\n", packet->function );
213 logic_packet_terminate( packet );
214 }
215 }
216
217 VG_STATIC void logic_soundscape_call( world_instance *world,
218 struct logic_brick_ref *ref,
219 logic_packet *packet )
220 {
221 struct classtype_soundscape *inf = mdl_get_entdata( world->meta, ref->node );
222 struct soundscape *soundscape = &world->soundscapes[ ref->internal_id ];
223
224 if( packet->function == 0 ) /* play */
225 {
226 /* TODO: Only spawn within certain range of player */
227
228 if( packet->type != k_mdl_128bit_datatype_target )
229 {
230 vg_error( "[INVALID ARGUMENT] logic_soundscape:play( ref sound )\n" );
231 vg_warn( " got datatype: %u\n", packet->type );
232 logic_packet_terminate( packet );
233 return;
234 }
235
236 mdl_node *data = mdl_node_from_id( world->meta, packet->data._u32 );
237
238 if( data->classtype != k_classtype_audio )
239 {
240 vg_error( "[INVALID TARGET] logic_soundscape:play( ref sound )\n" );
241 logic_packet_terminate( packet );
242 return;
243 }
244
245 if( soundscape->usage_count < soundscape->max_instances )
246 {
247 struct world_audio_thing *audio = &world->audio_things[data->sub_uid];
248
249
250 for( int i=0; i<soundscape->max_instances; i++ )
251 {
252 if( !soundscape->channels[i] )
253 {
254 audio_lock();
255 soundscape->channels[i] = audio_request_channel(
256 &audio->temp_embedded_clip,
257 audio->flags );
258
259 if( soundscape->channels[i] )
260 {
261 if( audio->flags & AUDIO_FLAG_SPACIAL_3D )
262 {
263 audio_channel_set_spacial( soundscape->channels[i],
264 soundscape->spawn_position,
265 audio->range );
266 }
267
268 audio_channel_edit_volume( soundscape->channels[i],
269 audio->volume,
270 1 );
271 }
272
273 audio_unlock();
274
275 soundscape->usage_count ++;
276 break;
277 }
278 }
279
280 }
281
282 logic_packet_terminate( packet );
283 }
284 else if( packet->function == 1 ) /* set position */
285 {
286 if( packet->type != k_mdl_128bit_datatype_vec3 )
287 {
288 vg_error( "[INVALID ARGUMENT] logic_soundscape:position( v3f co )\n" );
289 logic_packet_terminate( packet );
290 return;
291 }
292
293 v3_copy( packet->data._v4f, soundscape->spawn_position );
294 logic_packet_terminate( packet );
295 }
296 else
297 {
298 vg_error( "[INVALID FUNCTION] logic_wire:[%u]\n", packet->function );
299 logic_packet_terminate( packet );
300 }
301 }
302
303 VG_STATIC void _logic_trigger_base_call( world_instance *world, u32 target,
304 logic_packet *packet )
305 {
306 if( packet->function == 0 ) /* pass onwards */
307 {
308 if( target )
309 {
310 mdl_node *next = mdl_node_from_id( world->meta, target );
311 packet->location = next->sub_uid;
312 packet->function = 0; /* always call the default function */
313 }
314 else
315 logic_packet_terminate( packet );
316 }
317 else
318 {
319 vg_error( "[INVALID FUNCTION] logic_trigger:[%u]\n", packet->function );
320 logic_packet_terminate( packet );
321 }
322 }
323
324 VG_STATIC void logic_trigger_call( world_instance *world,
325 struct logic_brick_ref *ref,
326 logic_packet *packet )
327 {
328 struct classtype_trigger *inf = mdl_get_entdata( world->meta, ref->node );
329 _logic_trigger_base_call( world, inf->target, packet );
330 }
331
332 VG_STATIC void logic_particle_call( world_instance *world,
333 struct logic_brick_ref *ref,
334 logic_packet *packet )
335 {
336 struct classtype_particle_box *inf =
337 mdl_get_entdata( world->meta, ref->node );
338
339 /* rate of 1.0 means we get one a second or 0.1 per tick */
340
341 if( vg_randf() < inf->rate * 0.1f )
342 {
343 ref->usage += 1.0f;
344 _logic_trigger_base_call( world, inf->target, packet );
345 }
346 else
347 {
348 logic_packet_terminate( packet );
349 }
350 }
351
352 VG_STATIC void logic_bricks_send_packet( world_instance *world,
353 logic_packet *packet );
354
355 VG_STATIC void logic_splitter( world_instance *world,
356 struct logic_brick_ref *ref,
357 logic_packet *packet )
358 {
359 struct classtype_signal_splitter *inf
360 = mdl_get_entdata( world->meta, ref->node );
361
362 if( packet->function == 0 ) /* pass onwards */
363 {
364 for( int i=0; i<4; i++ )
365 {
366 if( inf->next[i] )
367 {
368 logic_packet copy = *packet;
369
370 mdl_node *next = mdl_node_from_id( world->meta, inf->next[i] );
371 copy.location = next->sub_uid;
372 copy.function = 0; /* always call the default function */
373
374 logic_bricks_send_packet( world, &copy );
375 }
376 }
377
378 logic_packet_terminate( packet );
379 }
380 else
381 {
382 vg_error( "[INVALID FUNCTION] logic_splitter:[%u]\n", packet->function );
383 logic_packet_terminate( packet );
384 }
385 }
386
387 VG_STATIC void logic_bricks_send_packet( world_instance *world,
388 logic_packet *packet )
389 {
390 while( packet->location != 0xffffffff )
391 {
392 struct logic_brick_ref *ref = &world->logic_bricks[ packet->location ];
393 enum classtype type = ref->node->classtype;
394
395 if( type == k_classtype_logic_wire )
396 {
397 logic_wire_call( world, ref, packet );
398 }
399 else if( type == k_classtype_logic_chances )
400 {
401 logic_chances_call( world, ref, packet );
402 }
403 else if( type == k_classtype_soundscape )
404 {
405 logic_soundscape_call( world, ref, packet );
406 }
407 else if( type == k_classtype_trigger )
408 {
409 logic_trigger_call( world, ref, packet );
410 }
411 else if( type == k_classtype_particle_box )
412 {
413 logic_particle_call( world, ref, packet );
414 continue;
415 }
416 else if( type == k_classtype_signal_splitter )
417 {
418 logic_splitter( world, ref, packet );
419 }
420 else
421 {
422 vg_error( "Undefined logic brick (entity type %d)\n", type );
423 logic_packet_terminate( packet );
424 }
425
426 ref->usage += 1.0f;
427 }
428 }
429
430 VG_STATIC void logic_bricks_world_gen_allocate( world_instance *world )
431 {
432 /* REVISION: unify allocations, loaders and extensions for entities.
433 * we currently seem to do every which entity a different way */
434
435 world->logic_brick_count = 0;
436 world->logic_bricks = NULL;
437 world->soundscape_count = 0;
438 world->trigger_count = 0;
439
440 for( int i=0; i<world->meta->info.node_count; i++ )
441 {
442 mdl_node *pnode = mdl_node_from_id( world->meta, i );
443
444 if( pnode->classtype == k_classtype_logic_wire ||
445 pnode->classtype == k_classtype_logic_chances ||
446 pnode->classtype == k_classtype_soundscape ||
447 pnode->classtype == k_classtype_trigger ||
448 pnode->classtype == k_classtype_particle_box ||
449 pnode->classtype == k_classtype_signal_splitter )
450 {
451 world->logic_bricks =
452 vg_linear_extend( world_global.generic_heap,
453 world->logic_bricks,
454 sizeof( struct logic_brick_ref ) );
455
456 pnode->sub_uid = world->logic_brick_count;
457
458 struct logic_brick_ref *ref =
459 &world->logic_bricks[ world->logic_brick_count ];
460 ref->node = pnode;
461 ref->internal_id = 0;
462
463 if( pnode->classtype == k_classtype_soundscape )
464 {
465 u32 id = world->soundscape_count;
466
467 struct soundscape *soundscape = &world->soundscapes[ id ];
468
469 struct classtype_soundscape *inf =
470 mdl_get_entdata( world->meta, pnode );
471
472 soundscape->label = mdl_pstr( world->meta, inf->label );
473 soundscape->max_instances = inf->max_instances;
474 soundscape->allow_transitions = inf->allow_transitions;
475 soundscape->transition_duration = inf->transition_duration;
476 v3_copy( pnode->co, soundscape->spawn_position );
477
478 ref->internal_id = id;
479 world->soundscape_count ++;
480 }
481 else if( pnode->classtype == k_classtype_trigger ||
482 pnode->classtype == k_classtype_particle_box )
483 {
484 u32 id = world->trigger_count;
485 struct trigger_zone *trigger = &world->triggers[ id ];
486
487 mdl_node_transform( pnode, trigger->transform );
488 m4x3_invert_full( trigger->transform, trigger->inv_transform );
489 trigger->target_logic_brick = world->logic_brick_count;
490 trigger->classtype = pnode->classtype;
491
492 world->trigger_count ++;
493 }
494
495 world->logic_brick_count ++;
496 }
497 }
498 }
499
500
501
502
503
504 /*
505 * BVH implementation
506 * ----------------------------------------------------------------------------
507 */
508
509 VG_STATIC void trigger_bh_expand_bound( void *user, boxf bound, u32 item_index )
510 {
511 world_instance *world = user;
512 struct trigger_zone *trigger = &world->triggers[ item_index ];
513
514 m4x3_expand_aabb_point( trigger->transform, bound, (v3f){ 1.0f, 1.0f, 1.0f} );
515 m4x3_expand_aabb_point( trigger->transform, bound, (v3f){ 1.0f, 1.0f,-1.0f} );
516 m4x3_expand_aabb_point( trigger->transform, bound, (v3f){ 1.0f,-1.0f, 1.0f} );
517 m4x3_expand_aabb_point( trigger->transform, bound, (v3f){ 1.0f,-1.0f,-1.0f} );
518 m4x3_expand_aabb_point( trigger->transform, bound, (v3f){-1.0f, 1.0f, 1.0f} );
519 m4x3_expand_aabb_point( trigger->transform, bound, (v3f){-1.0f, 1.0f,-1.0f} );
520 m4x3_expand_aabb_point( trigger->transform, bound, (v3f){-1.0f,-1.0f, 1.0f} );
521 m4x3_expand_aabb_point( trigger->transform, bound, (v3f){-1.0f,-1.0f,-1.0f} );
522 }
523
524 VG_STATIC float trigger_bh_centroid( void *user, u32 item_index, int axis )
525 {
526 world_instance *world = user;
527 struct trigger_zone *trigger = &world->triggers[ item_index ];
528
529 return trigger->transform[3][axis];
530 }
531
532 VG_STATIC void trigger_bh_swap( void *user, u32 ia, u32 ib )
533 {
534 world_instance *world = user;
535 struct trigger_zone *a = &world->triggers[ ia ],
536 *b = &world->triggers[ ib ],
537 temp;
538
539 temp = *a;
540 *a = *b;
541 *b = temp;
542 }
543
544 VG_STATIC void trigger_bh_debug( void *user, u32 item_index )
545 {
546 world_instance *world = user;
547 struct trigger_zone *zone = &world->triggers[ item_index ];
548
549 vg_line_boxf_transformed( zone->transform, (boxf){{-1.0f,-1.0f,-1.0f},
550 { 1.0f, 1.0f, 1.0f}},
551 0xff00ff00 );
552 }
553
554 VG_STATIC bh_system bh_system_triggers =
555 {
556 .expand_bound = trigger_bh_expand_bound,
557 .item_centroid = trigger_bh_centroid,
558 .item_closest = NULL,
559 .item_swap = trigger_bh_swap,
560 .item_debug = trigger_bh_debug,
561 .cast_ray = NULL
562 };
563
564 #endif /* WORLD_LOGIC_BRICKS_H */