123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647 |
- /*
- * Seven Kingdoms: Ancient Adversaries
- *
- * Copyright 1997,1998 Enlight Software Ltd.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- */
- //Filename : OBULLET.CPP
- //Description : Object Bullet
- //Owner : Alex
- #include <OVGA.h>
- #include <OUNIT.h>
- #include <OBULLET.h>
- #include <OWORLD.h>
- #include <OSERES.h>
- #include <OU_CART.h>
- #include <OTOWN.h>
- #include <ONATIONA.h>
- // -------- Define constant ---------//
- const int SCAN_RADIUS = 2;
- const int SCAN_RANGE = SCAN_RADIUS * 2 + 1;
- // from the closet to the far
- static char spiral_x[SCAN_RANGE*SCAN_RANGE] =
- { 0, 0,-1, 0, 1,-1,-1, 1, 1, 0,-2, 0, 2, -1,-2,-2,-1, 1, 2, 2, 1,-2,-2, 2, 2};
- static char spiral_y[SCAN_RANGE*SCAN_RANGE] =
- { 0,-1, 0, 1, 0,-1, 1, 1,-1,-2, 0, 2, 0, -2,-1, 1, 2, 2, 1,-1,-2,-2, 2, 2,-2};
- //--------- Begin of function Bullet::Bullet -------//
- Bullet::Bullet()
- {
- sprite_id = 0;
- }
- //--------- End of function Bullet::Bullet -------//
- //--------- Begin of function Bullet::init ---------//
- //
- // <char> parentType - the type of object emits the bullet
- // <short> parentRecno - the recno of the object
- // <short> targetXLoc - the x loc of the target
- // <short> targetYLoc - the y loc of the target
- // <char> targetMobileType - target mobile type
- //
- void Bullet::init(char parentType, short parentRecno, short targetXLoc, short targetYLoc, char targetMobileType)
- {
- parent_type = parentType;
- parent_recno = parentRecno;
- target_mobile_type = targetMobileType;
- //**** BUGHERE, using parentType and parentRecno to allow bullet by firm, town, etc.
- //**** BUGHERE, only allow bullet by unit for this version
- err_when(parent_type!=BULLET_BY_UNIT);
- Unit *parentUnit = unit_array[parentRecno];
- //---------- copy attack info from the parent unit --------//
- AttackInfo* attackInfo = parentUnit->attack_info_array+parentUnit->cur_attack;
- attack_damage = parentUnit->actual_damage();
- damage_radius = attackInfo->bullet_radius;
- nation_recno = parentUnit->nation_recno;
- // ###### begin Gilbert 26/6 ########## //
- fire_radius = attackInfo->fire_radius;
- // ###### end Gilbert 26/6 ########## //
- //----- clone vars from sprite_res for fast access -----//
- sprite_id = attackInfo->bullet_sprite_id;
- sprite_info = sprite_res[sprite_id];
- sprite_info->load_bitmap_res(); // the sprite bitmap will be freed by ~Sprite(), so we don't have to add ~Bullet() to free it.
- //--------- set the starting position of the bullet -------//
- cur_action = SPRITE_MOVE;
- cur_frame = 1;
- set_dir(parentUnit->attack_dir);
- SpriteFrame* spriteFrame = cur_sprite_frame();
- origin_x = cur_x = parentUnit->cur_x;
- origin_y = cur_y = parentUnit->cur_y;
- //------ set the target position and bullet mobile_type -------//
- target_x_loc = targetXLoc;
- target_y_loc = targetYLoc;
- go_x = target_x_loc * ZOOM_LOC_WIDTH + ZOOM_LOC_WIDTH/2 - spriteFrame->offset_x - spriteFrame->width/2; // -spriteFrame->offset_x to make abs_x1 & abs_y1 = original x1 & y1. So the bullet will be centered on the target
- go_y = target_y_loc * ZOOM_LOC_HEIGHT + ZOOM_LOC_HEIGHT/2 - spriteFrame->offset_y - spriteFrame->height/2;
- mobile_type = parentUnit->mobile_type;
- //---------- set bullet movement steps -----------//
- int xStep = (go_x - cur_x)/attackInfo->bullet_speed;
- int yStep = (go_y - cur_y)/attackInfo->bullet_speed;
- total_step = max(1, max(abs(xStep), abs(yStep)));
- cur_step = 0;
- err_when( total_step < 0 ); // number overflow
- }
- //----------- End of function Bullet::init -----------//
- //--------- Begin of function Bullet::process_move --------//
- void Bullet::process_move()
- {
- //-------------- update position -----------------//
- //
- // If it gets very close to the destination, fit it
- // to the destination ingoring the normal vector.
- //
- //------------------------------------------------//
- cur_x = origin_x + (int)(go_x-origin_x) * cur_step / total_step;
- cur_y = origin_y + (int)(go_y-origin_y) * cur_step / total_step;
- //cur_step++;
- //------- update frame id. --------//
- if( ++cur_frame > cur_sprite_move()->frame_count )
- cur_frame = 1;
- //----- if the sprite has reach the destintion ----//
- //if( cur_step > total_step )
- if( ++cur_step > total_step )
- {
- check_hit();
- cur_action = SPRITE_DIE; // Explosion
- // ###### begin Gilbert 17/5 #########//
- // if it has die frame, adjust cur_x, cur_y to be align with the target_x_loc, target_y_loc
- if( sprite_info->die.first_frame_recno )
- {
- next_x = cur_x = target_x_loc * ZOOM_LOC_WIDTH;
- next_y =cur_y = target_y_loc * ZOOM_LOC_HEIGHT;
- }
- // ###### end Gilbert 17/5 #########//
- cur_frame = 1;
- }
- else if( total_step - cur_step == 1 )
- {
- warn_target();
- }
- }
- //---------- End of function Bullet::process_move ----------//
- //--------- Begin of function Bullet::process_die --------//
- //
- // return : <int> 1 - dying animation completes.
- // 0 - still dying
- //
- int Bullet::process_die()
- {
- // ------- sound effect --------//
- se_res.sound(cur_x_loc(), cur_y_loc(), cur_frame, 'S',sprite_id,"DIE");
- //--------- next frame ---------//
- if( ++cur_frame > sprite_info->die.frame_count )
- // ####### begin Gilbert 28/6 ########//
- if( ++cur_frame > sprite_info->die.frame_count )
- {
- // ------- set fire on the target area --------//
- if( fire_radius > 0)
- {
- Location *locPtr;
- if( fire_radius == 1)
- {
- locPtr = world.get_loc(target_x_loc, target_y_loc);
- if( locPtr->can_set_fire() && locPtr->fire_str() < 30 )
- locPtr->set_fire_str(30);
- if( locPtr->fire_src() > 0 )
- locPtr->set_fire_src(1); // such that the fire will be put out quickly
- }
- else
- {
- short x, y, x1, y1, x2, y2;
- // ##### begin Gilbert 2/10 ######//
- x1 = target_x_loc - fire_radius + 1;
- if( x1 < 0 )
- x1 = 0;
- y1 = target_y_loc - fire_radius + 1;
- if( y1 < 0 )
- y1 = 0;
- x2 = target_x_loc + fire_radius - 1;
- if( x2 >= world.max_x_loc )
- x2 = world.max_x_loc-1;
- y2 = target_y_loc + fire_radius - 1;
- if( y2 >= world.max_y_loc )
- y2 = world.max_y_loc-1;
- // ##### end Gilbert 2/10 ######//
- for( y = y1; y <= y2; ++y)
- {
- locPtr = world.get_loc(x1, y);
- for( x = x1; x <= x2; ++x, ++locPtr)
- {
- // ##### begin Gilbert 30/10 ######//
- int dist = abs(x-target_x_loc) + abs(y-target_y_loc);
- if( dist > fire_radius)
- continue;
- int fl = 30 - dist * 7;
- if( fl < 10 )
- fl = 10;
- if( locPtr->can_set_fire() && locPtr->fire_str() < fl )
- locPtr->set_fire_str(fl);
- if( locPtr->fire_src() > 0 )
- locPtr->set_fire_src(1); // such that the fire will be put out quickly
- // ##### begin Gilbert 30/10 ######//
- }
- }
- }
- }
- return 1;
- }
- // ####### end Gilbert 28/6 ########//
- return 0;
- }
- //--------- End of function Bullet::process_die --------//
- //--------- Begin of function Bullet::hit_target --------//
- // ####### begin Gilbert 14/5 #########//
- void Bullet::hit_target(short x, short y)
- {
- //---- check if there is any unit in the target location ----//
- Location* locPtr = world.get_loc(x, y);
- // ####### end Gilbert 14/5 #########//
- short targetUnitRecno = locPtr->unit_recno(target_mobile_type);
- if(unit_array.is_deleted(targetUnitRecno))
- return; // the target unit is deleted
- Unit* targetUnit = unit_array[targetUnitRecno];
- Unit* parentUnit;
- if(unit_array.is_deleted(parent_recno))
- //### begin alex 26/9 ###//
- // parentUnit = NULL; // parent is dead
- {
- parentUnit = NULL; // parent is dead
- if(nation_array.is_deleted(nation_recno))
- return;
- }
- //#### end alex 26/9 ####//
- else
- {
- parentUnit = unit_array[parent_recno];
- nation_recno = parentUnit->nation_recno;
- }
- float attackDamage = attenuated_damage(targetUnit->cur_x, targetUnit->cur_y);
- // -------- if the unit is guarding reduce damage ----------//
- err_when(unit_array.is_deleted(locPtr->unit_recno(target_mobile_type)));
- // ##### begin Gilbert 14/5 #########//
- if( attackDamage == 0 )
- return;
- if( targetUnit->is_nation(nation_recno) )
- {
- if( targetUnit->unit_id == UNIT_EXPLOSIVE_CART )
- ((UnitExpCart *)targetUnit)->trigger_explode();
- return;
- }
- // ##### end Gilbert 14/5 #########//
- // ##### begin Gilbert 3/9 #########//
- if( !nation_array.should_attack(nation_recno, targetUnit->nation_recno) )
- return;
- // ##### end Gilbert 3/9 #########//
- if(targetUnit->is_guarding())
- {
- switch(targetUnit->cur_action)
- {
- case SPRITE_IDLE:
- case SPRITE_READY_TO_MOVE:
- case SPRITE_TURN:
- case SPRITE_MOVE:
- // #ifdef AMPLUS
- case SPRITE_ATTACK:
- // #endif
- // ####### begin Gilbert 9/9 #######//
- // check if on the opposite direction
- if( (targetUnit->cur_dir & 7)== ((cur_dir + 4 ) & 7)
- || (targetUnit->cur_dir & 7)== ((cur_dir + 3 ) & 7)
- || (targetUnit->cur_dir & 7)== ((cur_dir + 5 ) & 7) )
- // ####### end Gilbert 9/9 #######//
- {
- attackDamage = attackDamage > (float)10/ATTACK_SLOW_DOWN ? attackDamage - (float)10/ATTACK_SLOW_DOWN : 0;
- se_res.sound( targetUnit->cur_x_loc(), targetUnit->cur_y_loc(), 1,
- 'S', targetUnit->sprite_id, "DEF", 'S', sprite_id );
- }
- break;
- }
- }
- targetUnit->hit_target(parentUnit, targetUnit, attackDamage);
- }
- //---------- End of function Bullet::hit_target ----------//
- //------- Begin of function Bullet::hit_building -----//
- // building means firm or town
- //
- // ###### begin Gilbert 14/5 #########//
- void Bullet::hit_building(short x, short y)
- {
- Location* locPtr = world.get_loc(x, y);
- if(locPtr->is_firm())
- {
- Firm *firmPtr = firm_array[locPtr->firm_recno()];
- // ##### begin Gilbert 3/9 #########//
- if( !firmPtr || !nation_array.should_attack(nation_recno, firmPtr->nation_recno) )
- // ##### end Gilbert 3/9 #########//
- return;
- }
- else if(locPtr->is_town())
- {
- Town *townPtr = town_array[locPtr->town_recno()];
- // ##### begin Gilbert 3/9 #########//
- if( !townPtr || !nation_array.should_attack(nation_recno, townPtr->nation_recno) )
- // ##### end Gilbert 3/9 #########//
- return;
- }
- else
- return;
- float attackDamage = attenuated_damage(x * ZOOM_LOC_WIDTH, y * ZOOM_LOC_HEIGHT );
- // BUGHERE : hit building of same nation?
- if( attackDamage == 0)
- return;
- Unit *virtualUnit, *parentUnit;
- if(unit_array.is_deleted(parent_recno))
- {
- parentUnit = NULL;
- //### begin alex 26/9 ###//
- if(nation_array.is_deleted(nation_recno))
- return;
- //#### end alex 26/9 ####//
- for(int i=unit_array.size(); i>0; i--)
- {
- if(unit_array.is_deleted(i))
- continue;
- virtualUnit = unit_array[i];
- break;
- }
- if(!virtualUnit)
- return; //**** BUGHERE
- }
- else
- virtualUnit = parentUnit = unit_array[parent_recno];
- virtualUnit->hit_building(parentUnit, target_x_loc, target_y_loc, attackDamage);
- // ####### end Gilbert 14/5 ########//
- }
- //---------- End of function Bullet::hit_building ----------//
- //------- Begin of function Bullet::hit_wall -----//
- // ###### begin Gilbert 14/5 #########//
- void Bullet::hit_wall(short x, short y)
- {
- Location* locPtr = world.get_loc(x, y);
- if(!locPtr->is_wall())
- return;
- float attackDamage = attenuated_damage(x * ZOOM_LOC_WIDTH, y * ZOOM_LOC_HEIGHT );
- if( attackDamage == 0)
- return;
- // ###### end Gilbert 14/5 #########//
- Unit *virtualUnit, *parentUnit;
- if(unit_array.is_deleted(parent_recno))
- {
- parentUnit = NULL;
- //### begin alex 26/9 ###//
- if(nation_array.is_deleted(nation_recno))
- return;
- //#### end alex 26/9 ####//
- for(int i=unit_array.size(); i>0; i--)
- {
- if(unit_array.is_deleted(i))
- continue;
- virtualUnit = unit_array[i];
- break;
- }
- if(!virtualUnit)
- return; //**** BUGHERE
- }
- else
- virtualUnit = parentUnit = unit_array[parent_recno];
- // ###### begin Gilbert 14/5 #########//
- virtualUnit->hit_wall(parentUnit, target_x_loc, target_y_loc, attackDamage);
- // ###### end Gilbert 14/5 ########//
- }
- //---------- End of function Bullet::hit_wall ----------//
- //--------- Begin of function Bullet::check_hit -------//
- // check if the bullet hit a target
- // return true if hit
- int Bullet::check_hit()
- {
- err_when(SCAN_RANGE != 5);
-
- short x,y;
- short townHit[SCAN_RANGE*SCAN_RANGE];
- short firmHit[SCAN_RANGE*SCAN_RANGE];
- int hitCount = 0;
- int townHitCount = 0;
- int firmHitCount = 0;
- for( int c = 0; c < SCAN_RANGE*SCAN_RANGE; ++c )
- {
- x = target_x_loc + spiral_x[c];
- y = target_y_loc + spiral_y[c];
- if( x >= 0 && x < world.max_x_loc && y >= 0 && y < world.max_y_loc )
- {
- Location *locPtr = world.get_loc(x, y);
- if(target_mobile_type==UNIT_AIR)
- {
- if(locPtr->has_unit(UNIT_AIR))
- {
- hit_target(x,y);
- hitCount++;
- }
- }
- else
- {
- if(locPtr->is_firm())
- {
- short firmRecno = locPtr->firm_recno();
- // check this firm has not been attacked
- short *firmHitPtr;
- for( firmHitPtr = firmHit+firmHitCount-1; firmHitPtr >= firmHit; --firmHitPtr )
- {
- if( *firmHitPtr == firmRecno )
- break;
- }
- if( firmHitPtr < firmHit ) // not found
- {
- firmHit[firmHitCount++] = firmRecno;
- hit_building(x,y);
- hitCount++;
- }
- }
- else if( locPtr->is_town() )
- {
- short townRecno = locPtr->town_recno();
- // check this town has not been attacked
- short *townHitPtr;
- for( townHitPtr = townHit+townHitCount-1; townHitPtr >= townHit; --townHitPtr )
- {
- if( *townHitPtr == townRecno )
- break;
- }
- if( townHitPtr < townHit ) // not found
- {
- townHit[townHitCount++] = townRecno;
- hit_building(x,y);
- hitCount++;
- }
- }
- else if(locPtr->is_wall())
- {
- hit_wall(x,y);
- hitCount++;
- }
- else
- {
- hit_target(x,y); // note: no error checking here because mobile_type should be taken into account
- hitCount++;
- }
- }
- }
- }
- return hitCount;
- }
- //--------- End of function Bullet::check_hit -------//
- //--------- Begin of function Bullet::warn_target -------//
- //
- // warn a unit before hit
- // return true if a unit is warned
- int Bullet::warn_target()
- {
- err_when(SCAN_RANGE != 5);
-
- short x,y;
- int warnCount = 0;
- for( int c = 0; c < SCAN_RANGE*SCAN_RANGE; ++c )
- {
- x = target_x_loc + spiral_x[c];
- y = target_y_loc + spiral_y[c];
- if( x >= 0 && x < world.max_x_loc && y >= 0 && y < world.max_y_loc )
- {
- Location *locPtr = world.get_loc(x, y);
- //char targetMobileType;
- //if( (targetMobileType = locPtr->has_any_unit()) != 0)
- //{
- // short unitRecno = locPtr->unit_recno(UNIT_LAND);
- short unitRecno = locPtr->unit_recno(target_mobile_type);
- if( !unit_array.is_deleted(unitRecno) )
- {
- Unit *unitPtr = unit_array[unitRecno];
- // ####### begin Gilbert 9/9 ########//
- if( attenuated_damage( unitPtr->cur_x, unitPtr->cur_y) > 0 )
- // ####### end Gilbert 9/9 ########//
- {
- warnCount++;
- switch(unitPtr->cur_action)
- {
- case SPRITE_IDLE:
- case SPRITE_READY_TO_MOVE:
- //case SPRITE_TURN:
- if( unitPtr->can_stand_guard() && !unitPtr->is_guarding() )
- {
- unitPtr->set_dir( (cur_dir + 4 ) & 7); // opposite direction of arrow
- unitPtr->set_guard_on();
- }
- break;
- case SPRITE_MOVE:
- if( unitPtr->can_move_guard() && !unitPtr->is_guarding()
- // ###### begin Gilbert 9/9 #######//
- && ( (unitPtr->cur_dir & 7)== ((cur_dir + 4 ) & 7)
- || (unitPtr->cur_dir & 7)== ((cur_dir + 5 ) & 7)
- || (unitPtr->cur_dir & 7)== ((cur_dir + 3 ) & 7)
- )
- )
- // ###### end Gilbert 9/9 #######//
- {
- unitPtr->set_guard_on();
- }
- break;
- #ifdef AMPLUS
- case SPRITE_ATTACK:
- if( unitPtr->can_attack_guard() && !unitPtr->is_guarding()
- && unitPtr->remain_attack_delay >= GUARD_COUNT_MAX
- && ( (unitPtr->cur_dir & 7)== ((cur_dir + 4 ) & 7)
- || (unitPtr->cur_dir & 7)== ((cur_dir + 5 ) & 7)
- || (unitPtr->cur_dir & 7)== ((cur_dir + 3 ) & 7)
- )
- )
- {
- unitPtr->set_guard_on();
- }
- break;
- #endif
- }
- }
- }
- //}
- }
- }
- return warnCount;
- }
- //--------- End of function Bullet::warn_target -------//
- //--------- Begin of function Bullet::display_layer -------//
- char Bullet::display_layer()
- {
- if( mobile_type == UNIT_AIR || target_mobile_type == UNIT_AIR )
- return 8;
- else
- return 1;
- }
- //--------- End of function Bullet::display_layer -------//
- //------- Begin of function Bullet::attenuated_damage -----//
- float Bullet::attenuated_damage(short curX, short curY)
- {
- short d = m.points_distance(curX, curY, target_x_loc * ZOOM_LOC_WIDTH, target_y_loc * ZOOM_LOC_HEIGHT);
- // damage drops from attack_damage to attack_damage/2, as range drops from 0 to damage_radius
- err_when(damage_radius == 0);
- if( d > damage_radius)
- return (float) 0;
- else
- //return ((attack_damage * (2*damage_radius-d) + 2*damage_radius-1)/ (2*damage_radius) ); // ceiling
- return attack_damage - attack_damage*d/(2*damage_radius);
- }
- //------- End of function Bullet::attenuated_damage -----//
- #ifdef DEBUG
- //------- Begin of function BulletArray::operator[] -----//
- Bullet* BulletArray::operator[](int recNo)
- {
- Bullet* bulletPtr = (Bullet*) get_ptr(recNo);
- if( !bulletPtr )
- err.run( "BulletArray[] is deleted" );
- return bulletPtr;
- }
- //--------- End of function BulletArray::operator[] ----//
- #endif
|