Advertisement
snake5

SGScript | ld29 project source - entities/player.sgs

Apr 28th, 2014
333
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. function ent_create_bullet( params )
  2. {
  3.     Bullet =
  4.     {
  5.         pos = clone(params.pos),
  6.         dir = clone(params.dir),
  7.         from_player = params.from_player,
  8.     };
  9.    
  10.     function Bullet.tick( delta )
  11.     {
  12.         p1 = this.pos;
  13.         p2 = p1 + this.dir * 350 * delta;
  14.        
  15.         hit = null;
  16.         gPhysicsWorld.RayCast( function( fixture, point, normal, fraction ) use( hit )
  17.         {
  18.             owner = fixture.body.userData;
  19.             if( owner )
  20.             {
  21.                 hit = owner;
  22.                 owner.handleBulletHit( point, normal );
  23.             }
  24.         },
  25.         p1, p2 );
  26.        
  27.         if( hit )
  28.             gGameWorld.remove_entity( this.id );
  29.        
  30.         this.pos = p2;
  31.     }
  32.    
  33.     function Bullet.draw()
  34.     {
  35.         p1 = this.pos;
  36.         p2 = this.pos - this.dir * 10;
  37.         q = 0.3;
  38.         SS_DrawColorLine( p1.x-q, p1.y-q, p2.x-q, p2.y-q, 0.1, 0.1, 0.1, 0.9 );
  39.         SS_DrawColorLine( p1.x+q, p1.y-q, p2.x+q, p2.y-q, 0.1, 0.1, 0.1, 0.9 );
  40.         SS_DrawColorLine( p1.x-q, p1.y+q, p2.x-q, p2.y+q, 0.1, 0.1, 0.1, 0.9 );
  41.         SS_DrawColorLine( p1.x+q, p1.y+q, p2.x+q, p2.y+q, 0.1, 0.1, 0.1, 0.9 );
  42.         SS_DrawColorLine( p1.x, p1.y, p2.x, p2.y, 0.9, 0.8, 0.7, 0.9 );
  43.     }
  44.    
  45.     return Bullet;
  46. }
  47.  
  48.  
  49. function char_init( obj, pos, textype )
  50. {
  51.     bodyDef = Box2D_CreateBodyDef();
  52.     bodyDef.type = Box2D_BodyType_Dynamic;
  53.     bodyDef.position = pos;
  54.     bodyDef.active = true;
  55.     bodyDef.allowSleep = false;
  56.    
  57.     shape = Box2D_CreateCircleShape();
  58.     shape.radius = 12;
  59.    
  60.     obj.phy_body = gPhysicsWorld.CreateBody( bodyDef );
  61.     obj.phy_body.CreateFixtureFromShape( shape, 1 );
  62.     obj.pos = pos;
  63.     obj.dir = vec2(1,0);
  64.    
  65.     obj.move_dir = vec2(0);
  66.     obj.move_speed = 0;
  67.     obj.move_factor = 0;
  68.     obj.move_amount = 0;
  69.     obj.moved = false;
  70.     obj.bob_time = 0;
  71.    
  72.     obj.tx_char = SS_CreateTexture( "images/"$textype$".png", "nolerp" );
  73.     obj.tx_leg = SS_CreateTexture( "images/"$textype$"_leg.png", "nolerp" );
  74.     obj.ps_shot = ParticleSystem.create( PSFX_Shot );
  75.     obj.ps_hit = ParticleSystem.create( PSFX_BloodHit );
  76.     obj.shoot_timeout = 0;
  77.     obj.texname = textype;
  78.     obj.dead = false;
  79.    
  80.     char = obj;
  81.     function char.handleBulletHit( point, normal )
  82.     {
  83.         this.ps_hit.emitpos = point;
  84.         this.ps_hit.emitdir = normal;
  85.         this.ps_hit.play();
  86.         this.dead = true;
  87.        
  88.         if( this.texname == "player" )
  89.             gCurMission.hit_player = true;
  90.         if( this.texname == "enemy" )
  91.             gCurMission.hit_enemy = true;
  92.     }
  93.     function char.added()
  94.     {
  95.         this.phy_body.userData = this;
  96.     }
  97.     function char.removed()
  98.     {
  99.         this.phy_body.userData = null;
  100.     }
  101. }
  102.  
  103. function char_tick( obj, delta, move_dir, move_speed, tgt )
  104. {
  105.     obj.ps_shot.tick( delta );
  106.     obj.ps_hit.tick( delta );
  107.    
  108.     if( obj.dead )
  109.     {
  110.         obj.phy_body.linearVelocity = vec2(0);
  111.         return;
  112.     }
  113.    
  114.     obj.shoot_timeout = max( obj.shoot_timeout - delta, 0 );
  115.     obj.move_dir = move_dir;
  116.     obj.move_speed = move_speed * move_dir.length;
  117.     obj.move_factor = max( 0, min( 1, obj.move_factor + if( move_dir != vec2(0), delta, -delta ) * 5 ) );
  118.     obj.move_amount += obj.move_factor;
  119.     obj.moved = obj.move_amount > 10;
  120.     obj.bob_time += delta * move_speed / 8,
  121.    
  122.     obj.phy_body.linearVelocity = move_dir * move_speed;
  123.     obj.pos = obj.phy_body.position;
  124.     obj.dir = ( tgt - obj.pos ).normalized;
  125. }
  126.  
  127. function char_draw( obj )
  128. {
  129.     bob_off = obj.pos + obj.dir.perp * ( sin( obj.bob_time ) * obj.move_factor );
  130.     leg1_off = obj.pos + obj.dir.perp * 4 + obj.move_dir * 4 * sin( obj.bob_time ) * obj.move_factor;
  131.     leg2_off = obj.pos - obj.dir.perp * 4 + obj.move_dir * 4 * -sin( obj.bob_time ) * obj.move_factor;
  132.    
  133.     if( !obj.dead )
  134.     {
  135.         SS_Draw({ preset = "box", texture = obj.tx_leg, position = leg1_off,
  136.             angle = obj.dir.angle + M_PI * 0.5, scale = vec2(6,12), color = color(1) });
  137.         SS_Draw({ preset = "box", texture = obj.tx_leg, position = leg2_off,
  138.             angle = obj.dir.angle + M_PI * 0.5, scale = vec2(6,12), color = color(1) });
  139.     }
  140.     SS_Draw({ preset = "box", texture = obj.tx_char, position = bob_off,
  141.         angle = obj.dir.angle + M_PI * 0.5, scale = vec2(24), color = color(1) });
  142.    
  143.     obj.ps_shot.draw();
  144.     obj.ps_hit.draw();
  145. }
  146.  
  147. function char_shoot( obj )
  148. {
  149.     if( obj.shoot_timeout <= 0 )
  150.     {
  151.         bpos = obj.pos + obj.dir * 8;
  152.         bdir = obj.dir;
  153.         obj.ps_shot.emitpos = bpos;
  154.         obj.ps_shot.emitdir = bdir;
  155.         obj.ps_shot.play();
  156.        
  157.         gGameWorld.add_entity( EntityBuilder.build( "bullet", {
  158.             pos = bpos,
  159.             dir = bdir,
  160.             from_player = obj.texname == "player",
  161.         }) );
  162.        
  163.         obj.shoot_timeout = 0.2;
  164.     }
  165. }
  166.  
  167.  
  168. function ent_create_player( params )
  169. {
  170.     Player =
  171.     {
  172.         z_index = 100,
  173.        
  174.         tx_crosshair = SS_CreateTexture( "images/crosshair.png" ),
  175.     };
  176.    
  177.     char_init( Player, vec2(params.x,params.y), "player" );
  178.    
  179.     global gCurPlayer = Player;
  180.    
  181.     gActionMap.add_action( "move_up" );
  182.     gActionMap.add_action( "move_dn" );
  183.     gActionMap.add_action( "move_lf" );
  184.     gActionMap.add_action( "move_rt" );
  185.     gActionMap.add_action( "move_slow" );
  186.     gActionMap.add_action( "shoot" );
  187.     gActionMap.add_default_binding( "move_up", SDLK_W );
  188.     gActionMap.add_default_binding( "move_dn", SDLK_S );
  189.     gActionMap.add_default_binding( "move_lf", SDLK_A );
  190.     gActionMap.add_default_binding( "move_rt", SDLK_D );
  191.     gActionMap.add_default_binding( "move_slow", SDLK_LSHIFT );
  192.     gActionMap.add_default_binding( "shoot", SDLK_LMB );
  193.    
  194.     function Player.setPos( pos )
  195.     {
  196.         this.pos = pos;
  197.         this.phy_body.position = pos;
  198.     }
  199.    
  200.     function Player.tick( delta )
  201.     {
  202.         move_dir = vec2(
  203.             gActionMap.get( "move_rt" ) - gActionMap.get( "move_lf" ),
  204.             gActionMap.get( "move_dn" ) - gActionMap.get( "move_up" )
  205.         ).normalized;
  206.         move_speed = lerp( 96, 32, gActionMap.get( "move_slow" ) );
  207.         tgt = gCurWorld.screenToWorld( gCursorPos );
  208.        
  209.         char_tick( this, delta, move_dir, move_speed, tgt );
  210.        
  211.         if( gActionMap.is_pressed( "shoot" ) )
  212.         {
  213.             char_shoot( this );
  214.         }
  215.        
  216.         curpos = gCurWorld.camera_follow_pos;
  217.         curpos = lerp( curpos, this.pos, pow( 0.1, 1 - clamp( delta, 0, 1 ) ) );
  218.         gCurWorld.camera_follow_pos = curpos;
  219.        
  220.         gCurWorld.light_pos = this.pos;
  221.         gCurWorld.light_dir = this.dir;
  222.     }
  223.    
  224.     function Player.draw()
  225.     {
  226.         char_draw( this );
  227.     }
  228.    
  229.     function Player.draw_ui()
  230.     {
  231.         if( !Game.Paused )
  232.         {
  233.             SS_Draw({ preset = "box", texture = this.tx_crosshair,
  234.                 position = gCursorPos+vec2(1), scale = vec2(32), color = color(0.5,1,0.5,1) });
  235.         }
  236.     }
  237.    
  238.     return Player;
  239. }
  240.  
  241.  
  242. global ENEMY_ENTER_VIEW = 1;
  243. global ENEMY_ATTACK = 2;
  244. global ENEMY_WAIT = 3;
  245. global ENEMY_ESCAPE_VIEW = 4;
  246.  
  247. global ENEMY_TILE_MEMORY_LIMIT = 16;
  248.  
  249. function ent_create_enemy( params )
  250. {
  251.     Enemy =
  252.     {
  253.         z_index = 99,
  254.         stage = ENEMY_ENTER_VIEW,
  255.         lastSeenPos = null,
  256.         preWaitTime = 0.7 + randf() * 0.5,
  257.         waitTime = null,
  258.         lastEscapeTiles = [],
  259.     };
  260.     char_init( Enemy, vec2(params.x,params.y), "enemy" );
  261.    
  262.     function Enemy.added()
  263.     {
  264.         global gCurEnemy = this;
  265.         this.phy_body.userData = this;
  266.     }
  267.     function Enemy.removed()
  268.     {
  269.         global gCurEnemy = null;
  270.         this.phy_body.userData = null;
  271.     }
  272.    
  273.     function Enemy.isPlayerVisible()
  274.     {
  275.         visionObstructed = false;
  276.         p1 = this.pos;
  277.         p2 = gCurPlayer.pos;
  278.         dir = ( p2 - p1 ).normalized;
  279.         p1 += dir * 13;
  280.         p2 -= dir * 13;
  281.         gPhysicsWorld.RayCast(function() use (visionObstructed)
  282.         {
  283.             visionObstructed = true;
  284.         },
  285.         p1, p2 );
  286.         return !visionObstructed;
  287.     }
  288.    
  289.     function Enemy.pickNearestTileTowards( tgt, regpos )
  290.     {
  291.         pos = this.pos;
  292.         tiles = gCurWorld.getWalkableTilesRightOutsideAABB( aabb2v( pos, pos ) );
  293.         positions = [];
  294.         factors = [];
  295.         foreach( tile : tiles )
  296.         {
  297.             tilepos = vec2( tile[0] + 0.5, tile[1] + 0.5 ) * 32;
  298.             positions.push( tilepos );
  299.             factors.push( ( tgt - tilepos ).length );
  300.         }
  301.         if( positions )
  302.         {
  303.             positions.sort_mapped( factors );
  304.             pos = positions[0];
  305.             if( regpos )
  306.             {
  307.                 // if haven't reached a new tile, just return previous result
  308.                 mytilepos = vec2( floor( pos.x / 32 ) + 0.5, floor( pos.y / 32 ) + 0.5 ) * 32;
  309.                 if( @this.prevtile == mytilepos && this.lastEscapeTiles )
  310.                     return this.lastEscapeTiles.last;
  311.                 this.prevtile = mytilepos;
  312.                
  313.                 // if last found tile was same, ignore that it's in the "already found" array
  314.                 if( this.lastEscapeTiles.size && this.lastEscapeTiles.last == pos )
  315.                     return pos;
  316.                
  317.                 // target tile cannot be visited before
  318.                 while( this.lastEscapeTiles.find( pos ) !== null )
  319.                 {
  320.                     positions.shift();
  321.                     if( positions.size )
  322.                         pos = positions[0];
  323.                     else
  324.                     {
  325.                         // stuck
  326.                         this.lastEscapeTiles.clear(); // panic!
  327.                         return null;
  328.                     }
  329.                 }
  330.                
  331.                 // register currently found tile, pushing out old ones if necessary
  332.                 while( this.lastEscapeTiles.size > ENEMY_TILE_MEMORY_LIMIT )
  333.                     this.lastEscapeTiles.shift();
  334.                 this.lastEscapeTiles.push( pos );
  335.             }
  336.             return pos;
  337.         }
  338.         return null;
  339.     }
  340.    
  341.     function Enemy.attackOrWait()
  342.     {
  343.         if( this.isPlayerVisible() )
  344.         {
  345.             this.lastSeenPos = clone( gCurPlayer.pos ); // can't have same instance to avoid following
  346.             this.waitTime = randf() * 1.5 + 1.4;
  347.             this.stage = ENEMY_ATTACK;
  348.         }
  349.         else if( this.preWaitTime <= 0 )
  350.         {
  351.             this.waitTime = randf() * 1.5 + 2.4;
  352.             this.stage = ENEMY_WAIT;
  353.         }
  354.     }
  355.    
  356.     function Enemy.tick( delta )
  357.     {
  358.         move_dir = vec2(0);
  359.         move_speed = 120;
  360.         tgt = this.pos + this.dir;
  361.        
  362.         // try to move towards player, determine shooting possibility
  363.         if( this.stage == ENEMY_ENTER_VIEW )
  364.         {
  365.             movetgt = this.pickNearestTileTowards( gCurPlayer.pos );
  366.             if( movetgt )
  367.             {
  368.                 move_dir = ( movetgt - this.pos ).normalized;
  369.                 if( move_dir )
  370.                     tgt = this.pos + move_dir * 120;
  371.             }
  372.             this.preWaitTime -= delta;
  373.             this.attackOrWait();
  374.         }
  375.         // attack player
  376.         else if( this.stage == ENEMY_ATTACK )
  377.         {
  378.             tgt = this.lastSeenPos;
  379.             this.waitTime -= delta;
  380.             if( this.waitTime <= 0 )
  381.                 this.stage = ENEMY_ESCAPE_VIEW;
  382.             char_shoot( this );
  383.         }
  384.         // wait for some time, look or walk around maybe
  385.         else if( this.stage == ENEMY_WAIT )
  386.         {
  387.             if( this.isPlayerVisible() )
  388.             {
  389.                 this.waitTime -= delta * 5;
  390.                 if( vec2_dot( this.dir, ( gCurPlayer.pos - this.pos ).normalized ) > 0.5 )
  391.                 {
  392.                     this.tgt = gCurPlayer.pos;
  393.                     char_shoot( this );
  394.                 }
  395.             }
  396.             this.waitTime -= delta;
  397.             if( this.waitTime <= 0 )
  398.                 this.stage = ENEMY_ESCAPE_VIEW;
  399.         }
  400.         // try to escape
  401.         else if( this.stage == ENEMY_ESCAPE_VIEW )
  402.         {
  403.             ovtgt = this.pos + ( this.pos - gCurPlayer.pos ).normalized * 120;
  404.             movetgt = this.pickNearestTileTowards( ovtgt, true );
  405.             if( movetgt )
  406.             {
  407.                 move_dir = ( movetgt - this.pos );
  408.                 if( move_dir.length < 4 )
  409.                     move_dir = vec2(0);
  410.                 move_dir = move_dir.normalized;
  411.                 if( move_dir )
  412.                     tgt = this.pos + move_dir * 120;
  413.                 else
  414.                     this.attackOrWait();
  415.             }
  416.             camera_aabb = gCurWorld.getCameraAABB();
  417.             if( !aabb2_intersect( aabb2v( this.pos - vec2(16), this.pos + vec2(16) ), camera_aabb ) )
  418.             {
  419.                 // escaped
  420.                 gGameWorld.remove_entity( this.id );
  421.             }
  422.         }
  423.        
  424.         char_tick( this, delta, move_dir, move_speed, tgt );
  425.     }
  426.    
  427.     function Enemy.draw()
  428.     {
  429.         char_draw( this );
  430.     }
  431.    
  432.     return Enemy;
  433. }
  434.  
  435.  
  436. function ent_create_mission( params )
  437. {
  438.     Mission =
  439.     {
  440.         pos = vec2(params.x,params.y),
  441.         ext = vec2(48),
  442.         player_near = false,
  443.         quitting = false,
  444.         hit_player = false,
  445.         hit_enemy = false,
  446.        
  447.         chance_timeout = 1,
  448.     };
  449.    
  450.     function Mission.added()
  451.     {
  452.         global gCurMission = this;
  453.     }
  454.     function Mission.removed()
  455.     {
  456.         global gCurMission = null;
  457.     }
  458.    
  459.     /* chance, in units per second */
  460.     function Mission.calcSpawnChance()
  461.     {
  462.         chance = 1.2 / 3;
  463.        
  464.         P = @gCurPlayer;
  465.         if( P )
  466.             chance *= P.move_speed / 160 + 0.4;
  467.        
  468.         return chance;
  469.     }
  470.    
  471.     function Mission.findSpawnPoint()
  472.     {
  473.         world = @gCurWorld;
  474.         if( world )
  475.         {
  476.             tiles = world.getTilesNearView();
  477.             positions = [];
  478.             foreach( tile : tiles )
  479.             {
  480.                 pos = vec2(tile[0]+0.5,tile[1]+0.5) * 32;
  481.                
  482.                 // not even close to the starting point
  483.                 if( ( pos - this.pos ).length < 400 ) continue;
  484.                
  485.                 positions.push( pos );
  486.             }
  487.             if( positions )
  488.             {
  489.                 return positions[ rand() % positions.size ];
  490.             }
  491.         }
  492.         return null;
  493.     }
  494.    
  495.     function Mission.spawnEnemy()
  496.     {
  497.         pos = this.findSpawnPoint();
  498.         if( !pos )
  499.             return false;
  500.        
  501.         gGameWorld.add_entity( EntityBuilder.build( "enemy", { x = pos.x, y = pos.y } ) );
  502.         return true;
  503.     }
  504.    
  505.     function Mission.canSpawnEnemy()
  506.     {
  507.         enemy = @gCurEnemy;
  508.         return !enemy;
  509.     }
  510.    
  511.     function Mission.tick( delta )
  512.     {
  513.         player = @gCurPlayer;
  514.         xmin = this.pos.x - this.ext.x;
  515.         xmax = this.pos.x + this.ext.x;
  516.         ymin = this.pos.y - this.ext.y;
  517.         ymax = this.pos.y + this.ext.y;
  518.         ppos = player.pos;
  519.        
  520.         this.player_near = false;
  521.         if( player )
  522.         {
  523.             if( ppos.x >= xmin && ppos.x <= xmax &&
  524.                 ppos.y >= ymin && ppos.y <= ymax )
  525.             {
  526.                 if( ppos.x < this.pos.x && !this.quitting )
  527.                 {
  528.                     this.quitting = true;
  529.                     Game.FadeOutFactor = 0;
  530.                 }
  531.                 this.player_near = true;
  532.             }
  533.         }
  534.        
  535.         if( this.hit_player || this.hit_enemy )
  536.         {
  537.             if( !this.quitting )
  538.             {
  539.                 this.quitting = true;
  540.                 Game.FadeOutFactor = 0;
  541.             }
  542.             if( this.hit_player )
  543.             {
  544.                 if( this.hit_enemy )
  545.                     Game.PauseText = "Heroic effort.";
  546.                 else
  547.                     Game.PauseText = "Failure.";
  548.             }
  549.             else
  550.                 Game.PauseText = "Success.";
  551.             Game.CanUnpause = false;
  552.             if( Game.FadeOutFactor >= 5 )
  553.                 Game.SetPaused( true );
  554.         }
  555.        
  556.         if( this.quitting && !this.hit_player && !this.hit_enemy )
  557.         {
  558.             Game.PauseText = "Pursuit stopped. Suspect escaped.";
  559.             Game.CanUnpause = false;
  560.             player.setPos(vec2( this.pos.x - Game.FadeOutFactor * 10, ppos.y ));
  561.             if( Game.FadeOutFactor >= 5 )
  562.                 Game.SetPaused( true );
  563.         }
  564.        
  565.         if( this.canSpawnEnemy() )
  566.         {
  567.             this.chance_timeout -= delta;
  568.             while( this.chance_timeout <= 0 )
  569.             {
  570.                 mustspawn = this.calcSpawnChance() > randf();
  571.                 if( mustspawn )
  572.                 {
  573.                     this.spawnEnemy();
  574.                     // one more second after disappearance to reappear
  575.                     this.chance_timeout += 1;
  576.                 }
  577.                 this.chance_timeout += 1;
  578.             }
  579.         }
  580.     }
  581.    
  582.     function Mission.draw_ui()
  583.     {
  584.         text = null;
  585.         if( !this.quitting && this.player_near )
  586.         {
  587.             text = "This is a way out. Going through here means stopping the pursuit. Proceed to confirm.";
  588.         }
  589.         else if( !@gCurPlayer.moved )
  590.         {
  591.             text = "Use W/A/S/D to move, Left Shift to walk slowly, Left mouse button to shoot";
  592.         }
  593.        
  594.         if( text )
  595.             SS_DrawTextRect( text, gActionFont2, color(0.9,0.9), DT_LEFT | DT_BOTTOM, W/5, W*4/5, H/5, H*4/5 );
  596.     }
  597.    
  598.     return Mission;
  599. }
  600.  
  601.  
  602. return
  603. {
  604.     bullet =
  605.     {
  606.         create = ent_create_bullet,
  607.         default = { pos = vec2(0), dir = vec2(0), from_player = false },
  608.     },
  609.     player =
  610.     {
  611.         create = ent_create_player,
  612.         default = { x = 0, y = 0 },
  613.     },
  614.     enemy =
  615.     {
  616.         create = ent_create_enemy,
  617.         default = { x = 0, y = 0 },
  618.     },
  619.     mission =
  620.     {
  621.         create = ent_create_mission,
  622.         default = { x = 0, y = 0 },
  623.     },
  624. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement