/* Copyright (C) 1998 Pyrosoft Inc. (www.pyrosoftgames.com), Matthew Bogue 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "Util/Log.hpp" #include "Vehicle.hpp" #include "MapInterface.hpp" #include "PathScheduler.hpp" #include "ProjectileInterface.hpp" #include "UnitInterface.hpp" #include "UnitBlackBoard.hpp" #include "NetworkState.hpp" #include "System/Sound.hpp" // NOTE: Temp unit new sprites put in #include "GameConfig.hpp" #include "ConsoleInterface.hpp" #include "ParticleInterface.hpp" enum{ _rotate_and_move, _rotate_stop_move }; Vehicle::Vehicle(PlayerState* player, UnitID id, iXY initial_loc) : UnitBase(player, id) { smolderWait = 0.0f; smolderWaitMin = 0.0f; if(!MapInterface::inside(initial_loc)) throw std::runtime_error("Invalid position"); iXY loc = MapInterface::mapXYtoPointXY(initial_loc); unit_state.location = loc; UnitBlackBoard::markUnitLoc( initial_loc ); fsm_timer.changeRate( 10 ); unit_state_timer.changeRate( 10 ); setAiFsmDefendHold(); pending_AI_comm = false; unit_state.lifecycle_state = _UNIT_LIFECYCLE_ACTIVE; memset( fsm_active_list, 0, sizeof( bool ) * 7 ); in_sync_flag = true; body_anim_shadow.setDrawModeBlend(&Palette::colorTableDarkenALot); turret_anim_shadow.setDrawModeBlend(&Palette::colorTableDarkenALot); path_generated = false; critical_ai_section = false; ai_fsm_transition_complete = false; reload_counter = 0; death_counter = 0; ai_command_state = 0; external_ai_event = _external_event_null; fsmBodyRotate_rotation = 0; fsmBodyRotate_goal_angle = 0; fsmTurretRotate_rotation = 0; fsmTurretRotate_goal_angle = 0; interpolation_speed = 0; fsmMove_first_stamp = false; fsmMove_offset_x = fsmMove_offset_y = 0; fsmMove_moves_counter = 0; fsmMove_moves_per_square = 0; move_opcode_sent = false; fsmMoveMapSquare_movement_type = 0; aiFsmDefendHold_state = 0; } void Vehicle::updateUnitStateProperties() { if ( reload_counter < unit_state.reload_time ) reload_counter++; if ( (unit_state.lifecycle_state == _UNIT_LIFECYCLE_PENDING_DESTRUCT) && (ai_command_state == _ai_command_idle) ) { unit_state.lifecycle_state = _UNIT_LIFECYCLE_DESTROYED; death_counter = 0; } if ( unit_state.lifecycle_state == _UNIT_LIFECYCLE_DESTROYED ) { if ( death_counter == 5 ) { unit_state.lifecycle_state = _UNIT_LIFECYCLE_INACTIVE; DestructUnitOpcode destruct_opcode; destruct_opcode.setUnitID(id); UnitInterface::sendOpcode(&destruct_opcode); } else death_counter++; } } void Vehicle::orientationToOffset( unsigned short orientation, signed char *offset_x, signed char *offset_y ) { switch ( orientation ) { case 0: { *offset_x = 1; *offset_y = 0; } break; case 1: { *offset_x = 1; *offset_y = -1; } break; case 2: { *offset_x = 0; *offset_y = -1; } break; case 3: { *offset_x = -1; *offset_y = -1; } break; case 4: { *offset_x = -1; *offset_y = 0; } break; case 5: { *offset_x = -1; *offset_y = 1; } break; case 6: { *offset_x = 0; *offset_y = 1; } break; case 7: { *offset_x = 1; *offset_y = 1; } break; } } unsigned short Vehicle::mapXYtoOrientation( unsigned long square, long *goal_angle ) { iXY current_loc, next_loc; MapInterface::pointXYtoMapXY( unit_state.location, ¤t_loc ); MapInterface::offsetToMapXY( square, &next_loc ); if ( (next_loc.x > current_loc.x) && (next_loc.y == current_loc.y) ) { *goal_angle = 0; return( 0 ); } if ( (next_loc.x > current_loc.x) && (next_loc.y < current_loc.y) ) { *goal_angle = 5; return( 1 ); } if ( (next_loc.x == current_loc.x) && (next_loc.y < current_loc.y) ) { *goal_angle = 9; return( 2 ); } if ( (next_loc.x < current_loc.x) && (next_loc.y < current_loc.y) ) { *goal_angle = 14; return( 3 ); } if ( (next_loc.x < current_loc.x) && (next_loc.y == current_loc.y) ) { *goal_angle = 18; return( 4 ); } if ( (next_loc.x < current_loc.x) && (next_loc.y > current_loc.y) ) { *goal_angle = 23; return( 5 ); } if ( (next_loc.x == current_loc.x) && (next_loc.y > current_loc.y) ) { *goal_angle = 27; return( 6 ); } if ( (next_loc.x > current_loc.x) && (next_loc.y > current_loc.y) ) { *goal_angle = 32; return( 7 ); } return( 0xFFFF ); } unsigned short Vehicle::shortestRotation( AngleInt &angle, long goal_angle, long *delta ) { long delta_plus, delta_minus; if( goal_angle > angle.angle_int ) delta_plus = ( goal_angle ) - (angle.angle_int) ; else delta_plus = (angle.angle_limit + goal_angle) - angle.angle_int; if ( goal_angle > angle.angle_int ) delta_minus = angle.angle_limit - (goal_angle - angle.angle_int) ; else delta_minus = (angle.angle_int ) - ( goal_angle ); if (delta_minus > delta_plus) { *delta = delta_plus; return( _rotate_pos ); } else { *delta = delta_minus; return( _rotate_neg ); } } void Vehicle::locationOffset( unsigned long square, iXY &offset ) { iXY square_map_loc; iXY unit_map_loc; MapInterface::offsetToMapXY( square, &square_map_loc ); MapInterface::pointXYtoMapXY( unit_state.location, &unit_map_loc ); offset = unit_map_loc - square_map_loc ; } void Vehicle::setFsmBodyRotate( long goal_angle, unsigned short rotation ) { fsmBodyRotate_rotation = rotation; fsmBodyRotate_goal_angle = goal_angle; } bool Vehicle::fsmBodyRotate() { if( unit_state.body_angle.angle_int != fsmBodyRotate_goal_angle ) { if ( fsmBodyRotate_rotation == _rotate_pos ) { ++unit_state.body_angle; } else { --unit_state.body_angle; } // ** else turn_rotation ** } else { return( true ); // goal direction achieved } return( false ); } void Vehicle::setFsmTurretRotate( long goal_angle, unsigned short rotation ) { fsmTurretRotate_rotation = rotation; fsmTurretRotate_goal_angle = goal_angle; } bool Vehicle::fsmTurretRotate() { if( unit_state.turret_angle.angle_int != fsmTurretRotate_goal_angle ) { if ( fsmTurretRotate_rotation == _rotate_pos ) { ++unit_state.turret_angle; } else { --unit_state.turret_angle; } // ** else turn_rotation ** } else { return( true ); // goal direction achieved } return( false ); } void Vehicle::setFsmMove( unsigned short orientation ) { fsm_timer.changeRate( unit_state.speed_rate ); orientationToOffset( orientation, &fsmMove_offset_x, &fsmMove_offset_y ); fsmMove_moves_counter = 0; fsmMove_moves_per_square = 32 / unit_state.speed_factor; /* fsmMove_moves_per_square = 32; interpolation_speed = unit_state.speed_rate * unit_state.speed_factor; if ( NetworkState::status == _network_state_client ) { interpolation_speed += 2; } fsmMove_first_stamp = true; */ } bool Vehicle::fsmMove() { /* long move_offset; if( fsmMove_first_stamp == true ) { start_move_stamp = now(); fsmMove_first_stamp = false; } end_move_stamp = now(); move_offset = (end_move_stamp - start_move_stamp) * interpolation_speed; if( (move_offset + fsmMove_moves_counter) > fsmMove_moves_per_square ) { move_offset = fsmMove_moves_per_square - fsmMove_moves_counter; } */ if ( fsmMove_moves_counter < fsmMove_moves_per_square ) { /* fsmMove_moves_counter += move_offset; unit_state.location.x = unit_state.location.x + ( move_offset * fsmMove_offset_x ); unit_state.location.y = unit_state.location.y + ( move_offset * fsmMove_offset_y ); */ fsmMove_moves_counter++; unit_state.location.x = unit_state.location.x + ( unit_state.speed_factor * fsmMove_offset_x ); unit_state.location.y = unit_state.location.y + ( unit_state.speed_factor * fsmMove_offset_y ); ParticleInterface::addMoveDirtPuff(unit_state); //start_move_stamp = now(); } if( fsmMove_moves_counter >= fsmMove_moves_per_square) { //fsmMove_first_stamp = true; fsm_timer.changeRate( 10 ); return( true ); } return( false ); } void Vehicle::setFsmMoveMapSquare( unsigned long square ) { long goal_angle = 0; long delta; unsigned short rotation; unit_state.orientation = mapXYtoOrientation( square, &goal_angle ); rotation = shortestRotation( unit_state.body_angle, goal_angle, &delta ); setFsmBodyRotate( goal_angle, rotation ); setFsmMove( unit_state.orientation ); if ( delta <= 9 ) fsmMoveMapSquare_movement_type = _rotate_and_move; else fsmMoveMapSquare_movement_type = _rotate_stop_move; fsm_active_list[ _control_move_map_square ] = true; critical_ai_section = true; if ( NetworkState::status == _network_state_server ) { iXY loc_offset; move_opcode.opcode = _UNIT_OPCODE_MOVE; move_opcode.setUnitID(id); move_opcode.setSquare(square); locationOffset( square, loc_offset ); move_opcode.loc_x_offset = (signed char) loc_offset.x; move_opcode.loc_y_offset = (signed char) loc_offset.y; if ( move_opcode_sent == true ) UnitInterface::sendOpcode(&move_opcode); } } bool Vehicle::fsmMoveMapSquare() { if ( move_opcode_sent == false && NetworkState::status == _network_state_server ) if ( opcode_move_timer.count() ) { UnitInterface::sendOpcode(&move_opcode); move_opcode_sent = true; } switch( fsmMoveMapSquare_movement_type ) { case _rotate_stop_move : { if ( fsmBodyRotate() ) if ( fsmMove() ) { fsm_active_list[ _control_move_map_square ] = false; critical_ai_section = false; if ( move_opcode_sent == false && NetworkState::status == _network_state_server ) { UnitInterface::sendOpcode(&move_opcode); move_opcode_sent = true; } return( true ); } } break; case _rotate_and_move : { fsmBodyRotate(); if ( fsmMove() ) { fsm_active_list[ _control_move_map_square ] = false; critical_ai_section = false; if ( move_opcode_sent == false && NetworkState::status == _network_state_server ) { UnitInterface::sendOpcode(&move_opcode); move_opcode_sent = true; } return( true ); } } break; } // ** switch return( false ); } void Vehicle::setFsmTurretTrackPoint(const iXY& target) { iXY direction_vector; fsmTurretTrackPoint_target = target; direction_vector = fsmTurretTrackPoint_target - unit_state.location; fsmTurretTrackPoint_target_angle.set( direction_vector ); fsmTurretTrackPoint_on_target = false; fsm_active_list[ _control_turret_track_point ] = true; if ( NetworkState::status == _network_state_server ) { TurretTrackPointOpcode track_point_opcode; track_point_opcode.opcode = _UNIT_OPCODE_TURRET_TRACK_POINT; track_point_opcode.setUnitID(id); track_point_opcode.setTarget(target); track_point_opcode.activate = true; UnitInterface::sendOpcode( &track_point_opcode ); } } void Vehicle::clearFsmTurretTrackPoint() { fsm_active_list[ _control_turret_track_point ] = false; fsmTurretTrackPoint_on_target = false; if ( NetworkState::status == _network_state_server ) { TurretTrackPointOpcode track_point_opcode; track_point_opcode.opcode = _UNIT_OPCODE_TURRET_TRACK_POINT; track_point_opcode.setUnitID(id); track_point_opcode.activate = false; UnitInterface::sendOpcode( &track_point_opcode ); } } void Vehicle::syncFsmTurretTrackPoint() { if ( fsm_active_list[ _control_turret_track_point ] == true ) { TurretTrackPointOpcode track_point_opcode; track_point_opcode.flags = _unit_opcode_flag_sync; track_point_opcode.opcode = _UNIT_OPCODE_TURRET_TRACK_POINT; track_point_opcode.setUnitID(id); track_point_opcode.setTarget(fsmTurretTrackPoint_target); track_point_opcode.activate = true; UnitInterface::sendOpcode( &track_point_opcode ); } } void Vehicle::fsmTurretTrackPoint() { long goal_angle; long delta; unsigned short rotation; iXY direction_vector; direction_vector = fsmTurretTrackPoint_target - unit_state.location; fsmTurretTrackPoint_target_angle.set( direction_vector ); goal_angle = fsmTurretTrackPoint_target_angle.DegreesInt( 10 ); rotation = shortestRotation( unit_state.turret_angle, goal_angle, &delta ); setFsmTurretRotate( goal_angle, rotation ); if ( fsmTurretRotate() == true ) fsmTurretTrackPoint_on_target = true; else fsmTurretTrackPoint_on_target = false; } void Vehicle::setFsmTurretTrackTarget(UnitID target_id) { fsmTurretTrackTarget_target_id = target_id; fsm_active_list[ _control_turret_track_target ] = true; if ( NetworkState::status == _network_state_server ) { TurretTrackTargetOpcode track_target_opcode; track_target_opcode.opcode = _UNIT_OPCODE_TURRET_TRACK_TARGET; track_target_opcode.setUnitID(id); track_target_opcode.setTargetUnitID(target_id); track_target_opcode.activate = true; UnitInterface::sendOpcode( &track_target_opcode ); } } void Vehicle::clearFsmTurretTrackTarget() { fsm_active_list[ _control_turret_track_target ] = false; if ( NetworkState::status == _network_state_server ) { TurretTrackTargetOpcode track_target_opcode; track_target_opcode.opcode = _UNIT_OPCODE_TURRET_TRACK_TARGET; track_target_opcode.setUnitID(id); track_target_opcode.activate = false; UnitInterface::sendOpcode( &track_target_opcode ); } } void Vehicle::syncFsmTurretTrackTarget() { if ( fsm_active_list[ _control_turret_track_target ] == true ) { TurretTrackTargetOpcode track_target_opcode; track_target_opcode.opcode = _UNIT_OPCODE_TURRET_TRACK_TARGET; track_target_opcode.flags = _unit_opcode_flag_sync; track_target_opcode.setUnitID(id); track_target_opcode.setTargetUnitID(fsmTurretTrackTarget_target_id); track_target_opcode.activate = true; UnitInterface::sendOpcode( &track_target_opcode ); } } void Vehicle::fsmTurretTrackTarget() { long goal_angle; long delta; unsigned short rotation; UnitBase *target_unit_ptr; iXY direction_vector; target_unit_ptr = UnitInterface::getUnit( fsmTurretTrackTarget_target_id ); if ( target_unit_ptr != 0 ) { direction_vector = target_unit_ptr->unit_state.location - unit_state.location; Angle float_angle( direction_vector ); goal_angle = float_angle.DegreesInt( 10 ); rotation = shortestRotation( unit_state.turret_angle, goal_angle, &delta ); setFsmTurretRotate( goal_angle, rotation ); } if ( fsmTurretRotate() == true ) { fsmTurretTrackTarget_on_target = true; } else { fsmTurretTrackTarget_on_target = false; } } void Vehicle::setFsmGunneryLocation(const iXY& target ) { if ( fsm_active_list[ _control_gunnery_target ] == true ) { clearFsmGunneryTarget(); } fsmGunneryLocation_target = target; setFsmTurretTrackPoint( target ); fsm_active_list[ _control_gunnery_location ] = true; } void Vehicle::clearFsmGunneryLocation() { fsm_active_list[ _control_gunnery_location ] = false; clearFsmTurretTrackPoint(); } void Vehicle::fsmGunneryLocation() { iXY range_vector; range_vector = fsmGunneryLocation_target - unit_state.location; if ( (range_vector.mag2() < unit_state.weapon_range) && (fsmTurretTrackPoint_on_target == true) && (reload_counter >= unit_state.reload_time) ) { fireWeapon( fsmGunneryLocation_target ); clearFsmGunneryLocation(); } } void Vehicle::setFsmGunneryTarget(UnitID target_id) { if ( fsm_active_list[ _control_gunnery_location ] == true ) { clearFsmGunneryLocation(); } if ( fsm_active_list[ _control_turret_track_point ] == true ) { clearFsmTurretTrackPoint(); } fsmGunneryTarget_target_id = target_id; setFsmTurretTrackTarget( target_id ); fsm_active_list[ _control_gunnery_target ] = true; } void Vehicle::clearFsmGunneryTarget() { fsm_active_list[ _control_gunnery_target ] = false; clearFsmTurretTrackTarget(); } void Vehicle::fsmGunneryTarget() { UnitBase *target_unit_ptr; UnitState *target_unit_state; iXY range_vector; target_unit_ptr = UnitInterface::getUnit( fsmGunneryTarget_target_id ); if ( target_unit_ptr == 0 ) { clearFsmGunneryTarget(); } else { target_unit_state = &(target_unit_ptr->unit_state); if ( target_unit_state->lifecycle_state == _UNIT_LIFECYCLE_DESTROYED ) { clearFsmGunneryTarget(); } // ** if else { range_vector = target_unit_state->location - unit_state.location; if ( (range_vector.mag2() < unit_state.weapon_range) && (fsmTurretTrackTarget_on_target == true) && (reload_counter == unit_state.reload_time ) ) { fireWeapon( target_unit_state->location ); } } } } void Vehicle::aiFsmIdle() { if ( pending_AI_comm == true ) { ai_fsm_transition_complete = true; } } bool Vehicle::ruleMoveToLoc_GoalReached() { iXY map_loc; MapInterface::pointXYtoMapXY( unit_state.location, &map_loc ); if ( map_loc == aiFsmMoveToLoc_goal ) return true; return false; } void Vehicle::aiFsmMoveToLoc() { bool end_cycle = false; do { switch ( aiFsmMoveToLoc_state ) { // ************************************************************* case _aiFsmMoveToLoc_path_generate : { // QueryPath: Has a path been generated for unit ? path_generated = PathScheduler::queryPath(id); if ( external_ai_event == _external_event_pending_unit_destruct ) { // External Event: This unit is about to be deleted // Action : Exit fsm gracefully aiFsmMoveToLoc_OnExitCleanUp(); iXY current_map_loc; MapInterface::pointXYtoMapXY( unit_state.location, ¤t_map_loc ); UnitBlackBoard::unmarkUnitLoc( current_map_loc ); external_ai_event = _external_event_null; ai_command_state = _ai_command_idle; end_cycle = true; } else if ( pending_AI_comm == true ) { // External Event: A new AI command is pending // Action: Allow command transition to occur ai_fsm_transition_complete = true; aiFsmMoveToLoc_OnExitCleanUp(); end_cycle = true; } else if ( path_generated == true ) { // Rule QueryPath: is true move to next state //LOG( ("Path Successfully Generated") ); aiFsmMoveToLoc_state = _aiFsmMoveToLoc_check_goal; } else end_cycle = true; } break; // ************************************************************* case _aiFsmMoveToLoc_check_goal : { // GoalReached: Has the goal been reached ? if ( ruleMoveToLoc_GoalReached() == true ) { // Rule GoalReached : is true // Action : Exit fsm aiFsmMoveToLoc_prev_loc = unit_state.location; MapInterface::pointXYtoMapXY( aiFsmMoveToLoc_prev_loc, &aiFsmMoveToLoc_prev_loc ); UnitBlackBoard::markUnitLoc( aiFsmMoveToLoc_prev_loc ); aiFsmMoveToLoc_OnExitCleanUp(); setAiFsmDefendHold(); end_cycle = true; } else { if ( aiFsmMoveToLoc_path_not_finished == false ) { // Rule: GoalReached is false AND Unit is at the end of path // Action : Request path generation to goal iXY start; PathRequest path_request; //LOG( ("Incomplete Path -- Regenerating Path") ); MapInterface::pointXYtoMapXY( unit_state.location, &start ); path_request.set(id, start, aiFsmMoveToLoc_goal, 0, &path, _path_request_full ); PathScheduler::requestPath( path_request ); aiFsmMoveToLoc_path_not_finished = true; aiFsmMoveToLoc_state = _aiFsmMoveToLoc_path_generate; } else { // Rule GoalReached: is false // Action: Get next move; aiFsmMoveToLoc_state = _aiFsmMoveToLoc_next_move; } } } break; // ************************************************************* case _aiFsmMoveToLoc_next_move : { // CurrentPathComplete: is Unit at the end of the current path aiFsmMoveToLoc_path_not_finished = path.popFirst( &aiFsmMoveToLoc_next_square ); MapInterface::offsetToMapXY( aiFsmMoveToLoc_next_square, &aiFsmMoveToLoc_next_loc ); if ( !aiFsmMoveToLoc_path_not_finished ) { // Rule: CurrentPathComplete is true // Action : check if unit is at the goal aiFsmMoveToLoc_state = _aiFsmMoveToLoc_check_goal; } else { // Rule: CurrentPathComplete is false // Action: Check if next location is empty aiFsmMoveToLoc_prev_loc = unit_state.location; MapInterface::pointXYtoMapXY( aiFsmMoveToLoc_prev_loc, &aiFsmMoveToLoc_prev_loc ); UnitBlackBoard::markUnitLoc( aiFsmMoveToLoc_prev_loc ); aiFsmMoveToLoc_wait_timer.changePeriod( 0.8f ); aiFsmMoveToLoc_state = _aiFsmMoveToLoc_wait_clear_loc; } } break; // ************************************************************* case _aiFsmMoveToLoc_wait_clear_loc : { if ( external_ai_event == _external_event_pending_unit_destruct ) { // External Event: This unit is about to be deleted // Action : Exit fsm gracefully aiFsmMoveToLoc_OnExitCleanUp(); UnitBlackBoard::unmarkUnitLoc( aiFsmMoveToLoc_prev_loc ); external_ai_event = _external_event_null; ai_command_state = _ai_command_idle; end_cycle = true; } else if ( pending_AI_comm == true ) { // External Event: A new AI command is pending // Action: Allow command transition to occur ai_fsm_transition_complete = true; aiFsmMoveToLoc_OnExitCleanUp(); end_cycle = true; } else // NextSquareOccupied: does the next square contain a abstruction if ( UnitBlackBoard::unitOccupiesLoc( aiFsmMoveToLoc_next_loc ) == true ) { // Rule: NextSquareOccupied is true // Action: Check Wait Timer if ( aiFsmMoveToLoc_wait_timer.count() ) { // Rule: NextSquareOccupied is true AND WaitTimer is finished // Action: Preform path update if ( aiFsmMoveToLoc_next_loc == aiFsmMoveToLoc_goal ) { UnitInterface::unit_placement_matrix.reset( aiFsmMoveToLoc_goal ); UnitInterface::unit_placement_matrix.getNextEmptyLoc( &aiFsmMoveToLoc_goal ); PathRequest path_request; path_request.set(id, aiFsmMoveToLoc_prev_loc, aiFsmMoveToLoc_goal, 0, &path, _path_request_full ); PathScheduler::requestPath( path_request ); } else { PathRequest path_request; path_request.set(id, aiFsmMoveToLoc_prev_loc, aiFsmMoveToLoc_goal, 0, &path, _path_request_update ); PathScheduler::requestPath( path_request ); } aiFsmMoveToLoc_state = _aiFsmMoveToLoc_path_generate; } end_cycle = true; } else { // Rule: NextSquareOccupied is false // Action: Begin move to next square UnitBlackBoard::markUnitLoc( aiFsmMoveToLoc_next_loc ); setFsmMoveMapSquare( aiFsmMoveToLoc_next_square ); aiFsmMoveToLoc_state = _aiFsmMoveToLoc_move_wait; } } break; // ************************************************************* case _aiFsmMoveToLoc_move_wait : { // MoveFinished : has the low level fsm finshed if ( fsm_active_list[ _control_move_map_square ] == false ) { // Rule: MoveFinished is true // Action: Check for a pending transition UnitBlackBoard::unmarkUnitLoc( aiFsmMoveToLoc_prev_loc ); aiFsmMoveToLoc_state = _aiFsmMoveToLoc_check_fsm_transition; end_cycle = true; } else { end_cycle = true; } // ** else } break; // ************************************************************* case _aiFsmMoveToLoc_check_fsm_transition : { if ( external_ai_event == _external_event_pending_unit_destruct ) { // External Event: This unit is about to be deleted // Action : Exit fsm gracefully aiFsmMoveToLoc_OnExitCleanUp(); UnitBlackBoard::unmarkUnitLoc( aiFsmMoveToLoc_next_loc ); external_ai_event = _external_event_null; ai_command_state = _ai_command_idle; end_cycle = true; } else if ( pending_AI_comm == true ) { // External Event: A new AI command is pending // Action: Allow command transition to occur ai_fsm_transition_complete = true; aiFsmMoveToLoc_OnExitCleanUp(); end_cycle = true; } else { aiFsmMoveToLoc_state = _aiFsmMoveToLoc_check_goal; } } break; } // ** switch } while (end_cycle == false); } void Vehicle::aiFsmMoveToLoc_OnExitCleanUp() { PathScheduler::killRequest(id); clearFsmTurretTrackPoint(); } void Vehicle::aiFsmAttackUnit() { bool end_cycle = false; UnitState *target_unit_state = 0; iXY range_vector; UnitBase* target_unit_ptr = UnitInterface::getUnit( aiFsmAttackUnit_target_ID ); if ( target_unit_ptr == 0 ) { aiFsmAttackUnit_target_destroyed = true; if(aiFsmAttackUnit_state != _aiFsmAttackUnit_move_wait) { setAiFsmDefendHold(); aiFsmAttackUnit_OnExitCleanUp(); return; } } else { target_unit_state = &(target_unit_ptr->unit_state); if ( target_unit_state->lifecycle_state == _UNIT_LIFECYCLE_DESTROYED ) { aiFsmAttackUnit_target_destroyed = true; if ( aiFsmAttackUnit_state != _aiFsmAttackUnit_move_wait ) { setAiFsmDefendHold(); aiFsmAttackUnit_OnExitCleanUp(); return; } } } do { switch ( aiFsmAttackUnit_state ) { // ************************************************************* case _aiFsmAttackUnit_path_generate : { // QueryPath: Has a path been generated for unit ? path_generated = PathScheduler::queryPath(id); if ( external_ai_event == _external_event_pending_unit_destruct ) { // External Event: This unit is about to be deleted // Action : Exit fsm gracefully aiFsmAttackUnit_OnExitCleanUp(); iXY current_map_loc; MapInterface::pointXYtoMapXY( unit_state.location, ¤t_map_loc ); UnitBlackBoard::unmarkUnitLoc( current_map_loc ); external_ai_event = _external_event_null; ai_command_state = _ai_command_idle; end_cycle = true; } else if ( pending_AI_comm == true ) { // External Event: A new AI command is pending // Action: Allow command transition to occur ai_fsm_transition_complete = true; aiFsmAttackUnit_OnExitCleanUp(); end_cycle = true; } else if ( path_generated == true ) { // Rule QueryPath: is true move to next state aiFsmAttackUnit_state = _aiFsmAttackUnit_range_check; } else end_cycle = true; } break; // ************************************************************* case _aiFsmAttackUnit_range_check : { // RangeVector: the absolute distance between unit and target range_vector = target_unit_state->location - unit_state.location; if ( range_vector.mag2() < unit_state.weapon_range ) { // Rule: RangeVector < WeaponRange, unit is in range // Action: Remain in position aiFsmAttackUnit_prev_loc = unit_state.location; MapInterface::pointXYtoMapXY( aiFsmAttackUnit_prev_loc, &aiFsmAttackUnit_prev_loc ); UnitBlackBoard::markUnitLoc( aiFsmAttackUnit_prev_loc ); aiFsmAttackUnit_state = _aiFsmAttackUnit_idle; end_cycle = true; } else { if ( aiFsmAttackUnit_path_not_finished == false ) { iXY start; MapInterface::pointXYtoMapXY( unit_state.location, &start ); PathRequest path_request; path_request.set(id, start, aiFsmAttackUnit_target_goal_loc, 0, &path, _path_request_full ); PathScheduler::requestPath( path_request ); aiFsmAttackUnit_path_not_finished = true; aiFsmAttackUnit_state = _aiFsmAttackUnit_path_generate; } else { // Rule: RangeVector > WeaponRange // Action: Get next move aiFsmAttackUnit_state = _aiFsmAttackUnit_next_move; } } } break; // ************************************************************* case _aiFsmAttackUnit_idle : { range_vector = target_unit_state->location - unit_state.location; if ( external_ai_event == _external_event_pending_unit_destruct ) { // External Event: This unit is about to be deleted // Action : Exit fsm gracefully aiFsmAttackUnit_OnExitCleanUp(); UnitBlackBoard::unmarkUnitLoc( aiFsmAttackUnit_prev_loc ); external_ai_event = _external_event_null; ai_command_state = _ai_command_idle; end_cycle = true; } else if ( pending_AI_comm == true ) { // External Event: A new AI command is pending // Action: Allow command transition to occur ai_fsm_transition_complete = true; aiFsmAttackUnit_OnExitCleanUp(); end_cycle = true; } else if ( range_vector.mag2() < unit_state.weapon_range ) { // Rule: RangeVector < WeaponRange, unit is in range // Action: Remain in position end_cycle = true; } else { // Rule: RangeVector > WeaponRange // Action: Get next move aiFsmAttackUnit_state = _aiFsmAttackUnit_next_move; } } break; // ************************************************************* case _aiFsmAttackUnit_next_move : { // CurrentPathComplete: is Unit at the end of the current path aiFsmAttackUnit_path_not_finished = path.popFirst( &aiFsmAttackUnit_next_square ); MapInterface::offsetToMapXY( aiFsmAttackUnit_next_square, &aiFsmAttackUnit_next_loc ); if ( !aiFsmAttackUnit_path_not_finished ) { // Rule: CurrentPathComplete is true // Action : check if unit is at the goal aiFsmAttackUnit_state = _aiFsmAttackUnit_range_check; } else { // Rule: CurrentPathComplete is false // Action: Check if next location is empty aiFsmAttackUnit_prev_loc = unit_state.location; MapInterface::pointXYtoMapXY( aiFsmAttackUnit_prev_loc, &aiFsmAttackUnit_prev_loc ); UnitBlackBoard::markUnitLoc( aiFsmAttackUnit_prev_loc ); aiFsmAttackUnit_wait_timer.changePeriod( 0.8f ); aiFsmAttackUnit_state = _aiFsmAttackUnit_wait_clear_loc; } } break; // ************************************************************* case _aiFsmAttackUnit_wait_clear_loc : { if ( external_ai_event == _external_event_pending_unit_destruct ) { // External Event: This unit is about to be deleted // Action : Exit fsm gracefully aiFsmAttackUnit_OnExitCleanUp(); UnitBlackBoard::unmarkUnitLoc( aiFsmAttackUnit_prev_loc ); external_ai_event = _external_event_null; ai_command_state = _ai_command_idle; end_cycle = true; } else if ( pending_AI_comm == true ) { // External Event: A new AI command is pending // Action: Allow command transition to occur ai_fsm_transition_complete = true; aiFsmAttackUnit_OnExitCleanUp(); end_cycle = true; } else if ( UnitBlackBoard::unitOccupiesLoc( aiFsmAttackUnit_next_loc ) == true ) { if ( aiFsmAttackUnit_wait_timer.count() ) { if ( aiFsmAttackUnit_next_loc == aiFsmAttackUnit_target_goal_loc ) { UnitInterface::unit_placement_matrix.reset( aiFsmAttackUnit_target_goal_loc ); UnitInterface::unit_placement_matrix.getNextEmptyLoc( &aiFsmAttackUnit_target_goal_loc ); PathRequest path_request; path_request.set( id, aiFsmAttackUnit_prev_loc, aiFsmAttackUnit_target_goal_loc, 0, &path, _path_request_full ); PathScheduler::requestPath( path_request ); } else { PathRequest path_request; path_request.set( id, aiFsmAttackUnit_prev_loc, aiFsmAttackUnit_target_goal_loc, 0, &path, _path_request_update ); PathScheduler::requestPath( path_request ); } aiFsmAttackUnit_state = _aiFsmAttackUnit_path_generate; } end_cycle = true; } else { UnitBlackBoard::markUnitLoc( aiFsmAttackUnit_next_loc ); setFsmMoveMapSquare( aiFsmAttackUnit_next_square ); aiFsmAttackUnit_state = _aiFsmAttackUnit_move_wait; } } break; // ************************************************************* case _aiFsmAttackUnit_move_wait : { if ( fsm_active_list[ _control_move_map_square ] == false ) { UnitBlackBoard::unmarkUnitLoc( aiFsmAttackUnit_prev_loc ); aiFsmAttackUnit_state = _aiFsmAttackUnit_check_fsm_transition; } end_cycle = true; } break; // ************************************************************* case _aiFsmAttackUnit_check_fsm_transition : { if ( external_ai_event == _external_event_pending_unit_destruct ) { // External Event: This unit is about to be deleted // Action : Exit fsm gracefully aiFsmAttackUnit_OnExitCleanUp(); UnitBlackBoard::unmarkUnitLoc( aiFsmAttackUnit_next_loc ); external_ai_event = _external_event_null; ai_command_state = _ai_command_idle; end_cycle = true; } else if ( pending_AI_comm == true ) { ai_fsm_transition_complete = true; aiFsmAttackUnit_OnExitCleanUp(); end_cycle = true; } else if ( aiFsmAttackUnit_target_destroyed == true ) { setAiFsmDefendHold(); aiFsmAttackUnit_OnExitCleanUp(); end_cycle = true; } else { aiFsmAttackUnit_state = _aiFsmAttackUnit_check_path_deviation; } } break; // ************************************************************* case _aiFsmAttackUnit_check_path_deviation : { iXY deviation_vector; iXY goal_point_loc; MapInterface::mapXYtoPointXY( aiFsmAttackUnit_target_goal_loc, &goal_point_loc ); deviation_vector = target_unit_state->location - goal_point_loc; if ( deviation_vector.mag2() > unit_state.weapon_range ) { MapInterface::pointXYtoMapXY( target_unit_state->location, &aiFsmAttackUnit_target_goal_loc ); PathRequest path_request; path_request.set( id, aiFsmAttackUnit_next_loc, aiFsmAttackUnit_target_goal_loc, 0, &path, _path_request_full ); PathScheduler::requestPath( path_request ); aiFsmAttackUnit_state = _aiFsmAttackUnit_path_generate; end_cycle = true; } else { aiFsmAttackUnit_state = _aiFsmAttackUnit_range_check; } } break; } // ** switch } while ( end_cycle == false ); } void Vehicle::aiFsmAttackUnit_OnExitCleanUp() { PathScheduler::killRequest( id ); clearFsmGunneryTarget(); } void Vehicle::setAiFsmDefendHold() { ai_command_state = _ai_command_defend_hold; aiFsmDefendHold_state = _aiFsmDefendHold_search_for_enemy; aiFsmDefendHold_search_timer.changePeriod( .5 ); } void Vehicle::aiFsmDefendHold() { bool end_cycle = false; UnitBase *target_unit_ptr; UnitState *target_unit_state; iXY range_vector; do { switch ( aiFsmDefendHold_state ) { case _aiFsmDefendHold_search_for_enemy : { if ( external_ai_event == _external_event_pending_unit_destruct ) { // External Event: This unit is about to be deleted // Action : Exit fsm gracefully external_ai_event = _external_event_null; ai_command_state = _ai_command_idle; end_cycle = true; } else if ( pending_AI_comm == true ) { // External Event: A new AI command is pending // Action: Allow command transition to occur ai_fsm_transition_complete = true; end_cycle = true; } else if ( aiFsmDefendHold_search_timer.count() ) { if (UnitInterface::queryClosestEnemyUnit( &target_unit_ptr, unit_state.location, player->getID() ) ) { target_unit_state = &(target_unit_ptr->unit_state); range_vector = target_unit_state->location - unit_state.location; if ( range_vector.mag2() <= unit_state.defend_range ) { aiFsmDefendHold_target_ID = target_unit_ptr->id; setFsmGunneryTarget( aiFsmDefendHold_target_ID ); aiFsmDefendHold_state = _aiFsmDefendHold_attack_enemy; end_cycle = true; } else { end_cycle = true; } } // ** quearyClosestEnemyUnit else { end_cycle = true; } } // ** aiFsmDefendHold_search_timer.count() else { end_cycle = true; } } break; case _aiFsmDefendHold_attack_enemy : { if ( external_ai_event == _external_event_pending_unit_destruct ) { // External Event: This unit is about to be deleted // Action : Exit fsm gracefully clearFsmGunneryTarget(); external_ai_event = _external_event_null; ai_command_state = _ai_command_idle; end_cycle = true; } else if ( pending_AI_comm == true ) { // External Event: A new AI command is pending // Action: Allow command transition to occur clearFsmGunneryTarget(); ai_fsm_transition_complete = true; end_cycle = true; } else { target_unit_ptr = UnitInterface::getUnit( aiFsmDefendHold_target_ID ); if ( target_unit_ptr == 0 ) { clearFsmGunneryTarget(); aiFsmDefendHold_state = _aiFsmDefendHold_search_for_enemy; end_cycle = true; } else { target_unit_state = &(target_unit_ptr->unit_state); if ( target_unit_state->lifecycle_state == _UNIT_LIFECYCLE_DESTROYED ) { clearFsmGunneryTarget(); aiFsmDefendHold_state = _aiFsmDefendHold_search_for_enemy; end_cycle = true; } // ** if else { range_vector = target_unit_state->location - unit_state.location; if ( range_vector.mag2() > unit_state.defend_range ) { clearFsmGunneryTarget(); aiFsmDefendHold_state = _aiFsmDefendHold_search_for_enemy; end_cycle = true; } } // else } // ** else } // ** else end_cycle = true; } break; } // ** switch } while ( end_cycle == false ); } void Vehicle::aiFsmManualMove() { bool end_cycle = false; signed char offset_x = 0, offset_y = 0; size_t next_square; do { switch( aiFsmManualMove_state ) { case _aiFsmManualMove_next_move : { aiFsmManualMove_prev_loc = aiFsmManualMove_next_loc; orientationToOffset( aiFsmManualMove_move_orientation, &offset_x, &offset_y ); aiFsmManualMove_next_loc.x += offset_x; aiFsmManualMove_next_loc.y += offset_y; next_square = MapInterface::mapXYtoOffset(aiFsmManualMove_next_loc); if ( MapInterface::getMovementValue( aiFsmManualMove_next_loc ) == 0xFF ) { setAiFsmDefendHold(); end_cycle = true; } else if( UnitBlackBoard::unitOccupiesLoc( aiFsmManualMove_next_loc ) == true ) { setAiFsmDefendHold(); end_cycle = true; } else { UnitBlackBoard::markUnitLoc( aiFsmManualMove_next_loc ); setFsmMoveMapSquare( next_square ); aiFsmManualMove_state = _aiFsmManualMove_move_wait; } } break; // ************************************************************* case _aiFsmManualMove_move_wait : { if ( fsm_active_list[ _control_move_map_square ] == false ) { UnitBlackBoard::unmarkUnitLoc( aiFsmManualMove_prev_loc ); aiFsmManualMove_state = _aiFsmManualMove_check_fsm_transition; end_cycle = true; } else { end_cycle = true; } // ** else } break; // ************************************************************* case _aiFsmManualMove_check_fsm_transition : { if ( external_ai_event == _external_event_pending_unit_destruct ) { // External Event: This unit is about to be deleted // Action : Exit fsm gracefully UnitBlackBoard::unmarkUnitLoc( aiFsmManualMove_next_loc ); external_ai_event = _external_event_null; ai_command_state = _ai_command_idle; end_cycle = true; } else if ( pending_AI_comm == true ) { ai_fsm_transition_complete = true; end_cycle = true; } else { aiFsmManualMove_state = _aiFsmManualMove_next_move; } } break; } // ** switch } while ( end_cycle == false ); } void Vehicle::fireWeapon( iXY &target_loc ) { reload_counter = 0; unsigned short projectile_type = launchProjectile(); ProjectileInterface::newProjectile(projectile_type, unit_state.unit_type, id, unit_state.damage_factor, unit_state.location, target_loc ); if ( NetworkState::status == _network_state_server ) { FireWeaponOpcode fire_opcode; fire_opcode.opcode = _UNIT_OPCODE_FIRE_WEAPON; fire_opcode.setUnitID(id); fire_opcode.setTarget(target_loc); UnitInterface::sendOpcode(&fire_opcode); } } //----------------------------------------------------------------- void Vehicle::soundSelected() { sound->playSound("yessir"); } void Vehicle::accessThreatLevels() { if ( unit_state.threat_level == _threat_level_under_attack ) { if( threat_level_under_attack_timer.count() ) { unit_state.threat_level = _threat_level_all_clear; } } } void Vehicle::updateFsmState() { if ( fsm_timer.count() ) { if ( fsm_active_list[ _control_move_map_square ] == true ) fsmMoveMapSquare(); if ( fsm_active_list[ _control_turret_track_point ] == true ) fsmTurretTrackPoint(); if ( fsm_active_list[ _control_turret_track_target ] == true ) fsmTurretTrackTarget(); if ( fsm_active_list[ _control_gunnery_location ] == true ) fsmGunneryLocation(); if ( fsm_active_list[ _control_gunnery_target ] == true ) fsmGunneryTarget(); } } void Vehicle::updateAIState() { switch(ai_command_state) { case _ai_command_idle: aiFsmIdle(); break; case _ai_command_defend_hold: aiFsmDefendHold(); break; case _ai_command_move_to_loc: aiFsmMoveToLoc(); break; case _ai_command_attack_unit: aiFsmAttackUnit(); break; case _ai_command_manual_move: aiFsmManualMove(); break; } } void Vehicle::checkPendingAICommStatus() { if ( (pending_AI_comm == true) && (ai_fsm_transition_complete == true) ) { pending_AI_comm = false; ai_fsm_transition_complete = false; if ( unit_state.lifecycle_state == _UNIT_LIFECYCLE_ACTIVE ) { switch( pending_AI_comm_mesg.command ) { case _command_move_to_loc : setCommandMoveToLoc( &pending_AI_comm_mesg ); break; case _command_attack_unit : setCommandAttackUnit( &pending_AI_comm_mesg ); break; case _command_start_manual_move : setCommandManualMove( &pending_AI_comm_mesg ); break; case _command_stop_manual_move : setCommandManualMove( &pending_AI_comm_mesg ); break; } // ** switch } // ** unit_state.lifecycle_state == _UNIT_LIFECYCLE_ACTIVE } // ** if } void Vehicle::setCommandMoveToLoc(const UMesgAICommand* message) { iXY start; if ( fsm_active_list[ _control_gunnery_location ] == true ) { clearFsmGunneryLocation(); } aiFsmMoveToLoc_goal = message->getGoalLoc(); ai_command_state = _ai_command_move_to_loc; aiFsmMoveToLoc_state = _aiFsmMoveToLoc_path_generate; aiFsmMoveToLoc_path_not_finished = true; opcode_move_timer.changePeriod( 0.10f ); move_opcode_sent = false; //move_opcode_sent = true; MapInterface::pointXYtoMapXY( unit_state.location, &start ); //LOG( ("UnitID %d, %d : Start %d, %d : Goal %d, %d", id.getPlayer(), id.getIndex(), // start.x, start.y, // aiFsmMoveToLoc_goal.x, aiFsmMoveToLoc_goal.y ) ); PathRequest path_request; path_request.set( id, start, aiFsmMoveToLoc_goal, 0, &path, _path_request_full ); PathScheduler::requestPath( path_request ); iXY target; MapInterface::mapXYtoPointXY( aiFsmMoveToLoc_goal, &target); setFsmTurretTrackPoint( target ); } void Vehicle::setCommandAttackUnit(const UMesgAICommand* message) { iXY start; UnitBase *target_unit_ptr; UnitState *target_unit_state; aiFsmAttackUnit_target_ID = message->getTargetUnitID(); target_unit_ptr = UnitInterface::getUnit( aiFsmAttackUnit_target_ID ); if ( target_unit_ptr == 0 ) return; target_unit_state = &(target_unit_ptr->unit_state); ai_command_state = _ai_command_attack_unit; aiFsmAttackUnit_state = _aiFsmAttackUnit_path_generate; aiFsmAttackUnit_path_not_finished = true; aiFsmAttackUnit_target_destroyed = false; MapInterface::pointXYtoMapXY( unit_state.location, &start ); MapInterface::pointXYtoMapXY( target_unit_state->location, &aiFsmAttackUnit_target_goal_loc ); PathRequest path_request; path_request.set( id, start, aiFsmAttackUnit_target_goal_loc, 0, &path, _path_request_full ); PathScheduler::requestPath( path_request ); opcode_move_timer.changePeriod( 0.10f ); move_opcode_sent = false; //move_opcode_sent = true; setFsmGunneryTarget( aiFsmAttackUnit_target_ID ); } void Vehicle::setCommandManualMove(const UMesgAICommand* message) { if ( message->command == _command_start_manual_move ) { aiFsmManualMove_move_orientation = message->manual_move_orientation; MapInterface::pointXYtoMapXY( unit_state.location, &aiFsmManualMove_next_loc ); aiFsmManualMove_state = _aiFsmManualMove_next_move; ai_command_state = _ai_command_manual_move; } else if ( message->command == _command_stop_manual_move ) { ai_command_state = _ai_command_idle; } } void Vehicle::setCommandManualFire(const UMesgAICommand* message) { setFsmGunneryLocation(message->getTargetLoc()); } void Vehicle::messageAICommand(const UnitMessage* message) { const UMesgAICommand *command_mesg = (const UMesgAICommand *) message; if ( unit_state.lifecycle_state == _UNIT_LIFECYCLE_ACTIVE ) { if (command_mesg->command == _command_manual_fire) { setCommandManualFire( command_mesg ); } else { memcpy(&pending_AI_comm_mesg, command_mesg, sizeof(UMesgAICommand)); pending_AI_comm = true; } } } void Vehicle::messageWeaponHit(const UnitMessage *message) { const UMesgWeaponHit *weapon_hit = (const UMesgWeaponHit *) message; if (!unit_state.bounds(weapon_hit->getHitLocation())) return; unit_state.hit_points -= weapon_hit->getDamageFactor(); unit_state.threat_level = _threat_level_under_attack; threat_level_under_attack_timer.changePeriod( gameconfig->attacknotificationtime); UpdateStateUnitOpcode update_state_opcode; update_state_opcode.setUnitID(id); update_state_opcode.setHitPoints(unit_state.hit_points); UnitInterface::sendOpcode( &update_state_opcode ); if ( unit_state.hit_points <= 0 ) { unit_state.lifecycle_state = _UNIT_LIFECYCLE_PENDING_DESTRUCT; external_ai_event = _external_event_pending_unit_destruct; UMesgEndLifeCycleUpdate lifecycle_update; lifecycle_update.set(id, weapon_hit->getOwnerUnitID(), unit_state.unit_type); UnitInterface::sendMessage(&lifecycle_update); // ** Note: Temp iXY current_map_loc; MapInterface::pointXYtoMapXY(unit_state.location, ¤t_map_loc); UnitBlackBoard::unmarkUnitLoc(current_map_loc); } } void Vehicle::messageSelectBoxUpdate(const UnitMessage* message) { const UMesgUpdateSelectBoxInfo *select_box_update = (const UMesgUpdateSelectBoxInfo *) message; switch (select_box_update->request_type) { case _select_box_allie_visibility: { select_info_box.setAllieIcon(select_box_update->allie_flag_visiblity ); break; } break; case _select_box_flag_visiblity: { select_info_box.setFlagIcon( select_box_update->flag_visiblity ); break; } case _select_box_is_allied: { select_info_box.setAllieState( select_box_update->allied_state ); break; } } } void Vehicle::messageSelfDestruct(const UnitMessage* ) { unit_state.lifecycle_state = _UNIT_LIFECYCLE_PENDING_DESTRUCT; external_ai_event = _external_event_pending_unit_destruct; // ** Note: Temp iXY current_map_loc; MapInterface::pointXYtoMapXY( unit_state.location, ¤t_map_loc ); UnitBlackBoard::unmarkUnitLoc( current_map_loc ); } void Vehicle::processMessage(const UnitMessage* message) { if (unit_state.lifecycle_state != _UNIT_LIFECYCLE_ACTIVE) return; switch(message->message_id) { case _umesg_ai_command: messageAICommand(message); break; case _umesg_weapon_hit: messageWeaponHit(message); break; case _umesg_update_select_box_info: messageSelectBoxUpdate(message); break; case _umesg_self_destruct: messageSelfDestruct(message); break; default: LOGGER.warning("Unknown unit message (id %d)", message->message_id); break; } } void Vehicle::unitOpcodeMove(const UnitOpcode* opcode) { if ( ( (in_sync_flag == false) && !(opcode->flags & _unit_opcode_flag_sync) ) || ( (in_sync_flag == true) && (opcode->flags & _unit_opcode_flag_sync) ) ) return; const MoveOpcode* move_opcode = (const MoveOpcode *) opcode; iXY sync_loc; iXY current_loc; MapInterface::offsetToMapXY(move_opcode->getSquare(), &sync_loc ); sync_loc.x = sync_loc.x + move_opcode->loc_x_offset; sync_loc.y = sync_loc.y + move_opcode->loc_y_offset; MapInterface::pointXYtoMapXY( unit_state.location, ¤t_loc ); if ( current_loc != sync_loc ) { MapInterface::mapXYtoPointXY( sync_loc, &(unit_state.location) ); } setFsmMoveMapSquare(move_opcode->getSquare()); } void Vehicle::unitOpcodeTrackPoint(const UnitOpcode* opcode ) { if ( ( (in_sync_flag == false) && !(opcode->flags & _unit_opcode_flag_sync) ) || ( (in_sync_flag == true) && (opcode->flags & _unit_opcode_flag_sync) ) ) return; const TurretTrackPointOpcode* track_point_opcode = (const TurretTrackPointOpcode *) opcode; if ( track_point_opcode->activate == true ) { iXY target = track_point_opcode->getTarget(); setFsmTurretTrackPoint( target ); } else { fsm_active_list[ _control_turret_track_point ] = false; } } void Vehicle::unitOpcodeTrackTarget(const UnitOpcode* opcode ) { if ( ( (in_sync_flag == false) && !(opcode->flags & _unit_opcode_flag_sync) ) || ( (in_sync_flag == true) && (opcode->flags & _unit_opcode_flag_sync) ) ) return; const TurretTrackTargetOpcode* track_target_opcode = (const TurretTrackTargetOpcode *) opcode; if ( track_target_opcode->activate == true ) { setFsmTurretTrackTarget( track_target_opcode->getTargetUnitID() ); } else { fsm_active_list[ _control_turret_track_target ] = false; } } void Vehicle::unitOpcodeFireWeapon(const UnitOpcode* opcode ) { iXY target_loc; if ( ( (in_sync_flag == false) && !(opcode->flags & _unit_opcode_flag_sync) ) || ( (in_sync_flag == true) && (opcode->flags & _unit_opcode_flag_sync) ) ) return; const FireWeaponOpcode *fire_weapon = (const FireWeaponOpcode *) opcode; target_loc = fire_weapon->getTarget(); fireWeapon( target_loc ); } void Vehicle::unitOpcodeSync(const UnitOpcode* ) { in_sync_flag = true; } void Vehicle::unitOpcodeUpdateState(const UnitOpcode* opcode ) { const UpdateStateUnitOpcode *update_state_opcode = (const UpdateStateUnitOpcode *) opcode; unit_state.hit_points = update_state_opcode->getHitPoints(); unit_state.threat_level = _threat_level_under_attack; threat_level_under_attack_timer.changePeriod( 30 ); } void Vehicle::unitOpcodeDestruct(const UnitOpcode* ) { unit_state.lifecycle_state = _UNIT_LIFECYCLE_INACTIVE; } void Vehicle::syncUnit() { syncFsmTurretTrackPoint(); syncFsmTurretTrackTarget(); SyncUnitOpcode sync_opcode; sync_opcode.opcode = _UNIT_OPCODE_SYNC_UNIT; sync_opcode.setUnitID(id); UnitInterface::sendOpcode( &sync_opcode ); } void Vehicle::processMoveOpcodeQueue() { if (!move_opcode_queue.empty()) { if( fsm_active_list[ _control_move_map_square ] == false ) { if ( move_opcode_queue.size() >= 3 ) { for( int i = 0; i < 2; i++ ) { move_opcode_queue.pop(); } //ConsoleInterface::postMessage( "Move Opcode Queue Adjusted" ); } UnitOpcodeStruct opcodedata = move_opcode_queue.front(); move_opcode_queue.pop(); const UnitOpcode* opcode = (UnitOpcode*) &opcodedata; unitOpcodeMove(opcode); } } } void Vehicle::processOpcodeQueue() { processMoveOpcodeQueue(); if (!opcode_queue.empty()) { UnitOpcodeStruct opcodedata = opcode_queue.front(); const UnitOpcode* opcode = (UnitOpcode*) &opcodedata; switch(opcode->opcode ) { case _UNIT_OPCODE_TURRET_TRACK_POINT: unitOpcodeTrackPoint(opcode); break; case _UNIT_OPCODE_TURRET_TRACK_TARGET: unitOpcodeTrackTarget(opcode); break; case _UNIT_OPCODE_FIRE_WEAPON: unitOpcodeFireWeapon(opcode); break; case _UNIT_OPCODE_SYNC_UNIT: unitOpcodeSync(opcode); break; case _UNIT_OPCODE_UPDATE_STATE: unitOpcodeUpdateState(opcode); break; case _UNIT_OPCODE_DESTRUCT: unitOpcodeDestruct(opcode); break; default: LOGGER.warning("Unknown Opcode: %d.\n", opcode->opcode); assert(false); break; } opcode_queue.pop(); } } void Vehicle::evalCommandOpcode(const UnitOpcode* opcode) { if (opcode->opcode == _UNIT_OPCODE_MOVE) { move_opcode_queue.push(*((const UnitOpcodeStruct*) opcode)); } else { opcode_queue.push(*((const UnitOpcodeStruct*) opcode)); } } void Vehicle::updateState() { updateFsmState(); if ( NetworkState::status == _network_state_server ) { if( unit_state_timer.count() ) updateUnitStateProperties(); updateAIState(); checkPendingAICommStatus(); } else { processOpcodeQueue(); } accessThreatLevels(); if( unit_state.hit_points < unit_state.max_hit_points ) { smolderWait += TimerInterface::getTimeSlice(); int intPercent = unit_state.percentDamageInt(); if (intPercent > 50 && (rand() % 100) < intPercent) { if (smolderWait > smolderWaitMin) { float percent = unit_state.percentDamageFloat(); smolderWaitMin = (float(100 - percent) / 100.0) + (float(100 - percent) / 100.0) * 0.3; ParticleInterface::addUnitDamagePuffParticle(unit_state); smolderWait = 0.0f; } } } } void Vehicle::offloadGraphics( SpriteSorter &sorter ) { body_anim_shadow.setWorldPos( unit_state.location); if ( sorter.cullSprite( body_anim_shadow ) == false ) { // Body body_anim.setWorldPos( unit_state.location ); body_anim.setFrame( unit_state.body_angle.angle_int ); // Turret turret_anim.setWorldPos( unit_state.location ); turret_anim.setFrame( unit_state.turret_angle.angle_int ); // Body Shadow body_anim_shadow.setFrame( unit_state.body_angle.angle_int ); // Turret Shadow turret_anim_shadow.setWorldPos( unit_state.location ); turret_anim_shadow.setFrame( unit_state.turret_angle.angle_int ); select_info_box.setBoxState( unit_state.select ); //Added layer selection to the selection box info. select_info_box.setAttrib( unit_state.location, gameconfig->unitinfodrawlayer ); select_info_box.setHitPoints( unit_state.hit_points ); sorter.forceAddSprite( &body_anim_shadow ); } //sorter.addSprite( &body_anim_shadow ); }