342 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			342 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * This file is part of MultiROM.
 | 
						|
 *
 | 
						|
 * MultiROM is free software: you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU General Public License as published by
 | 
						|
 * the Free Software Foundation, either version 3 of the License, or
 | 
						|
 * (at your option) any later version.
 | 
						|
 *
 | 
						|
 * MultiROM is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 * GNU General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License
 | 
						|
 * along with MultiROM.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdint.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <time.h>
 | 
						|
#include <math.h>
 | 
						|
#include <stdio.h>
 | 
						|
 | 
						|
#include "lib/framebuffer.h"
 | 
						|
#include "lib/input.h"
 | 
						|
#include "pong.h"
 | 
						|
#include "lib/util.h"
 | 
						|
#include "lib/containers.h"
 | 
						|
 | 
						|
#define SCORE_SPACE (75*DPI_MUL)
 | 
						|
#define L 0
 | 
						|
#define R 1
 | 
						|
 | 
						|
#define PADDLE_W (150*DPI_MUL)
 | 
						|
#define PADDLE_H (60*DPI_MUL)
 | 
						|
#define PADDLE_REF ((PADDLE_H/3)*2)
 | 
						|
#define PADDLE_Y (20*DPI_MUL)
 | 
						|
 | 
						|
#define BALL_W (25*DPI_MUL)
 | 
						|
#define DEFAULT_BALL_SPEED (10*DPI_MUL)
 | 
						|
#define BALL_SPEED_MOD ((PADDLE_W/2)/ball_speed)
 | 
						|
 | 
						|
#define COMPUTER L
 | 
						|
#define COMPUTER_SPEED (10*DPI_MUL)
 | 
						|
 | 
						|
static fb_text *score[2] = { NULL, NULL };
 | 
						|
static fb_rect *paddles[2] = { NULL, NULL };
 | 
						|
static fb_rect *ball = NULL;
 | 
						|
static int paddle_last_x[2] = { -1, -1 };
 | 
						|
static int paddle_touch_id[2] = { -1, -1 };
 | 
						|
static int score_val[2];
 | 
						|
static int ball_speed = DEFAULT_BALL_SPEED;
 | 
						|
static int enable_computer = 1;
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
    int x;
 | 
						|
    int y;
 | 
						|
    int collision;
 | 
						|
} ball_step;
 | 
						|
 | 
						|
enum
 | 
						|
{
 | 
						|
    COL_NONE    = 0,
 | 
						|
    COL_LEFT    = 1,
 | 
						|
    COL_RIGHT   = 2,
 | 
						|
    COL_TOP     = 3,
 | 
						|
    COL_BOTTOM  = 4,
 | 
						|
};
 | 
						|
 | 
						|
static ball_step **movement_steps = NULL;
 | 
						|
static float ball_speed_x = 0;
 | 
						|
static float ball_speed_y = 0;
 | 
						|
 | 
						|
static float ai_last_speed = -1000;
 | 
						|
static int ai_hit_pos = 0;
 | 
						|
 | 
						|
void pong(void)
 | 
						|
{
 | 
						|
    enable_computer = 1;
 | 
						|
    paddle_touch_id[L] = -1;
 | 
						|
    paddle_touch_id[R] = -1;
 | 
						|
 | 
						|
    score_val[L] = 0;
 | 
						|
    score_val[R] = 0;
 | 
						|
 | 
						|
    fb_set_background(BLACK);
 | 
						|
 | 
						|
    fb_text_proto *p = fb_text_create(0, 0, GRAYISH, SIZE_SMALL, "Press power button to go back");
 | 
						|
    p->style = STYLE_ITALIC;
 | 
						|
    fb_text *help = fb_text_finalize(p);
 | 
						|
    help->y = fb_height/2 - help->h*1.5;
 | 
						|
    center_text(help, 0, -1, fb_width, -1);
 | 
						|
 | 
						|
    // middle line
 | 
						|
    fb_add_rect(0, fb_height/2 - 1, fb_width, 1, WHITE);
 | 
						|
 | 
						|
    score[L] = fb_add_text(0, 0, WHITE, SIZE_EXTRA, "0");
 | 
						|
    score[L]->y = fb_height/2 - score[L]->h - 20*DPI_MUL;
 | 
						|
    score[R] = fb_add_text(0, fb_height/2 + 20*DPI_MUL, WHITE, SIZE_EXTRA, "0");
 | 
						|
 | 
						|
    paddles[L] = fb_add_rect(100, PADDLE_Y, PADDLE_W, PADDLE_H, WHITE);
 | 
						|
    paddles[R] = fb_add_rect(100, fb_height-PADDLE_Y-PADDLE_H, PADDLE_W, PADDLE_H, WHITE);
 | 
						|
 | 
						|
    ball = fb_add_rect(0, 0, BALL_W, BALL_W, WHITE);
 | 
						|
 | 
						|
    pong_spawn_ball(rand()%2);
 | 
						|
    pong_calc_movement();
 | 
						|
 | 
						|
    add_touch_handler(&pong_touch_handler, NULL);
 | 
						|
 | 
						|
    int step = 0;
 | 
						|
    volatile int run = 1;
 | 
						|
    while(run)
 | 
						|
    {
 | 
						|
        switch(get_last_key())
 | 
						|
        {
 | 
						|
            case KEY_POWER:
 | 
						|
                run = 0;
 | 
						|
                break;
 | 
						|
            case KEY_VOLUMEUP:
 | 
						|
                ball_speed += 5;
 | 
						|
                pong_spawn_ball(rand()%2);
 | 
						|
                pong_calc_movement();
 | 
						|
                step = 0;
 | 
						|
                break;
 | 
						|
            case KEY_VOLUMEDOWN:
 | 
						|
                if(ball_speed > 5)
 | 
						|
                    ball_speed -= 5;
 | 
						|
                pong_spawn_ball(rand()%2);
 | 
						|
                pong_calc_movement();
 | 
						|
                step = 0;
 | 
						|
                break;
 | 
						|
        }
 | 
						|
 | 
						|
        step = pong_do_movement(step);
 | 
						|
 | 
						|
        fb_request_draw();
 | 
						|
        usleep(16000);
 | 
						|
    }
 | 
						|
 | 
						|
    rm_touch_handler(&pong_touch_handler, NULL);
 | 
						|
 | 
						|
    list_clear(&movement_steps, &free);
 | 
						|
}
 | 
						|
 | 
						|
