logic
[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 )
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 void *entdata = mdl_get_entdata( world->meta, node );
54
55 if( ref->node->classtype == k_classtype_logic_wire )
56 {
57 struct classtype_logic_wire *wire = entdata;
58
59 logic_bricks_debug_connection( world, node, wire->next, white );
60 }
61 else if( ref->node->classtype == k_classtype_logic_chances )
62 {
63 struct classtype_logic_chances *chances = entdata;
64
65 logic_bricks_debug_connection(world, node, chances->targets[0], red );
66 logic_bricks_debug_connection(world, node, chances->targets[1], black);
67 }
68 else if( ref->node->classtype == k_classtype_signal_splitter )
69 {
70 struct classtype_signal_splitter *splitter = entdata;
71 logic_bricks_debug_connection( world, node, splitter->next[0], white );
72 logic_bricks_debug_connection( world, node, splitter->next[1], white );
73 logic_bricks_debug_connection( world, node, splitter->next[2], white );
74 logic_bricks_debug_connection( world, node, splitter->next[3], white );
75 }
76 else if( ref->node->classtype == k_classtype_soundscape )
77 {
78 struct classtype_soundscape *inf = entdata;
79 struct soundscape *s = &world->soundscapes[ ref->internal_id ];
80
81 v3f co;
82 v3_copy( ref->node->co, co );
83
84 boxf box;
85 box[0][0] = co[0]-0.12f;
86 box[0][1] = co[1]-0.12f;
87 box[0][2] = co[2]-0.12f;
88 box[1][0] = co[0]+0.12f;
89 box[1][1] = co[1]+0.12f + 0.1f*(float)inf->max_instances;
90 box[1][2] = co[2]+0.12f;
91
92 vg_line_boxf( box, VG__WHITE );
93
94 for( int i=0; i<s->usage_count; i++ )
95 {
96 vg_line_pt3( co, 0.09f, VG__GREEN );
97 co[1] += 0.2f;
98 }
99 }
100 else if( ref->node->classtype == k_classtype_trigger )
101 {
102 struct classtype_trigger *trigger = entdata;
103 logic_bricks_debug_connection( world, node, trigger->target, white );
104 }
105 else if( ref->node->classtype == k_classtype_particle_box )
106 {
107 struct classtype_particle_box *pb = entdata;
108 logic_bricks_debug_connection( world, node, pb->target, white );
109 }
110
111 ref->usage *= vg_maxf( 0.0f, 1.0f-vg.time_delta*5.0f );
112 }
113 }
114
115 VG_STATIC void logic_packet_terminate( logic_packet *packet )
116 {
117 packet->location = 0xffffffff;
118 }
119
120 VG_STATIC void logic_wire_call( world_instance *world,
121 struct logic_brick_ref *ref,
122 logic_packet *packet )
123 {
124 struct classtype_logic_wire *inf = mdl_get_entdata( world->meta, ref->node );
125
126 if( packet->function == 0 ) /* pass onwards */
127 {
128 if( inf->next )
129 {
130 if( inf->data_type != k_mdl_128bit_datatype_nothing )
131 {
132 packet->data = inf->data;
133 packet->type = inf->data_type;
134 }
135
136 mdl_node *next = mdl_node_from_id( world->meta, inf->next );
137 packet->location = next->sub_uid;
138 packet->function = inf->function;
139 }
140 else
141 logic_packet_terminate( packet );
142 }
143 else if( packet->function == 1 ) /* TODO enable */
144 {
145 logic_packet_terminate( packet );
146 }
147 else if( packet->function == 2 ) /* TODO disable */
148 {
149 logic_packet_terminate( packet );
150 }
151 else
152 {
153 vg_error( "[INVALID FUNCTION] logic_wire:[%u]\n", packet->function );
154 logic_packet_terminate( packet );
155 }
156 }
157
158 VG_STATIC void logic_chances_call( world_instance *world,
159 struct logic_brick_ref *ref,
160 logic_packet *packet )
161 {
162 struct classtype_logic_chances *inf =
163 mdl_get_entdata( world->meta, ref->node );
164
165 if( packet->function == 0 ) /* pass along */
166 {
167 int red = 1;
168
169 if( vg_randf() > inf->p )
170 red = 0;
171
172 if( inf->targets[red] )
173 {
174 mdl_node *pnext = mdl_node_from_id( world->meta, inf->targets[red] );
175
176 if( pnext->classtype == k_classtype_logic_wire )
177 {
178 packet->location = pnext->sub_uid;
179 }
180 else
181 {
182 vg_error( "[INVALID TARGET] logic_chances:pass( ... )\n" );
183 vg_warn( " target[%d] must be classtype logic_wire\n", red );
184 logic_packet_terminate( packet );
185 }
186 }
187 else
188 {
189 logic_packet_terminate( packet );
190 }
191 }
192 else if( packet->function == 1 ) /* set ratio */
193 {
194 if( packet->type == k_mdl_128bit_datatype_number )
195 {
196 inf->p = packet->data._f32;
197 }
198 else
199 {
200 vg_error( "[INVALID ARGUMENT] logic_chances:set_ratio( f32 p )\n" );
201 }
202
203 logic_packet_terminate( packet );
204 }
205 else
206 {
207 vg_error( "[INVALID FUNCTION] logic_chances:[%u]\n", packet->function );
208 logic_packet_terminate( packet );
209 }
210 }
211
212 VG_STATIC void logic_soundscape_call( world_instance *world,
213 struct logic_brick_ref *ref,
214 logic_packet *packet )
215 {
216 struct classtype_soundscape *inf = mdl_get_entdata( world->meta, ref->node );
217 struct soundscape *soundscape = &world->soundscapes[ ref->internal_id ];
218
219 if( packet->function == 0 ) /* play */
220 {
221 /* TODO: Only spawn within certain range of player */
222
223 if( packet->type != k_mdl_128bit_datatype_target )
224 {
225 vg_error( "[INVALID ARGUMENT] logic_soundscape:play( ref sound )\n" );
226 vg_warn( " got datatype: %u\n", packet->type );
227 logic_packet_terminate( packet );
228 return;
229 }
230
231 mdl_node *data = mdl_node_from_id( world->meta, packet->data._u32 );
232
233 if( data->classtype != k_classtype_audio )
234 {
235 vg_error( "[INVALID TARGET] logic_soundscape:play( ref sound )\n" );
236 logic_packet_terminate( packet );
237 return;
238 }
239
240 if( soundscape->usage_count < soundscape->max_instances )
241 {
242 struct world_audio_thing *audio = &world->audio_things[data->sub_uid];
243
244
245 for( int i=0; i<soundscape->max_instances; i++ )
246 {
247 if( !soundscape->channels[i] )
248 {
249 audio_lock();
250 soundscape->channels[i] = audio_request_channel(
251 &audio->temp_embedded_clip,
252 audio->flags );
253
254 if( soundscape->channels[i] )
255 {
256 if( audio->flags & AUDIO_FLAG_SPACIAL_3D )
257 {
258 audio_channel_set_spacial( soundscape->channels[i],
259 soundscape->spawn_position,
260 audio->range );
261 }
262
263 audio_channel_edit_volume( soundscape->channels[i],
264 audio->volume,
265 1 );
266 }
267
268 audio_unlock();
269
270 soundscape->usage_count ++;
271 break;
272 }
273 }
274
275 }
276
277 logic_packet_terminate( packet );
278 }
279 else if( packet->function == 1 ) /* set position */
280 {
281 if( packet->type != k_mdl_128bit_datatype_vec3 )
282 {
283 vg_error( "[INVALID ARGUMENT] logic_soundscape:position( v3f co )\n" );
284 logic_packet_terminate( packet );
285 return;
286 }
287
288 v3_copy( packet->data._v4f, soundscape->spawn_position );
289 logic_packet_terminate( packet );
290 }
291 else
292 {
293 vg_error( "[INVALID FUNCTION] logic_wire:[%u]\n", packet->function );
294 logic_packet_terminate( packet );
295 }
296 }
297
298 VG_STATIC void _logic_trigger_base_call( world_instance *world, u32 target,
299 logic_packet *packet )
300 {
301 if( packet->function == 0 ) /* pass onwards */
302 {
303 if( target )
304 {
305 mdl_node *next = mdl_node_from_id( world->meta, target );
306 packet->location = next->sub_uid;
307 packet->function = 0; /* always call the default function */
308 }
309 else
310 logic_packet_terminate( packet );
311 }
312 else
313 {
314 vg_error( "[INVALID FUNCTION] logic_trigger:[%u]\n", packet->function );
315 logic_packet_terminate( packet );
316 }
317 }
318
319 VG_STATIC void logic_trigger_call( world_instance *world,
320 struct logic_brick_ref *ref,
321 logic_packet *packet )
322 {
323 struct classtype_trigger *inf = mdl_get_entdata( world->meta, ref->node );
324 _logic_trigger_base_call( world, inf->target, packet );
325 }
326
327 VG_STATIC void logic_particle_call( world_instance *world,
328 struct logic_brick_ref *ref,
329 logic_packet *packet )
330 {
331 struct classtype_particle_box *inf =
332 mdl_get_entdata( world->meta, ref->node );
333
334 /* rate of 1.0 means we get one a second or 0.1 per tick */
335
336 if( vg_randf() < inf->rate * 0.1f )
337 {
338 ref->usage += 1.0f;
339 _logic_trigger_base_call( world, inf->target, packet );
340 }
341 else
342 {
343 logic_packet_terminate( packet );
344 }
345 }
346
347 VG_STATIC void logic_bricks_send_packet( world_instance *world,
348 logic_packet *packet );
349
350 VG_STATIC void logic_splitter( world_instance *world,
351 struct logic_brick_ref *ref,
352 logic_packet *packet )
353 {
354 struct classtype_signal_splitter *inf
355 = mdl_get_entdata( world->meta, ref->node );
356
357 if( packet->function == 0 ) /* pass onwards */
358 {
359 for( int i=0; i<4; i++ )
360 {
361 if( inf->next[i] )
362 {
363 logic_packet copy = *packet;
364
365 mdl_node *next = mdl_node_from_id( world->meta, inf->next[i] );
366 copy.location = next->sub_uid;
367 copy.function = 0; /* always call the default function */
368
369 logic_bricks_send_packet( world, &copy );
370 }
371 }
372
373 logic_packet_terminate( packet );
374 }
375 else
376 {
377 vg_error( "[INVALID FUNCTION] logic_splitter:[%u]\n", packet->function );
378 logic_packet_terminate( packet );
379 }
380 }
381
382 VG_STATIC void logic_bricks_send_packet( world_instance *world,
383 logic_packet *packet )
384 {
385 while( packet->location != 0xffffffff )
386 {
387 struct logic_brick_ref *ref = &world->logic_bricks[ packet->location ];
388 enum classtype type = ref->node->classtype;
389
390 if( type == k_classtype_logic_wire )
391 {
392 logic_wire_call( world, ref, packet );
393 }
394 else if( type == k_classtype_logic_chances )
395 {
396 logic_chances_call( world, ref, packet );
397 }
398 else if( type == k_classtype_soundscape )
399 {
400 logic_soundscape_call( world, ref, packet );
401 }
402 else if( type == k_classtype_trigger )
403 {
404 logic_trigger_call( world, ref, packet );
405 }
406 else if( type == k_classtype_particle_box )
407 {
408 logic_particle_call( world, ref, packet );
409 continue;
410 }
411 else if( type == k_classtype_signal_splitter )
412 {
413 logic_splitter( world, ref, packet );
414 }
415 else
416 {
417 vg_error( "Undefined logic brick (entity type %d)\n", type );
418 logic_packet_terminate( packet );
419 }
420
421 ref->usage += 1.0f;
422 }
423 }
424
425 VG_STATIC void logic_bricks_world_gen_allocate( world_instance *world )
426 {
427 /* REVISION: unify allocations, loaders and extensions for entities.
428 * we currently seem to do every which entity a different way */
429
430 world->logic_brick_count = 0;
431 world->logic_bricks = NULL;
432 world->soundscape_count = 0;
433 world->trigger_count = 0;
434
435 for( int i=0; i<world->meta->info.node_count; i++ )
436 {
437 mdl_node *pnode = mdl_node_from_id( world->meta, i );
438
439 if( pnode->classtype == k_classtype_logic_wire ||
440 pnode->classtype == k_classtype_logic_chances ||
441 pnode->classtype == k_classtype_soundscape ||
442 pnode->classtype == k_classtype_trigger ||
443 pnode->classtype == k_classtype_particle_box ||
444 pnode->classtype == k_classtype_signal_splitter )
445 {
446 world->logic_bricks =
447 vg_linear_extend( world_global.generic_heap,
448 world->logic_bricks,
449 sizeof( struct logic_brick_ref ) );
450
451 pnode->sub_uid = world->logic_brick_count;
452
453 struct logic_brick_ref *ref =
454 &world->logic_bricks[ world->logic_brick_count ];
455 ref->node = pnode;
456 ref->internal_id = 0;
457
458 if( pnode->classtype == k_classtype_soundscape )
459 {
460 u32 id = world->soundscape_count;
461
462 struct soundscape *soundscape = &world->soundscapes[ id ];
463
464 struct classtype_soundscape *inf =
465 mdl_get_entdata( world->meta, pnode );
466
467 soundscape->label = mdl_pstr( world->meta, inf->label );
468 soundscape->max_instances = inf->max_instances;
469 soundscape->allow_transitions = inf->allow_transitions;
470 soundscape->transition_duration = inf->transition_duration;
471 v3_copy( pnode->co, soundscape->spawn_position );
472
473 ref->internal_id = id;
474 world->soundscape_count ++;
475 }
476 else if( pnode->classtype == k_classtype_trigger ||
477 pnode->classtype == k_classtype_particle_box )
478 {
479 u32 id = world->trigger_count;
480 struct trigger_zone *trigger = &world->triggers[ id ];
481
482 mdl_node_transform( pnode, trigger->transform );
483 m4x3_invert_full( trigger->transform, trigger->inv_transform );
484 trigger->target_logic_brick = world->logic_brick_count;
485 trigger->classtype = pnode->classtype;
486
487 world->trigger_count ++;
488 }
489
490 world->logic_brick_count ++;
491 }
492 }
493 }
494
495 #endif /* WORLD_LOGIC_BRICKS_H */