/* ------------------------------------------------------------ Kobo Deluxe - An enhanced SDL port of XKobo ------------------------------------------------------------ * Copyright (C) 1995, 1996, Akira Higuchi * Copyright (C) 2001-2003, 2007 David Olofson * * 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. * * This program 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 this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "kobo.h" #include "screen.h" #include "myship.h" #include "enemies.h" #include "gamectl.h" #include "manage.h" #include "random.h" #include "sound.h" #include "prof.h" #define WING_GUN_OFFSET 4 // Unit-vector tables indexed by direction (1=N, 2=NE, ..., 8=NW). Index 0 unused. // Odd directions are cardinals; even directions are diagonals. static const int dir_dx[9] = { 0, 0, +1, +1, +1, 0, -1, -1, -1 }; static const int dir_dy[9] = { 0, -1, -1, 0, +1, +1, +1, 0, -1 }; _myship_state _myship::_state; int _myship::di; int _myship::virtx; int _myship::virty; int _myship::x; int _myship::y; int _myship::_health; int _myship::health_time; int _myship::explo_time; int _myship::nose_reload_timer; int _myship::nose_temperature; int _myship::nose_alt = 0; int _myship::tail_reload_timer; int _myship::tail_temperature; int _myship::tail_alt = WING_GUN_OFFSET; int _myship::lapx; int _myship::lapy; int _myship::boltx[MAX_BOLTS]; int _myship::bolty[MAX_BOLTS]; int _myship::boltdx[MAX_BOLTS]; int _myship::boltdy[MAX_BOLTS]; int _myship::boltst[MAX_BOLTS]; cs_obj_t *_myship::object; cs_obj_t *_myship::bolt_objects[MAX_BOLTS]; cs_obj_t *_myship::crosshair; _myship::_myship() { object = NULL; memset(bolt_objects, 0, sizeof(bolt_objects)); crosshair = NULL; } void _myship::state(_myship_state s) { switch (s) { case dead: if(object) gengine->free_obj(object); object = NULL; if(crosshair) gengine->free_obj(crosshair); crosshair = NULL; break; case normal: if(!object) object = gengine->get_obj(LAYER_PLAYER); if(object) cs_obj_show(object); break; } _state = s; } void _myship::free_bolt(int i) { boltst[i] = 0; if(bolt_objects[i]) { gengine->free_obj(bolt_objects[i]); bolt_objects[i] = NULL; } } void _myship::off() { state(dead); for(int i = 0; i < MAX_BOLTS; i++) free_bolt(i); } int _myship::init() { nose_reload_timer = 0; nose_temperature = 0; tail_reload_timer = 0; tail_temperature = 0; x = WORLD_SIZEX >> 1; y = (WORLD_SIZEY >> 2) * 3; virtx = x - (WSIZE >> 1); virty = y - (WSIZE >> 1); lapx = 0; lapy = 0; di = 1; state(normal); apply_position(); for(int i = 0; i < MAX_BOLTS; i++) { boltx[i] = 0; bolty[i] = 0; boltdx[i] = 0; boltdy[i] = 0; free_bolt(i); } return 0; } void _myship::explode(int x, int y) { if(explo_time == 56) manage.noise_out(1000); else if(explo_time > 64) return; int d = 4096 - (64 - explo_time)*(64 - explo_time); int i; for(i = 0; i < 2; ++i) { int dx = (int)(pubrand.get(6)) - 32; int dy = (int)(pubrand.get(6)) - 32; int vx = dx * (4096 - d) >> 8; int vy = dy * (4096 - d) >> 8; dx = dx * d >> 12; dy = dy * d >> 12; enemies.make(enemies.randexp(), x + dx, y + dy, vx, vy, explo_time >> 4); } ++explo_time; } #define BEAMV1 12 #define BEAMV2 (BEAMV1*2/3) int _myship::move() { int i; di = gamecontrol.dir(); if(++health_time >= game.health_fade) { health_time = 0; if(_health > game.health) --_health; } virtx = x - (WSIZE >> 1); virty = y - (WSIZE >> 1); if(_state == normal) { int vd, vo; if(!prefs->cmd_pushmove) { vd = 2; vo = 3; } else if(gamecontrol.dir_push()) { vd = 1; vo = 1; } else { vd = 0; vo = 0; } int spd = (di & 1) ? vo : vd; virtx += dir_dx[di] * spd; virty += dir_dy[di] * spd; explo_time = 0; } else if(_state == dead) explode(x, y); lapx = 0; lapy = 0; if(virtx < 0) { virtx += WORLD_SIZEX; lapx = WORLD_SIZEX; } if(virtx >= WORLD_SIZEX) { virtx -= WORLD_SIZEX; lapx = -WORLD_SIZEX; } if(virty < 0) { virty += WORLD_SIZEY; lapy = WORLD_SIZEY; } if(virty >= WORLD_SIZEY) { virty -= WORLD_SIZEY; lapy = -WORLD_SIZEY; } x = virtx + (WSIZE >> 1); y = virty + (WSIZE >> 1); nose_temperature -= game.nosecooling; if(nose_temperature < 0) nose_temperature = 0; tail_temperature -= game.tailcooling; if(tail_temperature < 0) tail_temperature = 0; if((_state == normal) && gamecontrol.get_shot()) { if(game.skill == SKILL_CLASSIC) { if(nose_reload_timer > 0) --nose_reload_timer; else { _myship::xkobo_shot(); nose_reload_timer = game.noseloadtime - 1; } } else { int fired = 0; if(tail_reload_timer > 0) --tail_reload_timer; else if(!_myship::tail_fire()) fired = 1; // Ok! else fired = 2; // Overheat! if(nose_reload_timer > 0) --nose_reload_timer; else if(!_myship::nose_fire()) fired = 1; // Ok! else fired = 2; // Overheat! if(fired == 1) sound.g_player_fire(); else if(fired == 2) sound.g_player_overheat(); } } else { if(!gamecontrol.get_shot()) { nose_alt = 1; sound.g_player_fire_off(); } if(nose_reload_timer > 0) --nose_reload_timer; if(tail_reload_timer > 0) --tail_reload_timer; } const char animtab[8] = { 3, 2, 1, 0, 1, 2, 1, 2 }; for(i = 0; i < MAX_BOLTS; i++) { if(!boltst[i]) continue; ++boltst[i]; boltx[i] += lapx; bolty[i] += lapy; boltx[i] += boltdx[i]; bolty[i] += boltdy[i]; if((ABS(boltx[i] - x) >= (VIEWLIMIT >> 1) + 16) || (ABS(bolty[i] - y) >= (VIEWLIMIT >> 1) + 16)) free_bolt(i); else if(bolt_objects[i]) cs_obj_image(bolt_objects[i], B_BOLT, (bolt_objects[i]->anim.frame & 0xfffffffc) + animtab[boltst[i] & 7]); } return 0; } void _myship::hit(int dmg) { if(_state != normal) return; if(!dmg) dmg = 1000; if(_health && (dmg < _health)) manage.noise_damage((float)dmg / _health); else manage.noise_damage(1.0f); _health -= dmg; if(_health > 0) sound.g_player_damage(); else { _health = 0; manage.lost_myship(); state(dead); sound.g_player_explo_start(); } } void _myship::health_bonus(int h) { if(game.skill == SKILL_CLASSIC) return; _health += game.health * h / 100; health_time = 0; if(_health > game.health * 2) _health = game.health * 2; } int _myship::hit_structure() { int x1, y1; int i, ch; // Check bolts/objects for(i = 0; i < MAX_BOLTS; i++) { if(!boltst[i]) continue; x1 = (boltx[i] & (WORLD_SIZEX - 1)) >> 4; y1 = (bolty[i] & (WORLD_SIZEY - 1)) >> 4; ch = screen.get_map(x1, y1); if(!IS_SPACE(ch) && (ch & HIT_MASK)) { sound.g_bolt_hit(x1<<12, y1<<12); enemies.make(&boltexpl, boltx[i], bolty[i]); free_bolt(i); } } // Check player/bases x1 = (x & (WORLD_SIZEX - 1)) >> 4; y1 = (y & (WORLD_SIZEY - 1)) >> 4; ch = screen.get_map(x1, y1); if(!IS_SPACE(ch) && (ch & HIT_MASK)) { if(prefs->cmd_indicator) sound.g_player_damage(); else _myship::hit(1000); } return 0; } int _myship::hit_bolt(int ex, int ey, int hitsize, int health) { PROF_ZONE("hit_bolt"); int dmg = 0; int i; for(i = 0; i < MAX_BOLTS; i++) { if(boltst[i] == 0) continue; if(ABS(ex - boltx[i]) >= hitsize) continue; if(ABS(ey - bolty[i]) >= hitsize) continue; if(!prefs->cmd_cheat) free_bolt(i); enemies.make(&boltexpl, boltx[i], bolty[i]); dmg += game.bolt_damage; if(dmg >= health) break; } return dmg; } void _myship::put_crosshair() { if(_state != normal) return; if(!crosshair) { crosshair = gengine->get_obj(LAYER_OVERLAY); if(crosshair) cs_obj_show(crosshair); } if(crosshair) { cs_obj_image(crosshair, B_CROSSHAIR, 0); crosshair->point.v.x = PIXEL2CS(mouse_x - 8 - MARGIN); crosshair->point.v.y = PIXEL2CS(mouse_y - MARGIN); } } int _myship::put() { /* Player */ apply_position(); if(object) cs_obj_image(object, B_PLAYER, (di - 1) * 2); /* Bullets */ int i; for(i = 0; i < MAX_BOLTS; i++) { if(!bolt_objects[i]) continue; if(boltst[i]) { bolt_objects[i]->point.v.x = PIXEL2CS(boltx[i]); bolt_objects[i]->point.v.y = PIXEL2CS(bolty[i]); } } return 0; } void _myship::shot_single(int i, int dir, int offset) { int doffset = (offset * 7071 + (offset > 0 ? 5000 : -5000)) / 10000; boltst[i] = 1; boltx[i] = x; bolty[i] = y; // Wing-gun position offset is perpendicular to the firing direction. int pd = (dir + 1) % 8 + 1; int poff = (pd & 1) ? offset : doffset; boltx[i] += dir_dx[pd] * poff; bolty[i] += dir_dy[pd] * poff; int v = (dir & 1) ? BEAMV1 : BEAMV2; boltdx[i] = dir_dx[dir] * v; boltdy[i] = dir_dy[dir] * v; if(!bolt_objects[i]) bolt_objects[i] = gengine->get_obj(LAYER_PLAYER); if(bolt_objects[i]) { bolt_objects[i]->point.v.x = PIXEL2CS(boltx[i]); bolt_objects[i]->point.v.y = PIXEL2CS(bolty[i]); cs_obj_image(bolt_objects[i], B_BOLT, (((dir - 1) & 0x3) << 2) + 3); cs_obj_show(bolt_objects[i]); } } int _myship::xkobo_shot() { int i, j; for(i = 0; i < game.bolts && boltst[i]; i++) ; for(j = i + 1; j < game.bolts && boltst[j]; j++) ; if(j >= game.bolts) { sound.g_player_overheat(1); return 1; } shot_single(i, di, 0); shot_single(j, (di > 4) ? (di - 4) : (di + 4), 0); sound.g_player_fire(); return 0; } int _myship::nose_fire() { int i, j; // Logic: If firing would cause overheat, wait! if(255 - nose_temperature < game.noseheatup) return 1; for(i = 0; i < game.bolts && boltst[i]; i++) ; if(!game.altfire) { if(i >= game.bolts) return 1; shot_single(i, di, 0); nose_temperature += game.noseheatup; nose_reload_timer = game.noseloadtime; return 0; } for(j = i + 1; j < game.bolts && boltst[j]; j++) ; if(j >= game.bolts) return 1; if(++nose_alt > 2) nose_alt = 0; shot_single(i, di, (nose_alt - 1) * WING_GUN_OFFSET); if(++nose_alt > 2) nose_alt = 0; shot_single(j, di, (nose_alt - 1) * WING_GUN_OFFSET); nose_temperature += game.noseheatup; nose_reload_timer = game.noseloadtime; return 0; } int _myship::tail_fire() { int i; if(255 - tail_temperature < game.tailheatup) return 1; for(i = 0; i < game.bolts && boltst[i]; i++) ; if(i >= game.bolts) return 1; if(game.altfire) { tail_alt = -tail_alt; shot_single(i, (di > 4) ? (di - 4) : (di + 4), tail_alt); } else shot_single(i, (di > 4) ? (di - 4) : (di + 4), 0); tail_temperature += game.tailheatup; tail_reload_timer = game.tailloadtime; return 0; } void _myship::set_position(int px, int py) { x = px; y = py; virtx = x - (WSIZE >> 1); virty = y - (WSIZE >> 1); if(object) { apply_position(); cs_point_force(&object->point); } } void _myship::apply_position() { if(object) { object->point.v.x = PIXEL2CS(x); object->point.v.y = PIXEL2CS(y); } sound.g_position(x, y); }