int pong_do_movement(int step)
 | 
						|
{
 | 
						|
    if(!movement_steps[step])
 | 
						|
    {
 | 
						|
        pong_calc_movement();
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    int col = movement_steps[step]->collision;
 | 
						|
    if(col == COL_NONE || col == COL_LEFT || col == COL_RIGHT)
 | 
						|
    {
 | 
						|
        ball->x = movement_steps[step]->x;
 | 
						|
        ball->y = movement_steps[step]->y;
 | 
						|
        if(enable_computer)
 | 
						|
            pong_handle_ai();
 | 
						|
    }
 | 
						|
 | 
						|
    switch(col)
 | 
						|
    {
 | 
						|
        case COL_NONE:
 | 
						|
            return step+1;
 | 
						|
        case COL_TOP:
 | 
						|
        case COL_BOTTOM:
 | 
						|
            ball_speed_x = -ball_speed_x;
 | 
						|
            break;
 | 
						|
        case COL_LEFT:
 | 
						|
        case COL_RIGHT:
 | 
						|
        {
 | 
						|
            int s = col - 1;
 | 
						|
            if(ball->x+BALL_W >= paddles[s]->x && ball->x <= paddles[s]->x+PADDLE_W)
 | 
						|
            {
 | 
						|
                // Increase X speed according to distance from center of paddle.
 | 
						|
                ball_speed_x = (float)((ball->x + BALL_W/2) - (paddles[s]->x + PADDLE_W/2))/BALL_SPEED_MOD;
 | 
						|
                ball_speed_y = -ball_speed_y;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                pong_add_score(!s);
 | 
						|
                for(col = 0; col < 1000; col += 16)
 | 
						|
                {
 | 
						|
                    fb_request_draw();
 | 
						|
                    usleep(16000);
 | 
						|
                }
 | 
						|
                pong_spawn_ball(s);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    pong_calc_movement();
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int pong_touch_handler(touch_event *ev, UNUSED void *data)
 | 
						|
{
 | 
						|
    int i = 0;
 | 
						|
    for(; i < 2; ++i)
 | 
						|
    {
 | 
						|
        if (paddle_touch_id[i] == -1 && (ev->changed & TCHNG_ADDED) &&
 | 
						|
            in_rect(ev->x, ev->y, paddles[i]->x, paddles[i]->y, paddles[i]->w, paddles[i]->h))
 | 
						|
        {
 | 
						|
            paddle_touch_id[i] = ev->id;
 | 
						|
            paddle_last_x[i] = ev->x;
 | 
						|
            if(i == L)
 | 
						|
                enable_computer = 0;
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
 | 
						|
        if(ev->id != paddle_touch_id[i])
 | 
						|
            continue;
 | 
						|
 | 
						|
        if(ev->changed & TCHNG_REMOVED)
 | 
						|
        {
 | 
						|
            paddle_touch_id[i] = -1;
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
 | 
						|
        int newX = paddles[i]->x + (ev->x - paddle_last_x[i]);
 | 
						|
        paddle_last_x[i] = ev->x;
 | 
						|
 | 
						|
        if(newX > 0 && newX < (int)fb_width-PADDLE_W)
 | 
						|
            paddles[i]->x = newX;
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
void pong_spawn_ball(int side)
 | 
						|
{
 | 
						|
    float angle;
 | 
						|
    if(side == L)
 | 
						|
        angle = 6.28319 - (float)(rand()%1570)/1000.f - 0.785398;
 | 
						|
    else
 | 
						|
        angle = (float)(rand()%1570)/1000.f + 0.785398;
 | 
						|
 | 
						|
    ball_speed_x = cos(angle)*ball_speed;
 | 
						|
    ball_speed_y = sin(angle)*ball_speed;
 | 
						|
 | 
						|
    ball->x = rand()%(int)(fb_width-BALL_W);
 | 
						|
    ball->y = fb_height/2 - BALL_W/2;
 | 
						|
}
 | 
						|
 | 
						|
void pong_calc_movement(void)
 | 
						|
{
 | 
						|
    list_clear(&movement_steps, &free);
 | 
						|
 | 
						|
    ball_step *step = NULL;
 | 
						|
    float x = ball->x;
 | 
						|
    float y = ball->y;
 | 
						|
 | 
						|
    if(y < PADDLE_Y+PADDLE_H)
 | 
						|
        y = PADDLE_Y+PADDLE_H;
 | 
						|
    else if(y > fb_height-PADDLE_Y-PADDLE_H-BALL_W)
 | 
						|
        y = fb_height-PADDLE_Y-PADDLE_H-BALL_W;
 | 
						|
 | 
						|
    if(x < 0)
 | 
						|
        x = 0;
 | 
						|
    else if(x > fb_width-BALL_W)
 | 
						|
        x = fb_width-BALL_W;
 | 
						|
 | 
						|
    while(!step || step->collision == COL_NONE)
 | 
						|
    {
 | 
						|
        x += ball_speed_x;
 | 
						|
        y += ball_speed_y;
 | 
						|
 | 
						|
        step = malloc(sizeof(ball_step));
 | 
						|
        step->x = x;
 | 
						|
        step->y = y;
 | 
						|
        step->collision = pong_get_collision(x, y);
 | 
						|
        list_add(&movement_steps, step);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
int pong_get_collision(int x, int y)
 | 
						|
{
 | 
						|
    if(y < PADDLE_Y+PADDLE_REF)
 | 
						|
        return COL_LEFT;
 | 
						|
    if(y > (int)fb_height-PADDLE_Y-PADDLE_REF-BALL_W)
 | 
						|
        return COL_RIGHT;
 | 
						|
 | 
						|
    if(x < 0)
 | 
						|
        return COL_BOTTOM;
 | 
						|
    if(x > (int)fb_width-BALL_W)
 | 
						|
        return COL_TOP;
 | 
						|
 | 
						|
    return COL_NONE;
 | 
						|
}
 | 
						|
 | 
						|
void pong_add_score(int side)
 | 
						|
{
 | 
						|
    char buff[8];
 | 
						|
    snprintf(buff, sizeof(buff), "%d", ++score_val[side]);
 | 
						|
    fb_text_set_content(score[side], buff);
 | 
						|
}
 | 
						|
 | 
						|
void pong_handle_ai(void)
 | 
						|
{
 | 
						|
    int ball_center = (ball->x + BALL_W/2);
 | 
						|
 | 
						|
    if(ai_last_speed != ball_speed_x)
 | 
						|
    {
 | 
						|
        ai_hit_pos = rand()%3;
 | 
						|
        ai_last_speed = ball_speed_x;
 | 
						|
    }
 | 
						|
 | 
						|
    int computer_x = 0;
 | 
						|
    switch(ai_hit_pos)
 | 
						|
    {
 | 
						|
        case 0:
 | 
						|
            computer_x = paddles[COMPUTER]->x + PADDLE_W/2;
 | 
						|
            break;
 | 
						|
        case 1:
 | 
						|
            computer_x = paddles[COMPUTER]->x;
 | 
						|
            break;
 | 
						|
        case 2:
 | 
						|
            computer_x = paddles[COMPUTER]->x + PADDLE_W;
 | 
						|
            break;
 | 
						|
    }
 | 
						|
 | 
						|
    int move_dist = abs(computer_x - ball_center);
 | 
						|
    if(move_dist > COMPUTER_SPEED)
 | 
						|
        move_dist = COMPUTER_SPEED;
 | 
						|
 | 
						|
    if(ball_center > computer_x)
 | 
						|
    {
 | 
						|
        if(paddles[COMPUTER]->x + PADDLE_W + COMPUTER_SPEED <= (int)fb_width)
 | 
						|
            paddles[COMPUTER]->x += move_dist;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        if(paddles[COMPUTER]->x - COMPUTER_SPEED >= 0)
 | 
						|
            paddles[COMPUTER]->x -= move_dist;
 | 
						|
    }
 | 
						|
}
 |