← index
Super.Stoner.420/level.c
Source: Super.Stoner.420/level.c
#include "level.h"
#include "smx.h"
#define FIRE_COOLDOWN_MS 250
Level *level;
Hero  hero;
Evil  evil[50];
EvilGfx evil_gfx[4];
int offset = 0;
static int jump = 0, jump_ok = 0;
int hero_ani = 0;
void logic();
static int shoot_ani = 0;
static Uint32 last_fire_ticks = 0;

static int tile_solid_safe(int idx) {
    if (!level) return 1;
    if (idx < 0 || idx >= MAX_TILE) return 1;
    return level->tiles[idx].solid ? 1 : 0;
}

static int tile_block_safe(int idx) {
    if (!level) return -1;
    if (idx < 0 || idx >= MAX_TILE) return -1;
    return level->tiles[idx].block;
}

static int gfx_index_valid(int idx) {
    return (idx >= 0 && idx < (int)(sizeof(gfx) / sizeof(gfx[0])) && gfx[idx] != NULL);
}

static int collect_index_valid(int idx) {
    return (idx >= 0 && idx < (int)(sizeof(collect) / sizeof(collect[0])) && collect[idx] != NULL);
}

static void fill_evil(Evil *e, int vpos, int type) {
	e->vpos = vpos;
	e->type = type;
	e->dir = 0;
	e->egfx = &evil_gfx[e->type];
	switch(type) {
		case 0:
			break;
	}
}

Level *load_level(const char *src) {
	Level *lvl = 0;
	FILE *fptr = 0;
	fptr = fopen(src, "rb");
	if(!fptr)
		return 0;
	lvl = (Level*) malloc ( sizeof( Level ) );
	memset( lvl, 0, sizeof(Level) );
	fread( (char*)lvl, 1, sizeof(Level), fptr);
	fclose(fptr);
	hero.hpos = lvl->start_pos;
	hero.cur_ani = 0;
	hero.dir = 1;
	{
		unsigned int i = 0;
		memset(evil, 0, sizeof(evil));
		for(i = 0; i < 50; i++)
			if(lvl->grandma[i] != 0) {
				fill_evil(&evil[i], lvl->grandma[i], 0); 
			}
			else evil[i].type = -1;
	}
	return lvl;
}

void release_level(Level *lvl) {
	free(lvl);
}

void render_pause() {
    SDL_Rect rc = { 50,50,640-100,480-100 };
    
    if(stick && SDL_JoystickGetButton(stick, 1)) {
        cur_scr = ID_GAME;
    }
    if(stick && SDL_JoystickGetButton(stick, 0)) {
        game_over();
    }
    SDL_BlitSurface(bg, 0, front, 0);
    SDL_FillRect(front, &rc, 0);
    SDL_PrintText(front, font, 75, 75, SDL_MapRGB(front->format, 255, 255, 255), "Paused - Press Circle to continue ");
    SDL_PrintText(front, font, 75, 110, SDL_MapRGB(front->format, 255, 0, 0), "Press Triangle to Return to Menu ");
}

