📄 tilemap.cpp
字号:
/*************************************************************************
"I Have No Tomatoes"
Copyright (c) 2004, Mika Halttunen
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute
it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
Mika Halttunen <lsoft@mbnet.fi>
*************************************************************************/
#include <stdlib.h>
#include "SDL.h"
#include "SDL_opengl.h"
#include "SDL_image.h"
#include "tilemap.h"
#include "texture.h"
#include "mymath.h"
#include "init.h"
#include "mpak.h"
#include "player.h"
// A dirty hack. Read the comments from the beginning of player.cpp
extern int players_on_block_x[2];
extern int players_on_block_y[2];
// Map display list
GLuint map_dlist;
// Map array with two layers
GLuint map[MAP_W][MAP_H][2];
// Teleport array
bool teleport_map[MAP_W][MAP_H];
// Teleport positions
int teleport_x[2] = { -1, -1 };
int teleport_y[2] = { -1, -1 };
// Teleport directions
int teleport_dir[2] = { 0, 0 };
// Map textures
GLuint maptex[NUM_TEX];
// Current tile roof texture
GLuint tile_roof_tex = 2;
// Load map textures
void load_maptex() {
for(int f=0; f<NUM_TEX; f++) {
char file[128] = "";
sprintf(file, "maptex%02d.jpg", f+1);
maptex[f] = load_jpg(file, false, true, true);
}
}
// Can an enemy teleport at given point?
bool can_teleport(int x, int y, bool player) {
// Check the boundaries
if(x < 0 || y < 0 || x > MAP_W-1 || y > MAP_H-1)
return false;
// If the player is teleporting, check the other teleports
if(player && ((x == teleport_x[0] && y == teleport_y[0]) || (x == teleport_x[1] && y == teleport_y[1])))
return false;
// Check the teleport areas
if(teleport_map[x][y])
return true;
return false;
}
// Is the map solid at given point?
bool map_solid(int x, int y) {
// Check the boundaries
if(x < 0 || y < 0 || x > MAP_W-1 || y > MAP_H-1)
return true;
// Check the blocks first
if(map[x][y][1])
return true;
// .. and then the floor tiles
return bool(!map[x][y][0]);
}
// Check if there's a block
bool is_block_at(int x, int y) {
if(x < 0 || y < 0 || x > MAP_W-1 || y > MAP_H-1)
return false;
return bool(map[x][y][1]);
}
// Build the map display list
void build_map() {
kill_map();
// Build the list
map_dlist = glGenLists(1);
glNewList(map_dlist, GL_COMPILE);
glEnable(GL_LIGHTING);
// Loop through the map
for(int y=0; y<MAP_H; y++) {
for(int x=0; x<MAP_W; x++) {
// Draw the upper tile
if(map[x][y][1]) {
BIND_TEXTURE(maptex[tile_roof_tex]);
// Top
glColor3f(0.8f, 0.8f, 0.8f);
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0,1,0);
glTexCoord2f(1,1); glVertex3f(x+1,TILE_H,y);
glTexCoord2f(0,1); glVertex3f(x,TILE_H,y);
glTexCoord2f(1,0); glVertex3f(x+1,TILE_H,y+1);
glTexCoord2f(0,0); glVertex3f(x,TILE_H,y+1);
glEnd();
// Left
BIND_TEXTURE(maptex[map[x][y][1] - 1]);
glColor3f(1,1,1);
if(!is_block_at(x,y+1)) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0,0,1);
glTexCoord2f(1,1); glVertex3f(x+1,TILE_H,y+1);
glTexCoord2f(0,1); glVertex3f(x,TILE_H,y+1);
glTexCoord2f(1,0); glVertex3f(x+1,0.0f,y+1);
glTexCoord2f(0,0); glVertex3f(x,0.0f,y+1);
glEnd();
}
// Right
if(!is_block_at(x+1,y)) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(1,0,0);
glTexCoord2f(1,1); glVertex3f(x+1,TILE_H,y);
glTexCoord2f(0,1); glVertex3f(x+1,TILE_H,y+1);
glTexCoord2f(1,0); glVertex3f(x+1,0.0f,y);
glTexCoord2f(0,0); glVertex3f(x+1,0.0f,y+1);
glEnd();
}
}
// Draw the lower tile
else if(map[x][y][0]) {
glDepthMask(GL_FALSE);
BIND_TEXTURE(maptex[map[x][y][0] - 1]);
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0,1,0);
glTexCoord2f(1,1); glVertex3f(x+1,0,y);
glTexCoord2f(0,1); glVertex3f(x,0,y);
glTexCoord2f(1,0); glVertex3f(x+1,0,y+1);
glTexCoord2f(0,0); glVertex3f(x,0,y+1);
glEnd();
glDepthMask(GL_TRUE);
}
}
}
glDisable(GL_LIGHTING);
glEndList();
}
// Draw the map
void draw_map() {
glColor3f(1,1,1);
#ifndef EDITOR
// This is a dirty hack cos I can't use the display list when the player is on a block.
if(players_on_block_x[0] == -1 && players_on_block_x[1] == -1)
glCallList(map_dlist);
else {
// Draw the map dynamically and alter the depth writing of the tile where the player is.
glEnable(GL_LIGHTING);
// Loop through the map
for(int y=0; y<MAP_H; y++) {
for(int x=0; x<MAP_W; x++) {
// Draw the upper tile
if(map[x][y][1]) {
// Is the player on this tile?
bool player_over = ((players_on_block_x[0] == x && players_on_block_y[0] == y) || (players_on_block_x[1] == x && players_on_block_y[1] == y)) ? true : false;
// Left
BIND_TEXTURE(maptex[map[x][y][1] - 1]);
glColor3f(1,1,1);
if(!is_block_at(x,y+1)) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0,0,1);
glTexCoord2f(1,1); glVertex3f(x+1,TILE_H,y+1);
glTexCoord2f(0,1); glVertex3f(x,TILE_H,y+1);
glTexCoord2f(1,0); glVertex3f(x+1,0.0f,y+1);
glTexCoord2f(0,0); glVertex3f(x,0.0f,y+1);
glEnd();
}
// Back left
if(player_over) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0,0,1);
glTexCoord2f(1,1); glVertex3f(x+1,TILE_H,y);
glTexCoord2f(0,1); glVertex3f(x,TILE_H,y);
glTexCoord2f(1,0); glVertex3f(x+1,0.0f,y);
glTexCoord2f(0,0); glVertex3f(x,0.0f,y);
glEnd();
}
// Right
if(!is_block_at(x+1,y)) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(1,0,0);
glTexCoord2f(1,1); glVertex3f(x+1,TILE_H,y);
glTexCoord2f(0,1); glVertex3f(x+1,TILE_H,y+1);
glTexCoord2f(1,0); glVertex3f(x+1,0.0f,y);
glTexCoord2f(0,0); glVertex3f(x+1,0.0f,y+1);
glEnd();
}
// Back right
if(player_over) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0,0,1);
glTexCoord2f(1,1); glVertex3f(x,TILE_H,y);
glTexCoord2f(0,1); glVertex3f(x,TILE_H,y+1);
glTexCoord2f(1,0); glVertex3f(x,0.0f,y);
glTexCoord2f(0,0); glVertex3f(x,0.0f,y+1);
glEnd();
}
// Draw a lower plane that will do some cover up work
// (e.g. the napalm explosions)
if(player_over) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0,1,0);
glTexCoord2f(1,1); glVertex3f(x+1,0.20f,y);
glTexCoord2f(0,1); glVertex3f(x,0.20f,y);
glTexCoord2f(1,0); glVertex3f(x+1,0.20f,y+1);
glTexCoord2f(0,0); glVertex3f(x,0.20f,y+1);
glEnd();
glDepthMask(GL_FALSE);
}
// We draw the top last because it has it's depth writing disable occasionally
glColor3f(0.8f, 0.8f, 0.8f);
BIND_TEXTURE(maptex[tile_roof_tex]);
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0,1,0);
glTexCoord2f(1,1); glVertex3f(x+1,TILE_H,y);
glTexCoord2f(0,1); glVertex3f(x,TILE_H,y);
glTexCoord2f(1,0); glVertex3f(x+1,TILE_H,y+1);
glTexCoord2f(0,0); glVertex3f(x,TILE_H,y+1);
glEnd();
if(player_over) {
glDepthMask(GL_TRUE);
}
glColor3f(1,1,1);
}
// Draw the lower tile
else if(map[x][y][0]) {
glDepthMask(GL_FALSE);
BIND_TEXTURE(maptex[map[x][y][0] - 1]);
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0,1,0);
glTexCoord2f(1,1); glVertex3f(x+1,0,y);
glTexCoord2f(0,1); glVertex3f(x,0,y);
glTexCoord2f(1,0); glVertex3f(x+1,0,y+1);
glTexCoord2f(0,0); glVertex3f(x,0,y+1);
glEnd();
glDepthMask(GL_TRUE);
}
}
}
glDisable(GL_LIGHTING);
}
#else
glEnable(GL_LIGHTING);
// Loop through the map
for(int y=0; y<MAP_H; y++) {
for(int x=0; x<MAP_W; x++) {
// Draw the upper tile
if(map[x][y][1]) {
BIND_TEXTURE(maptex[tile_roof_tex]);
// Top
glColor3f(0.8f, 0.8f, 0.8f);
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0,1,0);
glTexCoord2f(1,1); glVertex3f(x+1,TILE_H,y);
glTexCoord2f(0,1); glVertex3f(x,TILE_H,y);
glTexCoord2f(1,0); glVertex3f(x+1,TILE_H,y+1);
glTexCoord2f(0,0); glVertex3f(x,TILE_H,y+1);
glEnd();
// Left
BIND_TEXTURE(maptex[map[x][y][1] - 1]);
glColor3f(1,1,1);
if(!is_block_at(x,y+1)) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0,0,1);
glTexCoord2f(1,1); glVertex3f(x+1,TILE_H,y+1);
glTexCoord2f(0,1); glVertex3f(x,TILE_H,y+1);
glTexCoord2f(1,0); glVertex3f(x+1,0.0f,y+1);
glTexCoord2f(0,0); glVertex3f(x,0.0f,y+1);
glEnd();
}
// Right
if(!is_block_at(x+1,y)) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(1,0,0);
glTexCoord2f(1,1); glVertex3f(x+1,TILE_H,y);
glTexCoord2f(0,1); glVertex3f(x+1,TILE_H,y+1);
glTexCoord2f(1,0); glVertex3f(x+1,0.0f,y);
glTexCoord2f(0,0); glVertex3f(x+1,0.0f,y+1);
glEnd();
}
}
// Draw the lower tile
else if(map[x][y][0]) {
glDepthMask(GL_FALSE);
BIND_TEXTURE(maptex[map[x][y][0] - 1]);
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0,1,0);
glTexCoord2f(1,1); glVertex3f(x+1,0,y);
glTexCoord2f(0,1); glVertex3f(x,0,y);
glTexCoord2f(1,0); glVertex3f(x+1,0,y+1);
glTexCoord2f(0,0); glVertex3f(x,0,y+1);
glEnd();
glDepthMask(GL_TRUE);
}
// Draw the teleport areas
if(teleport_map[x][y]) {
BIND_TEXTURE(0);
glDepthMask(GL_FALSE);
glDisable(GL_LIGHTING);
glBegin(GL_TRIANGLES);
glColor3f(.1f,.4f,.5f);
glVertex3f(x+0.3f, 0, y+0.7f);
glVertex3f(x+0.7f, 0, y+0.3f);
glColor3f(.2f,.7f,1);
glVertex3f(x+.5f, 0.8f, y+.5f);
glEnd();
/*glBegin(GL_TRIANGLE_STRIP);
glVertex3f(x+0.75,0.3,y+0.25);
glVertex3f(x+0.25,0.3,y+0.25);
glVertex3f(x+0.75,0.3,y+0.75);
glVertex3f(x+0.25,0.3,y+0.75);
glEnd();
*/
glEnable(GL_LIGHTING);
glDepthMask(GL_TRUE);
glColor3f(1,1,1);
}
}
}
glDisable(GL_LIGHTING);
#endif
}
// Destroy the map display list
void kill_map() {
glDeleteLists(map_dlist, 1);
}
// Save the map to a file
void save_map(char *file) {
#ifdef EDITOR
// Open the file
FILE *f = fopen(file, "wb");
if(!f)
error_msg("Unable to save the level to %s!\n", file);
// Save the ID
fputc('M', f);
fputc('H', f);
fputc('!', f);
// Save the player position
fputc(p1.x, f);
fputc(p1.y, f);
// Save the player direction
fputc(p1.dir, f);
// Save the teleport positions and directions
fputc(teleport_x[0] + 1, f);
fputc(teleport_y[0] + 1, f);
fputc(teleport_x[1] + 1, f);
fputc(teleport_y[1] + 1, f);
fputc(teleport_dir[0], f);
fputc(teleport_dir[1], f);
// Save the roof texture
fputc(tile_roof_tex, f);
// Save the map layers
for(int y=0; y < MAP_H; y++) {
for(int x=0; x < MAP_W; x++) {
// Save the floors and blocks
fputc(map[x][y][0], f);
fputc(map[x][y][1], f);
// Save the teleport marks
fputc((teleport_map[x][y]) ? 1 : 0, f);
}
}
// Close the file
fclose(f);
#endif
}
// Load the map from a file
void load_map(char *file) {
#ifndef EDITOR
// Load the level from the pakfile
FILE *f = pakfile.open_file(file);
if(!f)
error_msg("Unable to load the level %s from the pakfile!\n", file);
#else
// Open the file
FILE *f = fopen(file, "rb");
if(!f)
return;
#endif
// Check the ID
char id[3];
id[0] = fgetc(f);
id[1] = fgetc(f);
id[2] = fgetc(f);
if(id[0] != 'M' || id[1] != 'H' || id[2] != '!')
error_msg("'%s' is not a level file for this game!\n", file);
// Get the player position
p1.x = fgetc(f);
p1.y = fgetc(f);
p1.tx = p1.x;
p1.ty = p1.y;
p2.x = p2.tx = p1.x;
p2.y = p2.ty = p1.y;
// Get the player direction
p1.dir = fgetc(f);
p1.nextdir = p1.dir;
p2.dir = p2.nextdir = p1.dir;
// Get the teleport positions and directions
teleport_x[0] = fgetc(f) - 1;
teleport_y[0] = fgetc(f) - 1;
teleport_x[1] = fgetc(f) - 1;
teleport_y[1] = fgetc(f) - 1;
teleport_dir[0] = fgetc(f);
teleport_dir[1] = fgetc(f);
// Get the roof texture
tile_roof_tex = fgetc(f);
// Load the map layers
for(int y=0; y < MAP_H; y++) {
for(int x=0; x < MAP_W; x++) {
// Get the floors and blocks
map[x][y][0] = fgetc(f);
map[x][y][1] = fgetc(f);
// Get the teleport marks
teleport_map[x][y] = (fgetc(f)) ? true : false;
}
}
// Close the file
fclose(f);
build_map();
}
// Clear the map
void clear_map() {
for(int y=0; y<MAP_H; y++) {
for(int x=0; x<MAP_W; x++) {
map[x][y][0] = 1;
map[x][y][1] = 0;
teleport_map[x][y] = false;
}
}
teleport_x[0] = teleport_y[1] = -1;
teleport_x[1] = teleport_y[1] = -1;
teleport_dir[0] = teleport_dir[1] = 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -