/***************************************************************************
copyright : (C) 2003 by Michael Speck
email : kulkanie@gmx.net
***************************************************************************/
/***************************************************************************
* *
* This program 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 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/***** INCLUDES ************************************************************/
#include "../client/lbreakout.h"
#include "mathfuncs.h"
#include "levels.h"
#include "extras.h"
#include "balls.h"
#include "shots.h"
#include "bricks.h"
#include "paddle.h"
/***** EXTERNAL VARIABLES **************************************************/
extern Game *cur_game;
/***** EXPORTS *************************************************************/
/***** FORWARDED DECLARATIONS **********************************************/
/***** LOCAL TYPE DEFINITIONS **********************************************/
/***** LOCAL VARIABLES *****************************************************/
/***** LOCAL FUNCTIONS *****************************************************/
/* debug */
void print_level( Level *level )
{
int i, j;
printf( "Title: %s\n", level->name );
printf( "Author: %s\n", level->author );
printf( "Bricks:\n" );
for ( j = 0; j < EDIT_HEIGHT; j++ ) {
for ( i = 0; i < EDIT_WIDTH; i++ )
printf( "%c", level->bricks[i][j] );
printf( "\n" );
}
printf( "Extras:\n" );
for ( j = 0; j < EDIT_HEIGHT; j++ ) {
for ( i = 0; i < EDIT_WIDTH; i++ )
printf( "%c", level->extras[i][j] );
printf( "\n" );
}
printf( "End\n" );
}
/***** PUBLIC FUNCTIONS ****************************************************/
/* pack paddle information
* 0-9 x position
* 10 left fire
* 11 right fire
* 12 return key pressed
*
* the invisible state is not send as the server has
* its own copy of it.
*/
void comm_pack_paddle( Paddle *paddle, unsigned char *msg, int *pos )
{
int info = 0;
info = paddle->x;
if ( paddle->fire_left ) SETBIT( info, 10 );
if ( paddle->fire_right ) SETBIT( info, 11 );
if ( paddle->ball_return_key_pressed ) SETBIT( info, 12 );
msg[(*pos)++] = info & 0xff;
msg[(*pos)++] = (info>>8) & 0xff;
}
/* apply packed paddle */
void comm_unpack_paddle( Paddle *paddle, unsigned char *msg, int *pos )
{
int new_x;
int info = msg[(*pos)] + (msg[(*pos)+1]<<8); *pos += 2;
new_x = info & 1023;
if ( new_x != paddle->x )
if ( paddle->invis )
/* visible for some time when position has changed */
paddle->invis_delay = PADDLE_INVIS_DELAY;
paddle->x = new_x;
paddle->cur_x = paddle->x;
paddle->fire_left = paddle->fire_right = paddle->ball_return_key_pressed = 0;
if ( GETBIT(info,10) )
paddle->fire_left = 1;
if ( GETBIT(info,11) )
paddle->fire_right = 1;
if ( GETBIT(info,12) )
paddle->ball_return_key_pressed = 1;
}
/* pack moving/attached ball and sound information
* 0-3 ball ammo of paddle bottom
* 4-7 ball ammo of paddle top
* 0-7 speedlevel
* 0-4 moving ball count (max: 31)
* 5 brick reflect sound
* 6 attach sound
* 7 fire sound (weapon)
* 32 each:
* 0-7 lower x
* 8-15 lower y
* 16 9th bit of x
* 17 10th bit of x
* 18 9th bit of y
* 24-31 angle 0-180
* 0-4 attached ball count (max: 31)
* 5 paddle reflect sound !!! HACK !!!
* 16 each:
* 0-7 x + 20
* 8-14 y + 20
* 15 paddle (bottom or top)
*/
void comm_pack_balls( unsigned char *msg, int *pos )
{
unsigned char *counter, count, level;
unsigned char byte;
Ball *ball;
/* ball ammo */
if ( cur_game->level_type != LT_PINGPONG )
msg[(*pos)++] = 0;
else {
count = cur_game->paddles[PADDLE_BOTTOM]->ball_ammo & 15;
count = count | ((cur_game->paddles[PADDLE_TOP]->ball_ammo & 15)<<4);
msg[(*pos)++] = count;
}
/* speedlevel */
if ( cur_game->extra_active[EX_SLOW] )
level = 0;
else if ( cur_game->extra_active[EX_FAST] )
level = 100;
else level = cur_game->speedup_level;
msg[(*pos)++] = level;
/* moving balls */
counter = &msg[(*pos)++];
list_reset( cur_game->balls ); count = 0;
while ( (ball = list_next(cur_game->balls)) ) {
if ( ball->attached ) continue;
byte = ball->x & 255;
msg[(*pos)++] = byte;
byte = ball->y & 255;
msg[(*pos)++] = byte;
byte = 0;
if ( GETBIT( ball->x, 8 ) ) SETBIT( byte, 0 );
if ( GETBIT( ball->x, 9 ) ) SETBIT( byte, 1 );
if ( GETBIT( ball->y, 8 ) ) SETBIT( byte, 2 );
msg[(*pos)++] = byte;
byte = ball->angle;
if ( ball->moving_back ) byte = 255;
msg[(*pos)++] = byte;
count++;
}
byte = count;
if ( cur_game->mod.brick_reflected_ball_count > 0 ) SETBIT( byte, 5 );
if ( cur_game->mod.attached_ball_count > 0 ) SETBIT( byte, 6 );
if ( cur_game->mod.fired_shot_count > 0 ) SETBIT( byte, 7 );
*counter = byte;
/* attached balls */
counter = &msg[(*pos)++];
list_reset( cur_game->balls ); count = 0;
while ( (ball = list_next(cur_game->balls)) ) {
if ( !ball->attached ) continue;
byte = ball->x + 20;
msg[(*pos)++] = byte;
byte = ball->y + 20;
if ( ball->paddle->type == PADDLE_TOP ) SETBIT( byte, 7 );
msg[(*pos)++] = byte;
count++;
}
byte = count;
if ( cur_game->mod.paddle_reflected_ball_count > 0 ) SETBIT( byte, 5 );
*counter = byte;
}
/* apply ball information */
void comm_unpack_balls( unsigned char *msg, int *pos )
{
Ball *ball;
unsigned char byte;
int count, level, i;
list_clear( cur_game->balls );
/* ball ammo */
count = msg[(*pos)++];
cur_game->paddles[PADDLE_BOTTOM]->ball_ammo = count & 15;
cur_game->paddles[PADDLE_TOP]->ball_ammo = (count>>4) & 15;
/* ball speed */
level = msg[(*pos)++];
cur_game->ball_v = cur_game->diff->v_start + cur_game->diff->v_add * level;
/* moving balls and sounds */
count = msg[(*pos)++];
cur_game->mod.brick_reflected_ball_count =
cur_game->mod.paddle_reflected_ball_count =
cur_game->mod.attached_ball_count =
cur_game->mod.fired_shot_count = 0;
if ( GETBIT(count,5) )
cur_game->mod.brick_reflected_ball_count = 1;
if ( GETBIT(count,6) )
cur_game->mod.attached_ball_count = 1;
if ( GETBIT(count,7) )
cur_game->mod.fired_shot_count = 1;
count = count & 31;
for ( i = 0; i < count; i++ ) {
ball = salloc( 1, sizeof( Ball ) );
ball->x = msg[(*pos)++];
ball->y = msg[(*pos)++];
byte = msg[(*pos)++];
if ( GETBIT(byte,0) ) ball->x += 256;
if ( GETBIT(byte,1) ) ball->x += 512;
if ( GETBIT(byte,2) ) ball->y += 256;
ball->angle = msg[(*pos)++];
ball->vel.x = ball->vel.y = 0;
if ( ball->angle != 255 ) {
angle2vec( ball->angle, &ball->vel );
vector_set_length( &ball->vel, cur_game->ball_v );
}
ball->cur.x = ball->x;
ball->cur.y = ball->y;
list_add( cur_game->balls, ball );
}
/* attached balls */
count = msg[(*pos)++];
if ( GETBIT(count,5) )
cur_game->mod.paddle_reflected_ball_count = 1;
count = count & 31;
for ( i = 0; i < count; i++ ) {
ball = salloc( 1, sizeof( Ball ) );
ball->x = msg[(*pos)++] - 20;
byte = msg[(*pos)++];
ball->y = (byte&127) - 20;
ball->attached = 1;
if ( GETBIT(byte,7) )
ball->paddle = cur_game->paddles[1];
else
ball->paddle = cur_game->paddles[0];
list_add( cur_game->balls, ball );
}
}
/* pack shot information
* 0-7 shot count
* 24 each:
* 0-7 lower x
* 8-15 lower y
* 16 9th bit of x
* 17 10th bit of x
* 18 9th bit of y
*
* as shot_y can be negative 20 pixels are added to keep
* the transfer value a positive. shots below this height
* level are not transferred.
*/
void comm_pack_shots( unsigned char *msg, int *pos )
{
unsigned char byte, *counter;
Shot *shot;
int shot_x, shot_y, count = 0;
counter = &msg[(*pos)++];
list_reset( cur_game->shots );
while ( (shot = list_next(cur_game->shots)) ) {
shot_x = shot->x;
shot_y = shot->y + 20;
if ( shot_y < 0 ) continue;
byte = shot_x & 255;
msg[(*pos)++] = byte;
byte = shot_y & 255;
msg[(*pos)++] = byte;
byte = 0;
if ( GETBIT( shot_x, 8 ) ) SETBIT( byte, 0 );
if ( GETBIT( shot_x, 9 ) ) SETBIT( byte, 1 );
if ( GETBIT( shot_y, 8 ) ) SETBIT( byte, 2 );
msg[(*pos)++] = byte;
count++;
}
*counter = count;
}
/* apply shots */
void comm_unpack_shots( unsigned char *msg, int *pos )
{
unsigned char byte;
Shot *shot;
int count, i;
list_clear( cur_game->shots );
count = msg[(*pos)++];
for ( i = 0; i < count; i++ ) {
shot = salloc( 1, sizeof( Shot ) );
shot->x = msg[(*pos)++];
shot->y = msg[(*pos)++];
byte = msg[(*pos)++];
if ( GETBIT(byte,0) ) shot->x += 256;
if ( GETBIT(byte,1) ) shot->x += 512;
if ( GETBIT(byte,2) ) shot->y += 256;
shot->y -= 20;
list_add( cur_game->shots, shot );
}
}
/* pack brick hit information
* 0-7 hit count (loose duration)
* 8 each:
* 0-7 id in edit window
* 0-7 heal count (one point)
* 8 each:
* 0-7 id in edit window
* 0-7 grow count
* 16 each:
* 0-7 id in edit window
* 0-7 brick id
* 0-7 remove count
* 16(+8) each:
* 0-7 id in edit window
* 8-9 destroy type (00 normal, 01 energy, 10 shot, 11 expl)
* 10 paddle (top or bottom)
* 11 goldshower (release 1000P)
* 12 draw explosion animation when killing brick
* 13 play a sound when killing brick
* 14-15 unused
* (16-23) clockwise impact position 0-180 for normal animation
*/
void comm_pack_brick_hits( unsigned char *msg, int *pos )
{
unsigned char *counter, byte;
BrickHit *hit;
int i, y_off = ( MAP_HEIGHT - EDIT_HEIGHT ) / 2, count;
/* duration */
counter = &msg[(*pos)++]; count = 0;
for ( i = 0; i < cur_game->mod.brick_hit_count; i++ ) {
hit = &cur_game->mod.brick_hits[i];
if ( hit->type == HT_HIT ) {
msg[(*pos)++] = (hit->y-y_off) * EDIT_WIDTH + hit->x - 1;
count++;
}
}
*counter = count;
/* heal */
counter = &msg[(*pos)++]; count = 0;
for ( i = 0; i < cur_game->mod.brick_hit_count; i++ ) {
hit = &cur_game->mod.brick_hits[i];
if ( hit->type == HT_HEAL ) {
msg[(*pos)++] = (hit->y-y_off) * EDIT_WIDTH + hit->x - 1;
count++;
}
}
*counter = count;
/* growth */
counter = &msg[(*pos)++]; count = 0;
for ( i = 0; i < cur_game->mod.brick_hit_count; i++ ) {
hit = &cur_game->mod.brick_hits[i];
if ( hit->type == HT_GROW ) {
msg[(*pos)++] = (hit->y-y_off) * EDIT_WIDTH + hit->x - 1;
msg[(*pos)++] = hit->brick_id;
count++;
}
}
*counter = count;
/* remove */
counter = &msg[(*pos)++]; count = 0;
for ( i = 0; i < cur_game->mod.brick_hit_count; i++ ) {
hit = &cur_game->mod.brick_hits[i];
if ( hit->type != HT_REMOVE ) continue;
msg[(*pos)++] = (hit->y-y_off) * EDIT_WIDTH + hit->x - 1;
byte = 0;
if ( hit->dest_type == SHR_BY_ENERGY_BALL || hit->dest_type == SHR_BY_EXPL )
SETBIT( byte, 0 );
if ( hit->dest_type == SHR_BY_SHOT || hit->dest_type == SHR_BY_EXPL )
SETBIT( byte, 1 );
if ( hit->paddle ) SETBIT( byte, 2 );
if ( hit->gold_shower ) SETBIT( byte, 3 );
if ( hit->draw_explosion ) SETBIT( byte, 4 );
if ( hit->no_sound ) SETBIT( byte, 5 );
msg[(*pos)++] = byte;
if ( hit->dest_type == SHR_BY_NORMAL_BALL ) {
byte = hit->degrees;
msg[(*pos)++] = byte;
}
count++;
}
*counter = count;
}
/* build client brick hits */
void comm_unpack_brick_hits( unsigned char *msg, int *pos )
{
BrickHit *hit;
int hit_count = 0;
int count, i, j;
unsigned char byte;
/* duration, heal, growth */
for ( j = 0; j < 3; j++ ) {
count = msg[(*pos)++];
for ( i = 0; i < count; i++ ) {
hit = &cur_game->mod.brick_hits[hit_count++];
byte = msg[(*pos)++];
hit->x = byte % EDIT_WIDTH + 1;
hit->y = byte / EDIT_WIDTH + (MAP_HEIGHT-EDIT_HEIGHT)/2;
hit->type = (j==0)?HT_HIT:(j==1)?HT_HEAL:HT_GROW;
if (j==2) /* growth */
{
byte = msg[(*pos)++];
hit->brick_id = byte;
}
}
}
/* removal */
count = msg[(*pos)++];
for ( i = 0; i < count; i++ ) {
hit = &cur_game->mod.brick_hits[hit_count++];
hit->type = HT_REMOVE;
byte = msg[(*pos)++];
hit->x = byte % EDIT_WIDTH + 1;
hit->y = byte / EDIT_WIDTH + (MAP_HEIGHT-EDIT_HEIGHT)/2;
byte = msg[(*pos)++];
hit->dest_type = byte & 3;
hit->paddle = GETBIT(byte,2);
hit->gold_shower = GETBIT(byte,3);
hit->draw_explosion = GETBIT(byte,4);
hit->no_sound = GETBIT(byte,5);
if ( hit->dest_type == SHR_BY_NORMAL_BALL )
hit->degrees = msg[(*pos)++];
else
if ( hit->dest_type == SHR_BY_SHOT ) {
if ( hit->paddle == PADDLE_BOTTOM )
hit->degrees = 135;
else
hit->degrees = 45;
}
}
cur_game->mod.brick_hit_count = hit_count;
}
/* pack collected extra information
* 0-7 paddle bottom count
* 8 each:
* 0-7 extra id
* 0-7 paddle top count
* 8 each:
* 0-7 extra id
*/
void comm_pack_collected_extras( unsigned char *msg, int *pos )
{
int i, j;
for ( i = 0; i < cur_game->paddle_count; i++ ) {
msg[(*pos)++] = cur_game->mod.collected_extra_count[i];
for ( j = 0; j < cur_game->mod.collected_extra_count[i]; j++ )
msg[(*pos)++] = (unsigned char)cur_game->mod.collected_extras[i][j];
}
}
/* build client collected extras */
void comm_unpack_collected_extras( unsigned char *msg, int *pos )
{
int i, j;
for ( i = 0; i < cur_game->paddle_count; i++ ) {
cur_game->mod.collected_extra_count[i] = msg[(*pos)++];
for ( j = 0; j < cur_game->mod.collected_extra_count[i]; j++ )
cur_game->mod.collected_extras[i][j] = msg[(*pos)++];
}
}
/* pack level data (in byte)
* 16 title
* 16 author
* 252 bricks
* 252 extras
*/
void comm_pack_level( Level *level, unsigned char *msg, int *pos )
{
char *ptr = (char*)msg + *pos;
snprintf( ptr, 16, "%s", level->name ); ptr[15] = 0; ptr += 16;
snprintf( ptr, 16, "%s", level->author); ptr[15] = 0; ptr += 16;
memcpy( ptr, level->bricks, 252 ); ptr += 252;
memcpy( ptr, level->extras, 252 ); ptr += 252;
*pos += 16 + 16 + 252 + 252;
}
/* unpack leveldata */
void comm_unpack_level( Level *level, unsigned char *msg, int *pos )
{
char *ptr = (char*)msg + *pos;
snprintf( level->name, 16, "%s", ptr ); ptr += 16;
snprintf( level->author, 16, "%s", ptr ); ptr += 16;
memcpy( level->bricks, ptr, 252 ); ptr += 252;
memcpy( level->extras, ptr, 252 ); ptr += 252;
*pos += 16 + 16 + 252 + 252;
}
/* pack scores
* 0-23 paddle bottom
* 24-47 paddle top
*/
void comm_pack_scores( unsigned char *msg, int *pos )
{
unsigned char *ptr = msg + *pos;
int i;
i = cur_game->paddles[0]->score;
ptr[0] = i & 0xff; ptr[1] = (i>>8) & 0xff; ptr[2] = (i>>16) & 0xff;
i = cur_game->paddles[1]->score;
ptr[3] = i & 0xff; ptr[4] = (i>>8) & 0xff; ptr[5] = (i>>16) & 0xff;
*pos += 6;
}
/* apply scores to paddles */
void comm_unpack_scores( unsigned char *msg, int *pos )
{
unsigned char *ptr = msg + *pos;
cur_game->paddles[0]->score = ptr[0] + (ptr[1]<<8) + (ptr[2]<<16);
cur_game->paddles[1]->score = ptr[3] + (ptr[4]<<8) + (ptr[5]<<16);
*pos += 6;
}
/* dummy unpack the various things thus simply adjust the 'pos'
* pointer but don't handle the message data */
void comm_unpack_paddle_dummy(unsigned char *msg, int *pos )
{
*pos += 2;
}
void comm_unpack_balls_dummy(unsigned char *msg, int *pos )
{
int count;
/* moving balls and sounds */
count = msg[(*pos)++]; *pos += (count&31) * 3;
/* attached balls */
count = msg[(*pos)++]; *pos += count * 2;
}
void comm_unpack_shots_dummy(unsigned char *msg, int *pos )
{
int count;
count = msg[(*pos)++]; *pos += count * 2;
}
void comm_unpack_scores_dummy(unsigned char *msg, int *pos )
{
*pos += 6;
}
void comm_unpack_brick_hits_dummy(unsigned char *msg, int *pos )
{
int count, i, j;
unsigned char byte;
/* duration, heal, growth */
for ( j = 0; j < 3; j++ ) {
count = msg[(*pos)++];
*pos += count;
}
/* removal */
count = msg[(*pos)++];
for ( i = 0; i < count; i++ ) {
*pos += 1;
byte = msg[(*pos)++];
if ( (byte&3) == SHR_BY_NORMAL_BALL )
*pos += 1;
}
}
void comm_unpack_collected_extras_dummy(unsigned char *msg, int *pos )
{
int i, count;
for ( i = 0; i < 2/* assume two paddles */; i++ ) {
count = msg[(*pos)++]; *pos += count;
}
}