void render_map(SDL_Surface *surf, Level *lvl) {
    static int startby;
    static int bx,by;
    int gcount = 0;
    Uint32 i;
    startby = 75;
    bx = 75; by = startby;   
    
#ifdef FOR_PSP
    if(stick == NULL) {
        try_init_joystick();
    }
    
    if(stick && SDL_JoystickGetButton(stick, 10)) {
        cur_scr = ID_PAUSED;
        return;
    }
#endif

#ifndef FOR_PSP
    if (bg && surf) SDL_BlitSurface(bg, 0, surf, 0);
#endif
    logic();
    if (!lvl) return;
    for(i = 0; i < 700-4+24; i++)
    {
        int idx = offset + (int)i;
        if (idx < 0 || idx >= MAX_TILE) { 
            bx += 16; by = ( ( (++gcount) > 23 ) ? (startby + ( (gcount = 0), (bx += 16, 0) )) : by );
        } else {
            int block = tile_block_safe(idx);
            if (block >= 0 && gfx_index_valid(block)) {
                SDL_Rect rc = { bx, by, 16, 16 };
                SDL_BlitSurface(gfx[block], 0, surf, &rc);
            }
        }
        by = by + 16;
        gcount++;
        if(gcount > 23)
        {
            gcount = 0;
            by = startby;
            bx = bx + 16;
        }           
    }

    {
		SDL_Rect rc = { 25, 15, 640-50, 30 };
		SDL_FillRect(surf,&rc, 0);
	}
	SDL_PrintText(surf, font, 25, 25, SDL_MapRGB(surf->format, 255, 255, 255), lvl->level_name);
	{
		static char buf[256];
		memset(buf, 0, sizeof(buf));
		snprintf(buf,255, "Score: %d Lives: %d", score, lives);
		SDL_PrintText(surf,cfont,350, 25, SDL_MapRGB(surf->format, rand()%255, rand()%255, 255), buf);
	}
	startby = 75;
	bx = 75; by = startby;
	gcount = 0;
	hero.x = hero.y = 0;
	if(hero_ani == 1) {
		static int w = 0;
		if(w++ >= 3) w = 0;
		hero.cur_ani = w;
	}

	if(hero.cur_ani < 0 || hero.cur_ani >= (int)(sizeof(hgfx)/sizeof(hgfx[0])) ) {
		printf("Warning: Invalid hero animation index: %d\n", hero.cur_ani);
		hero.cur_ani = 0;
	}

	for( i = 0; i < 700-4+24; i++) {
		if (!hgfx[hero.cur_ani]) {
			
		} else {
			SDL_Rect rc = { bx, by, hgfx[hero.cur_ani]->w-1, hgfx[hero.cur_ani]->h };
			if((int)i == hero.hpos) {
				hero.x = bx; hero.y = by;
				SDL_SetColorKey(hgfx[hero.cur_ani], SDL_TRUE, SDL_MapRGB(hgfx[hero.cur_ani]->format, 255, 255, 255));
				if(hero.dir == 1)
					SDL_BlitSurface( hgfx[hero.cur_ani], 0, surf, &rc);
				else {
					SDL_Rect rc2 = { 1, 1, hgfx[hero.cur_ani]->w-1, hgfx[hero.cur_ani]->h-1 };
					SDL_ReverseBlt(hgfx[hero.cur_ani], &rc2, surf, &rc, SDL_MapRGB(hgfx[hero.cur_ani]->format, 255, 255, 255));
				}
			}
		}

		{
			Uint8 pos = 0;
			for(pos = 0; pos < MAX_PARTICLE; pos++) {
				if(pos < MAX_PARTICLE && emiter.p[pos].type != 0 && emiter.p[pos].vpos == (int)i+offset) {
                    if (particles[0]) {
                        SDL_Rect rcX = { bx, by, particles[0]->w, particles[0]->h };
                        SDL_SetColorKey(particles[0], SDL_TRUE, SDL_MapRGB(particles[0]->format, 255, 255, 255));
                        SDL_BlitSurface(particles[0], 0, surf, &rcX );
                        emiter.p[pos].x = bx; emiter.p[pos].y = by;
                    }
				}
			}
			for(pos = 0; pos < 50; pos++) {
				if(pos < 50 && evil[pos].type != -1 && evil[pos].vpos == (int)i+offset) {
                    if(evil[pos].egfx && evil[pos].cur_ani >= 0 && evil[pos].cur_ani < 8) {
                        int ani = evil[pos].cur_ani;
                        if (evil[pos].egfx->gfx[ani]) {
                            SDL_Rect erc = { 0,  0, evil[pos].egfx->gfx[ani]->w-1, evil[pos].egfx->gfx[ani]->h };
                            SDL_Rect prc = { bx, by, evil[pos].egfx->gfx[ani]->w, evil[pos].egfx->gfx[ani]->h };
                            if(evil[pos].dir == 1) {
                                SDL_SetColorKey(evil[pos].egfx->gfx[ani], SDL_TRUE, SDL_MapRGB(evil[pos].egfx->gfx[ani]->format, 255, 255, 255));
                                SDL_BlitSurface(evil[pos].egfx->gfx[ani], 0, surf, &prc);
                            } else {
                                SDL_ReverseBlt(evil[pos].egfx->gfx[ani], &erc, surf, &prc, SDL_MapRGB(evil[pos].egfx->gfx[ani]->format, 255, 255, 255));
                            }
                            evil[pos].x = bx; evil[pos].y = by;
                        }
                    }
                }
                if(pos < 50 && level->items[pos].type != 0 && level->items[pos].vpos == (int)i+offset) {
                    int t = level->items[pos].type;
                    if (collect_index_valid(t)) {
                        SDL_Rect rc = { bx, by, collect[t]->w, collect[t]->h };
                        SDL_SetColorKey(collect[t], SDL_TRUE, SDL_MapRGB(collect[t]->format, 255, 255, 255));
                        SDL_BlitSurface(collect[t], 0, surf, &rc);
                    }
                }
            }
        }
        by = by + 16;
        gcount++;
        if(gcount > 23) {
            gcount = 0;
            by = startby;
            bx = bx + 16;
        }
    }

    int hero_idx = hero.hpos + offset;
    if(hero_idx >= 0 && hero_idx < MAX_TILE && level) {
        if(level->tiles[hero_idx].block == 14) {
            reload_level();
        }
    }
    if(lives < 0)
		game_over();
}

void scroll_left() {
	if(offset > 0)
		offset -= 24;
}

void scroll_right() {
	if(offset < MAX_TILE)
		offset += 24;
}

static void move_left() {
    if(!level) {
        printf("Warning: Null level or tiles in move_left\n");
        return;
    }

    hero.dir = 0;
    if(hero.hpos > 0 && offset == 0) {
        if(hero.hpos >= 24) {
            Uint8 check[5];
            int a0 = hero.hpos-24, a1 = hero.hpos+1-24, a2 = hero.hpos+2-24, a3 = hero.hpos+3-24, a4 = hero.hpos-48;
            check[0] = tile_solid_safe(a0);
            check[1] = tile_solid_safe(a1);
            check[2] = tile_solid_safe(a2);
            check[3] = tile_solid_safe(a3);
            check[4] = (a4 >= 0) ? tile_solid_safe(a4) : 1;
            if(!check[0] && !check[1] && !check[2] && !check[3] && !check[4]) hero.hpos -= 24;
            hero_ani = 1;
        }
    }
    else {
        if(hero.hpos + offset >= 24) {
            Uint8 check[5];
            int base = hero.hpos + offset;
            check[0] = tile_solid_safe(base-24);
            check[1] = tile_solid_safe(base+1-24);
            check[2] = tile_solid_safe(base+2-24);
            check[3] = tile_solid_safe(base+3-24);
            check[4] = (base >= 48) ? tile_solid_safe(base-48) : 1;
            if(!check[0] && !check[1] && !check[2] && !check[3] && !check[4]) scroll_left();
            hero_ani = 1;
        }
    }
}

static void move_right() {
    if(!level) {
        printf("Warning: Null level or tiles in move_right\n");
        return;
    }

    Uint8 check[5];
    hero.dir = 1;
    if(hero.hpos < 24*15) {
        if(hero.hpos + 27 < MAX_TILE) {
            check[0] = tile_solid_safe(hero.hpos + 27);
            check[1] = tile_solid_safe(hero.hpos + 27 + 24);
            check[2] = tile_solid_safe(hero.hpos + 27 + 23);
            check[3] = tile_solid_safe(hero.hpos + 27 + 22);
            check[4] = tile_solid_safe(hero.hpos + 27 + 21);
            if(!check[0] && !check[1] && !check[2] && !check[3] && !check[4]) hero.hpos += 24;
            hero_ani = 1;
            if(jump_ok == 0 && jump == 0 && hero_ani == 0 && shoot_ani == 0)
                hero.cur_ani = 0;
        }
    }
    else {
        int base = hero.hpos + 27 + offset;
        if(base < MAX_TILE) {
            check[0] = tile_solid_safe(base);
            check[1] = tile_solid_safe(base + 24);
            check[2] = tile_solid_safe(base + 23);
            check[3] = tile_solid_safe(base + 22);
            check[4] = tile_solid_safe(base + 21);
            if(!check[0] && !check[1] && !check[2] && !check[3] && !check[4]) scroll_right();
            hero_ani = 1;
        }
    }
}


