mirror of
https://github.com/ptitSeb/Serious-Engine
synced 2024-12-25 07:14:50 +01:00
1018 lines
30 KiB
C++
1018 lines
30 KiB
C++
/* Copyright (c) 2002-2012 Croteam Ltd.
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of version 2 of the GNU General Public License as published by
|
|
the Free Software Foundation
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
|
|
|
|
101
|
|
%{
|
|
#include "StdH.h"
|
|
%}
|
|
|
|
uses "EntitiesMP/MovingBrushMarker";
|
|
uses "EntitiesMP/SoundHolder";
|
|
uses "EntitiesMP\MirrorMarker";
|
|
uses "EntitiesMP/Debris";
|
|
|
|
event EHit {
|
|
};
|
|
event EBrushDestroyed { // sent to all children of a moving brush when it is destroyed
|
|
};
|
|
|
|
enum BlockAction {
|
|
0 BA_NONE "None", // continue moving
|
|
1 BA_BOUNCE "Bounce", // bounce when obstructed
|
|
2 BA_SKIPMARKER "Skip marker", // skip moving to next marker
|
|
};
|
|
|
|
enum TouchOrDamageEvent {
|
|
0 TDE_TOUCHONLY "Touch Only",
|
|
1 TDE_DAMAGEONLY "Damage Only",
|
|
2 TDE_BOTH "Both",
|
|
};
|
|
|
|
%{
|
|
static const float TRANSLATION_EPSILON=0.05f;
|
|
static const float ROTATION_EPSILON=0.05f;
|
|
extern void GetDefaultForce(INDEX iForce, const FLOAT3D &vPoint,
|
|
CForceStrength &fsGravity, CForceStrength &fsField);
|
|
%}
|
|
|
|
class CMovingBrush : CMovableBrushEntity {
|
|
name "Moving Brush";
|
|
thumbnail "Thumbnails\\MovingBrush.tbn";
|
|
features "HasName", "IsTargetable";
|
|
|
|
properties:
|
|
|
|
1 CTString m_strName "Name" 'N' = "Moving Brush",
|
|
2 CTString m_strDescription = "",
|
|
|
|
3 CEntityPointer m_penTarget "Target" 'T' COLOR(C_BLUE|0xFF),
|
|
4 BOOL m_bAutoStart "Auto start" 'A' = FALSE,
|
|
5 FLOAT m_fSpeed "Speed" 'S' = 1.0f,
|
|
6 FLOAT m_fWaitTime "Wait time" 'W' = 0.0f,
|
|
7 BOOL m_bMoveOnTouch "Move on touch" 'M' = FALSE,
|
|
8 enum BlockAction m_ebaAction "Block action" 'B' = BA_NONE,
|
|
9 FLOAT m_fBlockDamage "Block damage" 'D' = 10.0f,
|
|
10 BOOL m_bPlayersOnly "Players Only" 'P' = TRUE,
|
|
11 BOOL m_bDynamicShadows "Dynamic shadows" = FALSE,
|
|
12 BOOL m_bVeryBigBrush "Very Big Brush" = FALSE,
|
|
|
|
// send event on touch
|
|
13 enum EventEType m_eetTouchEvent "Touch Event - Type" 'U' = EET_IGNORE, // type of event to send
|
|
14 CEntityPointer m_penTouchEvent "Touch Event - Target" 'I' COLOR(C_dCYAN|0xFF), // target to send event to
|
|
19 enum TouchOrDamageEvent m_tdeSendEventOnDamage "Send touch event on damage" = TDE_TOUCHONLY,
|
|
|
|
15 CEntityPointer m_penSwitch "Switch", // for switch relaying
|
|
|
|
// send event on marker
|
|
16 enum EventEType m_eetMarkerEvent = EET_IGNORE,
|
|
17 CEntityPointer m_penMarkerEvent,
|
|
|
|
// rotation
|
|
18 FLOAT m_tmBankingRotation "Banking rotation speed" = 0.0f, // set if only banking rotation
|
|
|
|
// class properties
|
|
20 BOOL m_bMoving = FALSE, // the brush is moving
|
|
78 BOOL m_bRotating = FALSE, // the brush is rotating
|
|
79 BOOL m_bForceStop = FALSE, // the brush should stop immediately
|
|
80 BOOL m_bNoRotation = FALSE, // don't rotate to marker orientation
|
|
21 FLOAT3D m_vDesiredTranslation = FLOAT3D(0,0,0), // desired translation
|
|
22 ANGLE3D m_aDesiredRotation = FLOAT3D(0,0,0), // desired rotation
|
|
23 BOOL m_bInverseRotate = FALSE, // use inverse rotation to target
|
|
24 BOOL m_bStopMoving = FALSE, // stop moving brush on next target
|
|
25 BOOL m_bMoveToMarker = FALSE, // PerMoving acknowledge od brush moving
|
|
26 BOOL m_bSkipMarker = FALSE, // when obstructed skip next marker (actually stop moving)
|
|
27 BOOL m_bValidMarker = FALSE, // internal for moving through valid markers
|
|
|
|
// moving limits
|
|
30 FLOAT m_fXLimitSign = 0.0f,
|
|
31 FLOAT m_fYLimitSign = 0.0f,
|
|
32 FLOAT m_fZLimitSign = 0.0f,
|
|
33 ANGLE m_aHLimitSign = 0.0f,
|
|
34 ANGLE m_aPLimitSign = 0.0f,
|
|
35 ANGLE m_aBLimitSign = 0.0f,
|
|
|
|
// continuous speed change
|
|
40 FLOAT3D m_vStartTranslation = FLOAT3D(0,0,0), // start translation
|
|
41 ANGLE3D m_aStartRotation = ANGLE3D(0,0,0), // start rotation
|
|
42 FLOAT m_fCourseLength = 0.0f, // course length
|
|
43 ANGLE m_aHeadLenght = 0.0f, // head lenght
|
|
44 ANGLE m_aPitchLenght = 0.0f, // pitch lenght
|
|
45 ANGLE m_aBankLenght = 0.0f, // bank lenght
|
|
|
|
// sound target
|
|
50 CEntityPointer m_penSoundStart "Sound start entity" 'Q', // sound start entity
|
|
51 CEntityPointer m_penSoundStop "Sound stop entity" 'Z', // sound stop entity
|
|
52 CEntityPointer m_penSoundFollow "Sound follow entity" 'F', // sound follow entity
|
|
53 CSoundObject m_soStart,
|
|
54 CSoundObject m_soStop,
|
|
55 CSoundObject m_soFollow,
|
|
|
|
|
|
60 CEntityPointer m_penMirror0 "Mirror 0" 'M',
|
|
61 CEntityPointer m_penMirror1 "Mirror 1",
|
|
62 CEntityPointer m_penMirror2 "Mirror 2",
|
|
63 CEntityPointer m_penMirror3 "Mirror 3",
|
|
64 CEntityPointer m_penMirror4 "Mirror 4",
|
|
|
|
65 FLOAT m_fHealth "Health" 'H' = -1.0f,
|
|
66 BOOL m_bBlowupByBull "Blowup by Bull" = FALSE, // special feature for bull crushing doors
|
|
// send event on touch
|
|
67 enum EventEType m_eetBlowupEvent "Blowup Event - Type" = EET_IGNORE, // type of event to send
|
|
68 CEntityPointer m_penBlowupEvent "Blowup Event - Target" COLOR(C_BLACK|0xFF), // target to send event to
|
|
69 BOOL m_bZoning "Zoning" 'Z' =FALSE,
|
|
70 BOOL m_bMoveOnDamage "Move on damage" = FALSE, // move when recive damage
|
|
71 FLOAT m_fTouchDamage "Touch damage" = 0.0f,
|
|
72 COLOR m_colDebrises "Color of debrises" = C_WHITE,
|
|
74 INDEX m_ctDebrises "Debris count" = 12,
|
|
75 FLOAT m_fCandyEffect "Debris blow power" = 0.0f,
|
|
76 FLOAT m_fCubeFactor "Cube factor" = 1.0f,
|
|
77 BOOL m_bBlowupByDamager "Blowup by Damager" = FALSE, // if only damager can destroy brush
|
|
|
|
81 flags ClasificationBits m_cbClassificationBits "Clasification bits" 'C' = 0,
|
|
82 flags VisibilityBits m_vbVisibilityBits "Visibility bits" 'V' = 0,
|
|
|
|
|
|
components:
|
|
|
|
// ************** STONE PARTS **************
|
|
14 model MODEL_STONE "Models\\Effects\\Debris\\Stone\\Stone.mdl",
|
|
15 texture TEXTURE_STONE "Models\\Effects\\Debris\\Stone\\Stone.tex",
|
|
16 class CLASS_DEBRIS "Classes\\Debris.ecl",
|
|
4 class CLASS_BASIC_EFFECT "Classes\\BasicEffect.ecl",
|
|
|
|
|
|
functions:
|
|
|
|
// get visibility tweaking bits
|
|
ULONG GetVisTweaks(void)
|
|
{
|
|
return m_cbClassificationBits|m_vbVisibilityBits;
|
|
}
|
|
|
|
void Precache(void)
|
|
{
|
|
PrecacheClass(CLASS_DEBRIS);
|
|
PrecacheModel(MODEL_STONE);
|
|
PrecacheTexture(TEXTURE_STONE);
|
|
}
|
|
/* Get force in given point. */
|
|
void GetForce(INDEX iForce, const FLOAT3D &vPoint,
|
|
CForceStrength &fsGravity, CForceStrength &fsField)
|
|
{
|
|
GetDefaultForce(iForce, vPoint, fsGravity, fsField);
|
|
}
|
|
/* Receive damage */
|
|
void ReceiveDamage(CEntity *penInflictor, enum DamageType dmtType,
|
|
FLOAT fDamageAmmount, const FLOAT3D &vHitPoint, const FLOAT3D &vDirection)
|
|
{
|
|
if( m_bMoveOnDamage)
|
|
{
|
|
EHit eHit;
|
|
SendEvent( eHit);
|
|
return;
|
|
}
|
|
|
|
// send event on damage
|
|
if(m_tdeSendEventOnDamage!=TDE_TOUCHONLY && CanReactOnEntity(penInflictor)) {
|
|
SendToTarget(m_penTouchEvent, m_eetTouchEvent, penInflictor);
|
|
}
|
|
|
|
// if not destroyable
|
|
if(m_fHealth<0) {
|
|
// ignore damages
|
|
return;
|
|
}
|
|
|
|
// if special feature for bull crushing doors
|
|
if (m_bBlowupByBull)
|
|
{
|
|
// if impact by bull
|
|
if( dmtType == DMT_IMPACT && IsOfClass(penInflictor, "Werebull"))
|
|
{
|
|
// receive the damage so large to blowup
|
|
CMovableBrushEntity::ReceiveDamage(penInflictor, dmtType, m_fHealth*2, vHitPoint, vDirection);
|
|
// kill the bull in place, but make sure it doesn't blow up
|
|
((CLiveEntity*)penInflictor)->SetHealth(0.0f);
|
|
InflictDirectDamage(penInflictor, this, DMT_IMPACT, 1.0f,
|
|
GetPlacement().pl_PositionVector, FLOAT3D(0,1,0));
|
|
}
|
|
}
|
|
else if(m_bBlowupByDamager)
|
|
{
|
|
if( dmtType == DMT_DAMAGER)
|
|
{
|
|
CMovableBrushEntity::ReceiveDamage(penInflictor, dmtType, fDamageAmmount, vHitPoint, vDirection);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// react only on explosions
|
|
if( (dmtType == DMT_EXPLOSION) ||
|
|
(dmtType == DMT_PROJECTILE) ||
|
|
(dmtType == DMT_CANNONBALL) )
|
|
{
|
|
CMovableBrushEntity::ReceiveDamage(penInflictor, dmtType, fDamageAmmount, vHitPoint, vDirection);
|
|
}
|
|
}
|
|
};
|
|
|
|
// adjust angle
|
|
void AdjustAngle(ANGLE &a) {
|
|
if (m_bInverseRotate) {
|
|
if (a>0) { a = a - 360; }
|
|
else if (a<0) { a = 360 + a; }
|
|
}
|
|
};
|
|
|
|
|
|
/* Check if entity is moved on a route set up by its targets. */
|
|
BOOL MovesByTargetedRoute(CTString &strTargetProperty) const {
|
|
strTargetProperty = "Target";
|
|
return TRUE;
|
|
};
|
|
/* Check if entity can drop marker for making linked route. */
|
|
BOOL DropsMarker(CTFileName &fnmMarkerClass, CTString &strTargetProperty) const {
|
|
fnmMarkerClass = CTFILENAME("Classes\\MovingBrushMarker.ecl");
|
|
strTargetProperty = "Target";
|
|
return TRUE;
|
|
}
|
|
const CTString &GetDescription(void) const {
|
|
((CTString&)m_strDescription).PrintF("-><none>");
|
|
if (m_penTarget!=NULL) {
|
|
((CTString&)m_strDescription).PrintF("->%s", m_penTarget->GetName());
|
|
}
|
|
return m_strDescription;
|
|
}
|
|
/* Get mirror type name, return empty string if not used. */
|
|
const CTString &GetMirrorName(INDEX iMirror)
|
|
{
|
|
static const CTString strDummyName("");
|
|
static const CTString strMarkerUnused("Marker not set");
|
|
if (iMirror==0) {
|
|
return strDummyName;
|
|
}
|
|
|
|
switch (iMirror) {
|
|
case 1: { static const CTString str("std mirror 1"); return str; }; break;
|
|
case 2: { static const CTString str("std mirror 2"); return str; }; break;
|
|
case 3: { static const CTString str("std mirror 3"); return str; }; break;
|
|
case 4: { static const CTString str("std mirror 4"); return str; }; break;
|
|
case 5: { static const CTString str("std mirror 5"); return str; }; break;
|
|
case 6: { static const CTString str("std mirror 6"); return str; }; break;
|
|
case 7: { static const CTString str("std mirror 7"); return str; }; break;
|
|
case 8: { static const CTString str("std mirror 8"); return str; }; break;
|
|
default: {
|
|
iMirror-=9;
|
|
INDEX ctMirrorMarkers = &m_penMirror4-&m_penMirror0;
|
|
if (iMirror<ctMirrorMarkers){
|
|
CMirrorMarker *pfm = (CMirrorMarker *)&*(&m_penMirror0)[iMirror];
|
|
if (pfm != NULL) {
|
|
return pfm->GetMirrorName();
|
|
} else {
|
|
return strMarkerUnused;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return strDummyName;
|
|
}
|
|
|
|
/* Get mirror, return FALSE for none. */
|
|
BOOL GetMirror(INDEX iMirror, class CMirrorParameters &mpMirror)
|
|
{
|
|
if (iMirror==0) {
|
|
return FALSE;
|
|
}
|
|
if (iMirror>=1 && iMirror<=8) {
|
|
mpMirror.mp_ulFlags = 0;
|
|
return TRUE;
|
|
}
|
|
iMirror-=9;
|
|
INDEX ctMirrorMarkers = &m_penMirror4-&m_penMirror0;
|
|
if (iMirror<ctMirrorMarkers){
|
|
CMirrorMarker *pmm = (CMirrorMarker *)&*(&m_penMirror0)[iMirror];
|
|
if (pmm != NULL) {
|
|
pmm->GetMirror(mpMirror);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// pre moving
|
|
void PreMoving() {
|
|
if (m_bMoveToMarker) {
|
|
const FLOAT3D &vTarget = m_penTarget->GetPlacement().pl_PositionVector;
|
|
const ANGLE3D &aTarget = m_penTarget->GetPlacement().pl_OrientationAngle;
|
|
const FLOAT3D &vSource = GetPlacement().pl_PositionVector;
|
|
const ANGLE3D &aSource = GetPlacement().pl_OrientationAngle;
|
|
|
|
// translation
|
|
FLOAT3D vSpeed = (vTarget-vSource)/_pTimer->TickQuantum;
|
|
// X axis
|
|
if (Abs(vSpeed(1))<TRANSLATION_EPSILON) {
|
|
vSpeed(1) = 0.0f;
|
|
} else if ((vSpeed(1)-m_vDesiredTranslation(1))*m_fXLimitSign>0) {
|
|
vSpeed(1) = m_vDesiredTranslation(1);
|
|
}
|
|
// Y axis
|
|
if (Abs(vSpeed(2))<TRANSLATION_EPSILON) {
|
|
vSpeed(2) = 0.0f;
|
|
} else if ((vSpeed(2)-m_vDesiredTranslation(2))*m_fYLimitSign>0) {
|
|
vSpeed(2) = m_vDesiredTranslation(2);
|
|
}
|
|
// Z axis
|
|
if (Abs(vSpeed(3))<TRANSLATION_EPSILON) {
|
|
vSpeed(3) = 0.0f;
|
|
} else if ((vSpeed(3)-m_vDesiredTranslation(3))*m_fZLimitSign>0) {
|
|
vSpeed(3) = m_vDesiredTranslation(3);
|
|
}
|
|
|
|
// rotation
|
|
ANGLE3D aSpeed;
|
|
aSpeed(1) = NormalizeAngle(aTarget(1)-aSource(1)); // normalize angle
|
|
AdjustAngle(aSpeed(1)); // adjust angle (inverse rotation)
|
|
aSpeed(1) = Abs(aSpeed(1)) * m_aHLimitSign; // set sign (direction)
|
|
aSpeed(1) /= _pTimer->TickQuantum; // transform to tick speed
|
|
aSpeed(2) = NormalizeAngle(aTarget(2)-aSource(2));
|
|
AdjustAngle(aSpeed(2));
|
|
aSpeed(2) = Abs(aSpeed(2)) * m_aPLimitSign;
|
|
aSpeed(2) /= _pTimer->TickQuantum;
|
|
aSpeed(3) = NormalizeAngle(aTarget(3)-aSource(3));
|
|
AdjustAngle(aSpeed(3));
|
|
aSpeed(3) = Abs(aSpeed(3)) * m_aBLimitSign;
|
|
aSpeed(3) /= _pTimer->TickQuantum;
|
|
// Heading
|
|
if (Abs(aSpeed(1))<ROTATION_EPSILON) {
|
|
aSpeed(1) = 0.0f;
|
|
} else if ((aSpeed(1)-m_aDesiredRotation(1))*m_aHLimitSign>0) {
|
|
aSpeed(1) = m_aDesiredRotation(1);
|
|
}
|
|
// Pitch
|
|
if (Abs(aSpeed(2))<ROTATION_EPSILON) {
|
|
aSpeed(2) = 0.0f;
|
|
} else if ((aSpeed(2)-m_aDesiredRotation(2))*m_aPLimitSign>0) {
|
|
aSpeed(2) = m_aDesiredRotation(2);
|
|
}
|
|
// Banking
|
|
if (Abs(aSpeed(3))<ROTATION_EPSILON) {
|
|
aSpeed(3) = 0.0f;
|
|
} else if ((aSpeed(3)-m_aDesiredRotation(3))*m_aBLimitSign>0) {
|
|
aSpeed(3) = m_aDesiredRotation(3);
|
|
}
|
|
|
|
// stop moving ?
|
|
if (vSpeed(1)==0.0f && vSpeed(2)==0.0f && vSpeed(3)==0.0f
|
|
&& ((m_tmBankingRotation!=0 || m_bNoRotation)||(aSpeed(1)==0.0f && aSpeed(2)==0.0f && aSpeed(3)==0.0f)) )
|
|
{
|
|
// stop brush
|
|
ForceFullStop();
|
|
// stop PreMoving check
|
|
m_bMoveToMarker = FALSE;
|
|
// this EEnd event will end MoveToMarker autowait() and return to MoveBrush
|
|
SendEvent(EEnd());
|
|
|
|
// move brush
|
|
} else {
|
|
SetDesiredTranslation(vSpeed);
|
|
if (m_bRotating) {
|
|
MaybeActivateRotation();
|
|
} else if (!m_tmBankingRotation && !m_bNoRotation) {
|
|
SetDesiredRotation(aSpeed);
|
|
} else {
|
|
SetDesiredRotation(ANGLE3D(0,0,0));
|
|
}
|
|
}
|
|
|
|
}
|
|
CMovableBrushEntity::PreMoving();
|
|
};
|
|
|
|
|
|
// load marker parameters
|
|
BOOL LoadMarkerParameters() {
|
|
if (m_penTarget==NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!IsOfClass(m_penTarget, "Moving Brush Marker")) {
|
|
WarningMessage("Entity '%s' is not of Moving Brush Marker class!", m_penTarget->GetName());
|
|
return FALSE;
|
|
}
|
|
|
|
CMovingBrushMarker &mbm = (CMovingBrushMarker&) *m_penTarget;
|
|
if (mbm.m_penTarget==NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
// speed
|
|
if (mbm.m_fSpeed > 0.0f) { m_fSpeed = mbm.m_fSpeed; }
|
|
|
|
// wait time
|
|
if (mbm.m_fWaitTime >= 0.0f) { m_fWaitTime = mbm.m_fWaitTime; }
|
|
|
|
// inverse rotate
|
|
m_bInverseRotate = mbm.m_bInverseRotate;
|
|
|
|
// move on touch
|
|
SetBoolFromBoolEType(m_bMoveOnTouch, mbm.m_betMoveOnTouch);
|
|
|
|
// stop moving
|
|
m_bStopMoving = mbm.m_bStopMoving;
|
|
|
|
// block damage
|
|
if (mbm.m_fBlockDamage >= 0.0f) {
|
|
m_fBlockDamage = mbm.m_fBlockDamage;
|
|
}
|
|
|
|
// touch event
|
|
if (mbm.m_penTouchEvent != NULL) {
|
|
m_penTouchEvent = mbm.m_penTouchEvent;
|
|
m_eetTouchEvent = mbm.m_eetTouchEvent;
|
|
}
|
|
|
|
// marker event -> SEND ALWAYS (if target is valid) !!!
|
|
SendToTarget(mbm.m_penMarkerEvent, mbm.m_eetMarkerEvent);
|
|
|
|
// sound entity
|
|
if (mbm.m_penSoundStart!=NULL) {
|
|
m_penSoundStart = mbm.m_penSoundStart;
|
|
}
|
|
if (mbm.m_penSoundStop!=NULL) {
|
|
m_penSoundStop = mbm.m_penSoundStop;
|
|
}
|
|
if (mbm.m_penSoundFollow!=NULL) {
|
|
m_penSoundFollow = mbm.m_penSoundFollow;
|
|
}
|
|
|
|
m_bNoRotation = mbm.m_bNoRotation;
|
|
|
|
if (mbm.m_tmBankingRotation>=0.0f) {
|
|
m_tmBankingRotation = mbm.m_tmBankingRotation;
|
|
if (!mbm.m_bBankingClockwise) {
|
|
m_tmBankingRotation *= -1;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
};
|
|
|
|
|
|
// test if this door reacts on this entity
|
|
BOOL CanReactOnEntity(CEntity *pen)
|
|
{
|
|
if (pen==NULL) {
|
|
return FALSE;
|
|
}
|
|
// never react on non-live or dead entities
|
|
if (!(pen->GetFlags()&ENF_ALIVE)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (m_bPlayersOnly && !IsDerivedFromClass(pen, "Player")) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// play start sound
|
|
void PlayStartSound(void) {
|
|
// if sound entity exists
|
|
if (m_penSoundStart!=NULL) {
|
|
CSoundHolder &sh = (CSoundHolder&)*m_penSoundStart;
|
|
m_soStart.Set3DParameters(FLOAT(sh.m_rFallOffRange), FLOAT(sh.m_rHotSpotRange), sh.m_fVolume, 1.0f);
|
|
PlaySound(m_soStart, sh.m_fnSound, sh.m_iPlayType);
|
|
}
|
|
};
|
|
|
|
// play stop sound
|
|
void PlayStopSound(void) {
|
|
// if sound entity exists
|
|
if (m_penSoundStop!=NULL) {
|
|
CSoundHolder &sh = (CSoundHolder&)*m_penSoundStop;
|
|
m_soStop.Set3DParameters(FLOAT(sh.m_rFallOffRange), FLOAT(sh.m_rHotSpotRange), sh.m_fVolume, 1.0f);
|
|
PlaySound(m_soStop, sh.m_fnSound, sh.m_iPlayType);
|
|
}
|
|
};
|
|
|
|
// play follow sound
|
|
void PlayFollowSound(void) {
|
|
// if sound entity exists
|
|
if (m_penSoundFollow!=NULL) {
|
|
CSoundHolder &sh = (CSoundHolder&)*m_penSoundFollow;
|
|
m_soFollow.Set3DParameters(FLOAT(sh.m_rFallOffRange), FLOAT(sh.m_rHotSpotRange), sh.m_fVolume, 1.0f);
|
|
PlaySound(m_soFollow, sh.m_fnSound, sh.m_iPlayType);
|
|
}
|
|
};
|
|
|
|
// stop follow sound
|
|
void StopFollowSound(void) {
|
|
m_soFollow.Stop();
|
|
};
|
|
|
|
|
|
void MovingOn(void)
|
|
{
|
|
if (m_bMoving) {
|
|
return;
|
|
}
|
|
if (m_bVeryBigBrush) {
|
|
SetCollisionFlags(ECF_BRUSH|ECF_IGNOREMODELS);
|
|
}
|
|
m_bMoving = TRUE;
|
|
}
|
|
void MovingOff(void)
|
|
{
|
|
if (!m_bMoving) {
|
|
return;
|
|
}
|
|
if (m_bVeryBigBrush) {
|
|
SetCollisionFlags(ECF_BRUSH);
|
|
}
|
|
m_bMoving = FALSE;
|
|
}
|
|
|
|
void MaybeActivateRotation(void)
|
|
{
|
|
if (m_tmBankingRotation!=0) {
|
|
m_bRotating = TRUE;
|
|
SetDesiredRotation(ANGLE3D(0.0f,0.0f,360.0f/m_tmBankingRotation));
|
|
}
|
|
else {
|
|
m_bRotating = FALSE;
|
|
}
|
|
}
|
|
|
|
void DeactivateRotation(void)
|
|
{
|
|
m_bRotating = FALSE;
|
|
SetDesiredRotation(ANGLE3D(0.0f,0.0f,0.0f));
|
|
}
|
|
|
|
void SetCombinedRotation(ANGLE3D aRotAngle, ANGLE3D aAddAngle)
|
|
{
|
|
aRotAngle(1) += aAddAngle(1);
|
|
aRotAngle(2) += aAddAngle(2);
|
|
aRotAngle(3) += aAddAngle(3);
|
|
}
|
|
|
|
|
|
// returns bytes of memory used by this object
|
|
SLONG GetUsedMemory(void)
|
|
{
|
|
// initial
|
|
SLONG slUsedMemory = sizeof(CMovingBrush) - sizeof(CMovableBrushEntity) + CMovableBrushEntity::GetUsedMemory();
|
|
// add some more
|
|
slUsedMemory += m_strName.Length();
|
|
slUsedMemory += m_strDescription.Length();
|
|
slUsedMemory += 3* sizeof(CSoundObject); // 3 of them
|
|
return slUsedMemory;
|
|
}
|
|
|
|
|
|
|
|
procedures:
|
|
|
|
|
|
MoveToMarker() {
|
|
// move to target
|
|
const FLOAT3D &vTarget = m_penTarget->GetPlacement().pl_PositionVector;
|
|
const ANGLE3D &aTarget = m_penTarget->GetPlacement().pl_OrientationAngle;
|
|
const FLOAT3D &vSource = GetPlacement().pl_PositionVector;
|
|
const ANGLE3D &aSource = GetPlacement().pl_OrientationAngle;
|
|
|
|
// set new translation
|
|
m_vDesiredTranslation = (vTarget-vSource)/m_fSpeed;
|
|
m_fXLimitSign = Sgn(vTarget(1)-vSource(1));
|
|
m_fYLimitSign = Sgn(vTarget(2)-vSource(2));
|
|
m_fZLimitSign = Sgn(vTarget(3)-vSource(3));
|
|
|
|
// set new rotation
|
|
// heading
|
|
ANGLE aDelta = NormalizeAngle(aTarget(1)-aSource(1));
|
|
AdjustAngle(aDelta);
|
|
m_aDesiredRotation(1) = aDelta/m_fSpeed;
|
|
m_aHLimitSign = Sgn(aDelta);
|
|
// pitch
|
|
aDelta = NormalizeAngle(aTarget(2)-aSource(2));
|
|
AdjustAngle(aDelta);
|
|
m_aDesiredRotation(2) = aDelta/m_fSpeed;
|
|
m_aPLimitSign = Sgn(aDelta);
|
|
// banking
|
|
aDelta = NormalizeAngle(aTarget(3)-aSource(3));
|
|
AdjustAngle(aDelta);
|
|
m_aDesiredRotation(3) = aDelta/m_fSpeed;
|
|
m_aBLimitSign = Sgn(aDelta);
|
|
|
|
// start moving
|
|
m_bMoveToMarker = TRUE;
|
|
SetDesiredTranslation(m_vDesiredTranslation);
|
|
if (m_bRotating) {
|
|
MaybeActivateRotation();
|
|
} else if (!m_tmBankingRotation) {
|
|
SetDesiredRotation(m_aDesiredRotation);
|
|
}
|
|
|
|
// PreMoving will send EEnd event to end
|
|
wait() {
|
|
on (EBegin) : { resume; }
|
|
on (EStop) : {
|
|
//SetCollisionFlags(ECF_IMMATERIAL);
|
|
SetDesiredTranslation(FLOAT3D(0.0f, 0.0f, 0.0f));
|
|
if (m_tmBankingRotation==0) {
|
|
SetDesiredRotation(ANGLE3D(0.0f, 0.0f, 0.0f));
|
|
}
|
|
m_bForceStop = TRUE;
|
|
// stop PreMoving check
|
|
m_bMoveToMarker = FALSE;
|
|
m_bStopMoving = TRUE;
|
|
|
|
return EEnd();
|
|
}
|
|
// move is obstructed
|
|
on (EBlock eBlock) : {
|
|
// inflict damage to entity that block brush
|
|
InflictDirectDamage(eBlock.penOther, this, DMT_BRUSH, m_fBlockDamage,
|
|
FLOAT3D(0.0f,0.0f,0.0f), (FLOAT3D &)eBlock.plCollision);
|
|
if (m_ebaAction == BA_BOUNCE) {
|
|
// change direction for two ticks
|
|
SetDesiredTranslation(-m_vDesiredTranslation);
|
|
if (m_bRotating) {
|
|
MaybeActivateRotation();
|
|
} else if (!m_tmBankingRotation) {
|
|
SetDesiredRotation(-m_aDesiredRotation);
|
|
}
|
|
// wait for two ticks and reset direction
|
|
call BounceObstructed();
|
|
} else if (m_ebaAction == BA_SKIPMARKER) {
|
|
// stop moving brush
|
|
ForceFullStop();
|
|
// stop PreMoving check
|
|
m_bMoveToMarker = FALSE;
|
|
// skip this marker and move to next one
|
|
m_bSkipMarker = TRUE;
|
|
return EEnd();
|
|
}
|
|
resume;
|
|
}
|
|
}
|
|
}
|
|
|
|
BounceObstructed() {
|
|
autowait(0.2f);
|
|
// return to standard direction
|
|
SetDesiredTranslation(m_vDesiredTranslation);
|
|
if (m_bRotating) {
|
|
SetDesiredRotation(ANGLE3D(0.0f, 0.0f, 360.0f/m_tmBankingRotation));
|
|
} else if (!m_tmBankingRotation) {
|
|
SetDesiredRotation(m_aDesiredRotation);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*Rotating()
|
|
{
|
|
if (m_bAutoStart) {
|
|
jump RotActive();
|
|
} else {
|
|
jump RotInactive();
|
|
}
|
|
}
|
|
|
|
RotInactive()
|
|
{
|
|
SetDesiredRotation(ANGLE3D(0,0,0));
|
|
wait() {
|
|
on (EActivate) : {
|
|
jump RotActive();
|
|
}
|
|
otherwise() : {
|
|
resume;
|
|
}
|
|
};
|
|
}
|
|
|
|
RotActive()
|
|
{
|
|
SetDesiredRotation(ANGLE3D(0,0,360.0f/m_tmBankingRotation));
|
|
|
|
wait() {
|
|
on (EDeactivate) : {
|
|
jump RotInactive();
|
|
}
|
|
otherwise() : {
|
|
resume;
|
|
}
|
|
};
|
|
}*/
|
|
|
|
// move brush
|
|
MoveBrush()
|
|
{
|
|
if (m_penTarget==NULL) {
|
|
MovingOff();
|
|
return;
|
|
}
|
|
|
|
MovingOn();
|
|
|
|
// move through markers
|
|
do {
|
|
|
|
if (m_bForceStop==FALSE)
|
|
{
|
|
// new moving target
|
|
m_penTarget = m_penTarget->GetTarget();
|
|
}
|
|
else {
|
|
m_bForceStop=FALSE;
|
|
}
|
|
|
|
if (m_penTarget==NULL) {
|
|
MovingOff();
|
|
return EVoid();
|
|
}
|
|
|
|
// skip this marker / ignore wait time
|
|
if (m_bSkipMarker) {
|
|
m_bSkipMarker = FALSE;
|
|
// wait for a while
|
|
} else if (m_fWaitTime > 0.0f) {
|
|
//autowait(m_fWaitTime);
|
|
wait(m_fWaitTime) {
|
|
on (EBegin) : { resume; }
|
|
on (EStop) : {
|
|
//SetCollisionFlags(ECF_IMMATERIAL);
|
|
SetDesiredTranslation(FLOAT3D(0.0f, 0.0f, 0.0f));
|
|
if (m_tmBankingRotation==0) {
|
|
SetDesiredRotation(ANGLE3D(0.0f, 0.0f, 0.0f));
|
|
}
|
|
m_bForceStop = TRUE;
|
|
// stop PreMoving check
|
|
m_bMoveToMarker = FALSE;
|
|
m_bStopMoving = TRUE;
|
|
|
|
resume; }//return; }
|
|
on (ETimer) : { stop; }
|
|
}
|
|
}
|
|
|
|
if (!m_bForceStop) {
|
|
MaybeActivateRotation();
|
|
|
|
PlayStartSound();
|
|
PlayFollowSound();
|
|
autocall MoveToMarker() EEnd;
|
|
StopFollowSound();
|
|
PlayStopSound();
|
|
}
|
|
|
|
// load marker parameters or stop moving if there is no marker
|
|
if (!m_bForceStop) {
|
|
m_bValidMarker = LoadMarkerParameters();
|
|
}
|
|
|
|
// skip this marker / ignore stop moving
|
|
if (m_bSkipMarker) {
|
|
m_bStopMoving = FALSE;
|
|
}
|
|
} while (!m_bStopMoving && m_bValidMarker && !m_bForceStop);
|
|
MovingOff();
|
|
return;
|
|
}
|
|
|
|
TeleportToStopMarker()
|
|
{
|
|
MovingOn();
|
|
|
|
INDEX ctMarkers=0;
|
|
// new moving target
|
|
CMovingBrushMarker *pmbm = (CMovingBrushMarker *) &*m_penTarget;
|
|
while( pmbm!=NULL && IsOfClass(pmbm->m_penTarget, "Moving Brush Marker") && !pmbm->m_bStopMoving && ctMarkers<50)
|
|
{
|
|
pmbm = (CMovingBrushMarker *) &*pmbm->m_penTarget;
|
|
ctMarkers++;
|
|
}
|
|
|
|
if( pmbm!=NULL && IsOfClass(pmbm, "Moving Brush Marker") && ctMarkers<50)
|
|
{
|
|
SetPlacement(pmbm->GetPlacement());
|
|
en_plLastPlacement=pmbm->GetPlacement();
|
|
ForceFullStop();
|
|
m_soStart.Stop();
|
|
m_soStop.Stop();
|
|
m_soFollow.Stop();
|
|
}
|
|
|
|
// stop PreMoving check
|
|
m_bMoveToMarker = FALSE;
|
|
MovingOff();
|
|
return EReturn();
|
|
}
|
|
|
|
Main() {
|
|
// declare yourself as a brush
|
|
InitAsBrush();
|
|
SetPhysicsFlags(EPF_BRUSH_MOVING);
|
|
SetCollisionFlags(ECF_BRUSH);
|
|
SetHealth(m_fHealth);
|
|
|
|
// set zoning flag
|
|
if (m_bZoning) {
|
|
SetFlags(GetFlags()|ENF_ZONING);
|
|
} else {
|
|
SetFlags(GetFlags()&~ENF_ZONING);
|
|
}
|
|
|
|
|
|
// set dynamic shadows as needed
|
|
if (m_bDynamicShadows) {
|
|
SetFlags(GetFlags()|ENF_DYNAMICSHADOWS);
|
|
} else {
|
|
SetFlags(GetFlags()&~ENF_DYNAMICSHADOWS);
|
|
}
|
|
|
|
// stop moving brush
|
|
ForceFullStop();
|
|
|
|
autowait(0.1f);
|
|
|
|
// load marker parameters
|
|
m_bValidMarker = LoadMarkerParameters();
|
|
|
|
/*if (m_tmBankingRotation!=0) {
|
|
jump Rotating();
|
|
}*/
|
|
|
|
// start moving
|
|
wait() {
|
|
on (EBegin) : {
|
|
if (m_bAutoStart) {
|
|
// if not already moving and have target
|
|
MaybeActivateRotation();
|
|
if(!m_bMoving && m_bValidMarker) {
|
|
call MoveBrush();
|
|
}
|
|
}
|
|
resume;
|
|
}
|
|
on (EHit eHit) : {
|
|
if (!m_bMoving) {
|
|
MaybeActivateRotation();
|
|
call MoveBrush();
|
|
}
|
|
resume;
|
|
}
|
|
// move on touch
|
|
on (ETouch eTouch) : {
|
|
// inflict damage if required
|
|
if( m_fTouchDamage != 0.0f)
|
|
{
|
|
InflictDirectDamage( eTouch.penOther, this, DMT_SPIKESTAB, m_fTouchDamage,
|
|
eTouch.penOther->GetPlacement().pl_PositionVector, eTouch.plCollision);
|
|
}
|
|
// send event on touch
|
|
if(m_tdeSendEventOnDamage!=TDE_DAMAGEONLY && CanReactOnEntity(eTouch.penOther)) {
|
|
SendToTarget(m_penTouchEvent, m_eetTouchEvent);
|
|
}
|
|
// if not already moving
|
|
if(!m_bMoving) {
|
|
// move brush
|
|
if (m_bMoveOnTouch && CanReactOnEntity(eTouch.penOther) && m_bValidMarker) {
|
|
MaybeActivateRotation();
|
|
call MoveBrush();
|
|
}
|
|
}
|
|
// if special feature for bull crushing doors
|
|
if (m_bBlowupByBull) {
|
|
// if hit by bull
|
|
if (IsOfClass(eTouch.penOther, "Werebull")) {
|
|
// calculate speed along impact normal
|
|
FLOAT fImpactSpeed =
|
|
((CMovableEntity&)*eTouch.penOther).en_vCurrentTranslationAbsolute%
|
|
-(FLOAT3D&)eTouch.plCollision;
|
|
|
|
// if strong collision
|
|
if (fImpactSpeed>m_fHealth) {
|
|
// receive artificial impact damage
|
|
ReceiveDamage(eTouch.penOther, DMT_IMPACT, m_fHealth*2,
|
|
FLOAT3D(0,0,0), FLOAT3D(0,0,0));
|
|
}
|
|
}
|
|
}
|
|
resume;
|
|
}
|
|
on (EBlock eBlock) : {
|
|
// inflict damage to entity that block brush
|
|
InflictDirectDamage(eBlock.penOther, this, DMT_BRUSH, m_fBlockDamage,
|
|
FLOAT3D(0.0f,0.0f,0.0f), (FLOAT3D &)eBlock.plCollision);
|
|
if (m_ebaAction == BA_BOUNCE) {
|
|
// change direction for two ticks
|
|
SetDesiredTranslation(-m_vDesiredTranslation);
|
|
if (m_bRotating) {
|
|
SetDesiredRotation(-ANGLE3D(0.0f, 0.0f, 360.0f/m_tmBankingRotation));
|
|
} else if (!m_tmBankingRotation) {
|
|
SetDesiredRotation(-m_aDesiredRotation);
|
|
}
|
|
|
|
// wait for two ticks and reset direction
|
|
call BounceObstructed();
|
|
}
|
|
resume;
|
|
}
|
|
// move on start (usually trigger)
|
|
on (EStart) : {
|
|
// if not already moving and have target
|
|
if(!m_bMoving && m_bValidMarker) {
|
|
call MoveBrush();
|
|
}
|
|
resume;
|
|
}
|
|
on (EStop) : {
|
|
//SetCollisionFlags(ECF_IMMATERIAL);
|
|
resume;
|
|
}
|
|
on (ETeleportMovingBrush) : {
|
|
call TeleportToStopMarker();
|
|
resume;
|
|
}
|
|
on (ETrigger) : {
|
|
// if not already moving and have target
|
|
if(!m_bMoving && m_bValidMarker) {
|
|
call MoveBrush();
|
|
}
|
|
resume;
|
|
}
|
|
on (EActivate) : {
|
|
if (!m_bRotating) {
|
|
MaybeActivateRotation();
|
|
}
|
|
resume;
|
|
}
|
|
on (EDeactivate) : {
|
|
DeactivateRotation();
|
|
resume;
|
|
}
|
|
on (EDeath eDeath) : {
|
|
// get your size
|
|
FLOATaabbox3D box;
|
|
GetSize(box);
|
|
if( m_ctDebrises>0)
|
|
{
|
|
FLOAT fEntitySize = pow(box.Size()(1)*box.Size()(2)*box.Size()(3)/m_ctDebrises, 1.0f/3.0f)*m_fCubeFactor;
|
|
|
|
Debris_Begin(EIBT_ROCK, DPT_NONE, BET_NONE, fEntitySize, FLOAT3D(1.0f,2.0f,3.0f),
|
|
FLOAT3D(0,0,0), 1.0f+m_fCandyEffect/2.0f, m_fCandyEffect, m_colDebrises);
|
|
for(INDEX iDebris = 0; iDebris<m_ctDebrises; iDebris++) {
|
|
Debris_Spawn(this, this, MODEL_STONE, TEXTURE_STONE, 0, 0, 0, IRnd()%4, 1.0f,
|
|
FLOAT3D(FRnd()*0.8f+0.1f, FRnd()*0.8f+0.1f, FRnd()*0.8f+0.1f));
|
|
}
|
|
}
|
|
|
|
// notify children
|
|
FOREACHINLIST( CEntity, en_lnInParent, en_lhChildren, iten) {
|
|
iten->SendEvent( EBrushDestroyed());
|
|
}
|
|
// send event to blowup target
|
|
SendToTarget(m_penBlowupEvent, m_eetBlowupEvent, eDeath.eLastDamage.penInflictor);
|
|
|
|
// make sure it doesn't loop with destroying itself
|
|
m_tdeSendEventOnDamage = TDE_TOUCHONLY;
|
|
m_fHealth = -1;
|
|
m_bMoveOnDamage = FALSE;
|
|
ForceFullStop();
|
|
SetDefaultProperties();
|
|
|
|
// notify engine to kickstart entities that are cached in stationary position,
|
|
// before we turn off, so they can fall
|
|
NotifyCollisionChanged();
|
|
|
|
SetFlags( GetFlags()|ENF_HIDDEN);
|
|
SetCollisionFlags(ECF_IMMATERIAL);
|
|
|
|
// for each child of this entity
|
|
{FOREACHINLIST(CEntity, en_lnInParent, en_lhChildren, itenChild) {
|
|
// send it destruction event
|
|
itenChild->SendEvent(ERangeModelDestruction());
|
|
}}
|
|
|
|
stop;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
};
|