1
0
forked from 0ad/0ad

#Fixes to flank penalty, notifications, sectors, terrain conformance. Added speed bonus based on terrain slope.

-added NOTIFY_ORDER_CHANGE, which is used for flank penalty instead of
idle.
-entity speed is now actions.move.speed_curr for the current speed, but
the original speed is still actions.move.speed.  Changes take place in
entityEventMovemen.

This was SVN commit r3840.
This commit is contained in:
pyrolink 2006-05-04 04:14:48 +00:00
parent 16b94e5604
commit 39e89c406e
27 changed files with 444 additions and 277 deletions

View File

@ -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.

View File

@ -1,6 +1,7 @@
<formation Tag="Form Box 2"
bonus="actions.move.speed"
bonus="actions.move.speed_curr"
bonusbase="actions.move.speed"
bonustype="Melee"
bonusval=".2"
penalty=""

View File

@ -13,6 +13,9 @@
<!-- All but sea units are attached to the ground plane. -->
<Anchor>
<Type>Ground</Type>
<!-- If negative or > 90.0, entity will always conform to terrain (assuming traits.pitch allows it) -->
<ConformX>3.141592</ConformX>
<ConformZ>3.141592</ConformZ>
</Anchor>
<!-- Defaults to no armour. -->
@ -49,14 +52,13 @@
<Height>-1.0</Height>
<Name></Name>
</Rank>
<AnglePenalty>
<Flank_Penalty>
<Sectors>6</Sectors>
<Value>.2</Value>
</AnglePenalty>
</Flank_Penalty>
<Pitch>
<Max_Actor>0.03</Max_Actor>
<Min_Actor>-0.03</Min_Actor>
<Sectors>7</Sectors>
<Value>.1</Value>
</Pitch>
</Traits>

View File

@ -22,5 +22,6 @@
<Event On="Notification" Function="entityEventNotification" />
<Event On="Formation" Function="entityEventFormation" />
<Event On="Idle" Function="entityEventIdle" />
<Event On="Movement" Function="entityEventMovement" />
</Entity>

View File

@ -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();
}
}

View File

@ -77,6 +77,7 @@
<Move>
<Speed>7.0</Speed>
<Speed_Curr/>
<TurningRadius>0.0</TurningRadius>
<Run>

View File

@ -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;

View File

@ -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; j<m_MapSize; j++) {
@ -218,13 +218,14 @@ int CMapReader::ApplyData()
unit->GetModel()->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;
}

View File

@ -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);

View File

@ -14,6 +14,8 @@
#include "renderer/Renderer.h"
#include "renderer/WaterManager.h"
#include "EntityManager.h"
#include <string.h>
#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

View File

@ -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);

View File

@ -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;
}

View File

@ -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 );

View File

@ -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__

View File

@ -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;

View File

@ -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];

View File

@ -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() );
}
}
}

View File

@ -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<int, FormationSlot> m_slots;
std::vector<float> m_angleValues; //cosine of angle divisions
bool loadXML(CStr filename);
void AssignCategory(int order, CStr category); //takes care of formatting strings
};
#endif

View File

@ -36,21 +36,20 @@ using namespace std;
CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, const std::set<CStrW>& 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; i<m_sectorDivs; ++i )
{
m_sectorAngles[i] = cosf( step*i );
m_sectorValues[i] = false;
}
m_collisionPatch = NULL;
// Set our parent unit and build us an actor.
@ -141,6 +124,18 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons
m_actorSelections = actorSelections;
loadBase();
// 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 < 360))
{
debug_warn("invalid entity flank_penalty.sectors value");
m_sectorDivs = sane_sectordivs_default;
}
m_sectorValues.resize(m_sectorDivs);
for ( int i=0; i<m_sectorDivs; ++i )
m_sectorValues[i] = false;
if( m_bounds )
m_bounds->setPosition( 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; i<m_listeners.size(); i++ )
m_listeners[i].m_sender->DestroyNotifier( this );
DestroyAllNotifiers();
@ -249,7 +244,10 @@ void CEntity::kill()
CEntity* remove = this;
g_FormationManager.RemoveUnit(remove);
m_destroyNotifiers=true;
for ( size_t i=0; i<m_listeners.size(); i++ )
m_listeners[i].m_sender->DestroyNotifier( 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<CVector3D>( 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<float>( m_orientation_previous, m_orientation, relativeoffset );
m_graphics_pitchOrientation = Interpolate<float>( 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<CVector3D>( 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<maxAngle; tracker+=step, ++i )
{
if ( angle > 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<jsval, &CEntity::AddAura>( "addAura", 3 );
AddMethod<jsval, &CEntity::RemoveAura>( "removeAura", 1 );
AddMethod<jsval, &CEntity::SetActionParams>( "setActionParams", 5 );
AddMethod<int, &CEntity::GetCurrentRequest>( "getCurrentRequest", 0 );
AddMethod<bool, &CEntity::ForceCheckListeners>( "forceCheckListeners", 2 );
AddMethod<bool, &CEntity::RequestNotification>( "requestNotification", 4 );
AddMethod<jsval, &CEntity::DestroyNotifier>( "destroyNotifier", 1 );
@ -1331,7 +1363,9 @@ void CEntity::ScriptingInit()
AddMethod<jsval, &CEntity::GetFormationPenaltyType>( "getFormationPenaltyType", 0 );
AddMethod<jsval, &CEntity::GetFormationPenaltyVal>( "getFormationPenaltyVal", 0 );
AddMethod<jsval, &CEntity::RegisterDamage>( "registerDamage", 0 );
AddMethod<jsval, &CEntity::RegisterIdle>( "registerIdle", 0 );
AddMethod<jsval, &CEntity::RegisterOrderChange>( "registerOrderChange", 0 );
AddMethod<jsval, &CEntity::GetAttackDirections>( "getAttackDirections", 0 );
AddMethod<jsval, &CEntity::FindSector>("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<CEntity>( argv[0] );
(int&)notify.m_type = ToPrimitive<int>( argv[1] );
bool tmpDestroyNotifiers = ToPrimitive<bool>( argv[2] );
// TODO: ??? This local variable overrides the member variable of the same name...
bool m_destroyNotifiers = !ToPrimitive<bool>( argv[3] );
m_destroyNotifiers = !ToPrimitive<bool>( 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; i<m_sectorDivs; ++i )
{
//Wrap around to the start-if we've made it this far, it's here
if ( i == m_base->m_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; i<m_sectorDivs; ++i )
{
//Wrap around to the start-if we've made it this far, it's here
if ( i == m_base->m_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<int>( argv[0] );
float angle = ToPrimitive<float>( argv[1] );
float maxAngle = ToPrimitive<float>( argv[2] );
bool negative = ToPrimitive<bool>( 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<maxAngle; tracker+=step, ++i )
{
if ( angle > tracker && angle <= tracker+step )
return ToJSVal(i);
}
}
debug_warn("JS - FindSector(): invalid parameters");
return ToJSVal(-1);
}

View File

@ -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<bool> m_sectorValues;
std::vector<float> m_sectorAngles;
int m_sectorDivs;
float m_sectorPenalty;
int m_pitchDivs;
private:
CEntity( CBaseEntity* base, CVector3D position, float orientation, const std::set<CStrW>& 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 );

View File

@ -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()
{

View File

@ -63,6 +63,7 @@ public:
void InitializeAll();
void TickAll();
void renderAll();
void conformAll();
void invalidateAll();
void deleteAll();

View File

@ -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;

View File

@ -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;
}

View File

@ -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 );
}

View File

@ -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

View File

@ -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
{