diff --git a/binaries/data/config/system.cfg b/binaries/data/config/system.cfg index 927c0927ea..acb8b34e0e 100644 --- a/binaries/data/config/system.cfg +++ b/binaries/data/config/system.cfg @@ -24,8 +24,8 @@ ; System settings: novbo=false -noframebufferobject=false -shadows=true +noframebufferobject=true +shadows=false vsync=false ; Specify the render path. This can be one of: @@ -34,7 +34,7 @@ vsync=false ; vertexshader Use vertex shaders for transform and lighting where possible ; Using 'fixed' instead of 'default' may work around some graphics-related problems, ; but will reduce performance when a modern graphics card is available. -renderpath=default +renderpath=fixed ; Adjusts how OpenGL calculates mipmap level of detail. 0.0f is the default (blurry) value. ; Lower values sharpen/extend, and higher values blur/decrease. Clamped at -3.0 to 3.0. diff --git a/binaries/data/mods/official/entities/formations/BoxForm2.xml b/binaries/data/mods/official/entities/formations/BoxForm2.xml index 44a67e503e..2102982387 100644 --- a/binaries/data/mods/official/entities/formations/BoxForm2.xml +++ b/binaries/data/mods/official/entities/formations/BoxForm2.xml @@ -1,6 +1,7 @@ Ground + + 3.141592 + 3.141592 @@ -49,14 +52,13 @@ -1.0 - + 6 .2 - + - 0.03 - -0.03 - + 7 + .1 diff --git a/binaries/data/mods/official/entities/template_entity_full.xml b/binaries/data/mods/official/entities/template_entity_full.xml index 3840ba640c..9c01a7d8d6 100644 --- a/binaries/data/mods/official/entities/template_entity_full.xml +++ b/binaries/data/mods/official/entities/template_entity_full.xml @@ -22,5 +22,6 @@ + \ No newline at end of file diff --git a/binaries/data/mods/official/entities/template_entity_script.js b/binaries/data/mods/official/entities/template_entity_script.js index 581ab79616..0ea92c6add 100644 --- a/binaries/data/mods/official/entities/template_entity_script.js +++ b/binaries/data/mods/official/entities/template_entity_script.js @@ -157,6 +157,9 @@ function entityInit() // Register our actions with the generic order system if( this.actions ) { + if ( this.actions.move && this.actions.move.speed ) + this.actions.move.speed_curr = this.actions.move.speed; + if( this.actions.attack && this.actions.attack.melee ) { a = this.actions.attack.melee; @@ -341,7 +344,7 @@ function performAttack( evt ) // Attack logic. var dmg = new DamageType(); - + var flank = (evt.target.getAttackDirections()-1)*evt.target.traits.flank_penalty.value; if ( this.getRunState() ) { dmg.crush = parseInt(this.actions.attack.charge.damage * this.actions.attack.charge.crush); @@ -354,6 +357,9 @@ function performAttack( evt ) dmg.hack = parseInt(this.actions.attack.melee.damage * this.actions.attack.melee.hack); dmg.pierce = parseInt(this.actions.attack.melee.damage * this.actions.attack.melee.pierce); } + dmg.crush += dmg.crush * flank; + dmg.hack += dmg.hack * flank; + dmg.pierce += dmg.pierce * flank; evt.target.damage( dmg, this ); @@ -371,7 +377,12 @@ function performAttackRanged( evt ) dmg.hack = parseInt(this.actions.attack.ranged.damage * this.actions.attack.ranged.hack); dmg.pierce = parseInt(this.actions.attack.ranged.damage * this.actions.attack.ranged.pierce); - // The parameters for Projectile are: + var flank = (evt.target.getAttackDirections()-1)*evt.target.traits.flank_penalty.value; + dmg.crush += dmg.crush * flank; + dmg.hack += dmg.hack * flank; + dmg.pierce += dmg.pierce * flank; + + // The parameters for Projectile are: // 1 - The actor to use as the projectile. There are two ways of specifying this: // the first is by giving an entity. The projectile's actor is found by looking // in the actor of that entity. This way is usual, and preferred - visual @@ -564,7 +575,7 @@ function damage( dmg, inflictor ) if(!this.traits.armour) return; // corpses have no armour, everything else should this.last_combat_time = getGameTime(); - + // Apply armour and work out how much damage we actually take crushDamage = parseInt(dmg.crush - this.traits.armour.value * this.traits.armour.crush); if ( crushDamage < 0 ) crushDamage = 0; @@ -666,14 +677,14 @@ function damage( dmg, inflictor ) // If we're not already doing something else, take a measured response - hit 'em back. // You know, I think this is quite possibly the first AI code the AI divlead has written // for 0 A.D.... + //When the entity changes order, we can readjust flank penalty. We must destroy the notifiers ourselves later,however. + this.requestNotification( inflictor, NOTIFY_ORDER_CHANGE, false, true ); + this.registerDamage( inflictor ); if( this.isIdle() ) this.order( ORDER_GENERIC, inflictor, getAttackAction( this, inflictor ) ); } -// FIXME: These seemed to cause a crash I fixed the spelling from register to Register, so I've commented them out (Matei) - //When the entity is idle, we can readjust angle penalty. We must destroy the notifiers ourselves later, however. - //this.requestNotification( inflictor, NOTIFY_IDLE, false, true ); - //this.registerDamage( inflictor ); + } // ==================================================================== @@ -710,11 +721,18 @@ function entityEventGeneric( evt ) function entityEventNotification( evt ) { + //This is used to adjust the flank penalty (we're no longer being attacked). + if ( this.getCurrentRequest() == NOTIFY_ORDER_CHANGE ) + { + this.registerOrderChange(); + destroyNotifier( evt.target ); + return; + } //Add "true" to the end of order() to indicate that this is a notification order. switch( evt.notifyType ) { - case NOTIFY_GOTO: + case NOTIFY_GOTO: this.GotoInRange( evt.location.x, evt.location.z, false); break; case NOTIFY_RUN: @@ -731,13 +749,13 @@ function entityEventNotification( evt ) this.order( ORDER_GENERIC, evt.target, ACTION_GATHER, true ); break; case NOTIFY_IDLE: - //target is the unit that has become idle - this.registerIdle( evt.target ); + //target is the unit that has become idle. Eventually...do something here. break; default: console.write( "Unknown notification request " + evt.notifyType ); - break; + return; } + } // ==================================================================== @@ -765,9 +783,30 @@ function entityComplete() //===================================================================== function entityEventIdle( evt ) { - //Use our own data for target; we aren't affecting anyone, so listeners wants to know about us + //Use our own data for target; we aren't affecting anyone, so listeners want to know about us this.forceCheckListeners( NOTIFY_IDLE, this ); } +//===================================================================== +function entityEventMovement( evt ) +{ + var divs = this.traits.pitch.sectors; + var sector = this.findSector( divs, evt.slope, 3.141592, true ); + + if ( divs % 2 ) + { + sector += ((divs+1)/2 - sector)*2; + sector -= (divs+1)/2; + this.actions.move.speed_curr = this.actions.move.speed; + this.actions.move.speed_curr += this.actions.move.speed*this.traits.pitch.value*sector; + } + else + { + sector += ((divs)/2 - sector)*2; + sector -= (divs)/2; + this.actions.move.speed_curr = this.actions.move.speed; + this.actions.move.speed_curr += this.actions.move.speed*this.traits.pitch.value*sector; + } +} // ==================================================================== @@ -867,26 +906,40 @@ function entityEventPrepareOrder( evt ) //evt.notifySource is the entity order data will be obtained from, so if we're attacking and we //want our listeners to copy us, then we will use our own order as the source. + //registerOrderChange() is used to adjust the flank penalty switch( evt.orderType ) { case ORDER_GOTO: if ( !this.actions.move ) + { evt.preventDefault(); + return; + } evt.notifyType = NOTIFY_GOTO; evt.notifySource = this; + this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this ); break; case ORDER_RUN: if ( !this.actions.move.run ) + { evt.preventDefault(); + return; + } evt.notifyType = NOTIFY_RUN; evt.notifySource = this; + this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this ); break; case ORDER_PATROL: if ( !this.actions.patrol ) + { evt.preventDefault(); + return; + } + this.registerOrderChange(); + this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this ); break; case ORDER_GENERIC: @@ -897,26 +950,42 @@ function entityEventPrepareOrder( evt ) case ACTION_ATTACK_RANGED: evt.action = getAttackAction( this, evt.target ); if ( evt.action == ACTION_NONE ) - evt.preventDefault(); + { + evt.preventDefault(); + return; + } evt.notifyType = NOTIFY_ATTACK; + this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this ); break; case ACTION_GATHER: if ( !this.actions.gather ) + { evt.preventDefault(); + return; + } evt.notifyType = NOTIFY_GATHER; + this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this ); break; case ACTION_HEAL: if ( !this.actions.heal ) + { evt.preventDefault(); + return; + } evt.notifyType = NOTIFY_HEAL; + this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this ); break; case ACTION_BUILD: if ( !this.actions.build ) + { evt.preventDefault(); + return; + } evt.notifyType = NOTIFY_NONE; + this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this ); break; } break; @@ -1463,12 +1532,12 @@ function entityEventFormation( evt ) { if ( this.getFormationBonus() && this.hasClass( this.getFormationBonusType() ) ) { - eval( this + this.getFormationBonus() ) += eval( this + this.getFormationBonus() ) * + eval( this + this.getFormationBonus() ) += eval( this + this.getFormationBonusBase() ) * this.getFormationBonusVal(); } if ( this.getFormationPenalty() && this.hasInClass( this.getFormationPenaltyType() ) ) { - eval( this + this.getFormationPenalty() ) -= eval( this + this.getFormationbonus() ) * + eval( this + this.getFormationPenalty() ) -= eval( this + this.getFormationPenaltyBase() ) * this.getFormationPenaltyVal(); } } @@ -1477,12 +1546,12 @@ function entityEventFormation( evt ) { if ( this.getFormationPenalty() && this.hasInClass( this.getFormationPenaltyType() ) ) { - eval( this + this.getFormationPenalty() ) += eval( this + this.getFormationbonus() ) * + eval( this + this.getFormationPenalty() ) += eval( this + this.getFormationPenaltyBase() ) * this.getFormationPenaltyVal(); } if ( this.getFormationBonus() && this.hasClass( this.getFormationBonusType() ) ) { - eval( this + this.getFormationBonus() ) -= eval( this + this.getFormationBonus() ) * this.getFormationBonusVal(); + eval( this + this.getFormationBonus() ) -= eval( this + this.getFormationBonusBase() ) * this.getFormationBonusVal(); } } diff --git a/binaries/data/mods/official/entities/template_unit.xml b/binaries/data/mods/official/entities/template_unit.xml index de50c3de21..20ebf41ae7 100644 --- a/binaries/data/mods/official/entities/template_unit.xml +++ b/binaries/data/mods/official/entities/template_unit.xml @@ -77,6 +77,7 @@ 7.0 + 0.0 diff --git a/source/graphics/GameView.cpp b/source/graphics/GameView.cpp index a375ff4a25..3b2b1d414b 100644 --- a/source/graphics/GameView.cpp +++ b/source/graphics/GameView.cpp @@ -510,7 +510,7 @@ void CGameView::Update(float DeltaTime) if (m_UnitView) { - m_ViewCamera.m_Orientation.SetYRotation(m_UnitView->m_orientation); + m_ViewCamera.m_Orientation.SetYRotation(m_UnitView->m_orientation.Y); m_ViewCamera.m_Orientation.Translate(m_UnitViewProp->GetTransform().GetTranslation()); m_ViewCamera.UpdateFrustum(); return; diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp index a02f3080a3..0f1206c8dd 100644 --- a/source/graphics/MapReader.cpp +++ b/source/graphics/MapReader.cpp @@ -179,7 +179,7 @@ int CMapReader::ApplyData() { // initialise the terrain pTerrain->Initialize(m_MapSize, &m_Heightmap[0]); - + // setup the textures on the minipatches STileDesc* tileptr = &m_Tiles[0]; for (u32 j=0; jGetModel()->SetTransform(transform); } } + //Make units start out conforming correctly + g_EntityManager.conformAll(); if (unpacker.GetVersion() >= 2) { // copy over the lighting parameters *pLightEnv = m_LightEnv; } - return 0; } diff --git a/source/graphics/MapWriter.cpp b/source/graphics/MapWriter.cpp index f3ff3dea50..e1e244fea9 100644 --- a/source/graphics/MapWriter.cpp +++ b/source/graphics/MapWriter.cpp @@ -254,7 +254,7 @@ void CMapWriter::WriteXML(const char* filename, CUnitManager* pUnitMan, CLightEn } { - float angle = entity->m_orientation; + float angle = entity->m_orientation.Y; XML_Element("Orientation"); XML_Attribute("angle", angle); diff --git a/source/graphics/Terrain.cpp b/source/graphics/Terrain.cpp index c0bd66e385..3546916a5b 100644 --- a/source/graphics/Terrain.cpp +++ b/source/graphics/Terrain.cpp @@ -14,6 +14,8 @@ #include "renderer/Renderer.h" #include "renderer/WaterManager.h" +#include "EntityManager.h" + #include #include "Terrain.h" #include "MathUtil.h" @@ -207,14 +209,27 @@ float CTerrain::getSlope(float x, float z) const return MAX(MAX(h00, h01), MAX(h10, h11)) - MIN(MIN(h00, h01), MIN(h10, h11)); } -float CTerrain::getSlopeAngle( float x, float y ) const +CVector2D CTerrain::getSlopeAngleFace(float x, float y, CEntity*& entity ) const { + //side start at 0 at -180 degrees, and end up at 7 at 180 degrees + int side; + bool invert; + float top, bottom, topZ, bottomZ; float fCell = (float)CELL_SIZE; x /= fCell; y /= fCell; int xi = (int)floor(x); int yi = (int)floor(y); - + + CVector3D flat(0.0f, 0.0f, 0.0f); + CVector3D flatZ = flat; + CVector2D ret; + side = entity->findSector( 8, entity->m_orientation.Y, DEGTORAD(360.0f) ) - 1; + + //Wrap around + if ( side < 0 ) + side += 8; + //Keep it in bounds if (xi < 0) xi = 0; @@ -230,125 +245,45 @@ float CTerrain::getSlopeAngle( float x, float y ) const float h10 = m_Heightmap[yi*m_MapSize + xi + 1] * HEIGHT_SCALE; float h11 = m_Heightmap[yi*m_MapSize + xi + m_MapSize + 1] * HEIGHT_SCALE; - CVector3D flat, elevated; - float low, high; - - if ( h00 < h01 && h00 < h10 && h00 < h11 ) - low = h00; - else if ( h01 < h10 && h01 < h11 ) - low = h01; - else if ( h10 < h11 ) - low = h10; - else - low = h11; - - //Find correct vector representing the flat version of the vector from low to high points - if ( h00 > h01 && h00 > h10 && h00 > h11 ) + //Find heights + if ( side == 0 || side == 7 ) { - high = h00; - if ( low == h10 ) - flat = CVector3D( fCell, 0.0f, 0.0f ); - else if ( low == h01 ) - flat = CVector3D( 0.0f, 0.0f, fCell ); - else if ( low == h11 ) - flat = CVector3D( fCell, 0.0f, fCell ); + top = (h00 + h10)/2.0f; + bottom = (h01 + h11)/2.0f; + topZ = (h00 + h01)/2.0f; + bottomZ = (h10 + h11)/2.0f; + flat.Z = sqrtf(SQR(CELL_SIZE) - SQR(top-bottom)); + flatZ.X = sqrtf(SQR(CELL_SIZE) - SQR(topZ-bottomZ)); } - else if ( h01 > h10 && h01 > h11 ) + else if ( side == 1 || side == 2 ) { - high = h01; - if ( low == h00 ) - flat = CVector3D( 0.0f, 0.0f, fCell ); - else if ( low == h01 ) - flat = CVector3D( fCell, 0.0f, fCell ); - else if ( low == h11 ) - flat = CVector3D( fCell, 0.0f, 0.0f ); + top = (h00 + h01)/2.0f; + bottom = (h10 + h11)/2.0f; + topZ = (h01 + h11)/2.0f; + bottomZ = (h00 + h10)/2.0f; + flat.X = sqrtf(SQR(CELL_SIZE) - SQR(top-bottom)); + flatZ.Z = sqrtf(SQR(CELL_SIZE) - SQR(topZ-bottomZ)); } - else if ( h10 > h11 ) + else if ( side == 3 || side == 4 ) { - high = h10; - if ( low == h00 ) - flat = CVector3D( fCell, 0.0f, 0.0f ); - else if ( low == h01 ) - flat = CVector3D( fCell, 0.0f, fCell ); - else if ( low == h11 ) - flat = CVector3D( 0.0f, 0.0f, fCell ); + top = (h01 + h11)/2.0f; + bottom = (h00 + h10)/2.0f; + topZ = (h11 + h10)/2.0f; + bottomZ = (h00 + h01)/2.0f; + flat.Z = sqrtf(SQR(CELL_SIZE) - SQR(top-bottom)); + flatZ.X = sqrtf(SQR(CELL_SIZE) - SQR(topZ-bottomZ)); } - else + else { - high = h11; - if ( low == h10 ) - flat = CVector3D( 0.0f, 0.0f, fCell ); - else if ( low == h01 ) - flat = CVector3D( fCell, 0.0f, 0.0f ); - else if ( low == h00 ) - flat = CVector3D( fCell, 0.0f, fCell ); + top = (h11 + h10)/2.0f; + bottom = (h00 + h01)/2.0f; + topZ = (h00 + h10)/2.0f; + bottomZ = (h01 + h11)/2.0f; + flat.X = sqrtf(SQR(CELL_SIZE) - SQR(top-bottom)); + flatZ.Z = sqrtf(SQR(CELL_SIZE) - SQR(topZ-bottomZ)); } - elevated = flat; - elevated.Y = high - low; - elevated.Normalize(); - flat.Normalize(); - return acosf( flat.Dot( elevated ) ); -} -float CTerrain::getSlopeAngleFace(float x, float y, float orientation) const -{ - bool right; //true means use 0,0 and 1,1; false means use 1,0 and 0,1 - bool invert; - float top, bottom; - x /= (float)CELL_SIZE; - y /= (float)CELL_SIZE; - int xi = (int)floor(x); - int yi = (int)floor(y); - CVector3D flat( (float)CELL_SIZE, 0.0f, (float)CELL_SIZE ); CVector3D elevated=flat; - - /* JW: currently all unused, so commented out to avoid warning - const float a0 = DEGTORAD(0.0f); - const float a90 = DEGTORAD(90.0f); - const float a180 = DEGTORAD(180.0f); - const float neg = DEGTORAD(-90.0f); - const float a45 = DEGTORAD(45.0f); - const float a135 = DEGTORAD(135.0f);*/ - - //Find which side it's facing; use that and the opposite - if ( orientation > 0.0f && orientation < DEGTORAD(90.0f) ) - right = true; - else if ( orientation > DEGTORAD(90.0f) && orientation < DEGTORAD(180.0f) ) - right = false; - else if ( orientation < DEGTORAD(-180.0f) && orientation > DEGTORAD(-90.0f) ) - right = true; - else - right = false; - - //Keep it in bounds - if (xi < 0) - xi = 0; - else if (xi >= (int)m_MapSize-1) - xi = m_MapSize - 2; - if (yi < 0) - yi = 0; - else if (yi >= (int)m_MapSize-1) - yi = m_MapSize - 2; - - if ( right ) - { - bottom = m_Heightmap[yi*m_MapSize + xi]*HEIGHT_SCALE; - top = m_Heightmap[yi*m_MapSize+m_MapSize + xi + 1]*HEIGHT_SCALE; - if ( (orientation > DEGTORAD(-45.0f) && orientation < 0.0f) || - (orientation < DEGTORAD(135.0f) && orientation > 0.0f) ) - elevated.Y = top-bottom; - else - elevated.Y = bottom-top; - } - else - { - bottom = m_Heightmap[yi*m_MapSize + xi + 1]*HEIGHT_SCALE; - top = m_Heightmap[yi*m_MapSize+m_MapSize + xi]*HEIGHT_SCALE; - if ( (orientation > DEGTORAD(-135.0f) && orientation < 0.0f) || - (orientation < DEGTORAD(45.0f) && orientation > 0.0f) ) - elevated.Y = top-bottom; - else - elevated.Y = bottom-top; - } + elevated.Y = top-bottom; if ( elevated.Y > 0.0f ) invert=false; else @@ -356,12 +291,27 @@ float CTerrain::getSlopeAngleFace(float x, float y, float orientation) const elevated.Y = fabs(elevated.Y); elevated.Normalize(); flat.Normalize(); - float ret = elevated.Dot(flat); + if ( invert ) + ret.x = -acosf( elevated.Dot(flat) ); + else + ret.x = acosf( elevated.Dot(flat) ); - if (invert) - return -acosf(ret); - return acosf(ret); - + //Z component + elevated = flatZ; + elevated.Y = topZ-bottomZ; + if ( elevated.Y > 0.0f ) + invert=true; + else + invert=false; + elevated.Y = fabs(elevated.Y); + elevated.Normalize(); + flatZ.Normalize(); + if ( invert ) + ret.y = -acosf( elevated.Dot(flatZ) ); + else + ret.y = acosf( elevated.Dot(flatZ) ); + + return ret; } float CTerrain::getExactGroundLevel(float x, float z) const diff --git a/source/graphics/Terrain.h b/source/graphics/Terrain.h index 34bef90890..2cc907f6f6 100644 --- a/source/graphics/Terrain.h +++ b/source/graphics/Terrain.h @@ -13,6 +13,7 @@ #include "Patch.h" #include "Vector3D.h" #include "Vector2D.h" +#include "Entity.h" /////////////////////////////////////////////////////////////////////////////// // CTerrain: main terrain class; contains the heightmap describing elevation @@ -45,9 +46,8 @@ public: inline float getExactGroundLevel(const CVector2D& v) const { return getExactGroundLevel(v.x, v.y); } float getSlope(float x, float z) const ; - float getSlopeAngle( float x, float y) const; //In radians - //Same as above, but picks the two vertices that the unit is facing (front+back) for slope - float getSlopeAngleFace(float x, float y, float orientation) const; + //Find the slope of in X and Z axes depending on the way the entity is facing + CVector2D getSlopeAngleFace(float x, float y, CEntity*& entity) const; // resize this terrain such that each side has given number of patches void Resize(u32 size); diff --git a/source/ps/Game.cpp b/source/ps/Game.cpp index 4bd767148c..c73648e2d3 100644 --- a/source/ps/Game.cpp +++ b/source/ps/Game.cpp @@ -73,7 +73,6 @@ PSRETURN CGame::RegisterInit(CGameAttributes* pAttribs) m_GameView->RegisterInit(pAttribs); m_World->RegisterInit(pAttribs); m_Simulation->RegisterInit(pAttribs); - LDR_EndRegistering(); return 0; } diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 0aae703c98..6be9e881b1 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -522,6 +522,7 @@ static void InitScripting() g_ScriptingHost.DefineConstant( "NOTIFY_HEAL", CEntityListener::NOTIFY_HEAL ); g_ScriptingHost.DefineConstant( "NOTIFY_GATHER", CEntityListener::NOTIFY_GATHER ); g_ScriptingHost.DefineConstant( "NOTIFY_IDLE", CEntityListener::NOTIFY_IDLE ); + g_ScriptingHost.DefineConstant( "NOTIFY_ORDER_CHANGE", CEntityListener::NOTIFY_ORDER_CHANGE ); g_ScriptingHost.DefineConstant( "NOTIFY_ALL", CEntityListener::NOTIFY_ALL ); g_ScriptingHost.DefineConstant( "ORDER_NONE", -1 ); diff --git a/source/scripting/EventTypes.h b/source/scripting/EventTypes.h index 6f7a9b57c8..965a7eb900 100644 --- a/source/scripting/EventTypes.h +++ b/source/scripting/EventTypes.h @@ -23,6 +23,7 @@ enum EEventType EVENT_NOTIFICATION, EVENT_FORMATION, EVENT_IDLE, + EVENT_MOVEMENT, EVENT_LAST, // Projectile events @@ -51,7 +52,9 @@ static const wchar_t* const EventNames[EVENT_LAST] = /* EVENT_ORDER_TRANSITION */ L"onOrderTransition", /* When we change orders (sometimes...) */ /* EVENT_NOTIFICATION */ L"onNotification", /*When we receive a notification */ /* EVENT_FORMATION */ L"onFormation", /* When this unit does something with a formation */ - /* EVENT_IDLE */ L"onIdle" /* When this unit becomes idle, do something */ + /* EVENT_IDLE */ L"onIdle", /* When this unit becomes idle, do something */ + /* EVENT_MOVEMENT */ L"onMovement" /*Triggered by processGotoHelper(), when unit moves */ }; #endif // #ifndef EVENTTYPES_H__ + diff --git a/source/simulation/BaseEntity.cpp b/source/simulation/BaseEntity.cpp index 106cba9296..51ec78302e 100644 --- a/source/simulation/BaseEntity.cpp +++ b/source/simulation/BaseEntity.cpp @@ -17,9 +17,9 @@ CBaseEntity::CBaseEntity() AddProperty( L"tag", &m_Tag, false ); AddProperty( L"parent", &m_base, false ); - AddProperty( L"actions.move.speed", &m_speed ); + AddProperty( L"actions.move.speed_curr", &m_speed ); AddProperty( L"actions.move.turningradius", &m_turningRadius ); - AddProperty( L"actions.move.run.speed", &( m_run.m_Speed ) ); + AddProperty( L"actions.move.run.speed.curr", &( m_run.m_Speed ) ); AddProperty( L"actions.move.run.rangemin", &( m_run.m_MinRange ) ); AddProperty( L"actions.move.run.range", &( m_run.m_MaxRange ) ); AddProperty( L"actions.move.run.regen_rate", &m_runRegenRate ); @@ -47,10 +47,8 @@ CBaseEntity::CBaseEntity() AddProperty( L"traits.stamina.border_height", &m_staminaBorderHeight); AddProperty( L"traits.stamina.border_width", &m_staminaBorderWidth ); AddProperty( L"traits.stamina.border_name", &m_staminaBorderName ); - AddProperty( L"traits.angle_penalty.sectors", &m_sectorDivs ); - AddProperty( L"traits.angle_penalty.value", &m_sectorPenalty ); - AddProperty( L"traits.pitch.max_actor", &m_maxActorPitch ); - AddProperty( L"traits.pitch.min_actor", &m_minActorPitch ); + AddProperty( L"traits.flank_penalty.sectors", &m_sectorDivs ); + AddProperty( L"traits.pitch.sectors", &m_pitchDivs ); AddProperty( L"traits.rank.width", &m_rankWidth ); AddProperty( L"traits.rank.height", &m_rankHeight ); AddProperty( L"traits.rank.name", &m_rankName ); @@ -59,6 +57,8 @@ CBaseEntity::CBaseEntity() AddProperty( L"traits.minimap.green", &m_minimapG ); AddProperty( L"traits.minimap.blue", &m_minimapB ); AddProperty( L"traits.anchor.type", &m_anchorType ); + AddProperty( L"traits.anchor.conformx", &m_anchorConformX ); + AddProperty( L"traits.anchor.conformz", &m_anchorConformZ ); AddProperty( L"traits.vision.los", &m_los ); AddProperty( L"traits.vision.permanent", &m_permanent ); AddProperty( L"traits.foundation", &m_foundation ); @@ -76,6 +76,7 @@ CBaseEntity::CBaseEntity() m_foundation = CStrW(); // Sentinel values for stamina and health (so scripts can check if an entity has no stamina or no HP). + m_speed=0; m_staminaCurr = 0; m_staminaMax = 0; m_healthCurr = 0; diff --git a/source/simulation/BaseEntity.h b/source/simulation/BaseEntity.h index e1dbdf3737..9f6b07595b 100644 --- a/source/simulation/BaseEntity.h +++ b/source/simulation/BaseEntity.h @@ -111,10 +111,9 @@ public: SEntityAction m_generic; int m_sectorDivs; - float m_sectorPenalty; - - float m_maxActorPitch; - float m_minActorPitch; + int m_pitchDivs; + float m_anchorConformX; + float m_anchorConformZ; float m_turningRadius; CScriptObject m_EventHandlers[EVENT_LAST]; diff --git a/source/simulation/BaseFormation.cpp b/source/simulation/BaseFormation.cpp index 264847e682..a337c1253b 100644 --- a/source/simulation/BaseFormation.cpp +++ b/source/simulation/BaseFormation.cpp @@ -26,9 +26,11 @@ bool CBaseFormation::loadXML(CStr filename) AT(tag); AT(bonus); + AT(bonusbase); AT(bonustype); AT(bonusval); AT(penalty); + AT(penaltybase); AT(penaltytype); AT(penaltyval); AT(anglepenalty); @@ -63,12 +65,16 @@ bool CBaseFormation::loadXML(CStr filename) m_tag = CStr(Attr.Value); else if ( Attr.Name == at_bonus ) m_bonus = CStr(Attr.Value); + else if ( Attr.Name == at_bonusbase ) + m_bonusBase = CStr(Attr.Value); else if ( Attr.Name == at_bonustype ) m_bonusType = CStr(Attr.Value); else if ( Attr.Name == at_bonusval ) m_bonusVal = CStr(Attr.Value).ToFloat(); else if ( Attr.Name == at_penalty ) m_penalty = CStr(Attr.Value); + else if ( Attr.Name == at_penaltybase ) + m_penaltyBase = CStr(Attr.Value); else if ( Attr.Name == at_penaltytype ) m_penaltyType = CStr(Attr.Value); else if ( Attr.Name == at_penaltyval ) @@ -200,4 +206,4 @@ void CBaseFormation::AssignCategory(int order, CStr category) size_t off = category.find(temp); category.erase( off, temp.length() ); } -} +} \ No newline at end of file diff --git a/source/simulation/BaseFormation.h b/source/simulation/BaseFormation.h index 9704e9899b..11e1921a9b 100644 --- a/source/simulation/BaseFormation.h +++ b/source/simulation/BaseFormation.h @@ -29,10 +29,12 @@ public: ~CBaseFormation(){} CStr GetBonus(){ return m_bonus; } + CStr GetBonusBase(){ return m_bonusBase; } CStr GetBonusType(){ return m_bonusType; } float GetBonusVal(){ return m_bonusVal; } CStr GetPenalty(){ return m_penalty; } + CStr GetPenaltyBase(){ return m_penaltyBase; } CStr GetPenaltyType(){ return m_penaltyType; } float GetPenaltyVal(){ return m_penaltyVal; } @@ -46,10 +48,12 @@ private: CStr m_tag; CStr m_bonus; + CStr m_bonusBase; CStr m_bonusType; float m_bonusVal; CStr m_penalty; + CStr m_penaltyBase; CStr m_penaltyType; float m_penaltyVal; @@ -71,9 +75,9 @@ private: //The key is the "order" of the slot std::map m_slots; - std::vector m_angleValues; //cosine of angle divisions bool loadXML(CStr filename); void AssignCategory(int order, CStr category); //takes care of formatting strings }; #endif + diff --git a/source/simulation/Entity.cpp b/source/simulation/Entity.cpp index 0a6178e8eb..707fe1b54d 100644 --- a/source/simulation/Entity.cpp +++ b/source/simulation/Entity.cpp @@ -36,21 +36,20 @@ using namespace std; CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, const std::set& actorSelections, CStrW building ) { m_position = position; - m_orientation = orientation; - m_ahead.x = sin( m_orientation ); - m_ahead.y = cos( m_orientation ); + m_orientation.Y = orientation; + m_ahead.x = sin( m_orientation.Y ); + m_ahead.y = cos( m_orientation.Y ); // set sane default in case someone forgets to add this to the entity's // XML file, which is prone to happen. (prevents crash below when // using this value to resize a vector) const int sane_sectordivs_default = 4; - m_sectorDivs = sane_sectordivs_default; /* Anything added to this list MUST be added to BaseEntity.cpp (and variables used should also be added to BaseEntity.h */ - - AddProperty( L"actions.move.speed", &m_speed ); - AddProperty( L"actions.move.run.speed", &( m_run.m_Speed ) ); + + AddProperty( L"actions.move.speed_curr", &m_speed ); + AddProperty( L"actions.move.run.speed.curr", &( m_run.m_Speed ) ); AddProperty( L"actions.move.run.rangemin", &( m_run.m_MinRange ) ); AddProperty( L"actions.move.run.range", &( m_run.m_MaxRange ) ); AddProperty( L"actions.move.run.regen_rate", &m_runRegenRate ); @@ -61,7 +60,7 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons AddProperty( L"traits.corpse", &m_corpse ); AddProperty( L"actions.move.turningradius", &m_turningRadius ); AddProperty( L"position", &m_graphics_position, false, (NotifyFn)&CEntity::teleport ); - AddProperty( L"orientation", &m_graphics_orientation, false, (NotifyFn)&CEntity::reorient ); + AddProperty( L"orientation", &(m_orientation.Y), false, (NotifyFn)&CEntity::reorient ); AddProperty( L"player", &m_player, false, (NotifyFn)&CEntity::playerChanged ); AddProperty( L"traits.health.curr", &m_healthCurr ); AddProperty( L"traits.health.max", &m_healthMax ); @@ -83,10 +82,8 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons AddProperty( L"traits.stamina.border_height", &m_staminaBorderHeight); AddProperty( L"traits.stamina.border_width", &m_staminaBorderWidth ); AddProperty( L"traits.stamina.border_name", &m_staminaBorderName ); - AddProperty( L"traits.angle_penalty.sectors", &m_sectorDivs); - AddProperty( L"traits.angle_penalty.value", &m_sectorPenalty ); - AddProperty( L"traits.pitch.max_actor", &m_maxActorPitch ); - AddProperty( L"traits.pitch.min_actor", &m_minActorPitch ); + AddProperty( L"traits.flank_penalty.sectors", &m_sectorDivs); + AddProperty( L"traits.pitch.sectors", &m_pitchDivs ); AddProperty( L"traits.rank.width", &m_rankWidth ); AddProperty( L"traits.rank.height", &m_rankHeight ); AddProperty( L"traits.rank.name", &m_rankName ); @@ -95,6 +92,9 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons AddProperty( L"traits.minimap.green", &m_minimapG ); AddProperty( L"traits.minimap.blue", &m_minimapB ); AddProperty( L"traits.anchor.type", &m_anchorType ); + AddProperty( L"traits.anchor.conformx", &m_anchorConformX ); + AddProperty( L"traits.anchor.conformz", &m_anchorConformZ ); + AddProperty( L"traits.vision.los", &m_los ); AddProperty( L"traits.vision.permanent", &m_permanent ); AddProperty( L"last_combat_time", &m_lastCombatTime ); @@ -109,23 +109,6 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons AddHandler( t, &m_EventHandlers[t] ); } - // this has been the cause of several crashes (due to not being - // specified in the XML file), so in addition to the above default, - // we'll sanity check its value. - if(!(0 <= m_sectorDivs && m_sectorDivs < 1000)) - { - debug_warn("invalid entity angle_penalty.sectors value"); - m_sectorDivs = sane_sectordivs_default; - } - m_sectorAngles.resize(m_sectorDivs); - m_sectorValues.resize(m_sectorDivs); - float step = DEGTORAD(360.0f / m_sectorDivs); - for ( int i=0; isetPosition( m_position.X, m_position.Z ); @@ -151,7 +146,6 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons m_graphics_orientation = m_orientation; m_actor_transform_valid = false; - m_pitchOrientation = m_pitchOrientation_previous = m_graphics_pitchOrientation = 0.0f; m_destroyed = false; m_selected = false; @@ -172,7 +166,7 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons m_grouped = -1; m_building = building; - + m_player = g_Game->GetPlayer( 0 ); Initialize(); @@ -198,7 +192,8 @@ CEntity::~CEntity() delete it->second; } m_auras.clear(); - + + m_destroyNotifiers=true; for ( size_t i=0; iDestroyNotifier( this ); DestroyAllNotifiers(); @@ -249,7 +244,10 @@ void CEntity::kill() CEntity* remove = this; g_FormationManager.RemoveUnit(remove); - + + m_destroyNotifiers=true; + for ( size_t i=0; iDestroyNotifier( this ); DestroyAllNotifiers(); if( m_bounds ) @@ -286,22 +284,22 @@ void CEntity::SetPlayer(CPlayer *pPlayer) void CEntity::updateActorTransforms() { CMatrix3D m; - CMatrix3D mX; - float Cos = cosf( m_graphics_orientation ); - float Sin = sinf( m_graphics_orientation ); + CMatrix3D mXZ; + float Cos = cosf( m_graphics_orientation.Y ); + float Sin = sinf( m_graphics_orientation.Y ); m._11=-Cos; m._12=0.0f; m._13=-Sin; m._14=0.0f; m._21=0.0f; m._22=1.0f; m._23=0.0f; m._24=0.0f; m._31=Sin; m._32=0.0f; m._33=-Cos; m._34=0.0f; m._41=0.0f; m._42=0.0f; m._43=0.0f; m._44=1.0f; - mX.SetXRotation( m_graphics_pitchOrientation ); - mX = m*mX; - mX.Translate(m_graphics_position); - //m.RotateX(m_graphics_pitchOrientation); + mXZ.SetXRotation( m_graphics_orientation.X ); + mXZ.RotateZ( m_graphics_orientation.Z ); + mXZ = m*mXZ; + mXZ.Translate(m_graphics_position); if( m_actor ) - m_actor->GetModel()->SetTransform( mX ); + m_actor->GetModel()->SetTransform( mXZ ); } void CEntity::snapToGround() @@ -384,7 +382,6 @@ void CEntity::update( size_t timestep ) { m_position_previous = m_position; m_orientation_previous = m_orientation; - m_pitchOrientation_previous = m_pitchOrientation; CalculateRun( timestep ); CalculateHealth( timestep ); @@ -756,7 +753,7 @@ struct isListenerSender int CEntity::DestroyNotifier( CEntity* target ) { - if (target->m_listeners.empty() || !m_destroyNotifiers) + if ( target->m_listeners.empty() ) return 0; //Stop listening // (Don't just loop and use 'erase', because modifying the deque while @@ -812,9 +809,9 @@ void CEntity::repath() void CEntity::reorient() { m_orientation = m_graphics_orientation; - m_pitchOrientation = m_graphics_pitchOrientation; - m_ahead.x = sin( m_orientation ); - m_ahead.y = cos( m_orientation ); + + m_ahead.x = sin( m_orientation.Y ); + m_ahead.y = cos( m_orientation.Y ); if( m_bounds->m_type == CBoundingObject::BOUND_OABB ) ((CBoundingBox*)m_bounds)->setOrientation( m_ahead ); updateActorTransforms(); @@ -858,24 +855,27 @@ void CEntity::checkGroup() void CEntity::interpolate( float relativeoffset ) { CVector3D old_graphics_position = m_graphics_position; - float old_graphics_orientation = m_graphics_orientation; - float old_graphics_pitchOrientation = m_graphics_pitchOrientation; - + CVector3D old_graphics_orientation = m_graphics_orientation; + m_graphics_position = Interpolate( m_position_previous, m_position, relativeoffset ); // Avoid wraparound glitches for interpolating angles. - while( m_orientation < m_orientation_previous - PI ) - m_orientation_previous -= 2 * PI; - while( m_orientation > m_orientation_previous + PI ) - m_orientation_previous += 2 * PI; + while( m_orientation.Y < m_orientation_previous.Y - PI ) + m_orientation_previous.Y -= 2 * PI; + while( m_orientation.Y > m_orientation_previous.Y + PI ) + m_orientation_previous.Y += 2 * PI; - while( m_pitchOrientation < m_pitchOrientation_previous - PI ) - m_pitchOrientation_previous -= 2 * PI; - while( m_pitchOrientation > m_pitchOrientation_previous + PI ) - m_pitchOrientation_previous += 2 * PI; + while( m_orientation.X < m_orientation_previous.X - PI ) + m_orientation_previous.X -= 2 * PI; + while( m_orientation.X > m_orientation_previous.X + PI ) + m_orientation_previous.X += 2 * PI; - m_graphics_orientation = Interpolate( m_orientation_previous, m_orientation, relativeoffset ); - m_graphics_pitchOrientation = Interpolate( m_pitchOrientation_previous, m_pitchOrientation, relativeoffset ); + while( m_orientation.Z < m_orientation_previous.Z - PI ) + m_orientation_previous.Z -= 2 * PI; + while( m_orientation.Z > m_orientation_previous.Z + PI ) + m_orientation_previous.Z += 2 * PI; + + m_graphics_orientation = Interpolate( m_orientation_previous, m_orientation, relativeoffset ); // Mark the actor transform data as invalid if the entity has moved since // the last call to 'interpolate'. @@ -884,8 +884,7 @@ void CEntity::interpolate( float relativeoffset ) // handle flying units or moving terrain. if( m_graphics_orientation != old_graphics_orientation || m_graphics_position.X != old_graphics_position.X || - m_graphics_position.Z != old_graphics_position.Z || - m_graphics_pitchOrientation != old_graphics_pitchOrientation + m_graphics_position.Z != old_graphics_position.Z ) { m_actor_transform_valid = false; @@ -999,7 +998,39 @@ float CEntity::getAnchorLevel( float x, float z ) return max( groundLevel, g_Renderer.GetWaterManager()->m_WaterHeight ); } } - +int CEntity::findSector( int divs, float angle, float maxAngle, bool negative ) +{ + float step=maxAngle/divs; + if ( negative ) + { + float tracker; + int i=1, sectorRemainder; + for ( tracker=-maxAngle/2.0f; tracker+step<0.0f; tracker+=step, ++i ) + { + if ( angle > tracker && angle <= tracker+step ) + return i; + } + sectorRemainder = i; + i=divs; + for ( tracker=maxAngle/2.0f; tracker-step>0.0f; tracker-=step, --i ) + { + if ( angle < tracker && angle >= tracker-step ) + return i; + } + return sectorRemainder; + } + else + { + int i=0; + for ( float tracker=0.0f; tracker tracker && angle <= tracker+step ) + return i; + } + } + debug_warn("CEntity::findSector() - invalid parameters passed."); + return -1; +} void CEntity::renderSelectionOutline( float alpha ) { if( !m_bounds ) @@ -1047,8 +1078,8 @@ void CEntity::renderSelectionOutline( float alpha ) float d = ((CBoundingBox*)m_bounds)->m_d; float w = ((CBoundingBox*)m_bounds)->m_w; - u.x = sin( m_graphics_orientation ); - u.y = cos( m_graphics_orientation ); + u.x = sin( m_graphics_orientation.Y ); + u.y = cos( m_graphics_orientation.Y ); v.x = u.y; v.y = -u.x; @@ -1316,6 +1347,7 @@ void CEntity::ScriptingInit() AddMethod( "addAura", 3 ); AddMethod( "removeAura", 1 ); AddMethod( "setActionParams", 5 ); + AddMethod( "getCurrentRequest", 0 ); AddMethod( "forceCheckListeners", 2 ); AddMethod( "requestNotification", 4 ); AddMethod( "destroyNotifier", 1 ); @@ -1331,7 +1363,9 @@ void CEntity::ScriptingInit() AddMethod( "getFormationPenaltyType", 0 ); AddMethod( "getFormationPenaltyVal", 0 ); AddMethod( "registerDamage", 0 ); - AddMethod( "registerIdle", 0 ); + AddMethod( "registerOrderChange", 0 ); + AddMethod( "getAttackDirections", 0 ); + AddMethod("findSector", 4); AddClassProperty( L"template", (CBaseEntity* CEntity::*)&CEntity::m_base, false, (NotifyFn)&CEntity::loadBase ); AddClassProperty( L"traits.id.classes", (GetFn)&CEntity::getClassSet, (SetFn)&CEntity::setClassSet ); @@ -1793,8 +1827,7 @@ bool CEntity::RequestNotification( JSContext* cx, uintN argc, jsval* argv ) CEntity *target = ToNative( argv[0] ); (int&)notify.m_type = ToPrimitive( argv[1] ); bool tmpDestroyNotifiers = ToPrimitive( argv[2] ); - // TODO: ??? This local variable overrides the member variable of the same name... - bool m_destroyNotifiers = !ToPrimitive( argv[3] ); + m_destroyNotifiers = !ToPrimitive( argv[3] ); if (target == this) return false; @@ -1840,6 +1873,10 @@ bool CEntity::RequestNotification( JSContext* cx, uintN argc, jsval* argv ) target->m_listeners.push_back( notify ); return false; } +int CEntity::GetCurrentRequest( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) +{ + return m_currentRequest; +} bool CEntity::ForceCheckListeners( JSContext *cx, uintN argc, jsval* argv ) { if( argc < 2 ) @@ -1952,6 +1989,10 @@ jsval CEntity::GetFormationPenalty( JSContext* UNUSED(cx), uintN UNUSED(argc), j { return ToJSVal( GetFormation()->GetBase()->GetPenalty() ); } +jsval CEntity::GetFormationPenaltyBase( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) +{ + return ToJSVal( GetFormation()->GetBase()->GetPenaltyBase() ); +} jsval CEntity::GetFormationPenaltyType( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) { return ToJSVal( GetFormation()->GetBase()->GetPenaltyType() ); @@ -1964,6 +2005,10 @@ jsval CEntity::GetFormationBonus( JSContext* UNUSED(cx), uintN UNUSED(argc), jsv { return ToJSVal( GetFormation()->GetBase()->GetBonus() ); } +jsval CEntity::GetFormationBonusBase( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) +{ + return ToJSVal( GetFormation()->GetBase()->GetBonusBase() ); +} jsval CEntity::GetFormationBonusType( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) { return ToJSVal( GetFormation()->GetBase()->GetBonusType() ); @@ -1985,19 +2030,13 @@ jsval CEntity::RegisterDamage( JSContext* cx, uintN argc, jsval* argv ) CVector2D pos = CVector2D( inflictor->m_position.X, inflictor->m_position.Z ); CVector2D posDelta = (pos - m_position).normalize(); - float angle = up.dot(posDelta); + float angle = acosf( up.dot(posDelta) ); //Find what section it is between and "activate" it - for ( int i=0; im_sectorDivs ) - m_sectorValues[i] = true; - else if ( angle > m_sectorAngles[i] && angle < m_sectorAngles[i+1] ) - m_sectorValues[i] = true; - } + int sector = findSector(m_sectorDivs, angle, DEGTORAD(360.0f))-1; + m_sectorValues[sector]=true; return JS_TRUE; } -jsval CEntity::RegisterIdle( JSContext* cx, uintN argc, jsval* argv ) +jsval CEntity::RegisterOrderChange( JSContext* cx, uintN argc, jsval* argv ) { if ( argc < 1 ) { @@ -2010,16 +2049,10 @@ jsval CEntity::RegisterIdle( JSContext* cx, uintN argc, jsval* argv ) CVector2D pos = CVector2D( idleEntity->m_position.X, idleEntity->m_position.Z ); CVector2D posDelta = (pos - m_position).normalize(); - float angle = up.dot(posDelta); - //Find what section it is between and "activate" it - for ( int i=0; im_sectorDivs ) - m_sectorValues[i] = false; - else if ( angle > m_sectorAngles[i] && angle < m_sectorAngles[i+1] ) - m_sectorValues[i] = false; - } + float angle = acosf( up.dot(posDelta) ); + //Find what section it is between and "deactivate" it + int sector = MAX( 0.0, findSector(m_sectorDivs, angle, DEGTORAD(360.0f)) ); + m_sectorValues[sector]=false; return JS_TRUE; } jsval CEntity::GetAttackDirections( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) @@ -2033,3 +2066,46 @@ jsval CEntity::GetAttackDirections( JSContext* UNUSED(cx), uintN UNUSED(argc), j } return ToJSVal( directions ); } +jsval CEntity::FindSector( JSContext* cx, uintN argc, jsval* argv ) +{ + if ( argc < 4 ) + { + JS_ReportError( cx, "Too few parameters" ); + return( false ); + } + int divs = ToPrimitive( argv[0] ); + float angle = ToPrimitive( argv[1] ); + float maxAngle = ToPrimitive( argv[2] ); + bool negative = ToPrimitive( argv[3] ); + float step = maxAngle/divs; + + if ( negative ) + { + float tracker; + int i=1, sectorRemainder; + for ( tracker=-maxAngle/2.0f; tracker+step<0.0f; tracker+=step, ++i ) + { + if ( angle > tracker && angle <= tracker+step ) + return ToJSVal(i); + } + sectorRemainder = i; + i=divs; + for ( tracker=maxAngle/2.0f; tracker-step>0.0f; tracker-=step, --i ) + { + if ( angle < tracker && angle >= tracker-step ) + return ToJSVal(i); + } + return ToJSVal(sectorRemainder); + } + else + { + int i=1; + for ( float tracker=0.0f; tracker tracker && angle <= tracker+step ) + return ToJSVal(i); + } + } + debug_warn("JS - FindSector(): invalid parameters"); + return ToJSVal(-1); +} diff --git a/source/simulation/Entity.h b/source/simulation/Entity.h index 5f4308cec3..d19ec5146d 100644 --- a/source/simulation/Entity.h +++ b/source/simulation/Entity.h @@ -149,6 +149,8 @@ public: // Y anchor CStrW m_anchorType; + float m_anchorConformX; + float m_anchorConformZ; // LOS int m_los; @@ -168,13 +170,12 @@ public: CVector2D m_ahead; //-- Interpolated property - float m_orientation; - float m_orientation_previous; - float m_graphics_orientation; + CVector3D m_orientation; + CVector3D m_orientation_previous; + CVector3D m_graphics_orientation; - float m_pitchOrientation; - float m_pitchOrientation_previous; - float m_graphics_pitchOrientation; + CVector2D m_orientation_unclamped; + // If the actor's current transform data is valid (i.e. the entity hasn't // moved since it was last calculated, and the terrain hasn't been changed). @@ -213,9 +214,9 @@ public: bool m_destroyNotifiers; //True: we destroy them. False: the script does. std::vector m_sectorValues; - std::vector m_sectorAngles; int m_sectorDivs; - float m_sectorPenalty; + + int m_pitchDivs; private: CEntity( CBaseEntity* base, CVector3D position, float orientation, const std::set& actorSelections, CStrW building = L"" ); @@ -317,21 +318,26 @@ public: int DestroyNotifier( CEntity* target ); //Stop notifier from sending to us void DestroyAllNotifiers(); + int findSector( int divs, float angle, float maxAngle, bool negative=true ); + CEntityFormation* GetFormation(); - bool IsInClass( JSContext* cx, uintN argc, jsval* argv ); jsval GetFormationPenalty( JSContext* cx, uintN argc, jsval* argv ); + jsval GetFormationPenaltyBase( JSContext* cx, uintN argc, jsval* argv ); jsval GetFormationPenaltyType( JSContext* cx, uintN argc, jsval* argv ); jsval GetFormationPenaltyVal( JSContext* cx, uintN argc, jsval* argv ); jsval GetFormationBonus( JSContext* cx, uintN argc, jsval* argv ); + jsval GetFormationBonusBase( JSContext* cx, uintN argc, jsval* argv ); jsval GetFormationBonusType( JSContext* cx, uintN argc, jsval* argv ); jsval GetFormationBonusVal( JSContext* cx, uintN argc, jsval* argv ); void DispatchFormationEvent( int type ); jsval RegisterDamage( JSContext* cx, uintN argc, jsval* argv ); - jsval RegisterIdle( JSContext* cx, uintN argc, jsval* argv ); + jsval RegisterOrderChange( JSContext* cx, uintN argc, jsval* argv ); jsval GetAttackDirections( JSContext* cx, uintN argc, jsval* argv ); + + jsval FindSector( JSContext* cx, uintN argc, jsval* argv ); // Script constructor static JSBool Construct( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval ); @@ -355,6 +361,7 @@ public: bool RequestNotification( JSContext* cx, uintN argc, jsval* argv ); //Just in case we want to explicitly check the listeners without waiting for the order to be pushed bool ForceCheckListeners( JSContext* cx, uintN argc, jsval* argv ); + int GetCurrentRequest( JSContext* cx, uintN argc, jsval* argv ); void CheckListeners( int type, CEntity *target ); jsval DestroyAllNotifiers( JSContext* cx, uintN argc, jsval* argv ); jsval DestroyNotifier( JSContext* cx, uintN argc, jsval* argv ); diff --git a/source/simulation/EntityManager.cpp b/source/simulation/EntityManager.cpp index f7e5e1bdc4..060d921082 100644 --- a/source/simulation/EntityManager.cpp +++ b/source/simulation/EntityManager.cpp @@ -7,7 +7,7 @@ #include "Profile.h" #include "Terrain.h" #include "Game.h" - +#include "MathUtil.h" int SELECTION_CIRCLE_POINTS; int SELECTION_BOX_POINTS; int SELECTION_SMOOTHNESS_UNIFIED = 9; @@ -284,6 +284,30 @@ void CEntityManager::renderAll() if( m_entities[i].m_refcount && !m_entities[i].m_entity->m_destroyed ) m_entities[i].m_entity->render(); } +void CEntityManager::conformAll() +{ + PROFILE_START("conform all"); + for ( int i=0; i < MAX_HANDLES; i++ ) + { + if( m_entities[i].m_refcount && !m_entities[i].m_entity->m_destroyed ) + { + CEntity* entity = m_entities[i].m_entity; + CVector2D targetXZ = g_Game->GetWorld()->GetTerrain()->getSlopeAngleFace( entity->m_position.X, entity->m_position.Z, entity ); + + while( targetXZ.x > PI ) targetXZ.x -= 2 * PI; + while( targetXZ.x < -PI ) targetXZ.x += 2 * PI; + while( targetXZ.y > PI ) targetXZ.y -= 2 * PI; + while( targetXZ.y < -PI ) targetXZ.y += 2 * PI; + + entity->m_orientation.X = clamp( targetXZ.x, -entity->m_anchorConformX, entity->m_anchorConformX ); + entity->m_orientation.Z = clamp( targetXZ.y, -entity->m_anchorConformZ, entity->m_anchorConformZ ); + entity->m_orientation_unclamped.x = targetXZ.x; + entity->m_orientation_unclamped.y = targetXZ.y; + entity->updateActorTransforms(); + } + } + PROFILE_END("conform all"); +} void CEntityManager::invalidateAll() { diff --git a/source/simulation/EntityManager.h b/source/simulation/EntityManager.h index 692cdb4314..f9a000aec7 100644 --- a/source/simulation/EntityManager.h +++ b/source/simulation/EntityManager.h @@ -63,6 +63,7 @@ public: void InitializeAll(); void TickAll(); void renderAll(); + void conformAll(); void invalidateAll(); void deleteAll(); diff --git a/source/simulation/EntityOrders.h b/source/simulation/EntityOrders.h index 79abd4d9a8..080822b95c 100644 --- a/source/simulation/EntityOrders.h +++ b/source/simulation/EntityOrders.h @@ -68,7 +68,8 @@ public: NOTIFY_HEAL = 0x10, NOTIFY_GATHER = 0x20, - NOTIFY_IDLE = 0x40, + NOTIFY_IDLE = 0x40, + NOTIFY_ORDER_CHANGE = 0x80, //this isn't counted in NOTIFY_ALL NOTIFY_ALL = 0x7F } m_type; diff --git a/source/simulation/EntityStateProcessing.cpp b/source/simulation/EntityStateProcessing.cpp index 8294cdc4b5..4865695495 100644 --- a/source/simulation/EntityStateProcessing.cpp +++ b/source/simulation/EntityStateProcessing.cpp @@ -95,7 +95,7 @@ uint CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis, // trig every time.right m_targetorientation = atan2( delta.x, delta.y ); - float deltatheta = m_targetorientation - (float)m_orientation; + float deltatheta = m_targetorientation - (float)m_orientation.Y; while( deltatheta > PI ) deltatheta -= 2 * PI; while( deltatheta < -PI ) deltatheta += 2 * PI; @@ -106,10 +106,10 @@ uint CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis, float maxTurningSpeed = ( m_speed / m_turningRadius ) * timestep; deltatheta = clamp( deltatheta, -maxTurningSpeed, maxTurningSpeed ); } - m_orientation = m_orientation + deltatheta; + m_orientation.Y = m_orientation.Y + deltatheta; - m_ahead.x = sin( m_orientation ); - m_ahead.y = cos( m_orientation ); + m_ahead.x = sin( m_orientation.Y ); + m_ahead.y = cos( m_orientation.Y ); // We can only really attempt to smooth paths the pathfinder // has flagged for us. If the turning-radius calculations are @@ -127,20 +127,29 @@ uint CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis, // let's not. if( current->m_type != CEntityOrder::ORDER_GOTO_SMOOTHED ) - m_orientation = m_targetorientation; + m_orientation.Y = m_targetorientation; } else { m_ahead = delta / len; - m_orientation = m_targetorientation; + m_orientation.Y = m_targetorientation; } + CEntity* _this = this; + CVector2D targetXZ = g_Game->GetWorld()->GetTerrain()->getSlopeAngleFace( m_position.X, m_position.Z, _this ); - float targetpitch = g_Game->GetWorld()->GetTerrain()->getSlopeAngleFace( m_position.X, m_position.Z, m_orientation ); - while( targetpitch > PI ) targetpitch -= 2 * PI; - while( targetpitch < -PI ) targetpitch += 2 * PI; - m_pitchOrientation = clamp( targetpitch, m_minActorPitch, m_maxActorPitch ); + while( targetXZ.x > PI ) targetXZ.x -= 2 * PI; + while( targetXZ.x < -PI ) targetXZ.x += 2 * PI; + while( targetXZ.y > PI ) targetXZ.y -= 2 * PI; + while( targetXZ.y < -PI ) targetXZ.y += 2 * PI; + m_orientation.X = clamp( targetXZ.x, -m_anchorConformX, m_anchorConformX ); + m_orientation.Z = clamp( targetXZ.y, -m_anchorConformZ, m_anchorConformZ ); + m_orientation_unclamped.x = targetXZ.x; + m_orientation_unclamped.y = targetXZ.y; + + CMovementEvent evt( m_orientation_unclamped.x ); + DispatchEvent(&evt); if( m_bounds && m_bounds->m_type == CBoundingObject::BOUND_OABB ) ((CBoundingBox*)m_bounds)->setOrientation( m_ahead ); @@ -496,7 +505,7 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times else { // Close enough, but turn to face them. - m_orientation = atan2( delta.x, delta.y ); + m_orientation.Y = atan2( delta.x, delta.y ); m_ahead = delta.normalize(); m_isRunning = false; } diff --git a/source/simulation/EventHandlers.cpp b/source/simulation/EventHandlers.cpp index 29664d0b79..4116cd1cd5 100644 --- a/source/simulation/EventHandlers.cpp +++ b/source/simulation/EventHandlers.cpp @@ -114,4 +114,10 @@ CIdleEvent::CIdleEvent( CEntityOrder order, int notifyType ) : CScriptEvent( L"i AddLocalProperty( L"orderType", &m_orderType ); AddLocalProperty( L"target", &m_target ); AddLocalProperty( L"location", &m_location ); +} +CMovementEvent::CMovementEvent( float slope ) : CScriptEvent( L"movementEvent", EVENT_MOVEMENT, false ) +{ + m_slope = slope; + + AddLocalProperty( L"slope", &m_slope ); } \ No newline at end of file diff --git a/source/simulation/EventHandlers.h b/source/simulation/EventHandlers.h index 4d04282083..5bcd2be8f7 100644 --- a/source/simulation/EventHandlers.h +++ b/source/simulation/EventHandlers.h @@ -132,5 +132,10 @@ class CIdleEvent : public CScriptEvent public: CIdleEvent( CEntityOrder order, int notifyType ); }; - +class CMovementEvent : public CScriptEvent +{ + float m_slope; +public: + CMovementEvent( float slope ); +}; #endif diff --git a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp index d411ab56bd..ab421b696b 100644 --- a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp @@ -491,7 +491,7 @@ BEGIN_COMMAND(RotateObject) if (unit->GetEntity()) { - m_AngleOld = unit->GetEntity()->m_orientation; + m_AngleOld = unit->GetEntity()->m_orientation.Y; if (msg->usetarget) { CVector3D& pos = unit->GetEntity()->m_position; @@ -543,7 +543,7 @@ BEGIN_COMMAND(RotateObject) if (unit->GetEntity()) { - unit->GetEntity()->m_orientation = angle; + unit->GetEntity()->m_orientation.Y = angle; } else {