logic
[carveJwlIkooP6JGAAIwe30JlM.git] / world_logic_bricks.h
diff --git a/world_logic_bricks.h b/world_logic_bricks.h
new file mode 100644 (file)
index 0000000..2df236a
--- /dev/null
@@ -0,0 +1,495 @@
+#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, &copy );
+         }
+      }
+
+      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 */