/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx 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, or (at your option) any later version. ### File Specific: main-programmer: Christoph Renner co-programmer: */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_WORLD_ENTITY #include "loading/factory.h" #include "debug.h" #include "loading/load_param.h" #include "util/loading/load_param_xml.h" #include "state.h" #include "player.h" #include "playable.h" #include "particles/dot_emitter.h" #include "particles/emitter_node.h" #include "particles/sprite_particles.h" #include "actionbox_enemy.h" ObjectListDefinition(ActionboxEnemy); CREATE_FACTORY(ActionboxEnemy); ActionboxEnemy::ActionboxEnemy(const TiXmlElement* root) :weaponMan(this) { PRINTF(0)("ActionboxEnemy\n"); this->registerObject(this, ActionboxEnemy::_objectList); this->toList(OM_GROUP_00); this->isActive = true; this->pitch = 0.0f; this->dPitch = 0.0; this->maxSpeed = 30; this->acceleration = 3; this->speed = 0; this->agility = 1; this->onEscape = false; this->escaped = false; this->endPoint = Vector(0, 0, 0); this->weaponMan.setParentEntity( this); //weapons: this->weaponMan.setParentEntity( this); this->weaponMan.setSlotCount(8); this->weaponMan.createWeaponSlot(0, 3.270, 1.028, .155, WTYPE_ALLDIRS | WTYPE_DIRECTIONAL); this->weaponMan.createWeaponSlot(1, 3.270, 1.028, -.155, WTYPE_ALLDIRS | WTYPE_DIRECTIONAL); this->weaponMan.createWeaponSlot(2, 4.385, .063, .876, WTYPE_ALLDIRS | WTYPE_DIRECTIONAL); this->weaponMan.createWeaponSlot(3, 4.385, -.063, -.876, WTYPE_ALLDIRS | WTYPE_DIRECTIONAL); this->weaponMan.createWeaponSlot(4, 1.635, -.612, 2.691, WTYPE_ALLDIRS | WTYPE_DIRECTIONAL); this->weaponMan.createWeaponSlot(5, 1.536, -.612, -2.691, WTYPE_ALLDIRS | WTYPE_DIRECTIONAL); this->weaponMan.createWeaponSlot(6, 1.536, -.612, 3.254, WTYPE_ALLDIRS | WTYPE_DIRECTIONAL); this->weaponMan.createWeaponSlot(7, 1.536, -.612, -3.254, WTYPE_ALLDIRS | WTYPE_DIRECTIONAL); this->weaponMan.addWeaponToSlot(0, 0, "RFCannon"); this->weaponMan.addWeaponToSlot(0, 1, "RFCannon"); // this->weaponMan.addWeaponToSlot(0, 2, "RFCannon"); // this->weaponMan.addWeaponToSlot(0, 3, "RFCannon"); // this->weaponMan.addWeaponToSlot(1, 0, "RFCannon"); // this->weaponMan.addWeaponToSlot(1, 1, "RFCannon"); // this->weaponMan.addWeaponToSlot(1, 2, "RFCannon"); // this->weaponMan.addWeaponToSlot(1, 3, "RFCannon"); /* this->weaponMan.addWeaponToSlot(0, 4, "NadionLaser"); this->weaponMan.addWeaponToSlot(0, 5, "NadionLaser"); this->weaponMan.addWeaponToSlot(2, 4, "NadionLaser"); this->weaponMan.addWeaponToSlot(2, 5, "NadionLaser"); this->weaponMan.addWeaponToSlot(0, 6, "Disruptor"); this->weaponMan.addWeaponToSlot(0, 7, "Disruptor"); this->weaponMan.addWeaponToSlot(3, 6, "Disruptor"); this->weaponMan.addWeaponToSlot(3, 7, "Disruptor");*/ this->weaponMan.changeWeaponConfig(0); this->weaponMan.getFixedTarget()->setParent(this); this->weaponMan.getFixedTarget()->setRelCoor(100000,0,0); this->weaponMan.hideCrosshair(); if ( root ) { this->loadParams( root ); } } ActionboxEnemy::~ActionboxEnemy() { } void ActionboxEnemy::loadParams(const TiXmlElement* root) { WorldEntity::loadParams( root ); LoadParam(root, "endPoint", this, ActionboxEnemy, setEndPoint); LoadParam(root, "maxSpeed", this, ActionboxEnemy, setMaxSpeed); LoadParam(root, "agility", this, ActionboxEnemy, setAgility); LoadParam(root, "acceleration", this, ActionboxEnemy, setAcceleration); } void ActionboxEnemy::tick( float dt ) { if ( escaped ) { moveTowards( endPoint, endPoint - this->getAbsCoor(), dt ); return; } weaponMan.tick( dt ); this->bFire = false; if ( !State::getPlayer() || !State::getPlayer()->getPlayable() ) return; Vector playerDir = State::getPlayer()->getPlayable()->getAbsDir().apply( Vector(1, 0, 0) ); Vector dist = this->getAbsCoor() - State::getPlayer()->getPlayable()->getAbsCoor(); bool behindPlayer = playerDir.dot( dist ) < 0; if ( behindPlayer ) { this->escaped = true; PRINTF(0)("MOVING TOWARDS ENDPOINT\n"); return; } myDir = this->getAbsDir(); myCoor = this->getAbsCoor(); this->pitch += this->dPitch*dt; while ( pitch > 2*PI ) pitch -= 2*PI; while ( pitch < 0 ) pitch += 2*PI; myDir *= qPitch.inverse(); qPitch = Quaternion( pitch, Vector( 1, 0, 0 ) ); if ( isActive && State::getActionBox() ) { ActionBox* box = State::getActionBox(); if ( box->isPointInBox( this->getAbsCoor() ) ) { attackPlayer( box, dt ); } else { moveTowardsBox( box, dt ); onEscape = false; } } myDir *= qPitch; this->setAbsDir( myDir ); this->setAbsCoor( myCoor ); } void ActionboxEnemy::attackPlayer( ActionBox * box, float dt ) { if ( !State::getPlayer() || !State::getPlayer()->getPlayable() || !box ) return; float distance = (State::getPlayer()->getPlayable()->getAbsCoor() - getAbsCoor() ).len(); if ( distance > box->getDepth()/4.0 && !onEscape ) { Vector targetPos = State::getPlayer()->getPlayable()->getAbsCoor(); Vector targetDir = State::getPlayer()->getPlayable()->getAbsCoor()-this->getAbsCoor(); moveTowards( targetPos, targetDir, dt ); if ( this->getAbsDir().apply( Vector(1, 0, 0) ).dot(targetDir) > 0.9 ) { this->bFire = true; weaponMan.fire(); } } else { if ( !onEscape ) { Vector ds = this->getAbsCoor() - State::getPlayer()->getPlayable()->getAbsCoor(); float projy = box->getAbsDir().apply( Vector(0, 1, 0) ).dot( ds ); float projz = box->getAbsDir().apply( Vector(0, 0, 1) ).dot( ds ); this->escapePoint = Vector( 0, projy, projz ); this->escapePoint.normalize(); this->escapePoint*= 2*box->getWidth_2(); PRINTF(0)("ESCAPE\n"); } onEscape = true; Vector rEscapePoint = box->getAbsDir().apply(escapePoint + Vector( -10, 0, 0 )); Vector targetPos = State::getPlayer()->getPlayable()->getAbsCoor() + rEscapePoint; Vector targetDir = State::getPlayer()->getPlayable()->getAbsCoor() + rEscapePoint - this->getAbsCoor(); moveTowards( targetPos, targetDir, dt ); } } void ActionboxEnemy::moveTowards( Vector targetPos, Vector targetDir, float dt ) { Quaternion cur = myDir; Quaternion rx( agility*dt, Vector( 0, 0, 1 ) ); Quaternion tmp1 = cur * rx; Quaternion tmp2 = cur * rx.inverse(); Quaternion dec; if ( tmp1.apply( Vector(1, 0, 0) ).dot(targetDir) > tmp2.apply( Vector(1, 0, 0)).dot(targetDir) ) dec = tmp1; else dec = tmp2; float dp = dec.apply( Vector(1, 0, 0) ).dot(Vector(0, 1, 0)); if ( dp > -0.9 && dp < 0.9 ) { cur = dec; } Quaternion ry( agility*dt, cur.inverse().apply( Vector( 0, 1, 0 ) ) ); tmp1 = cur * ry; tmp2 = cur * ry.inverse(); if ( tmp1.apply( Vector(1, 0, 0) ).dot(targetDir) > tmp2.apply( Vector(1, 0, 0)).dot(targetDir) ) cur = tmp1; else cur = tmp2; myDir = cur; Vector fw = cur.apply( Vector(1, 0, 0) ); this->speed += this->acceleration*dt; if ( this->speed > this->maxSpeed ) this->speed = this->maxSpeed; this->myCoor += fw*speed*dt; } void ActionboxEnemy::moveTowardsBox( ActionBox * box, float dt ) { Vector targetPos = box->getAbsCoor() + box->getAbsDir().apply( Vector( 1, 0, 0 ) )*box->getDepth()*0.66f; Vector targetDir = targetPos - myCoor; moveTowards(targetPos, targetDir, dt); } void ActionboxEnemy::draw( ) const { #if 0 Vector fw = this->getAbsDir().apply( Vector( 1, 0, 0 ) ); fw.normalize(); fw = fw * 100; Vector mp = this->getAbsCoor(); Vector op = mp + fw; Vector targetPos = State::getPlayer()->getPlayable()->getAbsCoor(); Vector dv = targetPos - this->getAbsCoor(); dv.normalize(); dv *= 100; dv += mp; Vector spUp = this->getAbsDir().inverse().apply( this->getAbsDir().apply( Vector( 0, 1, 0 ) ) ); spUp.normalize(); spUp *= 100; spUp += mp; Vector up = fw.cross( dv ); up += mp; //PRINTF(0)("DEBUG\n"); //mp.debug(); //op.debug(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glPushAttrib(GL_ENABLE_BIT); glDisable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glLineWidth(2.0); glColor3f(1.0, 0.0, 0.0 ); glBegin(GL_LINE_STRIP); glVertex3f(mp.x, mp.y, mp.z); glVertex3f(op.x, op.y, op.z); glEnd(); glColor3f(0.0, 1.0, 0.0 ); glBegin(GL_LINE_STRIP); glVertex3f(mp.x, mp.y, mp.z); glVertex3f(dv.x, dv.y, dv.z); glEnd(); glColor3f(0.0, 0.0, 1.0 ); glBegin(GL_LINE_STRIP); glVertex3f(mp.x, mp.y, mp.z); glVertex3f(up.x, up.y, up.z); glEnd(); glColor3f(1.0, 1.0, 1.0 ); glBegin(GL_LINE_STRIP); glVertex3f(mp.x, mp.y, mp.z); glVertex3f(spUp.x, spUp.y, spUp.z); glEnd(); glPopMatrix(); #endif WorldEntity::draw(); } void ActionboxEnemy::destroy( WorldEntity * killer ) { EmitterNode* node = NULL; DotEmitter* emitter = NULL; SpriteParticles* explosionParticles = NULL; explosionParticles = new SpriteParticles(200); explosionParticles->setName("SpaceShipExplosionParticles"); explosionParticles->setLifeSpan(.3, .7); explosionParticles->setRadius(0.0, 10.0); explosionParticles->setRadius(.5, 6.0); explosionParticles->setRadius(1.0, 3.0); explosionParticles->setColor(0.0, 1,1,1,.9); explosionParticles->setColor(0.1, 1,1,0,.9); explosionParticles->setColor(0.5, .8,.4,0,.5); explosionParticles->setColor(1.0, .2,.2,.2,.5); emitter = new DotEmitter( 2000, 70, 360); emitter->setEmissionRate( 200.0); node = new EmitterNode( .4f ); node->setupParticle( emitter, explosionParticles); node->setAbsDir( this->getAbsDir()); node->setVelocity( this->getVelocity() * .9f); node->setAbsCoor( this->getAbsCoor()); if( !node->start()) PRINTF(0)("Explosion node not correctly started!"); this->setAbsCoor( this->getAbsCoor() + Vector(100,0,0) + Vector(1,0,0) * VECTOR_RAND(150).dot(Vector(1,0,0))); toList( OM_DEAD ); }