static void rls_bullet() {
    Uint32 now = SDL_GetTicks();
    if (now - last_fire_ticks < (Uint32)FIRE_COOLDOWN_MS) {
        return; 
    }
    last_fire_ticks = now;

    if (level == NULL) {
        return;
    }
    
    int particle_pos;
    if(hero.dir == 1) {
        particle_pos = offset + hero.hpos + 24 + 24 + 1;
    } else {
        particle_pos = offset + hero.hpos - 24 + 1;
    }
    
    if(particle_pos >= 0 && particle_pos < MAX_TILE) {
        rls_particle(&emiter, particle_pos, 1, hero.dir);
#ifdef HAS_MIXER
        Mix_PlayChannel(-1, fire_snd, 0);
#endif
    } else {
        printf("Warning: Attempted to fire particle outside level bounds\n");
    }
}

static int check_input() {
    const Uint8 *keys = SDL_GetKeyboardState(0);
    int axis_x = 0;
    
    
    if(stick != NULL) {
        axis_x = SDL_JoystickGetAxis(stick, 0); 
    }
    
    static int w = 0;
    
    if((keys[SDL_SCANCODE_A] || (stick && SDL_JoystickGetButton(stick, 0))) && jump_ok == 1 && jump == 0)
        jump = 1, shoot_ani = 0;
        
    if((keys[SDL_SCANCODE_S] || (stick && SDL_JoystickGetButton(stick, 1))) && jump_ok == 1 && jump == 0) {
        if(shoot_ani == 0) { shoot_ani = 1, hero.cur_ani = 5; }
    }
    
    
#ifdef FOR_PSP
    if(stick && SDL_JoystickGetButton(stick, 7)) {
#else
    if((keys[SDL_SCANCODE_LEFT] || (stick && axis_x < -8000))) {
#endif
        move_left();
        return 0;
    }
    
#ifdef FOR_PSP
    if(stick && SDL_JoystickGetButton(stick, 9)) {
#else
    if((keys[SDL_SCANCODE_RIGHT] || (stick && axis_x > 8000))) {
#endif
        move_right();
        return 0;
    }
    return 1;
}
static void collect_item(int type) {
	score += 10*type;
	#ifdef HAS_MIXER
	if(collect_snd != 0)
	Mix_PlayChannel( -1, collect_snd, 0);
	#endif
}
static void proc_collect() {
	Uint8 i;
	for(i = 0; i < 50; i++) {
		if(level->items[i].type != 0) {
			unsigned int z = 0;
			for(z = 0; z < 4; z++) {
			if((offset+hero.hpos+24+z == level->items[i].vpos) || (offset+hero.hpos-24-z == level->items[i].vpos) || (offset+hero.hpos+z == level->items[i].vpos) || offset+hero.hpos-z == level->items[i].vpos )
			{
				collect_item(level->items[i].type);
				level->items[i].type = 0;
				level->items[i].vpos = 0;
				return;
			}
			}
		}
	}
}

Uint32 proccess_game(Uint32 interval, void *p) {
    if(level == 0)
        return interval;
    int x = check_input();
    proc_collect();
    proc_particles(&emiter);
    if(jump == 0) {
        int idx1 = hero.hpos + offset + 4;
        int idx2 = hero.hpos + offset + 4 + 24;
        if (idx1 >= 0 && idx1 < MAX_TILE && idx2 >= 0 && idx2 < MAX_TILE) {
            if((! tile_solid_safe(idx1)) && (! tile_solid_safe(idx2)))
            {
                hero.hpos++;
                hero.cur_ani = 4;
            } else {
                if(shoot_ani == 0) {
                    jump_ok = 1; jump = 0; 
                    hero_ani = 1;
                    hero.cur_ani = 0;
                }
            }
        } else {
            if(shoot_ani == 0) {
                jump_ok = 1; jump = 0; 
                hero_ani = 1;
                hero.cur_ani = 0;
            }
        }
    }
    else {
        jump++;
        if(hero.hpos > 0) {
            int idx = hero.hpos + offset - 1;
            if (idx >= 0 && idx < MAX_TILE && !tile_solid_safe(idx))
                hero.hpos--;
        }
        hero.cur_ani = 4;
        if(jump > 8) {
            jump = 0;
            jump_ok = 0;
        }
    }
    if(x == 1)
        hero_ani = 0;
    return interval;
}
void logic() {
	 if(shoot_ani == 0 && hero_ani == 0 && jump_ok == 0 && jump == 0)
		hero.cur_ani = 4;
	else if(shoot_ani == 1 && hero_ani == 0 ) {
		 hero.cur_ani++;
		 if(hero.cur_ani > 8) {
			 rls_bullet();
			 hero.cur_ani = 0;
			 shoot_ani = 0;
			 hero_ani = 0;
		 }
	} else if(hero_ani == 1 && jump_ok == 0) { hero_ani = 0; }
}
void init_particles(Emiter *e) {
	memset(e, 0, sizeof(Emiter));
}
void game_over() {
	init_game();
}
static void hero_die() {
	Level *lvl = level;
	offset = 0;
	hero.hpos = level->start_pos;
	memset(&emiter, 0, sizeof(emiter));
	{
		unsigned int i = 0;
		memset(evil, 0, sizeof(evil));
		for(i = 0; i < 50; i++)
			if(lvl->grandma[i] != 0) {
				fill_evil(&evil[i], lvl->grandma[i], 0); 
			}
			else evil[i].type = -1;
	}
	lives--;
	hero_ani = 0;
	shoot_ani = 0;
	jump = 0;
	jump_ok = 1;
}
void proc_particles(Emiter *e) {
    unsigned int i = 0;
    static int lost_focus[50] = {0};
    
    if(!e || !level) {
        printf("Warning: Invalid parameters in proc_particles\n");
        return;
    }
    
    for(i = 0; i < MAX_PARTICLE; i++) {
        if(e->p[i].type == 0) {
            continue;
        }
        
        if(e->p[i].vpos >= MAX_TILE-24 || e->p[i].vpos <= 24 || tile_solid_safe(e->p[i].vpos))
        {
            e->p[i].type = 0;
            continue;
        }
        
        if(e->p[i].dir == 0) {
            if(e->p[i].vpos >= 48) {  
                e->p[i].vpos -= 48;
            } else {
                e->p[i].type = 0;  
            }
        }
        else {
            if(e->p[i].vpos + 48 < MAX_TILE) {  
                e->p[i].vpos += 48;
            } else {
                e->p[i].type = 0;  
            }
        }
    }
    for( i = 0; i < 50; i++ ) {
        Uint8 check[5];
        if(i >= 50) continue;
        if(evil[i].type != -1) {
            if(evil[i].vpos + 4 >= 0 && evil[i].vpos + 4 < MAX_TILE) {
                if(!tile_solid_safe(evil[i].vpos+4))
                    evil[i].vpos++;
            }
            
            int horizontal_distance = abs((int)evil[i].vpos - (int)(hero.hpos + offset));
            
            int level_width = 24; 
            
            int enemy_tile_x = (evil[i].vpos >= 0) ? (evil[i].vpos % level_width) : 0;
            int enemy_tile_y = (evil[i].vpos >= 0) ? (evil[i].vpos / level_width) : 0;
            int hero_tile_x = ((hero.hpos + offset) >= 0) ? ((hero.hpos + offset) % level_width) : 0;
            int hero_tile_y = ((hero.hpos + offset) >= 0) ? ((hero.hpos + offset) / level_width) : 0;
            
            int vertical_distance = abs(enemy_tile_y - hero_tile_y);
            
            int is_aggressive = (horizontal_distance < 96) &&          
                               (vertical_distance < 3) &&  
                               !lost_focus[i]; 
            
            if(evil[i].die == 0) {
                evil[i].cur_ani ++;
                if(evil[i].cur_ani >= 5)
                evil[i].cur_ani = 0;
            } else {
                evil[i].cur_ani++;
                if(evil[i].cur_ani > 7) {
                    evil[i].type = -1;
                    lost_focus[i] = 0; 
#ifdef HAS_MIXER
                    if(kill_snd != 0)
                        Mix_PlayChannel(-1, kill_snd, 0);
#endif
                    break;
                }
            }
            
            int move_speed = is_aggressive ? 48 : 24; 
            
            if(evil[i].dir == 0) {
                check[0] = tile_solid_safe(evil[i].vpos - move_speed);
                check[1] = tile_solid_safe(evil[i].vpos + 1 - move_speed);
                check[2] = tile_solid_safe(evil[i].vpos + 2 - move_speed);
                check[3] = tile_solid_safe(evil[i].vpos + 3 - move_speed);
                check[4] = tile_solid_safe(evil[i].vpos - move_speed - 24);
                
                if(!check[0] && !check[1] && !check[2] && !check[3] && !check[4]) 
                    evil[i].vpos -= move_speed; 
                else {
                    evil[i].dir = 1; 
                    lost_focus[i] = 1; 
                }
            }
            else if(evil[i].dir == 1) {
                int check_pos = move_speed + 3;
                check[0] = tile_solid_safe(evil[i].vpos + check_pos);
                check[1] = tile_solid_safe(evil[i].vpos + check_pos + 24);
                check[2] = tile_solid_safe(evil[i].vpos + check_pos + 23);
                check[3] = tile_solid_safe(evil[i].vpos + check_pos + 22);
                check[4] = tile_solid_safe(evil[i].vpos + check_pos + 21);
                
                if(!check[0] && !check[1] && !check[2] && !check[3] && !check[4]) 
                    evil[i].vpos += move_speed;
                else {
                    evil[i].dir = 0; 
                    lost_focus[i] = 0; 
                }
            }
            
            if(evil[i].type != -1 && hero.x > 0 && hero.y > 0 && evil[i].x > 0 && evil[i].y > 0)
            {
                int vertical_pixel_distance = abs(hero.y - evil[i].y);    
                if(vertical_pixel_distance < 32) 
                {
                    if(evil[i].egfx &&
                       evil[i].cur_ani >= 0 && evil[i].cur_ani < 8 &&
                       evil[i].egfx->gfx[evil[i].cur_ani] &&
                       hero.cur_ani >= 0 && hero.cur_ani < (int)(sizeof(hgfx)/sizeof(hgfx[0])) &&
                       hgfx[hero.cur_ani]) {
                        
                        SDL_Rect rcY = { evil[i].x + 2, evil[i].y + 2, evil[i].egfx->gfx[evil[i].cur_ani]->w - 4, evil[i].egfx->gfx[evil[i].cur_ani]->h - 4 };
                        SDL_Rect rcX = { hero.x + 2, hero.y + 2, hgfx[hero.cur_ani]->w - 4, hgfx[hero.cur_ani]->h - 4 };
                        if(SDL_Colide(&rcX, &rcY)) {
                            hero_die();
                            return;
                        }
                    }
                }
            }

            {
                Uint8 p = 0;
                for(; p < MAX_PARTICLE; p++) {
                    if(e->p[p].type != 0) {
                        if(e->p[p].x > 0 && e->p[p].y > 0 && e->p[p].x < 640 && e->p[p].y < 480 && 
                           evil[i].x > 0 && evil[i].y > 0 && evil[i].x < 640 && evil[i].y < 480 &&
                           particles[0] != NULL) {
                            
                            if(evil[i].egfx &&
                               evil[i].cur_ani >= 0 && evil[i].cur_ani < 8 &&
                               evil[i].egfx->gfx[evil[i].cur_ani]) {
                                
                                SDL_Rect rcX = { e->p[p].x, e->p[p].y, particles[0]->w, particles[0]->h };
                                SDL_Rect rcY = { evil[i].x, evil[i].y, evil[i].egfx->gfx[evil[i].cur_ani]->w, evil[i].egfx->gfx[evil[i].cur_ani]->h };
                                if(SDL_Colide(&rcX, &rcY)) {
                                    e->p[p].type = 0;
                                    evil[i].die = 1;
                                    evil[i].cur_ani = 5;
                                    score++;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

static int get_off_particle(Emiter *e) {
	unsigned int i = 0;
	for ( i = 0; i < MAX_PARTICLE; i++ ) {
		if(e->p[i].type == 0)
		return (int)i;
	}
	return -1;
}

void rls_particle(Emiter *e, int vpos, int type, int dir) {
    if (!e || vpos < 0 || vpos >= MAX_TILE || type <= 0) {
        return;
    }
    
    int off = get_off_particle(e);
    if(off != -1 && off < MAX_PARTICLE) {
        e->p[off].type = type;
        e->p[off].vpos = vpos;
        e->p[off].dir = dir;
    }
}