📄 main.c
字号:
#include <SDL/SDL.h>#include <SDL/SDL_thread.h>#include <stdlib.h>#include <time.h>#include <math.h>#include <string.h>#include <assert.h>#include "gamedefs.h"#include "particle.h"#include "background.h"#include "resources.h"#include "scripting.h"#include "network.h"#include "music.h"#include "audio.h"#include "weapon.h"#include "status.h"/* Player data */enum { OPP_COMPUTER, OPP_NETWORK } opponent_type;SDL_mutex *player_mutex; /* lock on player data, so the networking code can access it safely in the background */player_t player; /* the player sitting at the local keyboard */player_t opponent; /* scripted or networked opponent *//* The current camera position */static int camera_x, camera_y; /* position of 640x480 viewport within world */SDL_Surface *screen; /* global for convenience *//* Network link structure */net_link_t netlink;int network_ok = 1;/* These variables are set by the network thread. The main loop should copy them into the corresponding player structures at its earliest convenience. This is necessary because the network thread is decoupled from the main loop, and the values could get overwritten otherwise. */int local_player_hit = 0; /* local player has been hit */int local_player_dead = 0; /* local player has been destroyed */int network_opponent_respawn = 0; /* remote player has respawned *//* The main loop can set these variables to indicate various events to the network thread. The network thread will reset them to 0 after processing. */int network_opponent_hit = 0; /* remote player has been hit, subtract shields */int network_opponent_dead = 0; /* remote player has been destroyed */int local_player_respawn = 0; /* local player has respawned *//* Network thread */SDL_Thread *network_thread;/* Other misc. global variables */int fullscreen = 0;int hwsurface = 0;int doublebuf = 0;double time_scale = 0;SDL_Thread *music_update_thread = NULL;/* ========== Prototypes ========== */static unsigned int getrandom();static void initrandom();static void DrawPlayer(player_p p);static void InitPlayer(player_p p);static void UpdatePlayer(player_p p);static void PlayGame();/* This is a simple custom pseudorandom number generator. It's not a very good one, but it's sufficient for our purposes. Never trust the rand() included with the C library. Its quality varies between implementations, and it's easy to run into patterns within the generated numbers. At least this one is consistent. */static Sint32 seed = 0;static void initrandom(){ seed = time(NULL);}static unsigned int getrandom(){ Sint32 p1 = 1103515245; Sint32 p2 = 12345; seed = (seed*p1+p2) % 2147483647; return (unsigned)seed/3;}/* ======= Drawing ======= *//* Draws the given player to the screen. */static void DrawPlayer(player_p p){ SDL_Rect src, dest; int angle; /* Calculate the player's new screen coordinates. */ p->screen_x = (int)p->world_x - camera_x; p->screen_y = (int)p->world_y - camera_y; /* If the player isn't on the screen, don't draw anything. */ if (p->screen_x < -PLAYER_WIDTH/2 || p->screen_x >= SCREEN_WIDTH+PLAYER_WIDTH/2) return; if (p->screen_y < -PLAYER_HEIGHT/2 || p->screen_y >= SCREEN_HEIGHT+PLAYER_HEIGHT/2) return; /* Calculate drawing coordinates. */ angle = p->angle; if (angle < 0) angle += 360; src.x = PLAYER_WIDTH * (angle / 4); src.y = 0; src.w = PLAYER_WIDTH; src.h = PLAYER_HEIGHT; dest.x = p->screen_x - PLAYER_WIDTH/2; dest.y = p->screen_y - PLAYER_HEIGHT/2; dest.w = PLAYER_WIDTH; dest.h = PLAYER_HEIGHT; /* Draw the sprite. */ SDL_BlitSurface(ship_strip,&src,screen,&dest);}/* ============= Player Update ============= *//* Initializes the given player. */static void InitPlayer(player_p p){ p->world_x = getrandom() % WORLD_WIDTH; p->world_y = getrandom() % WORLD_HEIGHT; p->accel = 0; p->velocity = 0; p->angle = 0; p->charge = 0; p->firing = 0; p->shields = 100; UpdatePlayer(p);}/* Calculates a player's new world coordinates based on the camera and the player's velocity. Adds acceleration to velocity. Uses simple trigonometry to update the world coordinates. */static void UpdatePlayer(player_p p){ float angle; angle = (float)p->angle; SDL_LockMutex(player_mutex); p->velocity += p->accel * time_scale; if (p->velocity > PLAYER_MAX_VELOCITY) p->velocity = PLAYER_MAX_VELOCITY; if (p->velocity < PLAYER_MIN_VELOCITY) p->velocity = PLAYER_MIN_VELOCITY; p->world_x += p->velocity * cos(angle*PI/180.0) * time_scale; p->world_y += p->velocity * -sin(angle*PI/180.0) * time_scale; /* Make sure the player doesn't slide off the edge of the world. */ if (p->world_x < 0) p->world_x = 0; if (p->world_x >= WORLD_WIDTH) p->world_x = WORLD_WIDTH-1; if (p->world_y < 0) p->world_y = 0; if (p->world_y >= WORLD_HEIGHT) p->world_y = WORLD_HEIGHT-1; SDL_UnlockMutex(player_mutex);}/* This routine first sends a network packet, then waits for one to arrive. It should be called once per frame. Returns 0 on success, -1 on network error. */static int UpdateNetworkPlayer(){ net_pkt_t incoming, outgoing; int firing, hit, dead, respawn; /* Compose a packet. Don't hold the data lock any longer than necessary (don't want to hold up the main loop). */ SDL_LockMutex(player_mutex); if (network_opponent_hit) { network_opponent_hit--; hit = 1; } else hit = 0; CreateNetPacket(&outgoing, &player, hit, network_opponent_dead, local_player_respawn); network_opponent_dead = 0; local_player_respawn = 0; SDL_UnlockMutex(player_mutex); /* Send it. */ if (WriteNetgamePacket(&netlink, &outgoing) != 0) { fprintf(stderr, "Unable to write network packet.\n"); return -1; } /* Receive a packet from the remote host. */ if (ReadNetgamePacket(&netlink, &incoming) != 0) { fprintf(stderr, "Unable to read network packet.\n"); return -1; } /* Update our copy of the opponent's data. */ SDL_LockMutex(player_mutex); InterpretNetPacket(&incoming, &opponent.world_x, &opponent.world_y, &opponent.angle, &opponent.velocity, &firing, &hit, &dead, &respawn); opponent.firing = (double)firing; local_player_hit += hit; if (dead) local_player_dead = 1; if (respawn) network_opponent_respawn = 1; SDL_UnlockMutex(player_mutex); return 0;}static int NetworkThread(void *arg){ network_ok = 1; /* Avoid compiler warning. */ arg += 0; for (;;) { if (UpdateNetworkPlayer() != 0) { printf("Network error.\n"); network_ok = 0; break; } /* A slight speed brake. */ SDL_Delay(10); } return 0;}/* Returns 1 if the given player can fire, 0 otherwise. */int CanPlayerFire(player_p p){ if ((p->charge >= PHASER_CHARGE_FIRE) && (p->firing == 0)) return 1; return 0;}/* Turns on a phaser beam. Test CanPlayerFire first. */void FirePhasers(player_p p){ p->charge -= PHASER_CHARGE_FIRE; if (p->charge < 0) p->charge = 0; p->firing = PHASER_FIRE_TIME; /* Play the appropriate sound. */ if (p == &player) StartPlayerPhaserSound(); else StartOpponentPhaserSound();}/* Charge phasers by one increment. */static void ChargePhasers(player_p p){ p->charge += time_scale/30.0*PHASER_CHARGE_RATE; if (p->charge > PHASER_CHARGE_MAX) p->charge = PHASER_CHARGE_MAX;}/* Show a small explosion due to phaser damage. */static void ShowPhaserHit(player_p p){ CreateParticleExplosion( p->world_x, p->world_y, 255, 255, 255, 10, 300); CreateParticleExplosion( - p->world_x, p->world_y, 255, 0, 0, 5, 100); CreateParticleExplosion( p->world_x, p->world_y, 255, 255, 0, 2, 50);}/* Show a large ship explosion. */static void ShowShipExplosion(player_p p){ CreateParticleExplosion( p->world_x, p->world_y, 255, 255, 255, 15, 3000); CreateParticleExplosion( p->world_x, p->world_y, 255, 0, 0, 10, 1000); CreateParticleExplosion( p->world_x, p->world_y, 255, 255, 0, 5, 500);}/* Destroy the opponent. */static void KillOpponent(){ player.score++; ShowShipExplosion(&opponent); if (opponent_type == OPP_NETWORK) { /* Instruct the network thread to send a deathgram to the other end. */ network_opponent_hit = 0; network_opponent_dead++; } else { /* Just reset the opponent -- the script doesn't care. A more advanced AI system might need to be informed, though. */ InitPlayer(&opponent); }}/* Destroy the local player. */static void KillPlayer(){ ShowShipExplosion(&player); player.velocity = 0; player.accel = 0; opponent.score++;}/* Cause damage to the opponent. */static void DamageOpponent(){ opponent.shields -= PHASER_DAMAGE; network_opponent_hit++; if (opponent.shields <= 0) KillOpponent();}/* Music update thread. Calls UpdateMusic approximately twenty times a second. This decouples the music system from the game loop. See the text for an explanation. */int UpdateMusicThread(void *arg){ /* Avoid compiler warning. */ arg += 0; for (;;) { UpdateMusic(); SDL_Delay(10); } return 0;}/* ============== Main Game Loop ============== */static void PlayGame(){ Uint8 *keystate; int quit = 0; int turn; int prev_ticks = 0, cur_ticks = 0; /* for keeping track of timing */ int awaiting_respawn = 0; /* framerate counter variables */ int start_time, end_time; int frames_drawn = 0; /* respawn timer */ int respawn_timer = -1; prev_ticks = SDL_GetTicks(); start_time = time(NULL); /* Reset the score counters. */ player.score = 0; opponent.score = 0; /* Start sound playback. */ StartAudio(); StartMusic(); /* Start the music update thread. */ music_update_thread = SDL_CreateThread(UpdateMusicThread, NULL); if (music_update_thread == NULL) { printf("Unable to start music update thread.\n"); } /* Start the game! */ while ((quit == 0) && network_ok) { /* Determine how many milliseconds have passed since the last frame, and update our motion scaling. */ prev_ticks = cur_ticks; cur_ticks = SDL_GetTicks(); time_scale = (double)(cur_ticks-prev_ticks)/30.0; /* Update SDL's internal input state information. */ SDL_PumpEvents(); /* Grab a snapshot of the keyboard. */ keystate = SDL_GetKeyState(NULL); /* Lock the mutex so we can access the player's data. */ SDL_LockMutex(player_mutex);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -