--- /dev/null
+#include "common.h"
+
+#ifndef WORLD_LOGIC_BRICKS_H
+#define WORLD_LOGIC_BRICKS_H
+
+#include "world.h"
+
+typedef struct logic_packet logic_packet;
+struct logic_packet
+{
+ u32 location, function;
+ union mdl_128bit_union data;
+ enum mdl_128bit_datatype type;
+};
+
+VG_STATIC void logic_bricks_debug_connection( world_instance *world,
+ mdl_node *from, u32 next,
+ v3f colour )
+{
+ if( next == 0 )
+ return;
+
+ mdl_node *to = mdl_node_from_id( world->meta, next );
+
+ v3f c;
+ float brightness = 0.8f + world->logic_bricks[ from->sub_uid ].usage * 4.0f;
+ v3_muls( colour, brightness, c );
+
+ u32 clamped = 0xff000000;
+
+ for( int i=0; i<3; i++ )
+ {
+ u8 byte = vg_minf( 1.0f, c[i] ) * 255.0f;
+ clamped |= byte << (i*8);
+ }
+
+ vg_line( from->co, to->co, clamped );
+}
+
+VG_STATIC void logic_bricks_debug( world_instance *world )
+{
+ v3f white = {1.0f,1.0f,1.0f},
+ red = {1.0f,0.2f,0.1f},
+ black = {0.2f,0.2f,0.2f};
+
+ glLineWidth( 2.0f );
+
+ for( int i=0; i<world->logic_brick_count; i++ )
+ {
+ struct logic_brick_ref *ref = &world->logic_bricks[i];
+ mdl_node *node = ref->node;
+
+ void *entdata = mdl_get_entdata( world->meta, node );
+
+ if( ref->node->classtype == k_classtype_logic_wire )
+ {
+ struct classtype_logic_wire *wire = entdata;
+
+ logic_bricks_debug_connection( world, node, wire->next, white );
+ }
+ else if( ref->node->classtype == k_classtype_logic_chances )
+ {
+ struct classtype_logic_chances *chances = entdata;
+
+ logic_bricks_debug_connection(world, node, chances->targets[0], red );
+ logic_bricks_debug_connection(world, node, chances->targets[1], black);
+ }
+ else if( ref->node->classtype == k_classtype_signal_splitter )
+ {
+ struct classtype_signal_splitter *splitter = entdata;
+ logic_bricks_debug_connection( world, node, splitter->next[0], white );
+ logic_bricks_debug_connection( world, node, splitter->next[1], white );
+ logic_bricks_debug_connection( world, node, splitter->next[2], white );
+ logic_bricks_debug_connection( world, node, splitter->next[3], white );
+ }
+ else if( ref->node->classtype == k_classtype_soundscape )
+ {
+ struct classtype_soundscape *inf = entdata;
+ struct soundscape *s = &world->soundscapes[ ref->internal_id ];
+
+ v3f co;
+ v3_copy( ref->node->co, co );
+
+ boxf box;
+ box[0][0] = co[0]-0.12f;
+ box[0][1] = co[1]-0.12f;
+ box[0][2] = co[2]-0.12f;
+ box[1][0] = co[0]+0.12f;
+ box[1][1] = co[1]+0.12f + 0.1f*(float)inf->max_instances;
+ box[1][2] = co[2]+0.12f;
+
+ vg_line_boxf( box, VG__WHITE );
+
+ for( int i=0; i<s->usage_count; i++ )
+ {
+ vg_line_pt3( co, 0.09f, VG__GREEN );
+ co[1] += 0.2f;
+ }
+ }
+ else if( ref->node->classtype == k_classtype_trigger )
+ {
+ struct classtype_trigger *trigger = entdata;
+ logic_bricks_debug_connection( world, node, trigger->target, white );
+ }
+ else if( ref->node->classtype == k_classtype_particle_box )
+ {
+ struct classtype_particle_box *pb = entdata;
+ logic_bricks_debug_connection( world, node, pb->target, white );
+ }
+
+ ref->usage *= vg_maxf( 0.0f, 1.0f-vg.time_delta*5.0f );
+ }
+}
+
+VG_STATIC void logic_packet_terminate( logic_packet *packet )
+{
+ packet->location = 0xffffffff;
+}
+
+VG_STATIC void logic_wire_call( world_instance *world,
+ struct logic_brick_ref *ref,
+ logic_packet *packet )
+{
+ struct classtype_logic_wire *inf = mdl_get_entdata( world->meta, ref->node );
+
+ if( packet->function == 0 ) /* pass onwards */
+ {
+ if( inf->next )
+ {
+ if( inf->data_type != k_mdl_128bit_datatype_nothing )
+ {
+ packet->data = inf->data;
+ packet->type = inf->data_type;
+ }
+
+ mdl_node *next = mdl_node_from_id( world->meta, inf->next );
+ packet->location = next->sub_uid;
+ packet->function = inf->function;
+ }
+ else
+ logic_packet_terminate( packet );
+ }
+ else if( packet->function == 1 ) /* TODO enable */
+ {
+ logic_packet_terminate( packet );
+ }
+ else if( packet->function == 2 ) /* TODO disable */
+ {
+ logic_packet_terminate( packet );
+ }
+ else
+ {
+ vg_error( "[INVALID FUNCTION] logic_wire:[%u]\n", packet->function );
+ logic_packet_terminate( packet );
+ }
+}
+
+VG_STATIC void logic_chances_call( world_instance *world,
+ struct logic_brick_ref *ref,
+ logic_packet *packet )
+{
+ struct classtype_logic_chances *inf =
+ mdl_get_entdata( world->meta, ref->node );
+
+ if( packet->function == 0 ) /* pass along */
+ {
+ int red = 1;
+
+ if( vg_randf() > inf->p )
+ red = 0;
+
+ if( inf->targets[red] )
+ {
+ mdl_node *pnext = mdl_node_from_id( world->meta, inf->targets[red] );
+
+ if( pnext->classtype == k_classtype_logic_wire )
+ {
+ packet->location = pnext->sub_uid;
+ }
+ else
+ {
+ vg_error( "[INVALID TARGET] logic_chances:pass( ... )\n" );
+ vg_warn( " target[%d] must be classtype logic_wire\n", red );
+ logic_packet_terminate( packet );
+ }
+ }
+ else
+ {
+ logic_packet_terminate( packet );
+ }
+ }
+ else if( packet->function == 1 ) /* set ratio */
+ {
+ if( packet->type == k_mdl_128bit_datatype_number )
+ {
+ inf->p = packet->data._f32;
+ }
+ else
+ {
+ vg_error( "[INVALID ARGUMENT] logic_chances:set_ratio( f32 p )\n" );
+ }
+
+ logic_packet_terminate( packet );
+ }
+ else
+ {
+ vg_error( "[INVALID FUNCTION] logic_chances:[%u]\n", packet->function );
+ logic_packet_terminate( packet );
+ }
+}
+
+VG_STATIC void logic_soundscape_call( world_instance *world,
+ struct logic_brick_ref *ref,
+ logic_packet *packet )
+{
+ struct classtype_soundscape *inf = mdl_get_entdata( world->meta, ref->node );
+ struct soundscape *soundscape = &world->soundscapes[ ref->internal_id ];
+
+ if( packet->function == 0 ) /* play */
+ {
+ /* TODO: Only spawn within certain range of player */
+
+ if( packet->type != k_mdl_128bit_datatype_target )
+ {
+ vg_error( "[INVALID ARGUMENT] logic_soundscape:play( ref sound )\n" );
+ vg_warn( " got datatype: %u\n", packet->type );
+ logic_packet_terminate( packet );
+ return;
+ }
+
+ mdl_node *data = mdl_node_from_id( world->meta, packet->data._u32 );
+
+ if( data->classtype != k_classtype_audio )
+ {
+ vg_error( "[INVALID TARGET] logic_soundscape:play( ref sound )\n" );
+ logic_packet_terminate( packet );
+ return;
+ }
+
+ if( soundscape->usage_count < soundscape->max_instances )
+ {
+ struct world_audio_thing *audio = &world->audio_things[data->sub_uid];
+
+
+ for( int i=0; i<soundscape->max_instances; i++ )
+ {
+ if( !soundscape->channels[i] )
+ {
+ audio_lock();
+ soundscape->channels[i] = audio_request_channel(
+ &audio->temp_embedded_clip,
+ audio->flags );
+
+ if( soundscape->channels[i] )
+ {
+ if( audio->flags & AUDIO_FLAG_SPACIAL_3D )
+ {
+ audio_channel_set_spacial( soundscape->channels[i],
+ soundscape->spawn_position,
+ audio->range );
+ }
+
+ audio_channel_edit_volume( soundscape->channels[i],
+ audio->volume,
+ 1 );
+ }
+
+ audio_unlock();
+
+ soundscape->usage_count ++;
+ break;
+ }
+ }
+
+ }
+
+ logic_packet_terminate( packet );
+ }
+ else if( packet->function == 1 ) /* set position */
+ {
+ if( packet->type != k_mdl_128bit_datatype_vec3 )
+ {
+ vg_error( "[INVALID ARGUMENT] logic_soundscape:position( v3f co )\n" );
+ logic_packet_terminate( packet );
+ return;
+ }
+
+ v3_copy( packet->data._v4f, soundscape->spawn_position );
+ logic_packet_terminate( packet );
+ }
+ else
+ {
+ vg_error( "[INVALID FUNCTION] logic_wire:[%u]\n", packet->function );
+ logic_packet_terminate( packet );
+ }
+}
+
+VG_STATIC void _logic_trigger_base_call( world_instance *world, u32 target,
+ logic_packet *packet )
+{
+ if( packet->function == 0 ) /* pass onwards */
+ {
+ if( target )
+ {
+ mdl_node *next = mdl_node_from_id( world->meta, target );
+ packet->location = next->sub_uid;
+ packet->function = 0; /* always call the default function */
+ }
+ else
+ logic_packet_terminate( packet );
+ }
+ else
+ {
+ vg_error( "[INVALID FUNCTION] logic_trigger:[%u]\n", packet->function );
+ logic_packet_terminate( packet );
+ }
+}
+
+VG_STATIC void logic_trigger_call( world_instance *world,
+ struct logic_brick_ref *ref,
+ logic_packet *packet )
+{
+ struct classtype_trigger *inf = mdl_get_entdata( world->meta, ref->node );
+ _logic_trigger_base_call( world, inf->target, packet );
+}
+
+VG_STATIC void logic_particle_call( world_instance *world,
+ struct logic_brick_ref *ref,
+ logic_packet *packet )
+{
+ struct classtype_particle_box *inf =
+ mdl_get_entdata( world->meta, ref->node );
+
+ /* rate of 1.0 means we get one a second or 0.1 per tick */
+
+ if( vg_randf() < inf->rate * 0.1f )
+ {
+ ref->usage += 1.0f;
+ _logic_trigger_base_call( world, inf->target, packet );
+ }
+ else
+ {
+ logic_packet_terminate( packet );
+ }
+}
+
+VG_STATIC void logic_bricks_send_packet( world_instance *world,
+ logic_packet *packet );
+
+VG_STATIC void logic_splitter( world_instance *world,
+ struct logic_brick_ref *ref,
+ logic_packet *packet )
+{
+ struct classtype_signal_splitter *inf
+ = mdl_get_entdata( world->meta, ref->node );
+
+ if( packet->function == 0 ) /* pass onwards */
+ {
+ for( int i=0; i<4; i++ )
+ {
+ if( inf->next[i] )
+ {
+ logic_packet copy = *packet;
+
+ mdl_node *next = mdl_node_from_id( world->meta, inf->next[i] );
+ copy.location = next->sub_uid;
+ copy.function = 0; /* always call the default function */
+
+ logic_bricks_send_packet( world, © );
+ }
+ }
+
+ logic_packet_terminate( packet );
+ }
+ else
+ {
+ vg_error( "[INVALID FUNCTION] logic_splitter:[%u]\n", packet->function );
+ logic_packet_terminate( packet );
+ }
+}
+
+VG_STATIC void logic_bricks_send_packet( world_instance *world,
+ logic_packet *packet )
+{
+ while( packet->location != 0xffffffff )
+ {
+ struct logic_brick_ref *ref = &world->logic_bricks[ packet->location ];
+ enum classtype type = ref->node->classtype;
+
+ if( type == k_classtype_logic_wire )
+ {
+ logic_wire_call( world, ref, packet );
+ }
+ else if( type == k_classtype_logic_chances )
+ {
+ logic_chances_call( world, ref, packet );
+ }
+ else if( type == k_classtype_soundscape )
+ {
+ logic_soundscape_call( world, ref, packet );
+ }
+ else if( type == k_classtype_trigger )
+ {
+ logic_trigger_call( world, ref, packet );
+ }
+ else if( type == k_classtype_particle_box )
+ {
+ logic_particle_call( world, ref, packet );
+ continue;
+ }
+ else if( type == k_classtype_signal_splitter )
+ {
+ logic_splitter( world, ref, packet );
+ }
+ else
+ {
+ vg_error( "Undefined logic brick (entity type %d)\n", type );
+ logic_packet_terminate( packet );
+ }
+
+ ref->usage += 1.0f;
+ }
+}
+
+VG_STATIC void logic_bricks_world_gen_allocate( world_instance *world )
+{
+ /* REVISION: unify allocations, loaders and extensions for entities.
+ * we currently seem to do every which entity a different way */
+
+ world->logic_brick_count = 0;
+ world->logic_bricks = NULL;
+ world->soundscape_count = 0;
+ world->trigger_count = 0;
+
+ for( int i=0; i<world->meta->info.node_count; i++ )
+ {
+ mdl_node *pnode = mdl_node_from_id( world->meta, i );
+
+ if( pnode->classtype == k_classtype_logic_wire ||
+ pnode->classtype == k_classtype_logic_chances ||
+ pnode->classtype == k_classtype_soundscape ||
+ pnode->classtype == k_classtype_trigger ||
+ pnode->classtype == k_classtype_particle_box ||
+ pnode->classtype == k_classtype_signal_splitter )
+ {
+ world->logic_bricks =
+ vg_linear_extend( world_global.generic_heap,
+ world->logic_bricks,
+ sizeof( struct logic_brick_ref ) );
+
+ pnode->sub_uid = world->logic_brick_count;
+
+ struct logic_brick_ref *ref =
+ &world->logic_bricks[ world->logic_brick_count ];
+ ref->node = pnode;
+ ref->internal_id = 0;
+
+ if( pnode->classtype == k_classtype_soundscape )
+ {
+ u32 id = world->soundscape_count;
+
+ struct soundscape *soundscape = &world->soundscapes[ id ];
+
+ struct classtype_soundscape *inf =
+ mdl_get_entdata( world->meta, pnode );
+
+ soundscape->label = mdl_pstr( world->meta, inf->label );
+ soundscape->max_instances = inf->max_instances;
+ soundscape->allow_transitions = inf->allow_transitions;
+ soundscape->transition_duration = inf->transition_duration;
+ v3_copy( pnode->co, soundscape->spawn_position );
+
+ ref->internal_id = id;
+ world->soundscape_count ++;
+ }
+ else if( pnode->classtype == k_classtype_trigger ||
+ pnode->classtype == k_classtype_particle_box )
+ {
+ u32 id = world->trigger_count;
+ struct trigger_zone *trigger = &world->triggers[ id ];
+
+ mdl_node_transform( pnode, trigger->transform );
+ m4x3_invert_full( trigger->transform, trigger->inv_transform );
+ trigger->target_logic_brick = world->logic_brick_count;
+ trigger->classtype = pnode->classtype;
+
+ world->trigger_count ++;
+ }
+
+ world->logic_brick_count ++;
+ }
+ }
+}
+
+#endif /* WORLD_LOGIC_BRICKS_H */