diff --git a/CHANGELOG.md b/CHANGELOG.md index 1211e3b42..26392bba4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,122 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Executable can be compiled as 64bit. +- New INI and Lua (R/W) properties for Attachables: + `TransfersDamageToParent = 0/1`. If enabled, the Attachable will act like hardcoded ones and transfer damage to its parent. For `Attachables` attached to other `Attachables`, the parent `Attachable` (and any of its parents, etc.) must have this enabled for it to affect the root parent. + `ParentBreakWound = AEmitter...`. Use this to define a BreakWound that will be applied to the `Attachable's` parent when the `Attachable` is removed. + `BreakWound` is also now R/W accessible to Lua. + +- Added Lua (R/W) properties for all hardcoded Attachables. You can now set them on the fly to be created objects of the relevant type. Note that trying to set things inappropriately (e.g. setting an HDFirearm as something's Leg) will probably crash the game; that's your problem to deal with. + You can read and write the following properties: + AHuman - `Head, Jetpack, FGArm, BGArm, FGLeg, BGLeg, FGFoot, BGFoot` + ACrab - `Turret, Jetpack, LeftFGLeg, LeftBGLeg, RightFGLeg, RightBGLeg` + ACDropship - `RightEngine, LeftEngine, RightThruster, LeftThruster, RightHatch, LeftHatch` + ACRocket - `RightLeg, LeftLeg, MainEngine, LeftEngine, RightEngine, LeftThruster, RightThruster` + ADoor - `Door` + Turret - `MountedDevice` + Leg - `Foot` + HDFirearm - `Magazine, Flash` + AEmitter - `Flash` + +- Added `Vector:ClampMagnitude(upperLimit, lowerLimit)` Lua function that lets you limit a Vector's upper and lower magnitude. + +- Added `MOSRotating GibBlastStrength` INI and Lua (R/W) property. This lets you define how much force created `Gibs` and any `Attachables` will get launched when the MOSRotating gibs. + +- New INI and Lua (R/W) properties for Attachables: + `ParentBreakWound = AEmitter...` allows you to optionally define different BreakWounds for the `Attachable` and its parent. By default it matches `BreakWound` for ease of use. + `InheritsHFlipped = -1/0/1` allows you to define whether the `Attachable` will inherit its parent's HFlipped value or not. -1 means reversed inheritance (i.e. if the parent's HFlipped value is true, this Attachable's HFlipped value will be false), 0 means no inheritance, 1 means normal inheritance. Defaults to 1 to preserve normal behaviour. + `InheritedRotAngleRadOffset = angle` and `InheritedRotAngleDegOffset = angle` allow you specify an offset to keep an `Attachable's` rotation at when `InheritsRotAngle` is set to true. In Lua there's only `InheritedRotAngleOffset` which takes/returns radians, to avoid confusion. For example, `InheritedRotAngleDegOffset = 90` would make the Attachable always face perpendicular to its parent. Does nothing if the `Attachable's` `InheritsRotAngle` is set to false or the `Attachable` has no parent. + `GibWithParentChance = 0 - 1` allows you to specify whether this `Attachable` should be gibbed when its parent does and what the chance of that happening is. 0 means never, 1 means always. + `ParentGibBlastStrengthMultiplier = number` allows you to specify the multiplier this `Attachable` will apply to its parent's gib blast strength when the parent gibs. Usually this would be a positive number, but it doesn't have to be. + +- New INI and Lua (R/W) `Arm` property `GripStrength`. This effectively replaces the `JointStrength` of the held `HeldDevice`, allowing `Arms` to control how tightly equipment is held. + +- New INI and Lua (R/W) `HeldDevice` property `GripStrengthMultiplier`. This allows `HeldDevices` to multiply the `GripStrength` of their `Arms` to support them being more or less easy to hold. + +- New Lua `MovableObject` function `GetWhichMOToNotHit`. This provides access to the MO that has been set to not be hit by `SetWhichMOToNotHit`. + +- Added `HeldDevice` handling to limit which `Actor(s)` can pick it up. Note that pickup limitations are all done by PresetName, so you can not use this to precisely specify individual `Actors`. + The INI definition looks like this: + ``` + PickupableBy = PickupableByEntries + AddPresetNameEntry = First Actor PresetName Here + AddPresetNameEntry = Second Actor PresetName Here + //Alternatively, if you want this not to be pickupable + PickupableBy = None + ``` + The Lua properties and functions are as follows: + ``` + heldDevice.HasPickupLimitations; --(R) Whether or not this HeldDevice has any limitations affecting whether it can be picked up. + heldDevice.UnPickupable --(R/W) Whether this HeldDevice is/should be pickupable. + heldDevice:IsPickupableBy(actor) -- Whether or not a given Actor can pick up this HeldDevice. + heldDevice:AddPickupableByPresetName(presetName) -- Allows Actors with the given PresetName to pick up this HeldDevice. + heldDevice:RemovePickupableByPresetName(presetName) -- Disallows Actors with the given PresetNames from picking up this HeldDevice (as long as there are other pickup limitations). + ``` + +- Added `MOSRotating` Lua (R) property `IndividualMass`. This provides access to the `MOSRotating's` actual mass value, not including any `Attachables` or inventory items. Note that the normal `Mass` property is still used to set the `MOSRotating's` mass. + +- Added `Actor` Lua (R) property `InventoryMass`. This provides access to the mass of the `Actor's` inventory separate from the `Actor's` actual mass. + +- Added `LimbPath` INI property `EndSegCount`, which allows you to specify a segment after which the owning `Actor's` foot will not collide with terrain. This lets you add extra visual-only frames to your `LimbPaths`. + +- Added `AHuman` INI property `CrouchLimbPathBG` to allow you to specify a different `LimbPath` for the background leg while crouching. + +- Added `AHuman` INI properties `StandRotAngleTarget`, `WalkRotAngleTarget`, `CrouchRotAngleTarget` and `JumpRotAngleTarget` that let you define the rot angle the body should aim towards when in the corresponding `MovementState`. + +- Added `AHuman` Lua methods `GetRotAngleTarget(movementState)` and `SetRotAngleTarget(movementState, newRotAngleTarget)` that allow you to get and set rot angle targets for `MovementStates`. Note that only the `MovementStates` mentioned above will actually work. + +- `LimbPaths` are now Lua accessible for `ACrabs` and `AHumans`. You can use `GetLimbPath(Layer, MovementState)` for `AHumans` and `GetLimbPath(Side, Layer, MovementState)` for `ACrabs`. + `LimbPaths` have the following properties: + `limbPath.StartOffset` (R/W) - the start offset for the `LimbPath`. Also defines its position if it has no actual path. + `limbPath.SegmentCount` (R) - the number of segments in the `LimbPath`. + `limbPath:GetSegment(segmentIndex)` - Gets the segment Vector for the given segment index. You can use this to modify `LimbPaths`. + ### Changed +- Hands will now draw in transparent drawing mode, i.e. editing menu. + +- `AHuman` background `Leg` will no longer draw in front of the `AHuman`. The real result of this is that the background foot will no longer draw in front of the foreground one. + +- Everything draws better when flashing white, including craft which used to be terrible at it. + +- Reworked Attachable managment + `DamageMultiplier` on `Attachables` now works as expected, all `Attachables` can now transfer damage to their root parent. This will travel up chains of `Attachables`, as long as every `Attachable` in the chain has a non-zero DamageMultiplier (yes, negative numbers are supported if you wanna have healing instead of damage or weirdness with chaining negative multipliers). Note that the default `DamageMultiplier` for `Attachables` is 0, so you have to set it if you want it. Also note that wounds will default this value to 1 instead of 0. + `Attachable` terrain collision has been reworked so that it can be changed simply by setting `CollidesWithTerrainWhileAttached = true/false` in INI or Lua. Also, `Attachables` attached to other `Attachables` will now collide with terrain properly. + `BreakWounds` on `Attachables` now gets added to both the `Attachable` and the parent when the `Attachable` is broken off. If `ParentBreakWound` is defined, the parent will use this instead of the regular `BreakWound`. + +- `Attachable.BreakWound` now has R/W access in Lua. + +- `Attachable.DeleteWithParent` is now `Attachable.DeleteWhenRemovedFromParent`, since this more accurately describes what it does. + +- `Attachable.OnlyLinearForces` has been renamed to `Attachable.ApplyTransferredForcesAtOffset` and its effect has been reversed, so something that checked `OnlyLinearForces == true` would now check `ApplyTransferredForcesAtOffset == false`, since this makes more sense to use. + +- `Arms` and `Legs` on `AHumans` will no longer bleed out indefinitely. If you want this to happen, adjust their `BreakWound` or `ParentBreakWound` accordingly. + +- Reworked wound management + Wound management is now always done with `MOSRotating` functions, instead of requiring different ones for `Actors`. This means TotalWoundCount and RemoveAnyRandomWounds no longer exist. + You can get all wounds with `GetWounds`, get the wound count with `GetWoundCount` (or using the pre-existing WoundCount property), get the gib wound limit with `GetGibWoundLimit` (or using the pre-existing GibWoundLimit property), and remove wounds with `RemoveWounds`. + All of these functions have two variants, one lets you just specify any normal arguments (e.g. number of wounds to remove), the other lets you also specify whether you want to include `Attachables` with a positive `DamageMultiplier` (i.e. `Attachables` that damage their parent), `Attachables` with a negative `DamageMultiplier` (i.e. `Attachables` that heal their parent) or `Attachables` with no `DamageMultiplier` (i.e. `Attachables` that don't affect their parent). + Without any arguments, `GetWoundCount` and `RemoveWounds` will only include `Attachables` with a positive `DamageMultiplier` in their counting calculations, and `GetGibWoundLimit` will not include any `Attachables` in its counting calculations. The property variants (e.g. mosr.WoundCount) behave the same way as the no-argument versions. + Note that this process is recursive, so if an `Attachable` that satisfies the conditions has `Attachables` that also satisfy the conditions, their wounds will be included in the results. + +- Renamed `Turret` INI property `MountedMO` to `MountedDevice` to better match the new reality that `Turrets` can only mount `HeldDevices` and their child classes. + +- Renamed `ACrab` `LFGLeg`, `LBGLeg`, `RFGLeg` and `RBGLeg` Lua properties to `LeftFGLeg`, `LeftBGLeg`, `RightFGLeg`, `RightBGLeg` respectively, to be more consistent with other naming. + For the time being, the INI properties (as well as the ones for setting `FootGroups` and `LimbPaths`) support both single letter and written out versions (i.e. `LStandLimbPath` and `LeftStandLimbPath` are both supported). This single letter versions will probably be deprecated over time. + +- `MovableMan:AddMO` will now add `HeldDevices` (or any child class of `HeldDevice`) to its `Items` collection, making it able to provide the functionality of `AddParticle`, `AddActor` and `AddItem`. + +- Changed and cleaned up how gibbing works and how it affects `Attachables`. In particular, limbs will better inherit velocity during gibbing and things are more customizable. See `Attachable` properties for more details. + As an added bonus, `Attachables` on `ACDropShips` and `ACRockets` can now be shot down when the craft gibs; fight back against the baleful dropship engines! + +- Improved native recoil handling! Guns transfer recoil to arms/turrets, which transfer it to AHumans/ACrabs, all of it properly accounts for joint strengths (or grip strengths) and offsets at every step. ([Issue #7](https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/issues/7) and [Issue #8](https://github.com/cortex-command-community/Cortex-Command-Community-Project-Source/issues/8)). + +- `Attachables` now use their `GibImpulseLimit` as well as their `JointStrength` when determining whether they should be detached by strong forces. To maintain backwards compatibility, if the `GibImpulseLimit` is less than the `JointStrength`, the `JointStrength` will be used instead for this purpose. + +- The `FacingAngle` function has been moved from `Actor` to `MOSprite` so it can be used more widely. + +- Lifetime and ToDelete now work on wounds, giving modders more control over them. + - Some functionality has been moved from `AudioMan` to `SoundContainer` for consistency. As such, the following `AudioMan` Lua bindings have been replaced: `AudioMan:FadeOutSound(fadeOutTime);` has been replaced with `soundContainer:FadeOut(fadeOutTime);` `AudioMan:StopSound(soundContainer);` and `AudioMan:StopSound(soundContainer, player);` have been replaced with `soundContainer:Stop();` and `soundContainer:Stop(player);` @@ -20,6 +134,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed +- `MovableObject:SetWhichMOToNotHit` will now work properly for Attachables. They will also not hit the relevant MO. When they're removed, Attachables will check if they have the same MO for this value and, if so, unset it so they can hit that MO. + +- Craft sucking up objects now works properly again. + +- Getting the `Mass` of a `MOSRotating` has now been made more efficient. Additionally, `Attachables` of `Attachables` will now be included in Mass, so some things have gotten a lot heavier (e.g. Dummy Dreadnought). + +- The moment of inertia of `AtomGroups` now updates when the mass or Atoms change, meaning losing `Attachables` or changing mass will properly affect how rotational forces apply to MOSRotatings. + +- `WoundDamageMultipliers` on projectiles will now properly stack with wounds' `DamageMultiplier`. Prior to this, if you set the `DamageMultiplier` of a wound on some object, it'd be overwritten by the hitting projectile's `WoundDamageMultiplier`. Now they multiply together properly. + +- `Radius` and `Diameter` now account for `Attachables` on objects that can have them. If you want just the `Radius` or `Diameter` of the object, use `IndividualRadius` and `IndividualDiameter` (only available for `MOSRotating` and subclasses). This means that `Radius` and `Diameter` will now give you a good estimation of an object's total size. + - Fixed various audio bugs that were in Pre3, and fixed clicking noise on sounds that played far away. The game should sound way better now! - Mobile sounds (i.e. generally things that aren't GUI related) will now pause and resume when you pause and resume your activity. @@ -30,6 +156,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Removed +- Removed `Attachable` Lua write capability for `AtomSubGroupID` as changing this can cause all kinds of problems, and `RotTarget` as this didn't actually work. + +- Removed `MaxLength` property from `Leg`, since it was a mostly unused leftover caused by Leg being originally copied from Arm, and was actually a fake setting that just set other properties. To replace it, set the following: + ``` + ContractedOffset = Vector + X = //Old MaxLength/2 + Y = 0 + ExtendedOffset = Vector + X = //Old MaxLength + Y = 0 + ``` + +- Removed `Attachable.RotTarget` from Lua and INI. The property never worked and no longer exists. + +- Removed `Attachable:CollectDamage`, `Attachable:TransferJointForces` and `Attachable:TransferJointImpulses` Lua function definitions. These are internal functions that should never have been exposed to Lua. + +- Removed `MOSRotating:ApplyForces` and `MOSRotating:ApplyImpulses` Lua functions. These are both internal functions that should never have been exposed to Lua. + *** ## [0.1.0 pre-release 3.0][0.1.0-pre3.0] - 2020/12/25 @@ -301,6 +445,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - `AddSound` and `SelectNextSoundSet` Lua bindings have been moved from `SoundContainer` to `SoundSet`. The latter has been renamed and the former have been trimmed down slightly since some complexity is no longer needed. Their speciifcs are mentioned in the `Added` section. +- Pressing escape at the options, mod manager, game editors and credits screens no longer quits the game. + ### Fixed - Fix crash when returning to `MetaGame` scenario screen after activity end. @@ -323,8 +469,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - The "woosh" sound played when switching actors from a distance will now take scene wrapping into account. Additionally, attempting to switch to previous or next actor with only one actor will play the more correct "error" sound. -- Pressing escape at the options, mod manager, game editors and credits screens no longer quits the game. - - `HDFirearm` INI property `DeactivationSound` now works properly instead of constantly playing. - Gold mining sound has been set to restart its playback everytime it's played, making it way less annoying. It's still pretty wonky, but it's better. diff --git a/Entities/ACDropShip.cpp b/Entities/ACDropShip.cpp index 5c8b2b4eb..305d0800b 100644 --- a/Entities/ACDropShip.cpp +++ b/Entities/ACDropShip.cpp @@ -37,7 +37,6 @@ void ACDropShip::Clear() m_pULThruster = 0; m_pRHatch = 0; m_pLHatch = 0; -// TODO: don't hardcode m_HatchSwingRange.SetDegAngle(90); m_HatchOpeness = 0; m_LateralControl = 0; @@ -71,47 +70,35 @@ int ACDropShip::Create() ////////////////////////////////////////////////////////////////////////////////////////// // Description: Creates a ACDropShip to be identical to another, by deep copy. -int ACDropShip::Create(const ACDropShip &reference) -{ - ACraft::Create(reference); - - m_pBodyAG = dynamic_cast(reference.m_pBodyAG->Clone()); - m_pBodyAG->SetOwner(this); - - if (reference.m_pRThruster) - { - m_pRThruster = dynamic_cast(reference.m_pRThruster->Clone()); - m_pRThruster->SetCanCollideWithTerrainWhenAttached(true); - AddAttachable(m_pRThruster, true); +int ACDropShip::Create(const ACDropShip &reference) { + if (reference.m_pRThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRThruster->GetUniqueID()); + SetRightThruster(dynamic_cast(reference.m_pRThruster->Clone())); } - if (reference.m_pLThruster) - { - m_pLThruster = dynamic_cast(reference.m_pLThruster->Clone()); - m_pLThruster->SetCanCollideWithTerrainWhenAttached(true); - AddAttachable(m_pLThruster, true); + if (reference.m_pLThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLThruster->GetUniqueID()); + SetLeftThruster(dynamic_cast(reference.m_pLThruster->Clone())); } - if (reference.m_pURThruster) - { - m_pURThruster = dynamic_cast(reference.m_pURThruster->Clone()); - AddAttachable(m_pURThruster, true); + if (reference.m_pURThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pURThruster->GetUniqueID()); + SetURightThruster(dynamic_cast(reference.m_pURThruster->Clone())); } - if (reference.m_pULThruster) - { - m_pULThruster = dynamic_cast(reference.m_pULThruster->Clone()); - AddAttachable(m_pULThruster, true); + if (reference.m_pULThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pULThruster->GetUniqueID()); + SetULeftThruster(dynamic_cast(reference.m_pULThruster->Clone())); } - if (reference.m_pRHatch) - { - m_pRHatch = dynamic_cast(reference.m_pRHatch->Clone()); - m_pRHatch->SetCanCollideWithTerrainWhenAttached(true); - AddAttachable(m_pRHatch, true); + if (reference.m_pRHatch) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRHatch->GetUniqueID()); + SetRightHatch(dynamic_cast(reference.m_pRHatch->Clone())); } - if (reference.m_pLHatch) - { - m_pLHatch = dynamic_cast(reference.m_pLHatch->Clone()); - m_pLHatch->SetCanCollideWithTerrainWhenAttached(true); - AddAttachable(m_pLHatch, true); + if (reference.m_pLHatch) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLHatch->GetUniqueID()); + SetLeftHatch(dynamic_cast(reference.m_pLHatch->Clone())); } + ACraft::Create(reference); + + m_pBodyAG = dynamic_cast(reference.m_pBodyAG->Clone()); + m_pBodyAG->SetOwner(this); m_HatchSwingRange = reference.m_HatchSwingRange; m_HatchOpeness = reference.m_HatchOpeness; @@ -134,56 +121,60 @@ int ACDropShip::Create(const ACDropShip &reference) // is called. If the property isn't recognized by any of the base classes, // false is returned, and the reader's position is untouched. -int ACDropShip::ReadProperty(std::string propName, Reader &reader) -{ - if (propName == "RThruster") - { - delete m_pRThruster; +int ACDropShip::ReadProperty(std::string propName, Reader &reader) { + if (propName == "RThruster") { + RemoveAttachable(m_pRThruster); m_pRThruster = new AEmitter; reader >> m_pRThruster; - } - else if (propName == "LThruster") - { - delete m_pLThruster; + AddAttachable(m_pRThruster); + if (m_pRThruster->HasNoSetDamageMultiplier()) { m_pRThruster->SetDamageMultiplier(1.0F); } + m_pRThruster->SetInheritsRotAngle(false); + } else if (propName == "LThruster") { + RemoveAttachable(m_pLThruster); m_pLThruster = new AEmitter; reader >> m_pLThruster; - } - else if (propName == "URThruster") - { - delete m_pURThruster; + AddAttachable(m_pLThruster); + if (m_pLThruster->HasNoSetDamageMultiplier()) { m_pLThruster->SetDamageMultiplier(1.0F); } + m_pLThruster->SetInheritsRotAngle(false); + } else if (propName == "URThruster") { + RemoveAttachable(m_pURThruster); m_pURThruster = new AEmitter; reader >> m_pURThruster; - } - else if (propName == "ULThruster") - { - delete m_pULThruster; + AddAttachable(m_pURThruster); + if (m_pURThruster->HasNoSetDamageMultiplier()) { m_pURThruster->SetDamageMultiplier(1.0F); } + } else if (propName == "ULThruster") { + RemoveAttachable(m_pULThruster); m_pULThruster = new AEmitter; reader >> m_pULThruster; - } - else if (propName == "RHatchDoor") - { - delete m_pRHatch; + AddAttachable(m_pULThruster); + if (m_pULThruster->HasNoSetDamageMultiplier()) { m_pULThruster->SetDamageMultiplier(1.0F); } + } else if (propName == "RHatchDoor") { + RemoveAttachable(m_pRHatch); m_pRHatch = new Attachable; reader >> m_pRHatch; - } - else if (propName == "LHatchDoor") - { - delete m_pLHatch; + AddAttachable(m_pRHatch); + if (m_pRHatch->HasNoSetDamageMultiplier()) { m_pRHatch->SetDamageMultiplier(1.0F); } + m_pRHatch->SetInheritsRotAngle(false); + } else if (propName == "LHatchDoor") { + RemoveAttachable(m_pLHatch); m_pLHatch = new Attachable; reader >> m_pLHatch; - } - else if (propName == "HatchDoorSwingRange") + AddAttachable(m_pLHatch); + if (m_pLHatch->HasNoSetDamageMultiplier()) { m_pLHatch->SetDamageMultiplier(1.0F); } + m_pLHatch->SetInheritsRotAngle(false); + } else if (propName == "HatchDoorSwingRange") { reader >> m_HatchSwingRange; - else if (propName == "AutoStabilize") + } else if (propName == "AutoStabilize") { reader >> m_AutoStabilize; - else if (propName == "ScuttleIfFlippedTime") + } else if (propName == "ScuttleIfFlippedTime") { reader >> m_ScuttleIfFlippedTime; - else if (propName == "MaxEngineAngle") - reader >> m_MaxEngineAngle; - else if (propName == "LateralControlSpeed") - reader >> m_LateralControlSpeed; - else + } else if (propName == "MaxEngineAngle") { + reader >> m_MaxEngineAngle; + } else if (propName == "LateralControlSpeed") { + reader >> m_LateralControlSpeed; + } else { return ACraft::ReadProperty(propName, reader); + } return 0; } @@ -234,12 +225,6 @@ int ACDropShip::Save(Writer &writer) const void ACDropShip::Destroy(bool notInherited) { delete m_pBodyAG; - delete m_pRThruster; - delete m_pLThruster; - delete m_pURThruster; - delete m_pULThruster; - delete m_pRHatch; - delete m_pLHatch; if (!notInherited) ACraft::Destroy(); @@ -247,29 +232,6 @@ void ACDropShip::Destroy(bool notInherited) } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this ACDropShip, including the mass of its -// currently attached body parts and inventory. - -float ACDropShip::GetMass() const -{ - float totalMass = ACraft::GetMass(); - - if (m_pRThruster) - totalMass += m_pRThruster->GetMass(); - if (m_pLThruster) - totalMass += m_pLThruster->GetMass(); - if (m_pRHatch) - totalMass += m_pRHatch->GetMass(); - if (m_pLHatch) - totalMass += m_pLHatch->GetMass(); - - return totalMass; -} - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: GetAltitude ////////////////////////////////////////////////////////////////////////////////////////// @@ -355,25 +317,6 @@ MOID ACDropShip::DetectObstacle(float distance) return false; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. - -void ACDropShip::SetID(const MOID newID) -{ - MovableObject::SetID(newID); - if (m_pRThruster) - m_pRThruster->SetID(newID); - if (m_pLThruster) - m_pLThruster->SetID(newID); - if (m_pRHatch) - m_pRHatch->SetID(newID); - if (m_pLHatch) - m_pLHatch->SetID(newID); -} - /* ////////////////////////////////////////////////////////////////////////////////////////// // Method: OnBounce @@ -401,84 +344,6 @@ bool ACDropShip::OnSink(const Vector &pos) } */ -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. - -void ACDropShip::GibThis(Vector impactImpulse, float internalBlast, MovableObject *pIgnoreMO) -{ - // TODO: maybe make hardcoded attachables gib if their gib list isn't empty - // Detach all limbs and let loose - if (m_pRThruster && m_pRThruster->IsAttached()) - { - RemoveAttachable(m_pRThruster); - SetAttachableVelocitiesForGibbing(m_pRThruster, impactImpulse, internalBlast); - m_pRThruster->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(m_pRThruster); - m_pRThruster = 0; - } - if (m_pLThruster && m_pLThruster->IsAttached()) - { - RemoveAttachable(m_pLThruster); - SetAttachableVelocitiesForGibbing(m_pLThruster, impactImpulse, internalBlast); - m_pLThruster->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(m_pLThruster); - m_pLThruster = 0; - } - if (m_pRHatch && m_pRHatch->IsAttached()) - { - RemoveAttachable(m_pRHatch); - SetAttachableVelocitiesForGibbing(m_pRHatch, impactImpulse, internalBlast); - m_pRHatch->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(m_pRHatch); - m_pRHatch = 0; - } - if (m_pLHatch && m_pLHatch->IsAttached()) - { - RemoveAttachable(m_pLHatch); - SetAttachableVelocitiesForGibbing(m_pLHatch, impactImpulse, internalBlast); - m_pLHatch->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(m_pLHatch); - m_pLHatch = 0; - } - if (m_pURThruster && m_pURThruster->IsAttached()) - { - RemoveAttachable(m_pURThruster); - SetAttachableVelocitiesForGibbing(m_pURThruster, impactImpulse, internalBlast); - m_pURThruster->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(m_pURThruster); - m_pURThruster = 0; - } - if (m_pULThruster && m_pULThruster->IsAttached()) - { - RemoveAttachable(m_pULThruster); - SetAttachableVelocitiesForGibbing(m_pULThruster, impactImpulse, internalBlast); - m_pULThruster->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(m_pULThruster); - m_pULThruster = 0; - } - - Actor::GibThis(impactImpulse, internalBlast, pIgnoreMO); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. - -bool ACDropShip::IsOnScenePoint(Vector &scenePoint) const -{ - return ((m_pLThruster && m_pLThruster->IsOnScenePoint(scenePoint)) || - (m_pRThruster && m_pRThruster->IsOnScenePoint(scenePoint)) || - MOSRotating::IsOnScenePoint(scenePoint) || - (m_pLHatch && m_pLHatch->IsOnScenePoint(scenePoint)) || - (m_pRHatch && m_pRHatch->IsOnScenePoint(scenePoint))); -} - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: UpdateAI @@ -514,6 +379,7 @@ void ACDropShip::UpdateAI() // Check for something in the way of our descent, and hover to the side to avoid it MOID detected = g_NoMOID; + //TODO since GetRadius includes attachables, this could probably be lessened. Maybe could just use GetDiameter directly. if ((detected = DetectObstacle(GetRadius() * 4)) != g_NoMOID) { // Only check other craft in the way @@ -579,6 +445,7 @@ void ACDropShip::UpdateAI() // Check for something in the way of our ascent, and hover to the side to avoid it MOID detected = g_NoMOID; + //TODO since GetRadius includes attachables, this could probably be lessened. Maybe could just use GetDiameter directly. if ((detected = DetectObstacle(GetRadius() * 4)) != g_NoMOID) { // Only check other craft in the way @@ -629,8 +496,6 @@ void ACDropShip::UpdateAI() void ACDropShip::Update() { - float mass = GetMass(); - ///////////////////////////////// // Controller update and handling @@ -786,140 +651,71 @@ void ACDropShip::Update() } } - ///////////////////////////////////////////////// - // Update MovableObject, adds on the forces etc, updated viewpoint - - ACraft::Update(); - - - ///////////////////////////////// - // Update Attachable:s - -// TODO: don't hardcode the 20 deg tilt range! - Matrix engineRot = 0; - // Clamp engine rotation to within +-20 of body rotation - if (m_Rotation.GetDegAngle() > m_MaxEngineAngle) - engineRot.SetDegAngle(m_Rotation.GetDegAngle() - m_MaxEngineAngle); - else if (m_Rotation.GetDegAngle() < -m_MaxEngineAngle) - engineRot.SetDegAngle(m_Rotation.GetDegAngle() + m_MaxEngineAngle); - // Lateral control application - else - engineRot.SetDegAngle(m_MaxEngineAngle * m_LateralControl); + ///////////////////////////////// + // Manage Attachable:s + Matrix engineRot = 0; + if (m_Rotation.GetDegAngle() > m_MaxEngineAngle) { + engineRot.SetDegAngle(m_Rotation.GetDegAngle() - m_MaxEngineAngle); + } else if (m_Rotation.GetDegAngle() < -m_MaxEngineAngle) { + engineRot.SetDegAngle(m_Rotation.GetDegAngle() + m_MaxEngineAngle); + } else { + // Lateral control application + engineRot.SetDegAngle(m_MaxEngineAngle * m_LateralControl); + } - if (m_pRThruster && m_pRThruster->IsAttached()) - { + if (m_pRThruster && m_pRThruster->IsAttached()) { m_pRThruster->SetRotAngle(engineRot.GetRadAngle()); - m_pRThruster->SetJointPos(m_Pos + m_pRThruster->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - m_pRThruster->Update(); - - // Update the Atoms' offsets in the parent group - Matrix atomRot(FacingAngle(m_pRThruster->GetRotMatrix().GetRadAngle()) - FacingAngle(m_Rotation.GetRadAngle())); - m_pAtomGroup->UpdateSubAtoms(m_pRThruster->GetAtomSubgroupID(), m_pRThruster->GetParentOffset() - (m_pRThruster->GetJointOffset() * atomRot), atomRot); - - m_Health -= m_pRThruster->CollectDamage(); + m_pRThruster->SetAngularVel(0.0F); } - if (m_pLThruster && m_pLThruster->IsAttached()) - { + if (m_pLThruster && m_pLThruster->IsAttached()) { m_pLThruster->SetRotAngle(engineRot.GetRadAngle()); - m_pLThruster->SetJointPos(m_Pos + m_pLThruster->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - m_pLThruster->Update(); - - // Update the Atoms' offsets in the parent group - Matrix atomRot(FacingAngle(m_pLThruster->GetRotMatrix().GetRadAngle()) - FacingAngle(m_Rotation.GetRadAngle())); - m_pAtomGroup->UpdateSubAtoms(m_pLThruster->GetAtomSubgroupID(), m_pLThruster->GetParentOffset() - (m_pLThruster->GetJointOffset() * atomRot), atomRot); - - m_Health -= m_pLThruster->CollectDamage(); + m_pLThruster->SetAngularVel(0.0F); } // Auto balancing with the up thrusters - - if (m_pURThruster && m_pURThruster->IsAttached() && m_pULThruster && m_pULThruster->IsAttached()) - { - if (m_AutoStabilize) - { + if (m_pURThruster && m_pURThruster->IsAttached() && m_pULThruster && m_pULThruster->IsAttached()) { + if (m_AutoStabilize) { // Use a PD-controller for balance - float change = 0.9 * m_AngularVel + 0.8 * m_Rotation.GetRadAngle(); - if (change > 0.2) - { - if (!m_pURThruster->IsEmitting()) + float change = 0.9F * m_AngularVel + 0.8F * m_Rotation.GetRadAngle(); + if (change > 0.2F) { + if (!m_pURThruster->IsEmitting()) { m_pURThruster->TriggerBurst(); + } m_pURThruster->EnableEmission(true); - } - else + } else { m_pURThruster->EnableEmission(false); + } - if (change < -0.2) - { - if (!m_pULThruster->IsEmitting()) + if (change < -0.2F) { + if (!m_pULThruster->IsEmitting()) { m_pULThruster->TriggerBurst(); + } m_pULThruster->EnableEmission(true); - } - else + } else { m_pULThruster->EnableEmission(false); + } } - - m_pURThruster->SetRotAngle(m_Rotation.GetRadAngle()); - m_pURThruster->SetJointPos(m_Pos + m_pURThruster->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - m_pURThruster->Update(); - - m_pULThruster->SetRotAngle(m_Rotation.GetRadAngle()); - m_pULThruster->SetJointPos(m_Pos + m_pULThruster->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - m_pULThruster->Update(); } // Hatch door pieces - - if (m_pRHatch && m_pRHatch->IsAttached()) - { + if (m_pRHatch && m_pRHatch->IsAttached()) { m_pRHatch->SetRotAngle(m_Rotation.GetRadAngle() + m_HatchSwingRange.GetRadAngle() * m_HatchOpeness); - m_pRHatch->SetJointPos(m_Pos + m_pRHatch->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - m_pRHatch->Update(); - - // Update the Atoms' offsets in the parent group - Matrix atomRot(FacingAngle(m_pRHatch->GetRotMatrix().GetRadAngle()) - FacingAngle(m_Rotation.GetRadAngle())); - m_pAtomGroup->UpdateSubAtoms(m_pRHatch->GetAtomSubgroupID(), m_pRHatch->GetParentOffset() - (m_pRHatch->GetJointOffset() * atomRot), atomRot); - - m_Health -= m_pRHatch->CollectDamage(); } - if (m_pLHatch && m_pLHatch->IsAttached()) - { + if (m_pLHatch && m_pLHatch->IsAttached()) { m_pLHatch->SetRotAngle(m_Rotation.GetRadAngle() - m_HatchSwingRange.GetRadAngle() * m_HatchOpeness); - m_pLHatch->SetJointPos(m_Pos + m_pLHatch->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - m_pLHatch->Update(); - - // Update the Atoms' offsets in the parent group - Matrix atomRot(FacingAngle(m_pLHatch->GetRotMatrix().GetRadAngle()) - FacingAngle(m_Rotation.GetRadAngle())); - m_pAtomGroup->UpdateSubAtoms(m_pLHatch->GetAtomSubgroupID(), m_pLHatch->GetParentOffset() - (m_pLHatch->GetJointOffset() * atomRot), atomRot); - - m_Health -= m_pLHatch->CollectDamage(); } - /////////////////////////////////////////////////////////// - // Apply forces transferred from the attachables and - // add detachment wounds to this if applicable - - if (!ApplyAttachableForces(m_pRHatch)) - m_pRHatch = 0; - if (!ApplyAttachableForces(m_pLHatch)) - m_pLHatch = 0; - if (!ApplyAttachableForces(m_pRThruster)) - m_pRThruster = 0; - if (!ApplyAttachableForces(m_pLThruster)) - m_pLThruster = 0; - if (!ApplyAttachableForces(m_pURThruster)) - m_pURThruster = 0; - if (!ApplyAttachableForces(m_pULThruster)) - m_pULThruster = 0; - -// TODO: add hatch damage here + ///////////////////////////////////////////////// + // Update MovableObject, adds on the forces etc, updated viewpoint + ACraft::Update(); /////////////////////////////////// // Explosion logic if (m_Status == DEAD) - GibThis(Vector(), 50); + GibThis(); //////////////////////////////////////// // Balance stuff @@ -982,206 +778,137 @@ void ACDropShip::Update() */ } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ResetEmissionTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reset the timers of all emissions so they will start/stop at the -// correct relative offsets from now. +void ACDropShip::SetRightThruster(AEmitter *newThruster) { + if (newThruster == nullptr) { + if (m_pRThruster && m_pRThruster->IsAttached()) { RemoveAttachable(m_pRThruster); } + m_pRThruster = nullptr; + } else { + if (m_pRThruster && m_pRThruster->IsAttached()) { RemoveAttachable(m_pRThruster); } + m_pRThruster = newThruster; + AddAttachable(newThruster); -void ACDropShip::ResetEmissionTimers() -{ - if (m_pRThruster && m_pRThruster->IsAttached()) - m_pRThruster->ResetEmissionTimers(); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + AEmitter *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightThruster"); + dynamic_cast(parent)->SetRightThruster(castedAttachable); + }}); + } +} - if (m_pLThruster && m_pLThruster->IsAttached()) - m_pLThruster->ResetEmissionTimers(); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (m_pURThruster && m_pURThruster->IsAttached()) - m_pURThruster->ResetEmissionTimers(); +void ACDropShip::SetLeftThruster(AEmitter *newThruster) { + if (newThruster == nullptr) { + if (m_pLThruster && m_pLThruster->IsAttached()) { RemoveAttachable(m_pLThruster); } + m_pLThruster = nullptr; + } else { + if (m_pLThruster && m_pLThruster->IsAttached()) { RemoveAttachable(m_pLThruster); } + m_pLThruster = newThruster; + AddAttachable(newThruster); - if (m_pULThruster && m_pULThruster->IsAttached()) - m_pULThruster->ResetEmissionTimers(); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + AEmitter *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftThruster"); + dynamic_cast(parent)->SetLeftThruster(castedAttachable); + }}); + } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveAnyRandomWounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specified amount of wounds from the actor and all standard attachables. - -int ACDropShip::RemoveAnyRandomWounds(int amount) -{ - float damage = 0; - - for (int i = 0; i < amount; i++) - { - // Fill the list of damaged bodyparts - std::vector bodyParts; - if (GetWoundCount() > 0) - bodyParts.push_back(this); - - if (m_pRHatch && m_pRHatch->GetWoundCount()) - bodyParts.push_back(m_pRHatch); - if (m_pLHatch && m_pLHatch->GetWoundCount()) - bodyParts.push_back(m_pLHatch); - if (m_pRThruster && m_pRThruster->GetWoundCount()) - bodyParts.push_back(m_pRThruster); - if (m_pLThruster && m_pLThruster->GetWoundCount()) - bodyParts.push_back(m_pLThruster); - - // Stop removing wounds if there are not any left - if (bodyParts.size() == 0) - break; - - int partIndex = RandomNum(0, bodyParts.size() - 1); - MOSRotating * part = bodyParts[partIndex]; - damage += part->RemoveWounds(1); - } +void ACDropShip::SetURightThruster(AEmitter *newThruster) { + if (newThruster == nullptr) { + if (m_pURThruster && m_pURThruster->IsAttached()) { RemoveAttachable(m_pURThruster); } + m_pURThruster = nullptr; + } else { + if (m_pURThruster && m_pURThruster->IsAttached()) { RemoveAttachable(m_pURThruster); } + m_pURThruster = newThruster; + AddAttachable(newThruster); - return damage; + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + AEmitter *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetURightThruster"); + dynamic_cast(parent)->SetURightThruster(castedAttachable); + }}); + } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound count of this actor and all vital attachables. +void ACDropShip::SetULeftThruster(AEmitter *newThruster) { + if (newThruster == nullptr) { + if (m_pULThruster && m_pULThruster->IsAttached()) { RemoveAttachable(m_pULThruster); } + m_pULThruster = nullptr; + } else { + if (m_pULThruster && m_pULThruster->IsAttached()) { RemoveAttachable(m_pULThruster); } + m_pULThruster = newThruster; + AddAttachable(newThruster); -int ACDropShip::GetTotalWoundCount() const -{ - int count = ACraft::GetWoundCount(); - - if (m_pRHatch) - count += m_pRHatch->GetWoundCount(); - if (m_pLHatch) - count += m_pLHatch->GetWoundCount(); - if (m_pRThruster) - count += m_pRThruster->GetWoundCount(); - if (m_pLThruster) - count += m_pLThruster->GetWoundCount(); - - return count; + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + AEmitter *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetULeftThruster"); + dynamic_cast(parent)->SetULeftThruster(castedAttachable); + }}); + } } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound limit of this actor and all vital attachables. - -int ACDropShip::GetTotalWoundLimit() const -{ - int count = ACraft::GetGibWoundLimit(); - - if (m_pRHatch) - count += m_pRHatch->GetGibWoundLimit(); - if (m_pLHatch) - count += m_pLHatch->GetGibWoundLimit(); - if (m_pRThruster) - count += m_pRThruster->GetGibWoundLimit(); - if (m_pLThruster) - count += m_pLThruster->GetGibWoundLimit(); - - return count; -} +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. +void ACDropShip::SetRightHatch(Attachable *newHatch) { + if (newHatch == nullptr) { + if (m_pRHatch && m_pRHatch->IsAttached()) { RemoveAttachable(m_pRHatch); } + m_pRHatch = nullptr; + } else { + if (m_pRHatch && m_pRHatch->IsAttached()) { RemoveAttachable(m_pRHatch); } + m_pRHatch = newHatch; + AddAttachable(newHatch); -void ACDropShip::UpdateChildMOIDs(vector &MOIDIndex, - MOID rootMOID, - bool makeNewMOID) -{ - if (m_pRHatch) - m_pRHatch->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - if (m_pLHatch) - m_pLHatch->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - if (m_pRThruster) - m_pRThruster->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - if (m_pLThruster) - m_pLThruster->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - - ACraft::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newHatch->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + dynamic_cast(parent)->SetRightHatch(attachable); + }}); + } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. +void ACDropShip::SetLeftHatch(Attachable *newHatch) { + if (newHatch == nullptr) { + if (m_pLHatch && m_pLHatch->IsAttached()) { RemoveAttachable(m_pLHatch); } + m_pLHatch = nullptr; + } else { + if (m_pLHatch && m_pLHatch->IsAttached()) { RemoveAttachable(m_pLHatch); } + m_pLHatch = newHatch; + AddAttachable(newHatch); -void ACDropShip::GetMOIDs(std::vector &MOIDs) const -{ - if (m_pRHatch) - m_pRHatch->GetMOIDs(MOIDs); - if (m_pLHatch) - m_pLHatch->GetMOIDs(MOIDs); - if (m_pRThruster) - m_pRThruster->GetMOIDs(MOIDs); - if (m_pLThruster) - m_pLThruster->GetMOIDs(MOIDs); - - ACraft::GetMOIDs(MOIDs); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newHatch->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + dynamic_cast(parent)->SetLeftHatch(attachable); + }}); + } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw +// Method: ResetEmissionTimers ////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ACDropShip's current graphical representation to a -// BITMAP of choice. +// Description: Reset the timers of all emissions so they will start/stop at the +// correct relative offsets from now. -void ACDropShip::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const +void ACDropShip::ResetEmissionTimers() { - if (m_pRHatch) - m_pRHatch->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pLHatch) - m_pLHatch->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - if (m_pRThruster && !m_pRThruster->IsDrawnAfterParent()) - m_pRThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pLThruster && !m_pLThruster->IsDrawnAfterParent()) - m_pLThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - ACraft::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + if (m_pRThruster && m_pRThruster->IsAttached()) + m_pRThruster->ResetEmissionTimers(); - if (m_pRThruster && m_pRThruster->IsDrawnAfterParent()) - m_pRThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pLThruster && m_pLThruster->IsDrawnAfterParent()) - m_pLThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + if (m_pLThruster && m_pLThruster->IsAttached()) + m_pLThruster->ResetEmissionTimers(); - if (mode == g_DrawColor || mode == g_DrawMaterial) - { - if (m_pURThruster) - m_pURThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pULThruster) - m_pULThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - } + if (m_pURThruster && m_pURThruster->IsAttached()) + m_pURThruster->ResetEmissionTimers(); - if (mode == g_DrawColor) - { -#ifdef DEBUG_BUILD - acquire_bitmap(pTargetBitmap); - putpixel(pTargetBitmap, std::floor(m_Pos.m_X), - std::floor(m_Pos.m_Y), - 64); - putpixel(pTargetBitmap, std::floor(m_Pos.m_X), - std::floor(m_Pos.m_Y), - 64); - release_bitmap(pTargetBitmap); - - m_pAtomGroup->Draw(pTargetBitmap, targetPos, false, 122); -// m_pDeepGroup->Draw(pTargetBitmap, targetPos, false, 13); -#endif - } + if (m_pULThruster && m_pULThruster->IsAttached()) + m_pULThruster->ResetEmissionTimers(); } - } // namespace RTE \ No newline at end of file diff --git a/Entities/ACDropShip.h b/Entities/ACDropShip.h index bc43db3e8..6ff817a35 100644 --- a/Entities/ACDropShip.h +++ b/Entities/ACDropShip.h @@ -109,17 +109,6 @@ ClassInfoGetters void Destroy(bool notInherited = false) override; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this ACDropShip, including the mass of its -// currently attached body parts and inventory. -// Arguments: None. -// Return value: A float describing the mass value in Kilograms (kg). - - float GetMass() const override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: GetAltitude ////////////////////////////////////////////////////////////////////////////////////////// @@ -143,17 +132,6 @@ ClassInfoGetters MOID DetectObstacle(float distance); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. -// Arguments: A moid specifying the MOID that this MovableObject is -// assigned for this frame. -// Return value: None. - - void SetID(const MOID newID) override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: AutoStabilizing ////////////////////////////////////////////////////////////////////////////////////////// @@ -165,30 +143,6 @@ ClassInfoGetters bool AutoStabilizing() override { return true; } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. -// Arguments: The impulse (kg * m/s) of the impact causing the gibbing to happen. -// The internal blast impulse which will push the gibs away from the center. -// A pointer to an MO which the gibs shuold not be colliding with! -// Return value: None. - - void GibThis(Vector impactImpulse = Vector(), float internalBlast = 10, MovableObject *pIgnoreMO = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. -// Arguments: The point in absolute scene coordinates. -// Return value: Whether this' graphical rep overlaps the scene point. - - bool IsOnScenePoint(Vector &scenePoint) const override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: UpdateAI ////////////////////////////////////////////////////////////////////////////////////////// @@ -210,21 +164,6 @@ ClassInfoGetters void Update() override; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ACDropShip's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: GetMaxPassengers ////////////////////////////////////////////////////////////////////////////////////////// @@ -237,64 +176,77 @@ ClassInfoGetters int GetMaxPassengers() const override { return m_MaxPassengers > -1 ? m_MaxPassengers : 4; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRThruster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the right side engine. -// Arguments: None. -// Return value: An AEmitter pointer. - - AEmitter * GetRThruster() const { return m_pRThruster; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLThruster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the left side engine. -// Arguments: None. -// Return value: An AEmitter pointer. - - AEmitter * GetLThruster() const { return m_pLThruster; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetURThruster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the right side secondary thruster. -// Arguments: None. -// Return value: An AEmitter pointer. - - AEmitter * GetURThruster() const { return m_pURThruster; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetULThruster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the left side secondary thruster. -// Arguments: None. -// Return value: An AEmitter pointer. - - AEmitter * GetULThruster() const { return m_pULThruster; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLHatch -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the left side hatch. -// Arguments: None. -// Return value: An Attachable pointer. - - Attachable * GetLHatch() const { return m_pLHatch; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRHatch -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the right side hatch. -// Arguments: None. -// Return value: An Attachable pointer. - - Attachable * GetRHatch() const { return m_pRHatch; } + /// + /// Gets the right side thruster of this ACDropship. + /// + /// A pointer to the right side thruster of this ACDropship. Ownership is NOT transferred. + AEmitter * GetRightThruster() const { return m_pRThruster; } + + /// + /// Sets the right side thruster for this ACDropship. + /// + /// The new thruster to use. + void SetRightThruster(AEmitter *newThruster); + + /// + /// Gets the left side thruster of this ACDropship. + /// + /// A pointer to the left side thruster of this ACDropship. Ownership is NOT transferred. + AEmitter * GetLeftThruster() const { return m_pLThruster; } + + /// + /// Sets the left side thruster for this ACDropship. + /// + /// The new thruster to use. + void SetLeftThruster(AEmitter *newThruster); + + /// + /// Gets the right side secondary thruster of this ACDropship. + /// + /// A pointer to the right side secondary thruster of this ACDropship. Ownership is NOT transferred. + AEmitter * GetURightThruster() const { return m_pURThruster; } + + /// + /// Sets the right side secondary thruster for this ACDropship. + /// + /// The new thruster to use. + void SetURightThruster(AEmitter *newThruster); + + /// + /// Gets the left side secondary thruster of this ACDropship. + /// + /// A pointer to the left side secondary thruster of this ACDropship. Ownership is NOT transferred. + AEmitter * GetULeftThruster() const { return m_pULThruster; } + + /// + /// Sets the left side secondary thruster for this ACDropship. + /// + /// The new thruster to use. + void SetULeftThruster(AEmitter *newThruster); + + /// + /// Gets the left side hatch of this ACDropship. + /// + /// A pointer to the left side hatch of this ACDropship. Ownership is NOT transferred. + Attachable * GetLeftHatch() const { return m_pLHatch; } + + /// + /// Sets the left side hatch for this ACDropship. + /// + /// The new hatch to use. + void SetLeftHatch(Attachable *newHatch); + + /// + /// Gets the right side hatch of this ACDropship. + /// + /// A pointer to the right side hatch of this ACDropship. Ownership is NOT transferred. + Attachable * GetRightHatch() const { return m_pRHatch; } + + /// + /// Sets the right side hatch for this ACDropship. + /// + /// The new hatch to use. + void SetRightHatch(Attachable *newHatch); ////////////////////////////////////////////////////////////////////////////////////////// @@ -308,45 +260,6 @@ ClassInfoGetters void ResetEmissionTimers() override; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound count of this actor and all vital attachables. -// Arguments: None. -// Return value: Returns total number of wounds of this actor. - - int GetTotalWoundCount() const override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound limit of this actor and all vital attachables. -// Arguments: None. -// Return value: Returns total wound limit of this actor. - - int GetTotalWoundLimit() const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveAnyRandomWounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specified amount of wounds from the actor and all standard attachables. -// Arguments: Amount of wounds to remove. -// Return value: Damage taken from removed wounds. - - int RemoveAnyRandomWounds(int amount) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - - void GetMOIDs(std::vector &MOIDs) const override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: GetMaxEngineAngle ////////////////////////////////////////////////////////////////////////////////////////// @@ -402,26 +315,13 @@ ClassInfoGetters protected: -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. -// Arguments: The MOID index to register itself and its children in. -// The MOID of the root MO of this MO, ie the highest parent of this MO. -// 0 means that this MO is the root, ie it is owned by MovableMan. -// Whether this MO should make a new MOID to use for itself, or to use -// the same as the last one in the index (presumably its parent), -// Return value: None. - - void UpdateChildMOIDs(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) override; - // Member variables static Entity::ClassInfo m_sClass; // Body AtomGroups. AtomGroup *m_pBodyAG; // Thruster emitters. + //TODO when this class is cleaned up, these and their getters and setters should probably be renamed (I'd argue the lua bindings should be broken to match but that's debatable). L and R should be Left and Right and they should probably be Primary and Secondary. AEmitter *m_pRThruster; AEmitter *m_pLThruster; AEmitter *m_pURThruster; diff --git a/Entities/ACRocket.cpp b/Entities/ACRocket.cpp index 56152edf6..77bc56de4 100644 --- a/Entities/ACRocket.cpp +++ b/Entities/ACRocket.cpp @@ -91,20 +91,38 @@ int ACRocket::Create() ////////////////////////////////////////////////////////////////////////////////////////// // Description: Creates a ACRocket to be identical to another, by deep copy. -int ACRocket::Create(const ACRocket &reference) -{ - ACraft::Create(reference); - +int ACRocket::Create(const ACRocket &reference) { if (reference.m_pRLeg) { - m_pRLeg = dynamic_cast(reference.m_pRLeg->Clone()); - AddAttachable(m_pRLeg, true); + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRLeg->GetUniqueID()); + SetRightLeg(dynamic_cast(reference.m_pRLeg->Clone())); } - if (reference.m_pLLeg) { - m_pLLeg = dynamic_cast(reference.m_pLLeg->Clone()); - AddAttachable(m_pLLeg, true); + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLLeg->GetUniqueID()); + SetLeftLeg(dynamic_cast(reference.m_pLLeg->Clone())); + } + if (reference.m_pMThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pMThruster->GetUniqueID()); + SetMainThruster(dynamic_cast(reference.m_pMThruster->Clone())); + } + if (reference.m_pRThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRThruster->GetUniqueID()); + SetRightThruster(dynamic_cast(reference.m_pRThruster->Clone())); + } + if (reference.m_pLThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLThruster->GetUniqueID()); + SetLeftThruster(dynamic_cast(reference.m_pLThruster->Clone())); + } + if (reference.m_pURThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pURThruster->GetUniqueID()); + SetURightThruster(dynamic_cast(reference.m_pURThruster->Clone())); + } + if (reference.m_pULThruster) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pULThruster->GetUniqueID()); + SetULeftThruster(dynamic_cast(reference.m_pULThruster->Clone())); } + ACraft::Create(reference); + m_pBodyAG = dynamic_cast(reference.m_pBodyAG->Clone()); m_pBodyAG->SetOwner(this); @@ -119,32 +137,6 @@ int ACRocket::Create(const ACRocket &reference) m_pLFootGroup->SetOwner(this); } - if (reference.m_pMThruster) - { - m_pMThruster = dynamic_cast(reference.m_pMThruster->Clone()); - AddAttachable(m_pMThruster, true); - } - if (reference.m_pRThruster) - { - m_pRThruster = dynamic_cast(reference.m_pRThruster->Clone()); - AddAttachable(m_pRThruster, true); - } - if (reference.m_pLThruster) - { - m_pLThruster = dynamic_cast(reference.m_pLThruster->Clone()); - AddAttachable(m_pLThruster, true); - } - if (reference.m_pURThruster) - { - m_pURThruster = dynamic_cast(reference.m_pURThruster->Clone()); - AddAttachable(m_pURThruster, true); - } - if (reference.m_pULThruster) - { - m_pULThruster = dynamic_cast(reference.m_pULThruster->Clone()); - AddAttachable(m_pULThruster, true); - } - m_GearState = reference.m_GearState; for (int i = 0; i < GearStateCount; ++i) { @@ -166,77 +158,78 @@ int ACRocket::Create(const ACRocket &reference) // is called. If the property isn't recognized by any of the base classes, // false is returned, and the reader's position is untouched. -int ACRocket::ReadProperty(std::string propName, Reader &reader) -{ - if (propName == "RLeg") - { - delete m_pRLeg; +int ACRocket::ReadProperty(std::string propName, Reader &reader) { + if (propName == "RLeg") { + RemoveAttachable(m_pRLeg); m_pRLeg = new Leg; reader >> m_pRLeg; - } - else if (propName == "LLeg") - { - delete m_pLLeg; + AddAttachable(m_pRLeg); + if (m_pRLeg->HasNoSetDamageMultiplier()) { m_pRLeg->SetDamageMultiplier(1.0F); } + } else if (propName == "LLeg") { + RemoveAttachable(m_pLLeg); m_pLLeg = new Leg; reader >> m_pLLeg; - m_pLLeg->SetHFlipped(true); - } - else if (propName == "RFootGroup") - { + AddAttachable(m_pLLeg); + m_pLLeg->SetInheritsHFlipped(-1); + if (m_pLLeg->HasNoSetDamageMultiplier()) { m_pLLeg->SetDamageMultiplier(1.0F); } + } else if (propName == "RFootGroup") { delete m_pRFootGroup; m_pRFootGroup = new AtomGroup(); reader >> m_pRFootGroup; m_pRFootGroup->SetOwner(this); - } - else if (propName == "LFootGroup") - { + } else if (propName == "LFootGroup") { delete m_pLFootGroup; m_pLFootGroup = new AtomGroup(); reader >> m_pLFootGroup; m_pLFootGroup->SetOwner(this); - } - else if (propName == "MThruster") - { - delete m_pMThruster; + } else if (propName == "MThruster"){ + RemoveAttachable(m_pMThruster); m_pMThruster = new AEmitter; reader >> m_pMThruster; - } - else if (propName == "RThruster") - { - delete m_pRThruster; + AddAttachable(m_pMThruster); + if (m_pMThruster->HasNoSetDamageMultiplier()) { m_pMThruster->SetDamageMultiplier(1.0F); } + m_pMThruster->SetInheritedRotAngleOffset(-c_HalfPI); + } else if (propName == "RThruster") { + RemoveAttachable(m_pRThruster); m_pRThruster = new AEmitter; reader >> m_pRThruster; - } - else if (propName == "LThruster") - { - delete m_pLThruster; + AddAttachable(m_pRThruster); + if (m_pRThruster->HasNoSetDamageMultiplier()) { m_pRThruster->SetDamageMultiplier(1.0F); } + m_pRThruster->SetInheritedRotAngleOffset(c_EighthPI); + } else if (propName == "LThruster") { + RemoveAttachable(m_pLThruster); m_pLThruster = new AEmitter; reader >> m_pLThruster; - } - else if (propName == "URThruster") - { - delete m_pURThruster; + AddAttachable(m_pLThruster); + if (m_pLThruster->HasNoSetDamageMultiplier()) { m_pLThruster->SetDamageMultiplier(1.0F); } + m_pLThruster->SetInheritedRotAngleOffset(c_PI - c_EighthPI); + } else if (propName == "URThruster") { + RemoveAttachable(m_pURThruster); m_pURThruster = new AEmitter; reader >> m_pURThruster; - } - else if (propName == "ULThruster") - { - delete m_pULThruster; + AddAttachable(m_pURThruster); + if (m_pURThruster->HasNoSetDamageMultiplier()) { m_pURThruster->SetDamageMultiplier(1.0F); } + m_pURThruster->SetInheritedRotAngleOffset(c_HalfPI - c_EighthPI); + } else if (propName == "ULThruster") { + RemoveAttachable(m_pULThruster); m_pULThruster = new AEmitter; reader >> m_pULThruster; - } - else if (propName == "RaisedGearLimbPath") + AddAttachable(m_pULThruster); + if (m_pULThruster->HasNoSetDamageMultiplier()) { m_pULThruster->SetDamageMultiplier(1.0F); } + m_pULThruster->SetInheritedRotAngleOffset(c_HalfPI + c_EighthPI); + } else if (propName == "RaisedGearLimbPath") { reader >> m_Paths[RIGHT][RAISED]; - else if (propName == "LoweredGearLimbPath") + } else if (propName == "LoweredGearLimbPath") { reader >> m_Paths[RIGHT][LOWERED]; - else if (propName == "LoweringGearLimbPath") + } else if (propName == "LoweringGearLimbPath") { reader >> m_Paths[RIGHT][LOWERING]; - else if (propName == "RaisingGearLimbPath") + } else if (propName == "RaisingGearLimbPath") { reader >> m_Paths[RIGHT][RAISING]; - else if (propName == "ScuttleIfFlippedTime") + } else if (propName == "ScuttleIfFlippedTime") { reader >> m_ScuttleIfFlippedTime; - else + } else { return ACraft::ReadProperty(propName, reader); + } return 0; } @@ -292,17 +285,9 @@ int ACRocket::Save(Writer &writer) const void ACRocket::Destroy(bool notInherited) { - delete m_pRLeg; - delete m_pLLeg; delete m_pBodyAG; delete m_pRFootGroup; delete m_pLFootGroup; - - delete m_pMThruster; - delete m_pRThruster; - delete m_pLThruster; - delete m_pURThruster; - delete m_pULThruster; // for (deque::iterator itr = m_WalkPaths.begin(); // itr != m_WalkPaths.end(); ++itr) @@ -314,27 +299,6 @@ void ACRocket::Destroy(bool notInherited) } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this ACRocket, including the mass of its -// currently attached body parts and inventory. - -float ACRocket::GetMass() const -{ - float totalMass = ACraft::GetMass(); -// if (m_pCapsule) -// totalMass += m_pCapsule->GetMass(); - if (m_pRLeg) - totalMass += m_pRLeg->GetMass(); - if (m_pLLeg) - totalMass += m_pLLeg->GetMass(); - if (m_pMThruster) - totalMass += m_pMThruster->GetMass(); - return totalMass; -} - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: GetAltitude ////////////////////////////////////////////////////////////////////////////////////////// @@ -353,25 +317,6 @@ float ACRocket::GetAltitude(int max, int accuracy) return g_SceneMan.FindAltitude(pos, max, accuracy); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. - -void ACRocket::SetID(const MOID newID) -{ - MovableObject::SetID(newID); -// if (m_pCapsule) -// m_pCapsule->SetID(newID); - if (m_pRLeg) - m_pRLeg->SetID(newID); - if (m_pLLeg) - m_pLLeg->SetID(newID); - if (m_pMThruster) - m_pMThruster->SetID(newID); -} - /* ////////////////////////////////////////////////////////////////////////////////////////// // Method: OnBounce @@ -398,75 +343,6 @@ bool ACRocket::OnSink(const Vector &pos) return false; } */ -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. - -void ACRocket::GibThis(Vector impactImpulse, float internalBlast, MovableObject *pIgnoreMO) -{ - // TODO: maybe make hardcoded attachables gib if their gib list isn't empty - // Detach all limbs and let loose - if (m_pRLeg && m_pRLeg->IsAttached()) - { - RemoveAttachable(m_pRLeg); - SetAttachableVelocitiesForGibbing(m_pRLeg, impactImpulse, internalBlast); - m_pRLeg->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(m_pRLeg); - m_pRLeg = 0; - } - if (m_pLLeg && m_pLLeg->IsAttached()) - { - RemoveAttachable(m_pLLeg); - SetAttachableVelocitiesForGibbing(m_pLLeg, impactImpulse, internalBlast); - m_pLLeg->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(m_pLLeg); - m_pLLeg = 0; - } - if (m_pMThruster && m_pMThruster->IsAttached()) - { - RemoveAttachable(m_pMThruster); - SetAttachableVelocitiesForGibbing(m_pMThruster, impactImpulse, internalBlast); - m_pMThruster->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(m_pMThruster); - m_pMThruster = 0; - } - if (m_pRThruster && m_pRThruster->IsAttached()) - { - RemoveAttachable(m_pRThruster); - SetAttachableVelocitiesForGibbing(m_pRThruster, impactImpulse, internalBlast); - m_pRThruster->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(m_pRThruster); - m_pRThruster = 0; - } - if (m_pLThruster && m_pLThruster->IsAttached()) - { - RemoveAttachable(m_pLThruster); - SetAttachableVelocitiesForGibbing(m_pLThruster, impactImpulse, internalBlast); - m_pLThruster->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(m_pLThruster); - m_pLThruster = 0; - } - if (m_pURThruster && m_pURThruster->IsAttached()) - { - RemoveAttachable(m_pURThruster); - SetAttachableVelocitiesForGibbing(m_pURThruster, impactImpulse, internalBlast); - m_pURThruster->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(m_pURThruster); - m_pURThruster = 0; - } - if (m_pULThruster && m_pULThruster->IsAttached()) - { - RemoveAttachable(m_pULThruster); - SetAttachableVelocitiesForGibbing(m_pULThruster, impactImpulse, internalBlast); - m_pULThruster->SetToGetHitByMOs(false); - g_MovableMan.AddParticle(m_pULThruster); - m_pULThruster = 0; - } - - Actor::GibThis(impactImpulse, internalBlast, pIgnoreMO); -} ////////////////////////////////////////////////////////////////////////////////////////// @@ -647,7 +523,6 @@ void ACRocket::UpdateAI() void ACRocket::Update() { - float mass = GetMass(); float deltaTime = g_TimerMan.GetDeltaTimeSecs(); // Look/aim update, make the scanner point aftward if the rocket is falling @@ -860,106 +735,26 @@ void ACRocket::Update() m_GearState = LOWERED; } - ///////////////////////////////////////////////// - // Update MovableObject, adds on the forces etc, updated viewpoint - - ACraft::Update(); - ///////////////////////////////// - // Update Attachable:s -/* - if (m_pCapsule && m_pCapsule->IsAttached()) - { - m_pCapsule->SetJointPos(m_Pos + m_pCapsule->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - m_pCapsule->SetRotAngle(m_Rotation); - m_pCapsule->Update(); - m_Health -= m_pCapsule->CollectDamage(); - } -*/ - if (m_pRLeg && m_pRLeg->IsAttached()) - { - m_pRLeg->SetJointPos(m_Pos + m_pRLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); -/* Obsolete - if (!m_pMThruster->IsEmitting()) - m_pRLeg->ReachToward(m_Pos + Vector(18, 40) * m_Rotation); - else - m_pRLeg->ReachToward(m_Pos + Vector(13, 40) * m_Rotation); -*/ - m_pRLeg->ReachToward(m_pRFootGroup->GetLimbPos(m_HFlipped)); - m_pRLeg->Update(); - m_Health -= m_pRLeg->CollectDamage(); - } - - if (m_pLLeg && m_pLLeg->IsAttached()) - { - m_pLLeg->SetJointPos(m_Pos + m_pLLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); -/* Obsolete - if (!m_pMThruster->IsEmitting()) - m_pLLeg->ReachToward(m_Pos + Vector(-18, 40) * m_Rotation); - else - m_pLLeg->ReachToward(m_Pos + Vector(-13, 40) * m_Rotation); -*/ - m_pLLeg->ReachToward(m_pLFootGroup->GetLimbPos(!m_HFlipped)); - m_pLLeg->Update(); - m_Health -= m_pLLeg->CollectDamage(); - } - - // Apply forces transferred from the attachables and - // add detachment wounds to this if applicable - -// if (!ApplyAttachableForces(m_pCapsule)) -// m_pCapsule = 0; - if (!ApplyAttachableForces(m_pRLeg)) - m_pRLeg = 0; - if (!ApplyAttachableForces(m_pLLeg)) - m_pLLeg = 0; - - if (m_pMThruster) { - m_pMThruster->SetJointPos(m_Pos + RotateOffset(m_pMThruster->GetParentOffset())); -// m_pMThruster->SetVel(m_Vel); - m_pMThruster->SetRotAngle(m_Rotation.GetRadAngle() - c_HalfPI); -// m_pMThruster->SetEmitAngle(m_Rotation - c_HalfPI); - m_pMThruster->Update(); + // Manage Attachable:s + if (m_pRLeg && m_pRLeg->IsAttached()) { + m_pRLeg->SetTargetPosition(m_pRFootGroup->GetLimbPos(m_HFlipped)); } - if (m_pRThruster) { - m_pRThruster->SetJointPos(m_Pos + RotateOffset(m_pRThruster->GetParentOffset())); - m_pRThruster->SetVel(m_Vel); - m_pRThruster->SetRotAngle(m_Rotation.GetRadAngle() + c_EighthPI); -// m_pRThruster->SetEmitAngle(m_Rotation); - m_pRThruster->Update(); + if (m_pLLeg && m_pLLeg->IsAttached()) { + m_pLLeg->SetTargetPosition(m_pLFootGroup->GetLimbPos(!m_HFlipped)); } - if (m_pLThruster) { - m_pLThruster->SetJointPos(m_Pos + RotateOffset(m_pLThruster->GetParentOffset())); - m_pLThruster->SetVel(m_Vel); - m_pLThruster->SetRotAngle(m_Rotation.GetRadAngle() + c_PI - c_EighthPI); -// m_pLThruster->SetEmitAngle(m_Rotation + c_PI); - m_pLThruster->Update(); - } - - if (m_pURThruster) { - m_pURThruster->SetJointPos(m_Pos + RotateOffset(m_pURThruster->GetParentOffset())); - m_pURThruster->SetVel(m_Vel); - m_pURThruster->SetRotAngle(m_Rotation.GetRadAngle() + c_HalfPI - c_QuarterPI / 2); -// m_pURThruster->SetEmitAngle(-c_QuarterPI / 2); - m_pURThruster->Update(); - } - - if (m_pULThruster) { - m_pULThruster->SetJointPos(m_Pos + RotateOffset(m_pULThruster->GetParentOffset())); - m_pULThruster->SetVel(m_Vel); - m_pULThruster->SetRotAngle(m_Rotation.GetRadAngle() + c_HalfPI + c_QuarterPI / 2); -// m_pULThruster->SetEmitAngle(c_QuarterPI / 2); - m_pULThruster->Update(); - } + ///////////////////////////////////////////////// + // Update MovableObject, adds on the forces etc, updated viewpoint + ACraft::Update(); /////////////////////////////////// // Explosion logic if (m_Status == DEAD) - GibThis(Vector(), 50); + GibThis(); //////////////////////////////////////// // Balance stuff @@ -1046,43 +841,140 @@ void ACRocket::Update() } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. +void ACRocket::SetRightLeg(Leg *newLeg) { + if (newLeg == nullptr) { + if (m_pRLeg && m_pRLeg->IsAttached()) { RemoveAttachable(m_pRLeg); } + m_pRLeg = nullptr; + } else { + if (m_pRLeg && m_pRLeg->IsAttached()) { RemoveAttachable(m_pRLeg); } + m_pRLeg = newLeg; + AddAttachable(newLeg); -void ACRocket::UpdateChildMOIDs(vector &MOIDIndex, - MOID rootMOID, - bool makeNewMOID) -{ - if (m_pRLeg) - m_pRLeg->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - if (m_pLLeg) - m_pLLeg->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + Leg *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightLeg"); + dynamic_cast(parent)->SetRightLeg(castedAttachable); + }}); + } +} - ACraft::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ACRocket::SetLeftLeg(Leg *newLeg) { + if (newLeg == nullptr) { + if (m_pLLeg && m_pLLeg->IsAttached()) { RemoveAttachable(m_pLLeg); } + m_pLLeg = nullptr; + } else { + if (m_pLLeg && m_pLLeg->IsAttached()) { RemoveAttachable(m_pLLeg); } + m_pLLeg = newLeg; + AddAttachable(newLeg); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + Leg *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftLeg"); + dynamic_cast(parent)->SetLeftLeg(castedAttachable); + }}); + } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. +void ACRocket::SetMainThruster(AEmitter *newThruster) { + if (newThruster == nullptr) { + if (m_pMThruster && m_pMThruster->IsAttached()) { RemoveAttachable(m_pMThruster); } + m_pMThruster = nullptr; + } else { + if (m_pMThruster && m_pMThruster->IsAttached()) { RemoveAttachable(m_pMThruster); } + m_pMThruster = newThruster; + AddAttachable(newThruster); -void ACRocket::GetMOIDs(std::vector &MOIDs) const -{ - if (m_pRLeg) - m_pRLeg->GetMOIDs(MOIDs); - if (m_pLLeg) - m_pLLeg->GetMOIDs(MOIDs); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + AEmitter *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetMainThruster"); + dynamic_cast(parent)->SetMainThruster(castedAttachable); + }}); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ACRocket::SetRightThruster(AEmitter *newThruster) { + if (newThruster == nullptr) { + if (m_pRThruster && m_pRThruster->IsAttached()) { RemoveAttachable(m_pRThruster); } + m_pRThruster = nullptr; + } else { + if (m_pRThruster && m_pRThruster->IsAttached()) { RemoveAttachable(m_pRThruster); } + m_pRThruster = newThruster; + AddAttachable(newThruster); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + AEmitter *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightThruster"); + dynamic_cast(parent)->SetRightThruster(castedAttachable); + }}); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ACRocket::SetLeftThruster(AEmitter *newThruster) { + if (newThruster == nullptr) { + if (m_pLThruster && m_pLThruster->IsAttached()) { RemoveAttachable(m_pLThruster); } + m_pLThruster = nullptr; + } else { + if (m_pLThruster && m_pLThruster->IsAttached()) { RemoveAttachable(m_pLThruster); } + m_pLThruster = newThruster; + AddAttachable(newThruster); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + AEmitter *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftThruster"); + dynamic_cast(parent)->SetLeftThruster(castedAttachable); + }}); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ACRocket::SetURightThruster(AEmitter *newThruster) { + if (newThruster == nullptr) { + if (m_pURThruster && m_pURThruster->IsAttached()) { RemoveAttachable(m_pURThruster); } + m_pURThruster = nullptr; + } else { + if (m_pURThruster && m_pURThruster->IsAttached()) { RemoveAttachable(m_pURThruster); } + m_pURThruster = newThruster; + AddAttachable(newThruster); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + AEmitter *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetURightThruster"); + dynamic_cast(parent)->SetURightThruster(castedAttachable); + }}); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ACraft::GetMOIDs(MOIDs); +void ACRocket::SetULeftThruster(AEmitter *newThruster) { + if (newThruster == nullptr) { + if (m_pULThruster && m_pULThruster->IsAttached()) { RemoveAttachable(m_pULThruster); } + m_pULThruster = nullptr; + } else { + if (m_pULThruster && m_pULThruster->IsAttached()) { RemoveAttachable(m_pULThruster); } + m_pULThruster = newThruster; + AddAttachable(newThruster); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newThruster->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + AEmitter *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetULeftThruster"); + dynamic_cast(parent)->SetULeftThruster(castedAttachable); + }}); + } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// // Method: ResetEmissionTimers @@ -1108,158 +1000,17 @@ void ACRocket::ResetEmissionTimers() m_pULThruster->ResetEmissionTimers(); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveAnyRandomWounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specified amount of wounds from the actor and all standard attachables. - -int ACRocket::RemoveAnyRandomWounds(int amount) -{ - float damage = 0; - - for (int i = 0; i < amount; i++) - { - // Fill the list of damaged bodyparts - std::vector bodyParts; - if (GetWoundCount() > 0) - bodyParts.push_back(this); - - if (m_pRLeg && m_pRLeg->GetWoundCount()) - bodyParts.push_back(m_pRLeg); - if (m_pLLeg && m_pLLeg->GetWoundCount()) - bodyParts.push_back(m_pLLeg); - - // Stop removing wounds if there are not any left - if (bodyParts.size() == 0) - break; - - int partIndex = RandomNum(0, bodyParts.size() - 1); - MOSRotating * part = bodyParts[partIndex]; - damage += part->RemoveWounds(1); - } - - return damage; -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound count of this actor and all vital attachables. - -int ACRocket::GetTotalWoundCount() const -{ - int count = ACraft::GetWoundCount(); - -// if (m_pMThruster) -// count += m_pMThruster->GetWoundCount(); - if (m_pRLeg) - count += m_pRLeg->GetWoundCount(); - if (m_pLLeg) - count += m_pLLeg->GetWoundCount(); -// if (m_pCapsule) -// count += m_pCapsule->GetWoundCount(); - - return count; -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound limit of this actor and all vital attachables. - -int ACRocket::GetTotalWoundLimit() const -{ - int count = ACraft::GetGibWoundLimit(); - -// if (m_pMThruster) -// count += m_pMThruster->GetGibWoundLimit(); - if (m_pRLeg) - count += m_pRLeg->GetGibWoundLimit(); - if (m_pLLeg) - count += m_pLLeg->GetGibWoundLimit(); -// if (m_pCapsule) -// count += m_pCapsule->GetGibWoundLimit(); - - return count; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ACRocket's current graphical representation to a -// BITMAP of choice. - -void ACRocket::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const -{ - if (m_pMThruster && !m_pMThruster->IsDrawnAfterParent() && (mode == g_DrawColor || mode == g_DrawMaterial)) - m_pMThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - if (m_pRLeg && !m_pRLeg->IsDrawnAfterParent()) - m_pRLeg->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pLLeg && !m_pLLeg->IsDrawnAfterParent()) - m_pLLeg->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - if (mode == g_DrawColor || mode == g_DrawMaterial) { - if (m_pRThruster && !m_pRThruster->IsDrawnAfterParent()) - m_pRThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pLThruster && !m_pLThruster->IsDrawnAfterParent()) - m_pLThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pURThruster && !m_pURThruster->IsDrawnAfterParent()) - m_pURThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pULThruster && !m_pULThruster->IsDrawnAfterParent()) - m_pULThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - } - +#ifdef DEBUG_BUILD +void ACRocket::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { ACraft::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pMThruster && m_pMThruster->IsDrawnAfterParent() && (mode == g_DrawColor || mode == g_DrawMaterial)) - m_pMThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - if (m_pRLeg && m_pRLeg->IsDrawnAfterParent()) - m_pRLeg->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pLLeg && m_pLLeg->IsDrawnAfterParent()) - m_pLLeg->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - if (mode == g_DrawColor || mode == g_DrawMaterial) { - if (m_pRThruster && m_pRThruster->IsDrawnAfterParent()) - m_pRThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pLThruster && m_pLThruster->IsDrawnAfterParent()) - m_pLThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pURThruster && m_pURThruster->IsDrawnAfterParent()) - m_pURThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pULThruster && m_pULThruster->IsDrawnAfterParent()) - m_pULThruster->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - } -/* - if (m_pCapsule) - m_pCapsule->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); -*/ if (mode == g_DrawColor) { -#ifdef DEBUG_BUILD - acquire_bitmap(pTargetBitmap); - putpixel(pTargetBitmap, std::floor(m_Pos.m_X), - std::floor(m_Pos.m_Y), - 64); - putpixel(pTargetBitmap, std::floor(m_Pos.m_X), - std::floor(m_Pos.m_Y), - 64); - release_bitmap(pTargetBitmap); - m_pRFootGroup->Draw(pTargetBitmap, targetPos, true, 13); m_pLFootGroup->Draw(pTargetBitmap, targetPos, true, 13); -// m_pAtomGroup->Draw(pTargetBitmap, targetPos, false, 122); -// m_pDeepGroup->Draw(pTargetBitmap, targetPos, false, 13); -#endif -// m_pAtomGroup->Draw(pTargetBitmap, targetPos, false); -// m_pFGFootGroup->Draw(pTargetBitmap, targetPos, true); -// m_pBGFootGroup->Draw(pTargetBitmap, targetPos, true); -// m_pBGHandGroup->Draw(pTargetBitmap, targetPos, true); } } +#endif } // namespace RTE diff --git a/Entities/ACRocket.h b/Entities/ACRocket.h index e7bb361c5..6fcbc97f6 100644 --- a/Entities/ACRocket.h +++ b/Entities/ACRocket.h @@ -124,17 +124,6 @@ ClassInfoGetters void Destroy(bool notInherited = false) override; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this ACRocket, including the mass of its -// currently attached body parts and inventory. -// Arguments: None. -// Return value: A float describing the mass value in Kilograms (kg). - - float GetMass() const override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: GetAltitude ////////////////////////////////////////////////////////////////////////////////////////// @@ -148,30 +137,6 @@ ClassInfoGetters float GetAltitude(int max = 0, int accuracy = 0) override; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. -// Arguments: A MOID specifying the MOID that this MovableObject is -// assigned for this frame. -// Return value: None. - - void SetID(const MOID newID) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. -// Arguments: The impulse (kg * m/s) of the impact causing the gibbing to happen. -// The internal blast impulse which will push the gibs away from the center. -// A pointer to an MO which the gibs shuold not be colliding with! -// Return value: None. - - void GibThis(Vector impactImpulse = Vector(), float internalBlast = 10, MovableObject *pIgnoreMO = 0) override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: UpdateAI ////////////////////////////////////////////////////////////////////////////////////////// @@ -204,26 +169,7 @@ ClassInfoGetters void ResetEmissionTimers() override; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound count of this actor and all vital attachables. -// Arguments: None. -// Return value: Returns total number of wounds of this actor. - - int GetTotalWoundCount() const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound limit of this actor and all vital attachables. -// Arguments: None. -// Return value: Returns total wound limit of this actor. - - int GetTotalWoundLimit() const override; - - +#ifdef DEBUG_BUILD ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Draw ////////////////////////////////////////////////////////////////////////////////////////// @@ -237,6 +183,7 @@ ClassInfoGetters // Return value: None. void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; +#endif ////////////////////////////////////////////////////////////////////////////////////////// @@ -250,74 +197,89 @@ ClassInfoGetters int GetMaxPassengers() const override { return m_MaxPassengers > -1 ? m_MaxPassengers : 2; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveAnyRandomWounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specified amount of wounds from the actor and all standard attachables. -// Arguments: Amount of wounds to remove. -// Return value: Damage taken from removed wounds. - - int RemoveAnyRandomWounds(int amount) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - - void GetMOIDs(std::vector &MOIDs) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMThruster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the main thruster. -// Arguments: None. -// Return value: An AEmitter pointer. - - AEmitter * GetMThruster() const { return m_pMThruster; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRThruster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the right thruster. -// Arguments: None. -// Return value: An AEmitter pointer. - - AEmitter * GetRThruster() const { return m_pRThruster; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLThruster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the left thruster. -// Arguments: None. -// Return value: An AEmitter pointer. - - AEmitter * GetLThruster() const { return m_pLThruster; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetURThruster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the right side secondary thruster. -// Arguments: None. -// Return value: An AEmitter pointer. - - AEmitter * GetURThruster() const { return m_pURThruster; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetULThruster -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the left side secondary thruster. -// Arguments: None. -// Return value: An AEmitter pointer. - - AEmitter * GetULThruster() const { return m_pULThruster; } + /// + /// Gets the right leg of this ACRocket. + /// + /// A pointer to the right Leg of this ACRocket. Ownership is NOT transferred. + Leg * GetRightLeg() const { return m_pRLeg; } + + /// + /// Sets the right Leg for this ACRocket. + /// + /// The new Leg to use. + void SetRightLeg(Leg *newLeg); + + /// + /// Gets the left Leg of this ACRocket. + /// + /// A pointer to the left Leg of this ACRocket. Ownership is NOT transferred. + Leg * GetLeftLeg() const { return m_pLLeg; } + + /// + /// Sets the left Leg for this ACRocket. + /// + /// The new Leg to use. + void SetLeftLeg(Leg *newLeg); + + /// + /// Gets the main thruster of this ACRocket. + /// + /// A pointer to the main thruster of this ACRocket. Ownership is NOT transferred. + AEmitter * GetMainThruster() const { return m_pMThruster; } + + /// + /// Sets the main thruster for this ACRocket. + /// + /// The new thruster to use. + void SetMainThruster(AEmitter *newThruster); + + /// + /// Gets the right side thruster of this ACRocket. + /// + /// A pointer to the right side thruster of this ACRocket. Ownership is NOT transferred. + AEmitter * GetRightThruster() const { return m_pRThruster; } + + /// + /// Sets the right side thruster for this ACRocket. + /// + /// The new thruster to use. + void SetRightThruster(AEmitter *newThruster); + + /// + /// Gets the left side thruster of this ACRocket. + /// + /// A pointer to the left side thruster of this ACRocket. Ownership is NOT transferred. + AEmitter * GetLeftThruster() const { return m_pLThruster; } + + /// + /// Sets the left side thruster for this ACRocket. + /// + /// The new thruster to use. + void SetLeftThruster(AEmitter *newThruster); + + /// + /// Gets the right side secondary thruster of this ACRocket. + /// + /// A pointer to the right side secondary thruster of this ACRocket. Ownership is NOT transferred. + AEmitter * GetURightThruster() const { return m_pURThruster; } + + /// + /// Sets the right side secondary thruster for this ACRocket. + /// + /// The new thruster to use. + void SetURightThruster(AEmitter *newThruster); + + /// + /// Gets the left side secondary thruster of this ACRocket. + /// + /// A pointer to the left side secondary thruster of this ACRocket. Ownership is NOT transferred. + AEmitter * GetULeftThruster() const { return m_pULThruster; } + + /// + /// Sets the left side secondary thruster for this ACRocket. + /// + /// The new thruster to use. + void SetULeftThruster(AEmitter *newThruster); ////////////////////////////////////////////////////////////////////////////////////////// @@ -335,20 +297,6 @@ ClassInfoGetters protected: -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. -// Arguments: The MOID index to register itself and its children in. -// The MOID of the root MO of this MO, ie the highest parent of this MO. -// 0 means that this MO is the root, ie it is owned by MovableMan. -// Whether this MO should make a new MOID to use for itself, or to use -// the same as the last one in the index (presumably its parent), -// Return value: None. - - void UpdateChildMOIDs(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) override; - // Member variables static Entity::ClassInfo m_sClass; diff --git a/Entities/ACrab.cpp b/Entities/ACrab.cpp index cfb1a4030..993eac03a 100644 --- a/Entities/ACrab.cpp +++ b/Entities/ACrab.cpp @@ -46,9 +46,13 @@ void ACrab::Clear() m_pRFGLeg = 0; m_pRBGLeg = 0; m_pLFGFootGroup = 0; + m_BackupLFGFootGroup = nullptr; m_pLBGFootGroup = 0; + m_BackupLBGFootGroup = nullptr; m_pRFGFootGroup = 0; + m_BackupRFGFootGroup = nullptr; m_pRBGFootGroup = 0; + m_BackupRBGFootGroup = nullptr; m_StrideSound.Reset(); m_pJetpack = 0; m_JetTimeTotal = 0.0; @@ -161,67 +165,64 @@ int ACrab::Create(BITMAP *pSprite, ////////////////////////////////////////////////////////////////////////////////////////// // Description: Creates a ACrab to be identical to another, by deep copy. -int ACrab::Create(const ACrab &reference) -{ - Actor::Create(reference); - - if (reference.m_pTurret) - { - m_pTurret = dynamic_cast(reference.m_pTurret->Clone()); - m_pTurret->SetCanCollideWithTerrainWhenAttached(true); - AddAttachable(m_pTurret, true); +int ACrab::Create(const ACrab &reference) { + //Note - hardcoded attachable copying is organized based on desired draw order here. + if (reference.m_pLBGLeg) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLBGLeg->GetUniqueID()); + SetLeftBGLeg(dynamic_cast(reference.m_pLBGLeg->Clone())); } - - if (reference.m_pJetpack) - { - m_pJetpack = dynamic_cast(reference.m_pJetpack->Clone()); - AddAttachable(m_pJetpack, true); + if (reference.m_pRBGLeg) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRBGLeg->GetUniqueID()); + SetRightBGLeg(dynamic_cast(reference.m_pRBGLeg->Clone())); } - - m_JetTimeTotal = reference.m_JetTimeTotal; - m_JetTimeLeft = reference.m_JetTimeLeft; - - if (reference.m_pLFGLeg) - { - m_pLFGLeg = dynamic_cast(reference.m_pLFGLeg->Clone()); - AddAttachable(m_pLFGLeg, true); + if (reference.m_pJetpack) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pJetpack->GetUniqueID()); + SetJetpack(dynamic_cast(reference.m_pJetpack->Clone())); } - - if (reference.m_pLBGLeg) - { - m_pLBGLeg = dynamic_cast(reference.m_pLBGLeg->Clone()); - AddAttachable(m_pLBGLeg, true); + if (reference.m_pTurret) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pTurret->GetUniqueID()); + SetTurret(dynamic_cast(reference.m_pTurret->Clone())); } - - if (reference.m_pRFGLeg) - { - m_pRFGLeg = dynamic_cast(reference.m_pRFGLeg->Clone()); - AddAttachable(m_pRFGLeg, true); + if (reference.m_pLFGLeg) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pLFGLeg->GetUniqueID()); + SetLeftFGLeg(dynamic_cast(reference.m_pLFGLeg->Clone())); } - - if (reference.m_pRBGLeg) - { - m_pRBGLeg = dynamic_cast(reference.m_pRBGLeg->Clone()); - AddAttachable(m_pRBGLeg, true); + if (reference.m_pRFGLeg) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pRFGLeg->GetUniqueID()); + SetRightFGLeg(dynamic_cast(reference.m_pRFGLeg->Clone())); } + Actor::Create(reference); + + m_JetTimeTotal = reference.m_JetTimeTotal; + m_JetTimeLeft = reference.m_JetTimeLeft; m_pLFGFootGroup = dynamic_cast(reference.m_pLFGFootGroup->Clone()); m_pLFGFootGroup->SetOwner(this); + m_BackupLFGFootGroup = dynamic_cast(reference.m_BackupLFGFootGroup->Clone()); + m_BackupLFGFootGroup->SetOwner(this); + m_BackupLFGFootGroup->SetLimbPos(reference.m_BackupLFGFootGroup->GetLimbPos()); m_pLBGFootGroup = dynamic_cast(reference.m_pLBGFootGroup->Clone()); m_pLBGFootGroup->SetOwner(this); + m_BackupLBGFootGroup = dynamic_cast(reference.m_BackupLBGFootGroup->Clone()); + m_BackupLBGFootGroup->SetOwner(this); + m_BackupLBGFootGroup->SetLimbPos(reference.m_BackupLBGFootGroup->GetLimbPos()); m_pRFGFootGroup = dynamic_cast(reference.m_pRFGFootGroup->Clone()); m_pRFGFootGroup->SetOwner(this); + m_BackupRFGFootGroup = dynamic_cast(reference.m_BackupRFGFootGroup->Clone()); + m_BackupRFGFootGroup->SetOwner(this); + m_BackupRFGFootGroup->SetLimbPos(reference.m_BackupRFGFootGroup->GetLimbPos()); m_pRBGFootGroup = dynamic_cast(reference.m_pRBGFootGroup->Clone()); m_pRBGFootGroup->SetOwner(this); + m_BackupRBGFootGroup = dynamic_cast(reference.m_BackupRBGFootGroup->Clone()); + m_BackupRBGFootGroup->SetOwner(this); + m_BackupRBGFootGroup->SetLimbPos(reference.m_BackupRBGFootGroup->GetLimbPos()); m_StrideSound = reference.m_StrideSound; m_MoveState = reference.m_MoveState; - for (int side = 0; side < SIDECOUNT; ++side) - { - for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) - { + for (int side = 0; side < SIDECOUNT; ++side) { + for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) { m_Paths[side][FGROUND][i].Create(reference.m_Paths[side][FGROUND][i]); m_Paths[side][BGROUND][i].Create(reference.m_Paths[side][BGROUND][i]); } @@ -255,52 +256,50 @@ int ACrab::Create(const ACrab &reference) int ACrab::ReadProperty(std::string propName, Reader &reader) { - if (propName == "Turret") - { - delete m_pTurret; + if (propName == "Turret") { + RemoveAttachable(m_pTurret); m_pTurret = new Turret; reader >> m_pTurret; - if (!m_pTurret->IsDamageMultiplierRedefined()) - m_pTurret->SetDamageMultiplier(5); - } - else if (propName == "Jetpack") - { - delete m_pJetpack; + AddAttachable(m_pTurret); + if (m_pTurret->HasNoSetDamageMultiplier()) { m_pTurret->SetDamageMultiplier(5.0F); } + } else if (propName == "Jetpack") { + RemoveAttachable(m_pJetpack); m_pJetpack = new AEmitter; reader >> m_pJetpack; - } - else if (propName == "JumpTime") - { + AddAttachable(m_pJetpack); + if (m_pJetpack->HasNoSetDamageMultiplier()) { m_pJetpack->SetDamageMultiplier(0.0F); } + m_pJetpack->SetApplyTransferredForcesAtOffset(false); + m_pJetpack->SetDeleteWhenRemovedFromParent(true); + } else if (propName == "JumpTime") { reader >> m_JetTimeTotal; - // Convert to ms m_JetTimeTotal *= 1000; - } - else if (propName == "LFGLeg") - { - delete m_pLFGLeg; + } else if (propName == "LFGLeg" || propName == "LeftFGLeg") { + RemoveAttachable(m_pLFGLeg); m_pLFGLeg = new Leg; reader >> m_pLFGLeg; - } - else if (propName == "LBGLeg") - { - delete m_pLBGLeg; + AddAttachable(m_pLFGLeg); + if (m_pLFGLeg->HasNoSetDamageMultiplier()) { m_pLFGLeg->SetDamageMultiplier(1.0F); } + m_pLFGLeg->SetInheritsHFlipped(-1); + } else if (propName == "LBGLeg" || propName == "LeftFGLeg") { + RemoveAttachable(m_pLBGLeg); m_pLBGLeg = new Leg; reader >> m_pLBGLeg; - } - else if (propName == "RFGLeg") - { - delete m_pRFGLeg; + AddAttachable(m_pLBGLeg); + if (m_pLBGLeg->HasNoSetDamageMultiplier()) { m_pLBGLeg->SetDamageMultiplier(1.0F); } + m_pLBGLeg->SetInheritsHFlipped(-1); + } else if (propName == "RFGLeg" || propName == "LeftFGLeg") { + RemoveAttachable(m_pRFGLeg); m_pRFGLeg = new Leg; reader >> m_pRFGLeg; - } - else if (propName == "RBGLeg") - { - delete m_pRBGLeg; + AddAttachable(m_pRFGLeg); + if (m_pRFGLeg->HasNoSetDamageMultiplier()) { m_pRFGLeg->SetDamageMultiplier(1.0F); } + } else if (propName == "RBGLeg" || propName == "LeftFGLeg") { + RemoveAttachable(m_pRBGLeg); m_pRBGLeg = new Leg; reader >> m_pRBGLeg; - } - else if (propName == "LFootGroup") - { + AddAttachable(m_pRBGLeg); + if (m_pRBGLeg->HasNoSetDamageMultiplier()) { m_pRBGLeg->SetDamageMultiplier(1.0F); } + } else if (propName == "LFootGroup" || propName == "LeftFootGroup") { delete m_pLFGFootGroup; delete m_pLBGFootGroup; m_pLFGFootGroup = new AtomGroup(); @@ -309,9 +308,10 @@ int ACrab::ReadProperty(std::string propName, Reader &reader) m_pLBGFootGroup->Create(*m_pLFGFootGroup); m_pLFGFootGroup->SetOwner(this); m_pLBGFootGroup->SetOwner(this); - } - else if (propName == "RFootGroup") - { + m_BackupLFGFootGroup = new AtomGroup(*m_pLFGFootGroup); + m_BackupLFGFootGroup->RemoveAllAtoms(); + m_BackupLBGFootGroup = new AtomGroup(*m_BackupLFGFootGroup); + } else if (propName == "RFootGroup" || propName == "RightFootGroup") { delete m_pRFGFootGroup; delete m_pRBGFootGroup; m_pRFGFootGroup = new AtomGroup(); @@ -320,27 +320,30 @@ int ACrab::ReadProperty(std::string propName, Reader &reader) m_pRBGFootGroup->Create(*m_pRFGFootGroup); m_pRFGFootGroup->SetOwner(this); m_pRBGFootGroup->SetOwner(this); - } - else if (propName == "StrideSound") + m_BackupRFGFootGroup = new AtomGroup(*m_pRFGFootGroup); + m_BackupRFGFootGroup->RemoveAllAtoms(); + m_BackupRBGFootGroup = new AtomGroup(*m_BackupRFGFootGroup); + } else if (propName == "StrideSound") { reader >> m_StrideSound; - else if (propName == "LStandLimbPath") + } else if (propName == "LStandLimbPath" || propName == "LeftStandLimbPath") { reader >> m_Paths[LEFTSIDE][FGROUND][STAND]; - else if (propName == "LWalkLimbPath") + } else if (propName == "LWalkLimbPath" || propName == "LeftWalkLimbPath") { reader >> m_Paths[LEFTSIDE][FGROUND][WALK]; - else if (propName == "LDislodgeLimbPath") + } else if (propName == "LDislodgeLimbPath" || propName == "LeftDislodgeLimbPath") { reader >> m_Paths[LEFTSIDE][FGROUND][DISLODGE]; - else if (propName == "RStandLimbPath") + } else if (propName == "RStandLimbPath" || propName == "RightStandLimbPath") { reader >> m_Paths[RIGHTSIDE][FGROUND][STAND]; - else if (propName == "RWalkLimbPath") + } else if (propName == "RWalkLimbPath" || propName == "RightWalkLimbPath") { reader >> m_Paths[RIGHTSIDE][FGROUND][WALK]; - else if (propName == "RDislodgeLimbPath") + } else if (propName == "RDislodgeLimbPath" || propName == "RightDislodgeLimbPath") { reader >> m_Paths[RIGHTSIDE][FGROUND][DISLODGE]; - else if (propName == "AimRangeUpperLimit") + } else if (propName == "AimRangeUpperLimit") { reader >> m_AimRangeUpperLimit; - else if (propName == "AimRangeLowerLimit") + } else if (propName == "AimRangeLowerLimit") { reader >> m_AimRangeLowerLimit; - else + } else { return Actor::ReadProperty(propName, reader); + } return 0; } @@ -452,12 +455,6 @@ int ACrab::Save(ostream &stream) const void ACrab::Destroy(bool notInherited) { - delete m_pTurret; - delete m_pLFGLeg; - delete m_pLBGLeg; - delete m_pRFGLeg; - delete m_pRBGLeg; - delete m_pJetpack; delete m_pLFGFootGroup; delete m_pLBGFootGroup; delete m_pRFGFootGroup; @@ -471,29 +468,6 @@ void ACrab::Destroy(bool notInherited) Clear(); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this ACrab, including the mass of its -// currently attached body parts and inventory. - -float ACrab::GetMass() const -{ - float totalMass = Actor::GetMass(); - if (m_pTurret) - totalMass += m_pTurret->GetMass(); - if (m_pLFGLeg) - totalMass += m_pLFGLeg->GetMass(); - if (m_pLBGLeg) - totalMass += m_pLBGLeg->GetMass(); - if (m_pRFGLeg) - totalMass += m_pRFGLeg->GetMass(); - if (m_pRBGLeg) - totalMass += m_pRBGLeg->GetMass(); - return totalMass; -} - /* ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetTotalValue @@ -531,33 +505,128 @@ Vector ACrab::GetCPUPos() const Vector ACrab::GetEyePos() const { - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->GetMountedMO()) - return m_pTurret->GetMountedMO()->GetPos(); + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) + return m_pTurret->GetMountedDevice()->GetPos(); return m_Pos; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. +void ACrab::SetTurret(Turret *newTurret) { + if (newTurret == nullptr) { + if (m_pTurret && m_pTurret->IsAttached()) { RemoveAttachable(m_pTurret); } + m_pTurret = nullptr; + } else { + if (m_pTurret && m_pTurret->IsAttached()) { RemoveAttachable(m_pTurret); } + m_pTurret = newTurret; + AddAttachable(newTurret); -void ACrab::SetID(const MOID newID) -{ - MovableObject::SetID(newID); - if (m_pTurret) - m_pTurret->SetID(newID); - if (m_pLFGLeg) - m_pLFGLeg->SetID(newID); - if (m_pLBGLeg) - m_pLBGLeg->SetID(newID); - if (m_pRFGLeg) - m_pRFGLeg->SetID(newID); - if (m_pRBGLeg) - m_pRBGLeg->SetID(newID); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newTurret->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + Turret *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetTurret"); + dynamic_cast(parent)->SetTurret(castedAttachable); + }}); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ACrab::SetJetpack(AEmitter *newJetpack) { + if (newJetpack == nullptr) { + if (m_pJetpack && m_pJetpack->IsAttached()) { RemoveAttachable(m_pJetpack); } + m_pJetpack = nullptr; + } else { + if (m_pJetpack && m_pJetpack->IsAttached()) { RemoveAttachable(m_pJetpack); } + m_pJetpack = newJetpack; + AddAttachable(newJetpack); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newJetpack->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + AEmitter *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetJetpack"); + dynamic_cast(parent)->SetJetpack(castedAttachable); + }}); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ACrab::SetLeftFGLeg(Leg *newLeg) { + if (newLeg == nullptr) { + if (m_pLFGLeg && m_pLFGLeg->IsAttached()) { RemoveAttachable(m_pLFGLeg); } + m_pLFGLeg = nullptr; + } else { + if (m_pLFGLeg && m_pLFGLeg->IsAttached()) { RemoveAttachable(m_pLFGLeg); } + m_pLFGLeg = newLeg; + AddAttachable(newLeg); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + Leg *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftFGLeg"); + dynamic_cast(parent)->SetLeftFGLeg(castedAttachable); + }}); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ACrab::SetLeftBGLeg(Leg *newLeg) { + if (newLeg == nullptr) { + if (m_pLBGLeg && m_pLBGLeg->IsAttached()) { RemoveAttachable(m_pLBGLeg); } + m_pLBGLeg = nullptr; + } else { + if (m_pLBGLeg && m_pLBGLeg->IsAttached()) { RemoveAttachable(m_pLBGLeg); } + m_pLBGLeg = newLeg; + AddAttachable(newLeg); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + Leg *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetLeftBGLeg"); + dynamic_cast(parent)->SetLeftBGLeg(castedAttachable); + }}); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ACrab::SetRightFGLeg(Leg *newLeg) { + if (newLeg == nullptr) { + if (m_pRFGLeg && m_pRFGLeg->IsAttached()) { RemoveAttachable(m_pRFGLeg); } + m_pRFGLeg = nullptr; + } else { + if (m_pRFGLeg && m_pRFGLeg->IsAttached()) { RemoveAttachable(m_pRFGLeg); } + m_pRFGLeg = newLeg; + AddAttachable(newLeg); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + Leg *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightFGLeg"); + dynamic_cast(parent)->SetRightFGLeg(castedAttachable); + }}); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ACrab::SetRightBGLeg(Leg *newLeg) { + if (newLeg == nullptr) { + if (m_pRBGLeg && m_pRBGLeg->IsAttached()) { RemoveAttachable(m_pRBGLeg); } + m_pRBGLeg = nullptr; + } else { + if (m_pRBGLeg && m_pRBGLeg->IsAttached()) { RemoveAttachable(m_pRBGLeg); } + m_pRBGLeg = newLeg; + AddAttachable(newLeg); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + Leg *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetRightBGLeg"); + dynamic_cast(parent)->SetRightBGLeg(castedAttachable); + }}); + } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: CollideAtPoint @@ -664,7 +733,7 @@ bool ACrab::AddPieMenuSlices(PieMenuGUI *pPieMenu) Actor::AddPieMenuSlices(pPieMenu); // Add any custom slices from a currently held device - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->IsHeldDeviceMounted()) + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) m_pTurret->GetMountedDevice()->AddPieMenuSlices(pPieMenu); return true; @@ -710,22 +779,6 @@ bool ACrab::HandlePieCommand(int pieSliceIndex) } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: GetTurret -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns any attached turret. - -Attachable * ACrab::GetTurret() const -{ - if (m_pTurret && m_pTurret->IsAttached()) - { - return dynamic_cast(m_pTurret); - } - - return 0; -} - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual Method: GetEquippedItem ////////////////////////////////////////////////////////////////////////////////////////// @@ -733,9 +786,9 @@ Attachable * ACrab::GetTurret() const MovableObject * ACrab::GetEquippedItem() const { - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->IsSomethingMounted()) + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { - return m_pTurret->GetMountedMO(); + return m_pTurret->GetMountedDevice(); } return 0; @@ -750,9 +803,9 @@ MovableObject * ACrab::GetEquippedItem() const bool ACrab::FirearmIsReady() const { - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->IsSomethingMounted()) + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { - HDFirearm *pWeapon = dynamic_cast(m_pTurret->GetMountedMO()); + HDFirearm *pWeapon = dynamic_cast(m_pTurret->GetMountedDevice()); if (pWeapon && pWeapon->GetRoundInMagCount() != 0) return true; } @@ -768,9 +821,9 @@ bool ACrab::FirearmIsReady() const bool ACrab::FirearmIsEmpty() const { - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->IsHeldDeviceMounted()) + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { - HDFirearm *pWeapon = dynamic_cast(m_pTurret->GetMountedMO()); + HDFirearm *pWeapon = dynamic_cast(m_pTurret->GetMountedDevice()); if (pWeapon && pWeapon->GetRoundInMagCount() == 0) return true; } @@ -786,9 +839,9 @@ bool ACrab::FirearmIsEmpty() const bool ACrab::FirearmNeedsReload() const { - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->IsHeldDeviceMounted()) + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { - HDFirearm *pWeapon = dynamic_cast(m_pTurret->GetMountedMO()); + HDFirearm *pWeapon = dynamic_cast(m_pTurret->GetMountedDevice()); if (pWeapon && pWeapon->NeedsReloading()) return true; } @@ -804,9 +857,9 @@ bool ACrab::FirearmNeedsReload() const bool ACrab::FirearmIsSemiAuto() const { - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->IsHeldDeviceMounted()) + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { - HDFirearm *pWeapon = dynamic_cast(m_pTurret->GetMountedMO()); + HDFirearm *pWeapon = dynamic_cast(m_pTurret->GetMountedDevice()); return pWeapon && !pWeapon->IsFullAuto(); } return false; @@ -822,9 +875,9 @@ bool ACrab::FirearmIsSemiAuto() const void ACrab::ReloadFirearm() { - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->IsHeldDeviceMounted()) + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { - HDFirearm *pWeapon = dynamic_cast(m_pTurret->GetMountedMO()); + HDFirearm *pWeapon = dynamic_cast(m_pTurret->GetMountedDevice()); if (pWeapon) pWeapon->Reload(); } @@ -840,9 +893,9 @@ void ACrab::ReloadFirearm() int ACrab::FirearmActivationDelay() const { // Check if the currently held device is already the desired type - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->IsHeldDeviceMounted()) + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { - HDFirearm *pWeapon = dynamic_cast(m_pTurret->GetMountedMO()); + HDFirearm *pWeapon = dynamic_cast(m_pTurret->GetMountedDevice()); if (pWeapon) return pWeapon->GetActivationDelay(); } @@ -873,7 +926,7 @@ bool ACrab::IsWithinRange(Vector &point) const float range = m_AimDistance; // Add the sharp range of the equipped weapon - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->IsHeldDeviceMounted()) + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { range += m_pTurret->GetMountedDevice()->GetSharpLength() * m_SharpAimProgress; } @@ -899,7 +952,7 @@ bool ACrab::Look(float FOVSpread, float range) Vector aimPos = GetCPUPos(); // If aiming down the barrel, look through that - if (m_Controller.IsState(AIM_SHARP) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->IsHeldDeviceMounted()) + if (m_Controller.IsState(AIM_SHARP) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { aimPos = m_pTurret->GetMountedDevice()->GetPos(); aimDistance += m_pTurret->GetMountedDevice()->GetSharpLength(); @@ -941,7 +994,7 @@ MovableObject * ACrab::LookForMOs(float FOVSpread, unsigned char ignoreMaterial, float aimDistance = m_AimDistance + g_FrameMan.GetPlayerScreenWidth() * 0.51; // Set the length of the look vector // If aiming down the barrel, look through that - if (m_Controller.IsState(AIM_SHARP) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->IsHeldDeviceMounted()) + if (m_Controller.IsState(AIM_SHARP) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { aimPos = m_pTurret->GetMountedDevice()->GetPos(); aimDistance += m_pTurret->GetMountedDevice()->GetSharpLength(); @@ -971,86 +1024,6 @@ MovableObject * ACrab::LookForMOs(float FOVSpread, unsigned char ignoreMaterial, } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. - -void ACrab::GibThis(Vector impactImpulse, float internalBlast, MovableObject *pIgnoreMO) -{ - // Detach all limbs and let loose - if (m_pTurret && m_pTurret->IsAttached()) - { - RemoveAttachable(m_pTurret); - m_pTurret->SetVel(m_Vel + m_pTurret->GetParentOffset() * RandomNum()); - m_pTurret->SetAngularVel(RandomNormalNum()); - g_MovableMan.AddParticle(m_pTurret); - m_pTurret = 0; - } - if (m_pJetpack && m_pJetpack->IsAttached()) - { - // Jetpacks are really nothing, so just delete them safely - RemoveAttachable(m_pJetpack); - m_pJetpack->SetToDelete(true); - g_MovableMan.AddParticle(m_pJetpack); - m_pJetpack = 0; - } - if (m_pLFGLeg && m_pLFGLeg->IsAttached()) - { - RemoveAttachable(m_pLFGLeg); - m_pLFGLeg->SetVel(m_Vel + m_pLFGLeg->GetParentOffset() * RandomNum()); - m_pLFGLeg->SetAngularVel(RandomNormalNum()); - g_MovableMan.AddParticle(m_pLFGLeg); - m_pLFGLeg = 0; - } - if (m_pLBGLeg && m_pLBGLeg->IsAttached()) - { - RemoveAttachable(m_pLBGLeg); - m_pLBGLeg->SetVel(m_Vel + m_pLBGLeg->GetParentOffset() * RandomNum()); - m_pLBGLeg->SetAngularVel(RandomNormalNum()); - g_MovableMan.AddParticle(m_pLBGLeg); - m_pLBGLeg = 0; - } - if (m_pRFGLeg && m_pRFGLeg->IsAttached()) - { - RemoveAttachable(m_pRFGLeg); - m_pRFGLeg->SetVel(m_Vel + m_pRFGLeg->GetParentOffset() * RandomNum()); - m_pRFGLeg->SetAngularVel(RandomNormalNum()); - g_MovableMan.AddParticle(m_pRFGLeg); - m_pRFGLeg = 0; - } - if (m_pRBGLeg && m_pRBGLeg->IsAttached()) - { - RemoveAttachable(m_pRBGLeg); - m_pRBGLeg->SetVel(m_Vel + m_pRBGLeg->GetParentOffset() * RandomNum()); - m_pRBGLeg->SetAngularVel(RandomNormalNum()); - g_MovableMan.AddParticle(m_pRBGLeg); - m_pRBGLeg = 0; - } - - Actor::GibThis(impactImpulse, internalBlast, pIgnoreMO); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. - -bool ACrab::IsOnScenePoint(Vector &scenePoint) const -{ - return ((m_pTurret && m_pTurret->IsOnScenePoint(scenePoint)) || - (m_pLFGLeg && m_pLFGLeg->IsOnScenePoint(scenePoint)) || - (m_pRFGLeg && m_pRFGLeg->IsOnScenePoint(scenePoint)) || - Actor::IsOnScenePoint(scenePoint) || - (m_pJetpack && m_pJetpack->IsOnScenePoint(scenePoint)) || - (m_pLBGLeg && m_pLBGLeg->IsOnScenePoint(scenePoint)) || - (m_pRBGLeg && m_pRBGLeg->IsOnScenePoint(scenePoint))); -} - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: UpdateMovePath ////////////////////////////////////////////////////////////////////////////////////////// @@ -2455,17 +2428,14 @@ void ACrab::Update() //////////////////////////////////// // Fire/Activate held devices - if (m_pTurret && m_pTurret->IsAttached()) + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { // Activate held device, if a device is held. - if (m_pTurret->IsHeldDeviceMounted()) - { - m_pTurret->GetMountedDevice()->SetSharpAim(m_SharpAimProgress); - if (m_Controller.IsState(WEAPON_FIRE)) - m_pTurret->GetMountedDevice()->Activate(); - else - m_pTurret->GetMountedDevice()->Deactivate(); - } + m_pTurret->GetMountedDevice()->SetSharpAim(m_SharpAimProgress); + if (m_Controller.IsState(WEAPON_FIRE)) + m_pTurret->GetMountedDevice()->Activate(); + else + m_pTurret->GetMountedDevice()->Deactivate(); } // Controller disabled @@ -2485,6 +2455,24 @@ void ACrab::Update() if (m_Status == STABLE) { + // This exists to support disabling foot collisions if the limbpath has that flag set. + if ((m_pLFGFootGroup->GetAtomCount() == 0 && m_BackupLFGFootGroup->GetAtomCount() > 0) != m_Paths[LEFTSIDE][FGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { + m_BackupLFGFootGroup->SetLimbPos(m_pLFGFootGroup->GetLimbPos()); + std::swap(m_pLFGFootGroup, m_BackupLFGFootGroup); + } + if ((m_pLBGFootGroup->GetAtomCount() == 0 && m_BackupLBGFootGroup->GetAtomCount() > 0) != m_Paths[LEFTSIDE][BGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { + m_BackupLBGFootGroup->SetLimbPos(m_pLBGFootGroup->GetLimbPos()); + std::swap(m_pLBGFootGroup, m_BackupLBGFootGroup); + } + if ((m_pRFGFootGroup->GetAtomCount() == 0 && m_BackupRFGFootGroup->GetAtomCount() > 0) != m_Paths[RIGHTSIDE][FGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { + m_BackupRFGFootGroup->SetLimbPos(m_pRFGFootGroup->GetLimbPos()); + std::swap(m_pRFGFootGroup, m_BackupRFGFootGroup); + } + if ((m_pRBGFootGroup->GetAtomCount() == 0 && m_BackupRBGFootGroup->GetAtomCount() > 0) != m_Paths[RIGHTSIDE][BGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { + m_BackupRBGFootGroup->SetLimbPos(m_pRBGFootGroup->GetLimbPos()); + std::swap(m_pRBGFootGroup, m_BackupRBGFootGroup); + } + // WALKING if (m_MoveState == WALK) { @@ -2703,13 +2691,38 @@ void ACrab::Update() } } + ///////////////////////////////// + // Manage Attachable:s + if (m_pTurret && m_pTurret->IsAttached()) { + m_pTurret->SetMountedDeviceRotOffset((m_AimAngle * static_cast(GetFlipFactor())) - m_Rotation.GetRadAngle()); + } + + if (m_pLFGLeg && m_pLFGLeg->IsAttached()) { + m_pLFGLeg->EnableIdle(m_Status != UNSTABLE); + m_pLFGLeg->SetTargetPosition(m_pLFGFootGroup->GetLimbPos(m_HFlipped)); + } + + if (m_pLBGLeg && m_pLBGLeg->IsAttached()) { + m_pLBGLeg->EnableIdle(m_Status != UNSTABLE); + m_pLBGLeg->SetTargetPosition(m_pLBGFootGroup->GetLimbPos(m_HFlipped)); + } + + if (m_pRFGLeg && m_pRFGLeg->IsAttached()) { + m_pRFGLeg->EnableIdle(m_Status != UNSTABLE); + m_pRFGLeg->SetTargetPosition(m_pRFGFootGroup->GetLimbPos(m_HFlipped)); + } + + if (m_pRBGLeg && m_pRBGLeg->IsAttached()) { + m_pRBGLeg->EnableIdle(m_Status != UNSTABLE); + m_pRBGLeg->SetTargetPosition(m_pRBGFootGroup->GetLimbPos(m_HFlipped)); + } + + ///////////////////////////////////////////////// // Update MovableObject, adds on the forces etc // NOTE: this also updates the controller, so any setstates of it will be wiped! - Actor::Update(); - //////////////////////////////////// // Update viewpoint @@ -2720,7 +2733,7 @@ void ACrab::Update() // Reset this each frame m_SharpAimMaxedOut = false; - if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->IsHeldDeviceMounted()) + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { float maxLength = m_pTurret->GetMountedDevice()->GetSharpLength(); @@ -2757,97 +2770,6 @@ void ACrab::Update() if (m_Vel.GetMagnitude() > 10.0) m_ViewPoint += m_Vel * 6; - - ///////////////////////////////// - // Update Attachable:s - - if (m_pTurret && m_pTurret->IsAttached()) - { - m_pTurret->SetHFlipped(m_HFlipped); - m_pTurret->SetJointPos(m_Pos + m_pTurret->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - m_pTurret->SetRotAngle(m_Rotation.GetRadAngle()); - m_pTurret->SetMountedRotOffset((m_HFlipped ? -m_AimAngle : m_AimAngle) - m_Rotation.GetRadAngle()); - m_pTurret->Update(); - // Update the Atoms' offsets in the parent group -// Matrix atomRot(FacingAngle(m_pTurret->GetRotMatrix().GetRadAngle()) - FacingAngle(m_Rotation.GetRadAngle())); -// m_pAtomGroup->UpdateSubAtoms(m_pTurret->GetAtomSubgroupID(), m_pTurret->GetParentOffset() - (m_pTurret->GetJointOffset() * atomRot), atomRot); - - m_Health -= m_pTurret->CollectDamage();// * 5; - } - - if (m_pJetpack && m_pJetpack->IsAttached()) - { - m_pJetpack->SetHFlipped(m_HFlipped); - m_pJetpack->SetJointPos(m_Pos + m_pJetpack->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - m_pJetpack->SetRotAngle(m_Rotation.GetRadAngle()); - m_pJetpack->SetOnlyLinearForces(true); - m_pJetpack->Update(); -// m_Health -= m_pJetpack->CollectDamage() * 10; - } - - if (m_pLFGLeg && m_pLFGLeg->IsAttached()) - { - // Left legs always flipped the other way - m_pLFGLeg->SetHFlipped(!m_HFlipped); - // Don't flip the parent offset though, that's probably done in the ini - m_pLFGLeg->SetJointPos(m_Pos + m_pLFGLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - // Only have the leg go to idle position if the limb target is over the joint and if we're firing the jetpack... looks retarded otherwise - m_pLFGLeg->EnableIdle(m_Status != UNSTABLE); - m_pLFGLeg->ReachToward(m_pLFGFootGroup->GetLimbPos(m_HFlipped)); - m_pLFGLeg->Update(); - m_Health -= m_pLFGLeg->CollectDamage(); - } - - if (m_pLBGLeg && m_pLBGLeg->IsAttached()) - { - // Left legs always flipped the other way - m_pLBGLeg->SetHFlipped(!m_HFlipped); - // Don't flip the parent offset though, that's probably done in the ini - m_pLBGLeg->SetJointPos(m_Pos + m_pLBGLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - // Only have the leg go to idle position if the limb target is over the joint and if we're firing the jetpack... looks retarded otherwise - m_pLBGLeg->EnableIdle(m_Status != UNSTABLE); - m_pLBGLeg->ReachToward(m_pLBGFootGroup->GetLimbPos(m_HFlipped)); - m_pLBGLeg->Update(); - m_Health -= m_pLBGLeg->CollectDamage(); - } - - if (m_pRFGLeg && m_pRFGLeg->IsAttached()) - { - m_pRFGLeg->SetHFlipped(m_HFlipped); - m_pRFGLeg->SetJointPos(m_Pos + m_pRFGLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); // Only have the leg go to idle position if the limb target is over the joint and if we're firing the jetpack... looks retarded otherwise - m_pRFGLeg->EnableIdle(m_Status != UNSTABLE); - m_pRFGLeg->ReachToward(m_pRFGFootGroup->GetLimbPos(m_HFlipped)); - m_pRFGLeg->Update(); - m_Health -= m_pRFGLeg->CollectDamage(); - } - - if (m_pRBGLeg && m_pRBGLeg->IsAttached()) - { - m_pRBGLeg->SetHFlipped(m_HFlipped); - m_pRBGLeg->SetJointPos(m_Pos + m_pRBGLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - // Only have the leg go to idle position if the limb target is over the joint and if we're firing the jetpack... looks retarded otherwise - m_pRBGLeg->EnableIdle(m_Status != UNSTABLE); - m_pRBGLeg->ReachToward(m_pRBGFootGroup->GetLimbPos(m_HFlipped)); - m_pRBGLeg->Update(); - m_Health -= m_pRBGLeg->CollectDamage(); - } - - ///////////////////////////// - // Apply forces transferred from the attachables and - // add detachment wounds to this if applicable - - if (!ApplyAttachableForces(m_pTurret)) - m_pTurret = 0; - if (!ApplyAttachableForces(m_pJetpack)) - m_pJetpack = 0; - if (!ApplyAttachableForces(m_pLFGLeg)) - m_pLFGLeg = 0; - if (!ApplyAttachableForces(m_pLBGLeg)) - m_pLBGLeg = 0; - if (!ApplyAttachableForces(m_pRFGLeg)) - m_pRFGLeg = 0; - if (!ApplyAttachableForces(m_pRBGLeg)) - m_pRBGLeg = 0; /* Done by pie menu now, see HandlePieCommand() //////////////////////////////////////// // AI mode setting @@ -2940,202 +2862,20 @@ void ACrab::Update() } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. - -void ACrab::UpdateChildMOIDs(vector &MOIDIndex, - MOID rootMOID, - bool makeNewMOID) -{ - if (m_pLBGLeg) - m_pLBGLeg->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - if (m_pRBGLeg) - m_pRBGLeg->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - if (m_pJetpack) - m_pJetpack->UpdateMOID(MOIDIndex, m_RootMOID, false); - if (m_pLFGLeg) - m_pLFGLeg->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - if (m_pRFGLeg) - m_pRFGLeg->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - if (m_pTurret) - m_pTurret->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - - Actor::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - -void ACrab::GetMOIDs(std::vector &MOIDs) const -{ - if (m_pLBGLeg) - m_pLBGLeg->GetMOIDs(MOIDs); - if (m_pRBGLeg) - m_pRBGLeg->GetMOIDs(MOIDs); - if (m_pJetpack) - m_pJetpack->GetMOIDs(MOIDs); - if (m_pLFGLeg) - m_pLFGLeg->GetMOIDs(MOIDs); - if (m_pRFGLeg) - m_pRFGLeg->GetMOIDs(MOIDs); - if (m_pTurret) - m_pTurret->GetMOIDs(MOIDs); - - Actor::GetMOIDs(MOIDs); -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveAnyRandomWounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specified amount of wounds from the actor and all standard attachables. - -int ACrab::RemoveAnyRandomWounds(int amount) -{ - float damage = 0; - - for (int i = 0; i < amount; i++) - { - // Fill the list of damaged bodyparts - std::vector bodyParts; - if (GetWoundCount() > 0) - bodyParts.push_back(this); - - if (m_pLBGLeg && m_pLBGLeg->GetWoundCount()) - bodyParts.push_back(m_pLBGLeg); - if (m_pRBGLeg && m_pRBGLeg->GetWoundCount()) - bodyParts.push_back(m_pRBGLeg); - if (m_pJetpack && m_pJetpack->GetWoundCount()) - bodyParts.push_back(m_pJetpack); - if (m_pLFGLeg && m_pLFGLeg->GetWoundCount()) - bodyParts.push_back(m_pLFGLeg); - if (m_pRFGLeg && m_pRFGLeg->GetWoundCount()) - bodyParts.push_back(m_pRFGLeg); - if (m_pTurret && m_pTurret->GetWoundCount()) - bodyParts.push_back(m_pTurret); - - // Stop removing wounds if there are not any left - if (bodyParts.size() == 0) - break; - - int partIndex = RandomNum(0, bodyParts.size() - 1); - MOSRotating * part = bodyParts[partIndex]; - damage += part->RemoveWounds(1); - } - - return damage; -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound count of this actor and all vital attachables. - -int ACrab::GetTotalWoundCount() const -{ - int count = Actor::GetWoundCount(); - - if (m_pLBGLeg) - count += m_pLBGLeg->GetWoundCount(); - if (m_pRBGLeg) - count += m_pRBGLeg->GetWoundCount(); - if (m_pJetpack) - count += m_pJetpack->GetWoundCount(); - if (m_pLFGLeg) - count += m_pLFGLeg->GetWoundCount(); - if (m_pRFGLeg) - count += m_pRFGLeg->GetWoundCount(); - if (m_pTurret) - count += m_pTurret->GetWoundCount(); - - return count; -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound limit of this actor and all vital attachables. - -int ACrab::GetTotalWoundLimit() const -{ - int count = Actor::GetGibWoundLimit(); - - if (m_pLBGLeg) - count += m_pLBGLeg->GetGibWoundLimit(); - if (m_pRBGLeg) - count += m_pRBGLeg->GetGibWoundLimit(); - if (m_pJetpack) - count += m_pJetpack->GetGibWoundLimit(); - if (m_pLFGLeg) - count += m_pLFGLeg->GetGibWoundLimit(); - if (m_pRFGLeg) - count += m_pRFGLeg->GetGibWoundLimit(); - if (m_pTurret) - count += m_pTurret->GetGibWoundLimit(); - - return count; -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ACrab's current graphical representation to a -// BITMAP of choice. - -void ACrab::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const -{ - // Override color drawing with flash, if requested. - DrawMode realMode = (mode == g_DrawColor && m_FlashWhiteMS) ? g_DrawWhite : mode; - - if (m_pLBGLeg) - m_pLBGLeg->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); - if (m_pRBGLeg) - m_pRBGLeg->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); - if (m_pJetpack) - m_pJetpack->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); - if (m_pTurret && !m_pTurret->IsDrawnAfterParent()) - m_pTurret->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef DEBUG_BUILD +void ACrab::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { Actor::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pTurret && m_pTurret->IsDrawnAfterParent()) - m_pTurret->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); - if (m_pRFGLeg) - m_pRFGLeg->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); - if (m_pLFGLeg) - m_pLFGLeg->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); - -#ifdef DEBUG_BUILD -// if (mode == g_DrawDebug) - if (mode == g_DrawColor && !onlyPhysical) - { - acquire_bitmap(pTargetBitmap); - putpixel(pTargetBitmap, std::floor(m_Pos.m_X), - std::floor(m_Pos.m_Y), - 64); - putpixel(pTargetBitmap, std::floor(m_Pos.m_X), - std::floor(m_Pos.m_Y), - 64); - release_bitmap(pTargetBitmap); - - m_pAtomGroup->Draw(pTargetBitmap, targetPos, false, 122); + if (mode == g_DrawColor && !onlyPhysical) { m_pLFGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); m_pLBGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); m_pRFGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); m_pRBGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); } -#endif } +#endif ////////////////////////////////////////////////////////////////////////////////////////// @@ -3189,7 +2929,7 @@ void ACrab::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichScr // Player AI drawing // Device aiming reticule - if (m_Controller.IsState(AIM_SHARP) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->IsHeldDeviceMounted()) + if (m_Controller.IsState(AIM_SHARP) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) m_pTurret->GetMountedDevice()->DrawHUD(pTargetBitmap, targetPos, whichScreen, m_Controller.IsPlayerControlled()); ////////////////////////////////////// diff --git a/Entities/ACrab.h b/Entities/ACrab.h index 1c4ac5963..b6100ff53 100644 --- a/Entities/ACrab.h +++ b/Entities/ACrab.h @@ -16,17 +16,15 @@ #include "Actor.h" #include "LimbPath.h" +#include "Leg.h" struct BITMAP; namespace RTE { -class Attachable; class Turret; -class Leg; class AEmitter; -//class LimbPath; ////////////////////////////////////////////////////////////////////////////////////////// @@ -48,13 +46,13 @@ class ACrab : MOVEMENTSTATECOUNT }; - enum { + enum Side { LEFTSIDE = 0, RIGHTSIDE, SIDECOUNT }; - enum { + enum Layer { FGROUND = 0, BGROUND, LAYERCOUNT @@ -136,17 +134,6 @@ class ACrab : void Destroy(bool notInherited = false) override; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this ACrab, including the mass of its -// currently attached body parts and inventory. -// Arguments: None. -// Return value: A float describing the mass value in Kilograms (kg). - - float GetMass() const override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetGoldCarried ////////////////////////////////////////////////////////////////////////////////////////// @@ -167,54 +154,77 @@ class ACrab : Vector GetEyePos() const override; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLFGLeg -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the left FG Leg as an Attachable. This is for Lua binding mostly. -// Arguments: None. -// Return value: A pointer to the Leg Attachable. Ownership is NOT transferred! - - Attachable * GetLFGLeg() const { return (Attachable *)m_pLFGLeg; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLBGLeg -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the left BG Leg as an Attachable. This is for Lua binding mostly. -// Arguments: None. -// Return value: A pointer to the Leg Attachable. Ownership is NOT transferred! - - Attachable * GetLBGLeg() const { return (Attachable *)m_pLBGLeg; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRFGLeg -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the right FG Leg as an Attachable. This is for Lua binding mostly. -// Arguments: None. -// Return value: A pointer to the Leg Attachable. Ownership is NOT transferred! - - Attachable * GetRFGLeg() const { return (Attachable *)m_pRFGLeg; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetLFGLeg -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the right BG Leg as an Attachable. This is for Lua binding mostly. -// Arguments: None. -// Return value: A pointer to the Leg Attachable. Ownership is NOT transferred! - - Attachable * GetRBGLeg() const { return (Attachable *)m_pRBGLeg; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetJetpack -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the jetpack as an emitter. This is for Lua binding mostly. -// Arguments: None. -// Return value: A pointer to jetpack emitter. Ownership is NOT transferred! - - AEmitter * GetJetpack() const { return (AEmitter *)m_pJetpack; } + /// + /// Gets the Turret of this ACrab. + /// + /// A pointer to Turret of this ACrab. Ownership is NOT transferred! + Turret * GetTurret() const { return m_pTurret; } + + /// + /// Sets the Turret for this ACrab. Ownership IS transferred! + /// + /// The new Turret to use. + void SetTurret(Turret *newTurret); + + /// + /// Gets the jetpack of this ACrab. + /// + /// A pointer to the jetpack of this ACrab. Ownership is NOT transferred! + AEmitter * GetJetpack() const { return m_pJetpack; } + + /// + /// Sets the jetpack for this ACrab. Ownership IS Transferred! + /// + /// The new jetpack to use. + void SetJetpack(AEmitter *newJetpack); + + /// + /// Gets the left foreground Leg of this ACrab. + /// + /// A pointer to the left foreground Leg of this ACrab. Ownership is NOT transferred! + Leg * GetLeftFGLeg() const { return m_pLFGLeg; } + + /// + /// Sets the left foreground Leg for this ACrab. Ownership IS transferred! + /// + /// The new Leg to use. + void SetLeftFGLeg(Leg *newLeg); + + /// + /// Gets the left background Leg of this ACrab. + /// + /// A pointer to the left background Leg of this ACrab. Ownership is NOT transferred! + Leg * GetLeftBGLeg() const { return m_pLBGLeg; } + + /// + /// Sets the left background Leg for this ACrab. Ownership IS transferred! + /// + /// The new Leg to use. + void SetLeftBGLeg(Leg *newLeg); + + /// + /// Gets the right foreground Leg of this ACrab. + /// + /// A pointer to the right foreground Leg of this ACrab. Ownership is NOT transferred! + Leg * GetRightFGLeg() const { return m_pRFGLeg; } + + /// + /// Sets the right foreground Leg for this ACrab. Ownership IS transferred! + /// + /// The new Leg to use. + void SetRightFGLeg(Leg *newLeg); + + /// + /// Gets the right BG Leg of this ACrab. + /// + /// A pointer to the right background Leg of this ACrab. Ownership is NOT transferred! + Leg * GetRightBGLeg() const { return m_pRBGLeg; } + + /// + /// Sets the right background Leg for this ACrab. Ownership IS transferred! + /// + /// The new Leg to use. + void SetRightBGLeg(Leg *newLeg); ////////////////////////////////////////////////////////////////////////////////////////// @@ -245,17 +255,6 @@ class ACrab : float GetJetTimeLeft() const { return m_JetTimeLeft; } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. -// Arguments: A MOID specifying the MOID that this MovableObject is -// assigned for this frame. -// Return value: None. - - void SetID(const MOID newID) override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: CollideAtPoint ////////////////////////////////////////////////////////////////////////////////////////// @@ -292,16 +291,6 @@ class ACrab : bool HandlePieCommand(int pieSliceIndex) override; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetEquippedItem -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns any equipped turret. -// Arguments: None. -// Return value: A pointer to an attachable. - - Attachable * GetTurret() const; - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetEquippedItem ////////////////////////////////////////////////////////////////////////////////////////// @@ -412,30 +401,6 @@ int FirearmActivationDelay() const; MovableObject * LookForMOs(float FOVSpread = 45, unsigned char ignoreMaterial = 0, bool ignoreAllTerrain = false); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. -// Arguments: The impulse (kg * m/s) of the impact causing the gibbing to happen. -// The internal blast impulse which will push the gibs away from the center. -// A pointer to an MO which the gibs shuold not be colliding with! -// Return value: None. - - void GibThis(Vector impactImpulse = Vector(), float internalBlast = 10, MovableObject *pIgnoreMO = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. -// Arguments: The point in absolute scene coordinates. -// Return value: Whether this' graphical rep overlaps the scene point. - - bool IsOnScenePoint(Vector &scenePoint) const override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: UpdateMovePath ////////////////////////////////////////////////////////////////////////////////////////// @@ -467,25 +432,7 @@ int FirearmActivationDelay() const; void Update() override; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound count of this actor and all vital attachables. -// Arguments: None. -// Return value: Returns total number of wounds of this actor. - - int GetTotalWoundCount() const override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound limit of this actor and all vital attachables. -// Arguments: None. -// Return value: Returns total wound limit of this actor. - - int GetTotalWoundLimit() const override; - - +#ifdef DEBUG_BUILD ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Draw ////////////////////////////////////////////////////////////////////////////////////////// @@ -499,6 +446,7 @@ int FirearmActivationDelay() const; // Return value: None. void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; +#endif ////////////////////////////////////////////////////////////////////////////////////////// @@ -515,24 +463,14 @@ int FirearmActivationDelay() const; void DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveAnyRandomWounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specified amount of wounds from the actor and all standard attachables. -// Arguments: Amount of wounds to remove. -// Return value: Damage taken from removed wounds. - - int RemoveAnyRandomWounds(int amount) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - - void GetMOIDs(std::vector &MOIDs) const override; + /// + /// Gets the LimbPath corresponding to the passed in Side, Layer and MovementState values. + /// + /// Whether to get the left or right side. + /// Whether to get foreground or background LimbPath. + /// Which movement state to get the LimbPath for. + /// The LimbPath corresponding to the passed in Layer and MovementState values. + LimbPath *GetLimbPath(Side side, Layer layer, MovementState movementState) { return &m_Paths[side][layer][movementState]; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -582,26 +520,13 @@ int FirearmActivationDelay() const; protected: -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. -// Arguments: The MOID index to register itself and its children in. -// The MOID of the root MO of this MO, ie the highest parent of this MO. -// 0 means that this MO is the root, ie it is owned by MovableMan. -// Whether this MO should make a new MOID to use for itself, or to use -// the same as the last one in the index (presumably its parent), -// Return value: None. - - void UpdateChildMOIDs(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) override; - // Member variables static Entity::ClassInfo m_sClass; // Turret which can be mounted with a weapon Turret *m_pTurret; + //TODO when this class is cleaned up these legs and footgroups should probably be renamed. L and R should be expanded to Left and Right. I think FG and BG can stay as is cause they're everywhere. // Left Foreground leg. Leg *m_pLFGLeg; // Left Background leg. @@ -612,9 +537,13 @@ int FirearmActivationDelay() const; Leg *m_pRBGLeg; // Limb AtomGroups. AtomGroup *m_pLFGFootGroup; + AtomGroup *m_BackupLFGFootGroup; AtomGroup *m_pLBGFootGroup; + AtomGroup *m_BackupLBGFootGroup; AtomGroup *m_pRFGFootGroup; + AtomGroup *m_BackupRFGFootGroup; AtomGroup *m_pRBGFootGroup; + AtomGroup *m_BackupRBGFootGroup; // The sound of the actor taking a step (think robot servo) SoundContainer m_StrideSound; // Jetpack booster. diff --git a/Entities/ACraft.cpp b/Entities/ACraft.cpp index 76e157bdd..938551607 100644 --- a/Entities/ACraft.cpp +++ b/Entities/ACraft.cpp @@ -951,21 +951,6 @@ void ACraft::Update() } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ACraft's current graphical representation to a -// BITMAP of choice. - -void ACraft::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const -{ - Actor::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); -} - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: DrawHUD ////////////////////////////////////////////////////////////////////////////////////////// @@ -1091,19 +1076,4 @@ void ACraft::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc } } -/// -/// Helper method to remove code duplication in ACDropship and ACRocket gibbing -/// -/// The attachable to set velocities for -/// The impactImpulse passed in from GibThis -/// The internalBlast passed in from GibThis -void ACraft::SetAttachableVelocitiesForGibbing(Attachable * pAttachable, Vector impactImpulse, float internalBlast) -{ - Vector newVel(pAttachable->GetPos() - m_Pos); - newVel.SetMagnitude(internalBlast); - newVel += m_Vel + impactImpulse; - pAttachable->SetVel(newVel); - pAttachable->SetAngularVel(RandomNormalNum()); -} - } // namespace RTE \ No newline at end of file diff --git a/Entities/ACraft.h b/Entities/ACraft.h index dbf09e179..baaaabd4a 100644 --- a/Entities/ACraft.h +++ b/Entities/ACraft.h @@ -516,21 +516,6 @@ enum void Update() override; -////////////////////////////////////////////////////////////////////////////////////////// -// Pure v. method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this ACraft's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: DrawHUD ////////////////////////////////////////////////////////////////////////////////////////// @@ -569,25 +554,6 @@ enum virtual void SetMaxPassengers(int max) { m_MaxPassengers = max; } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound count of this actor and all vital attachables. -// Arguments: None. -// Return value: Returns total number of wounds of this actor. - - int GetTotalWoundCount() const override { return Actor::GetTotalWoundCount(); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound limit of this actor and all vital attachables. -// Arguments: None. -// Return value: Returns total wound limit of this actor. - - int GetTotalWoundLimit() const override { return Actor::GetTotalWoundLimit(); }; - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetDeliveryDelayMultiplier ////////////////////////////////////////////////////////////////////////////////////////// @@ -615,32 +581,6 @@ enum protected: - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. -// Arguments: The MOID index to register itself and its children in. -// The MOID of the root MO of this MO, ie the highest parent of this MO. -// 0 means that this MO is the root, ie it is owned by MovableMan. -// Whether this MO should make a new MOID to use for itself, or to use -// the same as the last one in the index (presumably its parent), -// Return value: None. - - void UpdateChildMOIDs(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) override { Actor::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); } - - void SetAttachableVelocitiesForGibbing(Attachable* pAttachable, Vector impactImpulse, float internalBlast); - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - - void GetMOIDs(std::vector &MOIDs) const override { Actor::GetMOIDs(MOIDs); } - // Member variables static Entity::ClassInfo m_sClass; // Current movement state. diff --git a/Entities/ADoor.cpp b/Entities/ADoor.cpp index b3c7cd0e5..ac704c65c 100644 --- a/Entities/ADoor.cpp +++ b/Entities/ADoor.cpp @@ -45,6 +45,11 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int ADoor::Create(const ADoor &reference) { + if (reference.m_Door) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_Door->GetUniqueID()); + SetDoor(dynamic_cast(reference.m_Door->Clone())); + } + Actor::Create(reference); m_InitialSpriteAnimDuration = reference.m_SpriteAnimDuration; @@ -54,11 +59,6 @@ namespace RTE { } m_SensorInterval = reference.m_SensorInterval; - if (reference.m_Door) { - m_Door = dynamic_cast(reference.m_Door->Clone()); - AddAttachable(m_Door, m_ClosedOffset, true); - } - // Set the initial door state to the opposite of default so it'll move to default when spawned and draw the door material layer. m_DoorState = reference.m_ClosedByDefault ? OPEN : CLOSED; @@ -85,9 +85,11 @@ namespace RTE { int ADoor::ReadProperty(std::string propName, Reader &reader) { if (propName == "Door") { - delete m_Door; + RemoveAttachable(m_Door); m_Door = new Attachable; reader >> m_Door; + AddAttachable(m_Door); + m_Door->SetInheritsRotAngle(false); m_DoorMaterialID = m_Door->GetMaterial()->GetIndex(); } else if (propName == "OpenOffset") { reader >> m_OpenOffset; @@ -191,7 +193,6 @@ namespace RTE { m_DoorMoveSound.Stop(); m_DoorDirectionChangeSound.Stop(); m_DoorMoveEndSound.Stop(); - delete m_Door; if (!notInherited) { Actor::Destroy(); } for (ADSensor &sensor : m_Sensors) { @@ -202,30 +203,19 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - float ADoor::GetMass() const { - float totalMass = Actor::GetMass(); - if (m_Door) { totalMass += m_Door->GetMass(); } - return totalMass; - } - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - void ADoor::GetMOIDs(std::vector &MOIDs) const { - if (m_Door) { m_Door->GetMOIDs(MOIDs); } - Actor::GetMOIDs(MOIDs); - } - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - void ADoor::SetID(const MOID newID) { - Actor::SetID(newID); - if (m_Door) { m_Door->SetID(newID); } - } - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ADoor::SetDoor(Attachable *newDoor) { + if (newDoor == nullptr) { + if (m_Door && m_Door->IsAttached()) { RemoveAttachable(m_Door); } + m_Door = nullptr; + } else { + if (m_Door && m_Door->IsAttached()) { RemoveAttachable(m_Door); } + m_Door = newDoor; + AddAttachable(newDoor); - bool ADoor::IsOnScenePoint(Vector &scenePoint) const { - return ((m_Door && m_Door->IsOnScenePoint(scenePoint)) || Actor::IsOnScenePoint(scenePoint)); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newDoor->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + dynamic_cast(parent)->SetDoor(attachable); + }}); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -285,45 +275,13 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int ADoor::RemoveAnyRandomWounds(int amount) { - float damage = 0; - for (int i = 0; i < amount; i++) { - std::vector woundedBodyParts; - if (GetWoundCount() > 0) { woundedBodyParts.push_back(this); } - if (m_Door && m_Door->GetWoundCount()) { woundedBodyParts.push_back(m_Door); } - - if (woundedBodyParts.size() == 0) { - return damage; - } - - int partIndex = RandomNum(0, woundedBodyParts.size() - 1); - MOSRotating *part = woundedBodyParts[partIndex]; - damage += part->RemoveWounds(1); - } - return damage; - } - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - void ADoor::GibThis(Vector impactImpulse, float internalBlast, MovableObject *ignoreMO) { + void ADoor::GibThis(const Vector &impactImpulse, MovableObject *movableObjectToIgnore) { if (m_Door && m_Door->IsAttached()) { EraseDoorMaterial(); m_Door->DeepCheck(true); m_Door->SetPinStrength(0); - m_Door->SetVel(m_Vel + m_Door->GetParentOffset() * RandomNum()); - m_Door->SetAngularVel(RandomNormalNum()); - g_MovableMan.AddParticle(m_Door); - RemoveAttachable(m_Door); - m_Door = 0; } - Actor::GibThis(impactImpulse, internalBlast, ignoreMO); - } - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - void ADoor::UpdateChildMOIDs(vector &MOIDIndex, MOID rootMOID, bool makeNewMOID) { - if (m_Door) { m_Door->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); } - Actor::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); + Actor::GibThis(impactImpulse, movableObjectToIgnore); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -397,16 +355,16 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ADoor::Update() { - Actor::Update(); if (m_Door && m_Door->IsAttached()) { if (m_DoorState != STOPPED && m_SensorTimer.IsPastSimMS(m_SensorInterval)) { UpdateSensors(); } - UpdateDoorAttachable(); + UpdateDoorAttachableActions(); } - if (m_Door && !ApplyAttachableForces(m_Door)) { + Actor::Update(); + + if (!m_Door) { EraseDoorMaterial(); - m_Door = 0; // Start the spinning out of control animation for the motor, start it slow m_SpriteAnimDuration *= 4; } @@ -426,7 +384,7 @@ namespace RTE { m_Health -= 0.4F; } - if (m_Status == DEAD) { GibThis(Vector(), 50); } + if (m_Status == DEAD) { GibThis(); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -460,14 +418,12 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ADoor::UpdateDoorAttachable() { + void ADoor::UpdateDoorAttachableActions() { Vector startOffset; Vector endOffset; float startAngle; float endAngle; - m_Door->SetHFlipped(m_HFlipped); - if (m_DoorState == OPEN || m_DoorState == OPENING) { startOffset = m_ClosedOffset; endOffset = m_OpenOffset; @@ -481,16 +437,16 @@ namespace RTE { } if (m_DoorState == OPEN || m_DoorState == CLOSED) { - m_Door->SetJointPos(m_Pos + endOffset.GetXFlipped(m_HFlipped) * m_Rotation); - m_Door->SetRotAngle(m_Rotation.GetRadAngle() + (endAngle * GetFlipFactor())); + m_Door->SetParentOffset(endOffset); + m_Door->SetRotAngle(m_Rotation.GetRadAngle() + (endAngle * static_cast(GetFlipFactor()))); } else if (m_DoorState == OPENING || m_DoorState == CLOSING) { if (!m_DoorMoveSound.IsBeingPlayed()) { m_DoorMoveSound.Play(m_Pos); } if (m_DoorMoveTimer.IsPastSimMS(m_DoorMoveTime)) { m_ResetToDefaultStateTimer.Reset(); - m_Door->SetJointPos(m_Pos + endOffset.GetXFlipped(m_HFlipped) * m_Rotation); - m_Door->SetRotAngle(m_Rotation.GetRadAngle() + (endAngle * GetFlipFactor())); + m_Door->SetParentOffset(endOffset); + m_Door->SetRotAngle(m_Rotation.GetRadAngle() + (endAngle * static_cast(GetFlipFactor()))); m_DoorMoveSound.Stop(); m_DoorMoveEndSound.Play(m_Pos); @@ -507,33 +463,13 @@ namespace RTE { // TODO: Make this work across rotation 0. Probably the best solution would be to setup an angle LERP that properly handles the 2PI border and +- angles. float updatedAngle = LERP(0, m_DoorMoveTime, startAngle, endAngle, m_DoorMoveTimer.GetElapsedSimTimeMS()); - m_Door->SetJointPos(m_Pos + updatedOffset.GetXFlipped(m_HFlipped) * m_Rotation); + m_Door->SetParentOffset(updatedOffset); m_Door->SetRotAngle(m_Rotation.GetRadAngle() + (updatedAngle * GetFlipFactor())); // Clear away any terrain debris when the door is moving but only after a short delay so it doesn't take a chunk out of the ground if (m_DoorMoveTimer.IsPastSimMS(50)) { m_Door->DeepCheck(true); } } } - m_Door->Update(); - } - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - void ADoor::Draw(BITMAP *targetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { - // Override color drawing with flash, if requested. - DrawMode realMode = (mode == g_DrawColor && m_FlashWhiteMS) ? g_DrawWhite : mode; - - if (m_Door && m_Door->IsAttached()) { - if (!m_Door->IsDrawnAfterParent()) { - m_Door->Draw(targetBitmap, targetPos, realMode, onlyPhysical); - Actor::Draw(targetBitmap, targetPos, mode, onlyPhysical); - } else { - Actor::Draw(targetBitmap, targetPos, mode, onlyPhysical); - m_Door->Draw(targetBitmap, targetPos, realMode, onlyPhysical); - } - } else { - Actor::Draw(targetBitmap, targetPos, mode, onlyPhysical); - } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Entities/ADoor.h b/Entities/ADoor.h index 70728458b..727facab4 100644 --- a/Entities/ADoor.h +++ b/Entities/ADoor.h @@ -74,28 +74,16 @@ namespace RTE { Attachable * GetDoor() const { return m_Door; } /// - /// Gets the current state of the door. + /// Sets the moving door Attachable for this ADoor. /// - /// The current state of this ADoor. See the DoorState enum. - DoorState GetDoorState() const { return m_DoorState; } + /// The new moving door attachable to use. + void SetDoor(Attachable *newDoor); /// - /// Gets the mass value of this ADoor, including the mass of its currently attached parts. - /// - /// A float describing the mass value in Kilograms (kg). - float GetMass() const override; - - /// - /// Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector to store MOIDs. - /// - /// - void GetMOIDs(std::vector &MOIDs) const override; - - /// - /// Sets the MOID of this ADoor for this frame. + /// Gets the current state of the door. /// - /// A MOID specifying the MOID that this ADoor is assigned for this frame. - void SetID(const MOID newID) override; + /// The current state of this ADoor. See the DoorState enum. + DoorState GetDoorState() const { return m_DoorState; } /// /// Sets whether this ADoor closes (or opens) after a while by default. @@ -108,13 +96,6 @@ namespace RTE { /// /// Whether a player can control this at all. bool IsControllable() const override { return false; } - - /// - /// Indicates whether this' current graphical representation overlaps a point in absolute scene coordinates. - /// - /// The point in absolute scene coordinates. - /// Whether this' graphical representation overlaps the scene point. - bool IsOnScenePoint(Vector &scenePoint) const override; #pragma endregion #pragma region Concrete Methods @@ -142,34 +123,18 @@ namespace RTE { #pragma region Virtual Override Methods /// - /// Removes a specified amount of wounds from the actor and all standard attachables. - /// - /// Amount of wounds to remove. - /// Damage taken from removed wounds. - int RemoveAnyRandomWounds(int amount) override; - - /// - /// Gibs this, effectively destroying it and creating multiple gibs or pieces in its place. + /// Destroys this ADoor and creates its specified Gibs in its place with appropriate velocities. + /// Any Attachables are removed and also given appropriate velocities. /// /// The impulse (kg * m/s) of the impact causing the gibbing to happen. - /// The internal blast impulse which will push the gibs away from the center. - /// A pointer to an MO which the gibs should not be colliding with! - void GibThis(Vector impactImpulse = Vector(), float internalBlast = 10, MovableObject *ignoreMO = 0) override; + /// A pointer to an MO which the Gibs and Attachables should not be colliding with. + void GibThis(const Vector &impactImpulse = Vector(), MovableObject *movableObjectToIgnore = nullptr) override; /// /// Updates this ADoor. Supposed to be done every frame. /// void Update() override; - /// - /// Draws this ADoor's current graphical representation to a BITMAP of choice. - /// - /// A pointer to a BITMAP to draw on. - /// The absolute position of the target bitmap's upper left corner in the Scene. - /// Which mode to draw in. See the DrawMode enumeration for the modes. - /// Whether to not draw any extra 'ghost' items of this ADoor, indicator arrows or hovering HUD text and so on. - void Draw(BITMAP *targetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - /// /// Draws this ADoor's current graphical HUD overlay representation to a BITMAP of choice. /// @@ -237,7 +202,7 @@ namespace RTE { /// /// Updates the door attachable position and movement based on the current state of this ADoor. This is called from Update(). /// - void UpdateDoorAttachable(); + void UpdateDoorAttachableActions(); #pragma endregion /// @@ -245,14 +210,6 @@ namespace RTE { /// void SharedDoorControls(); - /// - /// Makes this MO register itself and all its attached children in the MOID register and get IDs for itself and its children for this frame. - /// - /// The MOID index to register itself and its children in. - /// The MOID of the root MO of this MO, ie the highest parent of this MO. 0 means that this MO is the root, ie it is owned by MovableMan. - /// Whether this MO should make a new MOID to use for itself, or to use the same as the last one in the index (presumably its parent), - void UpdateChildMOIDs(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) override; - /// /// Draws the material under the position of the door attachable, to create terrain collision detection for the doors. /// diff --git a/Entities/AEmitter.cpp b/Entities/AEmitter.cpp index 5808bf54c..d51acf7da 100644 --- a/Entities/AEmitter.cpp +++ b/Entities/AEmitter.cpp @@ -55,25 +55,11 @@ void AEmitter::Clear() m_EmitDamage = 0; m_LastEmitTmr.Reset(); m_pFlash = 0; - m_FlashScale = 1.0f; - m_AvgBurstImpulse = -1.0f; - m_AvgImpulse = -1.0f; + m_FlashScale = 1.0F; + m_AvgBurstImpulse = -1.0F; + m_AvgImpulse = -1.0F; m_FlashOnlyOnBurst = true; - m_LoudnessOnEmit = 1.0f; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Emission object ready for use. - -int AEmitter::Create() -{ - if (Attachable::Create() < 0) - return -1; - - return 0; + m_LoudnessOnEmit = 1.0F; } @@ -82,12 +68,16 @@ int AEmitter::Create() ////////////////////////////////////////////////////////////////////////////////////////// // Description: Creates a AEmitter to be identical to another, by deep copy. -int AEmitter::Create(const AEmitter &reference) -{ +int AEmitter::Create(const AEmitter &reference) { + if (reference.m_pFlash) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pFlash->GetUniqueID()); + SetFlash(dynamic_cast(reference.m_pFlash->Clone())); + } Attachable::Create(reference); - for (list::const_iterator itr = reference.m_EmissionList.begin(); itr != reference.m_EmissionList.end(); ++itr) - m_EmissionList.push_back(dynamic_cast((*itr)->Clone())); + for (const Emission *referenceEmission : reference.m_EmissionList) { + m_EmissionList.push_back(dynamic_cast(referenceEmission->Clone())); + } m_EmissionSound = reference.m_EmissionSound; m_BurstSound = reference.m_BurstSound; @@ -107,12 +97,6 @@ int AEmitter::Create(const AEmitter &reference) m_EmitAngle = reference.m_EmitAngle; m_EmissionOffset = reference.m_EmissionOffset; m_EmitDamage = reference.m_EmitDamage; - if (reference.m_pFlash) - { - m_pFlash = dynamic_cast(reference.m_pFlash->Clone()); - if (m_pFlash) - m_pFlash->Attach(this); - } m_FlashScale = reference.m_FlashScale; m_FlashOnlyOnBurst = reference.m_FlashOnlyOnBurst; m_LoudnessOnEmit = reference.m_LoudnessOnEmit; @@ -129,84 +113,76 @@ int AEmitter::Create(const AEmitter &reference) // is called. If the property isn't recognized by any of the base classes, // false is returned, and the reader's position is untouched. -int AEmitter::ReadProperty(std::string propName, Reader &reader) -{ - if (propName == "AddEmission") - { +int AEmitter::ReadProperty(std::string propName, Reader &reader) { + if (propName == "AddEmission") { Emission * emission = new Emission(); reader >> *emission; m_EmissionList.push_back(emission); - } - else if (propName == "EmissionSound") + } else if (propName == "EmissionSound") { reader >> m_EmissionSound; - else if (propName == "BurstSound") + } else if (propName == "BurstSound") { reader >> m_BurstSound; - else if (propName == "EndSound") + } else if (propName == "EndSound") { reader >> m_EndSound; - else if (propName == "EmissionEnabled") + } else if (propName == "EmissionEnabled") { reader >> m_EmitEnabled; - else if (propName == "EmissionCount") + } else if (propName == "EmissionCount") { reader >> m_EmitCount; - else if (propName == "EmissionCountLimit") + } else if (propName == "EmissionCountLimit") { reader >> m_EmitCountLimit; - else if (propName == "ParticlesPerMinute") - { + } else if (propName == "ParticlesPerMinute") { float ppm; reader >> ppm; - // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility - for (list::iterator eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) - (*eItr)->m_PPM = ppm / m_EmissionList.size(); - } - else if (propName == "MinThrottleRange") + // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility. + for (Emission *emission : m_EmissionList) { emission->m_PPM = ppm / static_cast(m_EmissionList.size()); } + } else if (propName == "MinThrottleRange") { reader >> m_MinThrottleRange; - else if (propName == "MaxThrottleRange") + } else if (propName == "MaxThrottleRange") { reader >> m_MaxThrottleRange; - else if (propName == "Throttle") + } else if (propName == "Throttle") { reader >> m_Throttle; - else if (propName == "EmissionsIgnoreThis") + } else if (propName == "EmissionsIgnoreThis") { reader >> m_EmissionsIgnoreThis; - else if (propName == "BurstSize") - { + } else if (propName == "BurstSize") { int burstSize; reader >> burstSize; - // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility - for (list::iterator eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) - (*eItr)->m_BurstSize = std::ceil((float)burstSize / (float)m_EmissionList.size()); - } - else if (propName == "BurstScale") + // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility. + for (Emission *emission : m_EmissionList) { emission->m_BurstSize = std::ceil(static_cast(burstSize) / static_cast(m_EmissionList.size())); } + } else if (propName == "BurstScale") { reader >> m_BurstScale; - else if (propName == "BurstDamage") + } else if (propName == "BurstDamage") { reader >> m_BurstDamage; - else if (propName == "EmitterDamageMultiplier") + } else if (propName == "EmitterDamageMultiplier") { reader >> m_EmitterDamageMultiplier; - else if (propName == "BurstSpacing") + } else if (propName == "BurstSpacing") { reader >> m_BurstSpacing; - else if (propName == "BurstTriggered") + } else if (propName == "BurstTriggered") { reader >> m_BurstTriggered; - else if (propName == "EmissionAngle") + } else if (propName == "EmissionAngle") { reader >> m_EmitAngle; - else if (propName == "EmissionOffset") + } else if (propName == "EmissionOffset") { reader >> m_EmissionOffset; - else if (propName == "EmissionDamage") + } else if (propName == "EmissionDamage") { reader >> m_EmitDamage; - else if (propName == "Flash") - { - const Entity *pObj = g_PresetMan.GetEntityPreset(reader); - if (pObj) - { - m_pFlash = dynamic_cast(pObj->Clone()); - if (m_pFlash) - m_pFlash->Attach(this); + } else if (propName == "Flash") { + RemoveAttachable(m_pFlash); + const Entity *flashEntity = g_PresetMan.GetEntityPreset(reader); + if (flashEntity) { + m_pFlash = dynamic_cast(flashEntity->Clone()); + AddAttachable(m_pFlash); + m_pFlash->SetDrawnNormallyByParent(false); + m_pFlash->SetInheritsRotAngle(false); + m_pFlash->SetInheritsHFlipped(0); + m_pFlash->SetDeleteWhenRemovedFromParent(true); + m_pFlash->SetCollidesWithTerrainWhileAttached(false); } - } - else if (propName == "FlashScale") + } else if (propName == "FlashScale") { reader >> m_FlashScale; - else if (propName == "FlashOnlyOnBurst") + } else if (propName == "FlashOnlyOnBurst") { reader >> m_FlashOnlyOnBurst; - else if (propName == "LoudnessOnEmit") + } else if (propName == "LoudnessOnEmit") { reader >> m_LoudnessOnEmit; - else - { + } else { return Attachable::ReadProperty(propName, reader); } @@ -299,8 +275,6 @@ void AEmitter::Destroy(bool notInherited) m_EmissionSound.Stop(); // m_BurstSound.Stop(); - delete m_pFlash; - if (!notInherited) Attachable::Destroy(); Clear(); @@ -393,21 +367,24 @@ float AEmitter::EstimateImpulse(bool burst) return m_AvgImpulse * throttleFactor; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. +void AEmitter::SetFlash(Attachable *newFlash) { + if (newFlash == nullptr) { + if (m_pFlash && m_pFlash->IsAttached()) { RemoveAttachable(m_pFlash); } + m_pFlash = nullptr; + } else { + if (m_pFlash && m_pFlash->IsAttached()) { RemoveAttachable(m_pFlash); } + m_pFlash = newFlash; + AddAttachable(newFlash); -void AEmitter::GibThis(Vector impactImpulse, float internalBlast, MovableObject *pIgnoreMO) -{ - - - Attachable::GibThis(impactImpulse, internalBlast, pIgnoreMO); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newFlash->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + dynamic_cast(parent)->SetFlash(attachable); + }}); + } } -*/ + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Update @@ -425,6 +402,15 @@ void AEmitter::Update() m_Frame = 0; } + // Update and show flash if there is one + if (m_pFlash && (!m_FlashOnlyOnBurst || m_BurstTriggered)) { + m_pFlash->SetParentOffset(m_EmissionOffset); + // Don't set the flipping for the flash because that is wasting resources when drawing, just handle the flipping of the rotation here. + m_pFlash->SetRotAngle(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); + m_pFlash->SetScale(m_FlashScale); + m_pFlash->SetNextFrame(); + } + Attachable::Update(); if (m_EmitEnabled) @@ -562,26 +548,8 @@ void AEmitter::Update() } m_LastEmitTmr.Reset(); - // Apply recoil/push effects, scaled by the joint stiffness - if (m_pParent && !m_OnlyLinForces) - m_pParent->AddAbsImpulseForce(pushImpulses * m_JointStiffness, m_Pos + m_JointOffset); - else - m_ImpulseForces.push_back(make_pair(pushImpulses * m_JointStiffness, Vector())); - - // Update and show flash if there is one - if (m_pFlash && (!m_FlashOnlyOnBurst || m_BurstTriggered)) { - if (!m_EmissionOffset.IsZero()) - m_pFlash->SetJointPos(m_Pos + RotateOffset(m_EmissionOffset)/* + (m_MuzzleOff.GetXFlipped(m_HFlipped) * m_Rotation)*/); - else - m_pFlash->SetJointPos(m_Pos/* + (m_MuzzleOff.GetXFlipped(m_HFlipped) * m_Rotation)*/); - // Don't set the flipping for the flash because that is wasting resources when drawing, - // just handle the flipping of the rotation here. - m_pFlash->SetRotAngle(m_HFlipped ? c_PI + m_Rotation.GetRadAngle() - m_EmitAngle.GetRadAngle() : m_Rotation.GetRadAngle() + m_EmitAngle.GetRadAngle()); -// m_pFlash->SetFrame(std::floor((m_pFlash->GetFrameCount()/* - 1*/) * RandomNum() - 0.001)); - m_pFlash->SetScale(m_FlashScale); - m_pFlash->SetNextFrame(); - m_pFlash->Update(); - } + // Apply recoil/push effects. Joint stiffness will take effect when these are transferred to the parent. + if (!pushImpulses.IsZero()) { AddImpulseForce(pushImpulses); } // Count the the damage caused by the emissions, and only if we're not bursting if (!m_BurstTriggered) @@ -603,14 +571,6 @@ void AEmitter::Update() // Do stuff to stop emission else { - // Fix for when emitter is not emitting the flash is drawn on the wrong position - if (m_pFlash) { - if (!m_EmissionOffset.IsZero()) - m_pFlash->SetJointPos(m_Pos + RotateOffset(m_EmissionOffset)/* + (m_MuzzleOff.GetXFlipped(m_HFlipped) * m_Rotation)*/); - else - m_pFlash->SetJointPos(m_Pos/* + (m_MuzzleOff.GetXFlipped(m_HFlipped) * m_Rotation)*/); - } - if (m_WasEmitting) { m_EmissionSound.Stop(); diff --git a/Entities/AEmitter.h b/Entities/AEmitter.h index 04771f499..6d406f287 100644 --- a/Entities/AEmitter.h +++ b/Entities/AEmitter.h @@ -65,17 +65,6 @@ ClassInfoGetters ~AEmitter() override { Destroy(true); } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the AEmitter object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// @@ -316,6 +305,18 @@ ClassInfoGetters void SetBurstSpacing(const float spacing) { m_BurstSpacing = spacing; } + /// + /// Gets the flash of this AEmitter. + /// + /// A pointer to the AEmitter's flash. Ownership is NOT transferred! + Attachable * GetFlash() const { return m_pFlash; } + + /// + /// Sets the flash for this AEmitter. Ownership IS transferred! + /// + /// The new flash to use. + void SetFlash(Attachable *newFlash); + ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetFlashScale ////////////////////////////////////////////////////////////////////////////////////////// @@ -449,19 +450,6 @@ ClassInfoGetters void ResetAllTimers() override { Attachable::ResetAllTimers(); m_BurstTimer.Reset(); m_LastEmitTmr.Reset(); } -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. -// Arguments: The impulse (kg * m/s) of the impact causing the gibbing to happen. -// The internal blast impulse which will push the gibs away from the center. -// A pointer to an MO which the gibs shuold not be colliding with! -// Return value: None. - - void GibThis(Vector impactImpulse = Vector(), float internalBlast = 10, MovableObject *pIgnoreMO = 0) override; -*/ ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Update diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index f27b463b4..491296bf8 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -51,7 +51,9 @@ void AHuman::Clear() m_pFGHandGroup = 0; m_pBGHandGroup = 0; m_pFGFootGroup = 0; + m_BackupFGFootGroup = nullptr; m_pBGFootGroup = 0; + m_BackupBGFootGroup = nullptr; m_StrideSound.Reset(); m_ArmsState = WEAPON_READY; m_MoveState = STAND; @@ -62,6 +64,7 @@ void AHuman::Clear() m_Paths[BGROUND][i].Reset(); m_Paths[FGROUND][i].Terminate(); m_Paths[BGROUND][i].Terminate(); + m_RotAngleTargets[i] = 0.0F; } m_Aiming = false; m_ArmClimbing[FGROUND] = false; @@ -104,6 +107,12 @@ int AHuman::Create() if (Actor::Create() < 0) return -1; + // Cheat to make sure the FG Arm is always at the end of the Attachables list so it draws last. + if (m_pFGArm) { + m_Attachables.erase(std::find(m_Attachables.begin(), m_Attachables.end(), m_pFGArm)); + m_Attachables.push_back(m_pFGArm); + } + // Make the limb paths for the background limbs for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) { @@ -137,45 +146,37 @@ int AHuman::Create() ////////////////////////////////////////////////////////////////////////////////////////// // Description: Creates a AHuman to be identical to another, by deep copy. -int AHuman::Create(const AHuman &reference) -{ - Actor::Create(reference); - - m_ThrowPrepTime = reference.m_ThrowPrepTime; - - if (reference.m_pHead) { - m_pHead = dynamic_cast(reference.m_pHead->Clone()); - m_pHead->SetCanCollideWithTerrainWhenAttached(true); - AddAttachable(m_pHead, true); +int AHuman::Create(const AHuman &reference) { + //Note - hardcoded attachable copying is organized based on desired draw order here. + if (reference.m_pBGArm) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pBGArm->GetUniqueID()); + SetBGArm(dynamic_cast(reference.m_pBGArm->Clone())); } - - if (reference.m_pJetpack) { - m_pJetpack = dynamic_cast(reference.m_pJetpack->Clone()); - AddAttachable(m_pJetpack, true); + if (reference.m_pBGLeg) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pBGLeg->GetUniqueID()); + SetBGLeg(dynamic_cast(reference.m_pBGLeg->Clone())); } - - m_JetTimeTotal = reference.m_JetTimeTotal; - m_JetTimeLeft = reference.m_JetTimeLeft; - - if (reference.m_pFGArm) { - m_pFGArm = dynamic_cast(reference.m_pFGArm->Clone()); - AddAttachable(m_pFGArm, true); + if (reference.m_pJetpack) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pJetpack->GetUniqueID()); + SetJetpack(dynamic_cast(reference.m_pJetpack->Clone())); } - - if (reference.m_pBGArm) { - m_pBGArm = dynamic_cast(reference.m_pBGArm->Clone()); - AddAttachable(m_pBGArm, true); + if (reference.m_pHead) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pHead->GetUniqueID()); + SetHead(dynamic_cast(reference.m_pHead->Clone())); } - if (reference.m_pFGLeg) { - m_pFGLeg = dynamic_cast(reference.m_pFGLeg->Clone()); - AddAttachable(m_pFGLeg, true); + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pFGLeg->GetUniqueID()); + SetFGLeg(dynamic_cast(reference.m_pFGLeg->Clone())); } - - if (reference.m_pBGLeg) { - m_pBGLeg = dynamic_cast(reference.m_pBGLeg->Clone()); - AddAttachable(m_pBGLeg, true); + if (reference.m_pFGArm) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pFGArm->GetUniqueID()); + SetFGArm(dynamic_cast(reference.m_pFGArm->Clone())); } + Actor::Create(reference); + + m_ThrowPrepTime = reference.m_ThrowPrepTime; + m_JetTimeTotal = reference.m_JetTimeTotal; + m_JetTimeLeft = reference.m_JetTimeLeft; m_pFGHandGroup = dynamic_cast(reference.m_pFGHandGroup->Clone()); m_pFGHandGroup->SetOwner(this); @@ -183,8 +184,14 @@ int AHuman::Create(const AHuman &reference) m_pBGHandGroup->SetOwner(this); m_pFGFootGroup = dynamic_cast(reference.m_pFGFootGroup->Clone()); m_pFGFootGroup->SetOwner(this); + m_BackupFGFootGroup = dynamic_cast(reference.m_BackupFGFootGroup->Clone()); + m_BackupFGFootGroup->SetOwner(this); + m_BackupFGFootGroup->SetLimbPos(reference.m_BackupFGFootGroup->GetLimbPos()); m_pBGFootGroup = dynamic_cast(reference.m_pBGFootGroup->Clone()); m_pBGFootGroup->SetOwner(this); + m_BackupBGFootGroup = dynamic_cast(reference.m_BackupBGFootGroup->Clone()); + m_BackupBGFootGroup->SetOwner(this); + m_BackupBGFootGroup->SetLimbPos(reference.m_BackupBGFootGroup->GetLimbPos()); m_StrideSound = reference.m_StrideSound; @@ -192,10 +199,10 @@ int AHuman::Create(const AHuman &reference) m_MoveState = reference.m_MoveState; m_ProneState = reference.m_ProneState; - for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) - { + for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) { m_Paths[FGROUND][i].Create(reference.m_Paths[FGROUND][i]); m_Paths[BGROUND][i].Create(reference.m_Paths[BGROUND][i]); + m_RotAngleTargets[i] = reference.m_RotAngleTargets[i]; } m_GoldInInventoryChunk = reference.m_GoldInInventoryChunk; @@ -223,56 +230,57 @@ int AHuman::Create(const AHuman &reference) // is called. If the property isn't recognized by any of the base classes, // false is returned, and the reader's position is untouched. -int AHuman::ReadProperty(std::string propName, Reader &reader) -{ - if (propName == "ThrowPrepTime") - reader >> m_ThrowPrepTime; - else if (propName == "Head") - { - delete m_pHead; +int AHuman::ReadProperty(std::string propName, Reader &reader) { + if (propName == "ThrowPrepTime") { + reader >> m_ThrowPrepTime; + } else if (propName == "Head") { + RemoveAttachable(m_pHead); m_pHead = new Attachable; reader >> m_pHead; - if (!m_pHead->IsDamageMultiplierRedefined()) - m_pHead->SetDamageMultiplier(5); - } - else if (propName == "Jetpack") - { - delete m_pJetpack; + AddAttachable(m_pHead); + if (m_pHead->HasNoSetDamageMultiplier()) { m_pHead->SetDamageMultiplier(5.0F); } + if (m_pHead->IsDrawnAfterParent()) { m_pHead->SetDrawnNormallyByParent(false); } + m_pHead->SetInheritsRotAngle(false); + } else if (propName == "Jetpack") { + RemoveAttachable(m_pJetpack); m_pJetpack = new AEmitter; reader >> m_pJetpack; - } - else if (propName == "JumpTime") - { + AddAttachable(m_pJetpack); + if (m_pJetpack->HasNoSetDamageMultiplier()) { m_pJetpack->SetDamageMultiplier(0.0F); } + m_pJetpack->SetApplyTransferredForcesAtOffset(false); + } else if (propName == "JumpTime") { reader >> m_JetTimeTotal; // Convert to ms m_JetTimeTotal *= 1000; - } - else if (propName == "FGArm") - { - delete m_pFGArm; + } else if (propName == "FGArm") { + RemoveAttachable(m_pFGArm); m_pFGArm = new Arm; reader >> m_pFGArm; - } - else if (propName == "BGArm") - { - delete m_pBGArm; + AddAttachable(m_pFGArm); + if (m_pFGArm->HasNoSetDamageMultiplier()) { m_pFGArm->SetDamageMultiplier(1.0F); } + m_pFGArm->SetDrawnAfterParent(true); + m_pFGArm->SetDrawnNormallyByParent(false); + } else if (propName == "BGArm") { + RemoveAttachable(m_pBGArm); m_pBGArm = new Arm; reader >> m_pBGArm; - } - else if (propName == "FGLeg") - { - delete m_pFGLeg; + AddAttachable(m_pBGArm); + if (m_pBGArm->HasNoSetDamageMultiplier()) { m_pBGArm->SetDamageMultiplier(1.0F); } + m_pBGArm->SetDrawnAfterParent(false); + } else if (propName == "FGLeg") { + RemoveAttachable(m_pFGLeg); m_pFGLeg = new Leg; reader >> m_pFGLeg; - } - else if (propName == "BGLeg") - { - delete m_pBGLeg; + AddAttachable(m_pFGLeg); + if (m_pFGLeg->HasNoSetDamageMultiplier()) { m_pFGLeg->SetDamageMultiplier(1.0F); } + } else if (propName == "BGLeg") { + RemoveAttachable(m_pBGLeg); m_pBGLeg = new Leg; reader >> m_pBGLeg; - } - else if (propName == "HandGroup") - { + AddAttachable(m_pBGLeg); + if (m_pBGLeg->HasNoSetDamageMultiplier()) { m_pBGLeg->SetDamageMultiplier(1.0F); } + m_pBGLeg->SetDrawnAfterParent(false); + } else if (propName == "HandGroup") { delete m_pFGHandGroup; delete m_pBGHandGroup; m_pFGHandGroup = new AtomGroup(); @@ -281,43 +289,53 @@ int AHuman::ReadProperty(std::string propName, Reader &reader) m_pBGHandGroup->Create(*m_pFGHandGroup); m_pFGHandGroup->SetOwner(this); m_pBGHandGroup->SetOwner(this); - } - else if (propName == "FGFootGroup") - { + } else if (propName == "FGFootGroup") { delete m_pFGFootGroup; m_pFGFootGroup = new AtomGroup(); reader >> m_pFGFootGroup; m_pFGFootGroup->SetOwner(this); - } - else if (propName == "BGFootGroup") - { + m_BackupFGFootGroup = new AtomGroup(*m_pFGFootGroup); + m_BackupFGFootGroup->RemoveAllAtoms(); + } else if (propName == "BGFootGroup") { delete m_pBGFootGroup; m_pBGFootGroup = new AtomGroup(); reader >> m_pBGFootGroup; m_pBGFootGroup->SetOwner(this); - } - else if (propName == "StrideSound") + m_BackupBGFootGroup = new AtomGroup(*m_pBGFootGroup); + m_BackupBGFootGroup->RemoveAllAtoms(); + } else if (propName == "StrideSound") { reader >> m_StrideSound; - else if (propName == "StandLimbPath") + } else if (propName == "StandLimbPath") { reader >> m_Paths[FGROUND][STAND]; - else if (propName == "StandLimbPathBG") + } else if (propName == "StandLimbPathBG") { reader >> m_Paths[BGROUND][STAND]; - else if (propName == "WalkLimbPath") + } else if (propName == "WalkLimbPath") { reader >> m_Paths[FGROUND][WALK]; - else if (propName == "CrouchLimbPath") + } else if (propName == "CrouchLimbPath") { reader >> m_Paths[FGROUND][CROUCH]; - else if (propName == "CrawlLimbPath") + } else if (propName == "CrouchLimbPathBG") { + reader >> m_Paths[BGROUND][CROUCH]; + } else if (propName == "CrawlLimbPath") { reader >> m_Paths[FGROUND][CRAWL]; - else if (propName == "ArmCrawlLimbPath") + } else if (propName == "ArmCrawlLimbPath") { reader >> m_Paths[FGROUND][ARMCRAWL]; - else if (propName == "ClimbLimbPath") + } else if (propName == "ClimbLimbPath") { reader >> m_Paths[FGROUND][CLIMB]; - else if (propName == "JumpLimbPath") + } else if (propName == "JumpLimbPath") { reader >> m_Paths[FGROUND][JUMP]; - else if (propName == "DislodgeLimbPath") + } else if (propName == "DislodgeLimbPath") { reader >> m_Paths[FGROUND][DISLODGE]; - else + } else if (propName == "StandRotAngleTarget") { + reader >> m_RotAngleTargets[STAND]; + } else if (propName == "WalkRotAngleTarget") { + reader >> m_RotAngleTargets[WALK]; + } else if (propName == "CrouchRotAngleTarget") { + reader >> m_RotAngleTargets[CROUCH]; + } else if (propName == "JumpRotAngleTarget") { + reader >> m_RotAngleTargets[JUMP]; + } else { return Actor::ReadProperty(propName, reader); + } return 0; } @@ -387,50 +405,17 @@ int AHuman::Save(Writer &writer) const ////////////////////////////////////////////////////////////////////////////////////////// // Description: Destroys and resets (through Clear()) the AHuman object. -void AHuman::Destroy(bool notInherited) -{ - delete m_pBGLeg; - delete m_pFGLeg; - delete m_pBGArm; - delete m_pFGArm; - delete m_pJetpack; - delete m_pHead; +void AHuman::Destroy(bool notInherited) { + delete m_pFGHandGroup; delete m_pBGHandGroup; delete m_pFGFootGroup; delete m_pBGFootGroup; -// for (deque::iterator itr = m_WalkPaths.begin(); -// itr != m_WalkPaths.end(); ++itr) -// delete *itr; - if (!notInherited) - Actor::Destroy(); + if (!notInherited) { Actor::Destroy(); } Clear(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this AHuman, including the mass of its -// currently attached body parts and inventory. - -float AHuman::GetMass() const -{ - float totalMass = Actor::GetMass(); - if (m_pHead) - totalMass += m_pHead->GetMass(); - if (m_pFGArm) - totalMass += m_pFGArm->GetMass(); - if (m_pBGArm) - totalMass += m_pBGArm->GetMass(); - if (m_pFGLeg) - totalMass += m_pFGLeg->GetMass(); - if (m_pBGLeg) - totalMass += m_pBGLeg->GetMass(); - return totalMass; -} - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetTotalValue ////////////////////////////////////////////////////////////////////////////////////////// @@ -518,41 +503,126 @@ Vector AHuman::GetEyePos() const return m_Pos; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetHeadBitmap -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the sprite representing the head of this. +void AHuman::SetHead(Attachable *newHead) { + if (newHead == nullptr) { + if (m_pHead && m_pHead->IsAttached()) { RemoveAttachable(m_pHead); } + m_pHead = nullptr; + } else { + if (m_pHead && m_pHead->IsAttached()) { RemoveAttachable(m_pHead); } + m_pHead = newHead; + AddAttachable(newHead); -BITMAP * AHuman::GetHeadBitmap() const -{ - if (m_pHead && m_pHead->IsAttached()) - return m_pHead->GetSpriteFrame(0); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newHead->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + dynamic_cast(parent)->SetHead(attachable); + }}); + } +} - return 0; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void AHuman::SetJetpack(AEmitter *newJetpack) { + if (newJetpack == nullptr) { + if (m_pJetpack && m_pJetpack->IsAttached()) { RemoveAttachable(m_pJetpack); } + m_pJetpack = nullptr; + } else { + if (m_pJetpack && m_pJetpack->IsAttached()) { RemoveAttachable(m_pJetpack); } + m_pJetpack = newJetpack; + AddAttachable(newJetpack); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newJetpack->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + AEmitter *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetJetpack"); + dynamic_cast(parent)->SetJetpack(castedAttachable); + }}); + } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. +void AHuman::SetFGArm(Arm *newArm) { + if (newArm == nullptr) { + if (m_pFGArm && m_pFGArm->IsAttached()) { RemoveAttachable(m_pFGArm); } + m_pFGArm = nullptr; + } else { + if (m_pFGArm && m_pFGArm->IsAttached()) { RemoveAttachable(m_pFGArm); } + m_pFGArm = newArm; + AddAttachable(newArm); -void AHuman::SetID(const MOID newID) -{ - MovableObject::SetID(newID); - if (m_pHead) - m_pHead->SetID(newID); - if (m_pFGArm) - m_pFGArm->SetID(newID); - if (m_pBGArm) - m_pBGArm->SetID(newID); - if (m_pFGLeg) - m_pFGLeg->SetID(newID); - if (m_pBGLeg) - m_pBGLeg->SetID(newID); + m_HardcodedAttachableUniqueIDsAndSetters.insert({newArm->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + Arm *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetFGArm"); + dynamic_cast(parent)->SetFGArm(castedAttachable); + }}); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void AHuman::SetBGArm(Arm *newArm) { + if (newArm == nullptr) { + if (m_pBGArm && m_pBGArm->IsAttached()) { RemoveAttachable(m_pBGArm); } + m_pBGArm = nullptr; + } else { + if (m_pBGArm && m_pBGArm->IsAttached()) { RemoveAttachable(m_pBGArm); } + m_pBGArm = newArm; + AddAttachable(newArm); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newArm->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + Arm *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetBGArm"); + dynamic_cast(parent)->SetBGArm(castedAttachable); + }}); + } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void AHuman::SetFGLeg(Leg *newLeg) { + if (newLeg == nullptr) { + if (m_pFGLeg && m_pFGLeg->IsAttached()) { RemoveAttachable(m_pFGLeg); } + m_pFGLeg = nullptr; + } else { + if (m_pFGLeg && m_pFGLeg->IsAttached()) { RemoveAttachable(m_pFGLeg); } + m_pFGLeg = newLeg; + AddAttachable(newLeg); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + Leg *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetFGLeg"); + dynamic_cast(parent)->SetFGLeg(castedAttachable); + }}); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void AHuman::SetBGLeg(Leg *newLeg) { + if (newLeg == nullptr) { + if (m_pBGLeg && m_pBGLeg->IsAttached()) { RemoveAttachable(m_pBGLeg); } + m_pBGLeg = nullptr; + } else { + if (m_pBGLeg && m_pBGLeg->IsAttached()) { RemoveAttachable(m_pBGLeg); } + m_pBGLeg = newLeg; + AddAttachable(newLeg); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newLeg->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + Leg *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetBGLeg"); + dynamic_cast(parent)->SetBGLeg(castedAttachable); + }}); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +BITMAP *AHuman::GetHeadBitmap() const { + return (m_pHead && m_pHead->IsAttached()) ? m_pHead->GetSpriteFrame(0) : nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: CollideAtPoint @@ -1647,86 +1717,6 @@ MovableObject * AHuman::LookForMOs(float FOVSpread, unsigned char ignoreMaterial } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. - -void AHuman::GibThis(Vector impactImpulse, float internalBlast, MovableObject *pIgnoreMO) -{ - // Detach all limbs and let loose - if (m_pHead && m_pHead->IsAttached()) - { - RemoveAttachable(m_pHead); - m_pHead->SetVel(m_Vel + m_pHead->GetParentOffset() * RandomNum()); - m_pHead->SetAngularVel(RandomNormalNum()); - g_MovableMan.AddParticle(m_pHead); - m_pHead = 0; - } - if (m_pJetpack && m_pJetpack->IsAttached()) - { - // Jetpacks are really nothing, so just delete them safely - RemoveAttachable(m_pJetpack); - m_pJetpack->SetToDelete(true); - g_MovableMan.AddParticle(m_pJetpack); - m_pJetpack = 0; - } - if (m_pFGArm && m_pFGArm->IsAttached()) - { - RemoveAttachable(m_pFGArm); - m_pFGArm->SetVel(m_Vel + m_pFGArm->GetParentOffset() * RandomNum()); - m_pFGArm->SetAngularVel(RandomNormalNum()); - g_MovableMan.AddParticle(m_pFGArm); - m_pFGArm = 0; - } - if (m_pBGArm && m_pBGArm->IsAttached()) - { - RemoveAttachable(m_pBGArm); - m_pBGArm->SetVel(m_Vel + m_pBGArm->GetParentOffset() * RandomNum()); - m_pBGArm->SetAngularVel(RandomNormalNum()); - g_MovableMan.AddParticle(m_pBGArm); - m_pBGArm = 0; - } - if (m_pFGLeg && m_pFGLeg->IsAttached()) - { - RemoveAttachable(m_pFGLeg); - m_pFGLeg->SetVel(m_Vel + m_pFGLeg->GetParentOffset() * RandomNum()); - m_pFGLeg->SetAngularVel(RandomNormalNum()); - g_MovableMan.AddParticle(m_pFGLeg); - m_pFGLeg = 0; - } - if (m_pBGLeg && m_pBGLeg->IsAttached()) - { - RemoveAttachable(m_pBGLeg); - m_pBGLeg->SetVel(m_Vel + m_pBGLeg->GetParentOffset() * RandomNum()); - m_pBGLeg->SetAngularVel(RandomNormalNum()); - g_MovableMan.AddParticle(m_pBGLeg); - m_pBGLeg = 0; - } - - Actor::GibThis(impactImpulse, internalBlast, pIgnoreMO); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. - -bool AHuman::IsOnScenePoint(Vector &scenePoint) const -{ - return ((m_pFGArm && m_pFGArm->IsOnScenePoint(scenePoint)) || - (m_pFGLeg && m_pFGLeg->IsOnScenePoint(scenePoint)) || - (m_pHead && m_pHead->IsOnScenePoint(scenePoint)) || - Actor::IsOnScenePoint(scenePoint) || - (m_pJetpack && m_pJetpack->IsOnScenePoint(scenePoint)) || - (m_pBGArm && m_pBGArm->IsOnScenePoint(scenePoint)) || - (m_pBGLeg && m_pBGLeg->IsOnScenePoint(scenePoint))); -} - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: ResetAllTimers ////////////////////////////////////////////////////////////////////////////////////////// @@ -3103,7 +3093,7 @@ void AHuman::Update() Attachable *pNewHat = dynamic_cast(preset->Clone()); if (pNewHat) { - m_pHead->DetachOrDestroyAll(true); + m_pHead->RemoveOrDestroyAllAttachables(true); m_pHead->AddAttachable(pNewHat, pNewHat->GetParentOffset()); } } @@ -3608,8 +3598,7 @@ void AHuman::Update() } // Item currently set to be within reach has expired or is now out of range - if (m_pItemInReach && (!g_MovableMan.IsDevice(m_pItemInReach) || (m_pItemInReach->GetPos() - m_Pos).GetMagnitude() > reach)) - { + if (m_pItemInReach && (m_pItemInReach->IsUnPickupable() || (m_pItemInReach->HasPickupLimitations() && !m_pItemInReach->IsPickupableBy(this)) || !g_MovableMan.IsDevice(m_pItemInReach) || (m_pItemInReach->GetPos() - m_Pos).GetMagnitude() > reach)) { m_pItemInReach = 0; m_PieNeedsUpdate = true; } @@ -3644,6 +3633,16 @@ void AHuman::Update() if (m_Status == STABLE && m_MoveState != NOMOVE) { + // This exists to support disabling foot collisions if the limbpath has that flag set. + if ((m_pFGFootGroup->GetAtomCount() == 0 && m_BackupFGFootGroup->GetAtomCount() > 0) != m_Paths[FGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { + m_BackupFGFootGroup->SetLimbPos(m_pFGFootGroup->GetLimbPos()); + std::swap(m_pFGFootGroup, m_BackupFGFootGroup); + } + if ((m_pBGFootGroup->GetAtomCount() == 0 && m_BackupBGFootGroup->GetAtomCount() > 0) != m_Paths[BGROUND][m_MoveState].FootCollisionsShouldBeDisabled()) { + m_BackupBGFootGroup->SetLimbPos(m_pBGFootGroup->GetLimbPos()); + std::swap(m_pBGFootGroup, m_BackupBGFootGroup); + } + // WALKING, OR WE ARE JETPACKING AND STUCK if (m_MoveState == WALK || (m_MoveState == JUMP && m_Vel.GetLargest() < 1.0)) { @@ -3990,72 +3989,14 @@ void AHuman::Update() } } - ///////////////////////////////////////////////// - // Update MovableObject, adds on the forces etc - // NOTE: this also updates the controller, so any setstates of it will be wiped! - - Actor::Update(); - - //////////////////////////////////// - // Update viewpoint - - // Set viewpoint based on how we are aiming etc. - Vector aimSight(m_AimDistance, 0); - Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); - aimMatrix.SetXFlipped(m_HFlipped); - // Reset this each frame - m_SharpAimMaxedOut = false; - - if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->HoldsHeldDevice()) - { - float maxLength = m_pFGArm->GetHeldDevice()->GetSharpLength(); - - // Use a non-terrain check ray to cap the magnitude, so we can't see into objects etc - if (m_SharpAimProgress > 0) - { - Vector notUsed; - Vector sharpAimVector(maxLength, 0); - sharpAimVector *= aimMatrix; - - // See how far along the sharp aim vector there is opaque air -// float result = g_SceneMan.CastNotMaterialRay(m_pFGArm->GetHeldDevice()->GetMuzzlePos(), sharpAimVector, g_MaterialAir, 5); - float result = g_SceneMan.CastObstacleRay(m_pFGArm->GetHeldDevice()->GetMuzzlePos(), sharpAimVector, notUsed, notUsed, GetRootID(), g_MaterialAir, 5); - // If we didn't find anything but air before the sharpdistance, then don't alter the sharp distance - if (result >= 0 && result < (maxLength * m_SharpAimProgress)) - { - m_SharpAimProgress = result / maxLength; - m_SharpAimMaxedOut = true; - } - } - // Indicate maxed outedness if we really are, too - if (m_SharpAimProgress > 0.9) - m_SharpAimMaxedOut = true; - -// sharpDistance *= m_Controller.GetAnalogAim().GetMagnitude(); - aimSight.m_X += maxLength * m_SharpAimProgress; - } - - // Rotate the aiming spot vector and add it to the view point - aimSight *= aimMatrix; - m_ViewPoint = m_Pos.GetFloored() + aimSight; - - // Add velocity also so the viewpoint moves ahead at high speeds - if (m_Vel.GetMagnitude() > 10.0) - m_ViewPoint += m_Vel * 6; - ///////////////////////////////// - // Update Attachable:s - - if (m_pHead && m_pHead->IsAttached()) - { - m_pHead->SetHFlipped(m_HFlipped); - m_pHead->SetJointPos(m_Pos + m_pHead->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); + // Manage Attachable:s + if (m_pHead && m_pHead->IsAttached()) { float toRotate = 0; // Only rotate the head to match the aim angle if body is stable and upright - if (m_Status == STABLE && fabs(m_Rotation.GetRadAngle()) < (c_HalfPI + c_QuarterPI)) - { - toRotate = m_pHead->GetRotMatrix().GetRadAngleTo((m_HFlipped ? -m_AimAngle : m_AimAngle) * 0.7 + m_Rotation.GetRadAngle() * 0.2); - toRotate *= 0.15; + if (m_Status == STABLE && std::fabs(m_Rotation.GetRadAngle()) < (c_HalfPI + c_QuarterPI)) { + toRotate = m_pHead->GetRotMatrix().GetRadAngleTo((m_HFlipped ? -m_AimAngle : m_AimAngle) * 0.7F + m_Rotation.GetRadAngle() * 0.2F); + toRotate *= 0.15F; } // If dying upright, make head slump forward or back depending on body lean // TODO: Doesn't work too well, but probably could @@ -4065,154 +4006,128 @@ void AHuman::Update() // toRotate *= 0.10; // } // Make head just keep rotating loosely with the body if unstable or upside down - else - { + else { toRotate = m_pHead->GetRotMatrix().GetRadAngleTo(m_Rotation.GetRadAngle()); - toRotate *= 0.10; + toRotate *= 0.10F; } // Now actually rotate by the amount calculated above - m_pHead->SetRotAngle(m_pHead->GetRotMatrix().GetRadAngle() + toRotate); - - m_pHead->Update(); - // Update the Atoms' offsets in the parent group - Matrix headAtomRot(FacingAngle(m_pHead->GetRotMatrix().GetRadAngle()) - FacingAngle(m_Rotation.GetRadAngle())); - m_pAtomGroup->UpdateSubAtoms(m_pHead->GetAtomSubgroupID(), m_pHead->GetParentOffset() - (m_pHead->GetJointOffset() * headAtomRot), headAtomRot); - - m_Health -= m_pHead->CollectDamage();// * 5; // This is done in CollectDamage via m_DamageMultiplier now. + m_pHead->SetRotAngle(m_pHead->GetRotAngle() + toRotate); } - if (m_pJetpack && m_pJetpack->IsAttached()) - { - m_pJetpack->SetHFlipped(m_HFlipped); - m_pJetpack->SetJointPos(m_Pos + m_pJetpack->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - m_pJetpack->SetRotAngle(m_Rotation.GetRadAngle()); - m_pJetpack->SetOnlyLinearForces(true); - m_pJetpack->Update(); -// m_Health -= m_pJetpack->CollectDamage() * 10; - } - - if (m_pFGLeg && m_pFGLeg->IsAttached()) - { - m_pFGLeg->SetHFlipped(m_HFlipped); - m_pFGLeg->SetJointPos(m_Pos + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); // Only have the leg go to idle position if the limb target is over the joint and if we're firing the jetpack... looks retarded otherwise + if (m_pFGLeg && m_pFGLeg->IsAttached()) { m_pFGLeg->EnableIdle(m_ProneState == NOTPRONE && m_Status != UNSTABLE); -// if (!m_ArmClimbing[FGROUND]) - m_pFGLeg->ReachToward(m_pFGFootGroup->GetLimbPos(m_HFlipped)); - m_pFGLeg->Update(); - m_Health -= m_pFGLeg->CollectDamage(); + m_pFGLeg->SetTargetPosition(m_pFGFootGroup->GetLimbPos(m_HFlipped)); } - if (m_pBGLeg && m_pBGLeg->IsAttached()) - { - m_pBGLeg->SetHFlipped(m_HFlipped); - m_pBGLeg->SetJointPos(m_Pos + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - // Only have the leg go to idle position if the limb target is over the joint and if we're firing the jetpack... looks retarded otherwise + if (m_pBGLeg && m_pBGLeg->IsAttached()) { m_pBGLeg->EnableIdle(m_ProneState == NOTPRONE && m_Status != UNSTABLE); -// if (!m_ArmClimbing[BGROUND]) - m_pBGLeg->ReachToward(m_pBGFootGroup->GetLimbPos(m_HFlipped)); - m_pBGLeg->Update(); - m_Health -= m_pBGLeg->CollectDamage(); + m_pBGLeg->SetTargetPosition(m_pBGFootGroup->GetLimbPos(m_HFlipped)); } - if (m_pFGArm && m_pFGArm->IsAttached()) - { - m_pFGArm->SetHFlipped(m_HFlipped); - m_pFGArm->SetJointPos(m_Pos + m_pFGArm->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - m_pFGArm->SetRotAngle(m_HFlipped ? (-m_AimAngle/* + -m_Rotation*/) : (m_AimAngle/* + m_Rotation*/)); -// m_pFGArm->SetRotTarget(m_HFlipped ? (-m_AimAngle/* + -m_Rotation*/) : (m_AimAngle/* + m_Rotation*/)); + if (m_pFGArm && m_pFGArm->IsAttached()) { + m_pFGArm->SetRotAngle(m_AimAngle * static_cast(GetFlipFactor())); - if (m_Status == STABLE) - { - if (m_ArmClimbing[FGROUND]) - { + if (m_Status == STABLE) { + if (m_ArmClimbing[FGROUND]) { // Can't climb with anything in the arm? - // UnequipBGArm(); + //UnequipBGArm(); m_pFGArm->ReachToward(m_pFGHandGroup->GetLimbPos(m_HFlipped)); - } - // This will likely make the arm idle since the target will be out of range - else if (!m_pFGArm->IsReaching()) + } else if (!m_pFGArm->IsReaching()) { + // This will likely make the arm idle since the target will be out of range m_pFGArm->Reach(m_pFGHandGroup->GetLimbPos(m_HFlipped)); - } - // Unstable, so just drop the arm limply - else + } + } else { + // Unstable, so just drop the arm limply m_pFGArm->ReachToward(m_pFGHandGroup->GetLimbPos(m_HFlipped)); - - m_pFGArm->Update(); - m_Health -= m_pFGArm->CollectDamage(); + } } - if (m_pBGArm && m_pBGArm->IsAttached()) - { - m_pBGArm->SetHFlipped(m_HFlipped); - m_pBGArm->SetJointPos(m_Pos + m_pBGArm->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - if (m_Status == STABLE) - { - if (m_ArmClimbing[BGROUND]) - { + if (m_pBGArm && m_pBGArm->IsAttached()) { + if (m_Status == STABLE) { + if (m_ArmClimbing[BGROUND]) { // Can't climb with the shield UnequipBGArm(); m_pBGArm->ReachToward(m_pBGHandGroup->GetLimbPos(m_HFlipped)); - m_pBGArm->Update(); - } - else if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->HoldsHeldDevice() && !m_pBGArm->HoldsHeldDevice()) - { + } else if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->HoldsHeldDevice() && !m_pBGArm->HoldsHeldDevice()) { // Re-equip shield in BG arm after climbing EquipShieldInBGArm(); m_pBGArm->Reach(m_pFGArm->GetHeldDevice()->GetSupportPos()); - // m_pBGArm->ReachToward(m_Pos + m_WalkPaths.front()->GetCurrentPos()); - m_pBGArm->Update(); + // m_pBGArm->ReachToward(m_Pos + m_WalkPaths.front()->GetCurrentPos()); // BGArm does reach to support the device held by FGArm. - if (m_pBGArm->DidReach()) - { + if (m_pBGArm->DidReach()) { m_pFGArm->GetHeldDevice()->SetSupported(true); - m_pBGArm->SetRecoil(m_pFGArm->GetHeldDevice()->GetRecoilForce(), - m_pFGArm->GetHeldDevice()->GetRecoilOffset(), - m_pFGArm->GetHeldDevice()->IsRecoiled()); - } - // BGArm did not reach to support the device. - else - { + m_pBGArm->SetRecoil(m_pFGArm->GetHeldDevice()->GetRecoilForce(), m_pFGArm->GetHeldDevice()->GetRecoilOffset(), m_pFGArm->GetHeldDevice()->IsRecoiled()); + } else { + // BGArm did not reach to support the device. m_pFGArm->GetHeldDevice()->SetSupported(false); m_pBGArm->SetRecoil(Vector(), Vector(), false); } - } - else - { + } else { // Re-equip shield in BG arm after climbing EquipShieldInBGArm(); // This will likely make the arm idle since the target will be out of range m_pBGArm->Reach(m_pFGHandGroup->GetLimbPos(m_HFlipped)); m_pBGArm->SetRotAngle(m_HFlipped ? (-m_AimAngle + -m_Rotation.GetRadAngle()) : (m_AimAngle + m_Rotation.GetRadAngle())); - m_pBGArm->Update(); } + } else { + // Unstable, so just drop the arm limply + m_pBGArm->ReachToward(m_pBGHandGroup->GetLimbPos(m_HFlipped)); } - // Unstable, so just drop the arm limply - else + } + + ///////////////////////////////////////////////// + // Update MovableObject, adds on the forces etc + // NOTE: this also updates the controller, so any setstates of it will be wiped! + Actor::Update(); + + //////////////////////////////////// + // Update viewpoint + + // Set viewpoint based on how we are aiming etc. + Vector aimSight(m_AimDistance, 0); + Matrix aimMatrix(m_HFlipped ? -m_AimAngle : m_AimAngle); + aimMatrix.SetXFlipped(m_HFlipped); + // Reset this each frame + m_SharpAimMaxedOut = false; + + if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->HoldsHeldDevice()) + { + float maxLength = m_pFGArm->GetHeldDevice()->GetSharpLength(); + + // Use a non-terrain check ray to cap the magnitude, so we can't see into objects etc + if (m_SharpAimProgress > 0) { - m_pBGArm->ReachToward(m_pBGHandGroup->GetLimbPos(m_HFlipped)); - m_pBGArm->Update(); + Vector notUsed; + Vector sharpAimVector(maxLength, 0); + sharpAimVector *= aimMatrix; + + // See how far along the sharp aim vector there is opaque air +// float result = g_SceneMan.CastNotMaterialRay(m_pFGArm->GetHeldDevice()->GetMuzzlePos(), sharpAimVector, g_MaterialAir, 5); + float result = g_SceneMan.CastObstacleRay(m_pFGArm->GetHeldDevice()->GetMuzzlePos(), sharpAimVector, notUsed, notUsed, GetRootID(), g_MaterialAir, 5); + // If we didn't find anything but air before the sharpdistance, then don't alter the sharp distance + if (result >= 0 && result < (maxLength * m_SharpAimProgress)) + { + m_SharpAimProgress = result / maxLength; + m_SharpAimMaxedOut = true; + } } + // Indicate maxed outedness if we really are, too + if (m_SharpAimProgress > 0.9) + m_SharpAimMaxedOut = true; - m_Health -= m_pBGArm->CollectDamage(); +// sharpDistance *= m_Controller.GetAnalogAim().GetMagnitude(); + aimSight.m_X += maxLength * m_SharpAimProgress; } - ///////////////////////////// - // Apply forces transferred from the attachables and - // add detachment wounds to this if applicable - - if (!ApplyAttachableForces(m_pHead)) - m_pHead = 0; - if (!ApplyAttachableForces(m_pJetpack)) - m_pJetpack = 0; - if (!ApplyAttachableForces(m_pFGArm, true)) - m_pFGArm = 0; - if (!ApplyAttachableForces(m_pBGArm, true)) - m_pBGArm = 0; - if (!ApplyAttachableForces(m_pFGLeg, true)) - m_pFGLeg = 0; - if (!ApplyAttachableForces(m_pBGLeg, true)) - m_pBGLeg = 0; + // Rotate the aiming spot vector and add it to the view point + aimSight *= aimMatrix; + m_ViewPoint = m_Pos.GetFloored() + aimSight; + + // Add velocity also so the viewpoint moves ahead at high speeds + if (m_Vel.GetMagnitude() > 10.0) + m_ViewPoint += m_Vel * 6; + /* Done by pie menu now, see HandlePieCommand() //////////////////////////////////////// // AI mode setting @@ -4311,13 +4226,14 @@ void AHuman::Update() } } // Upright body posture - else - { - // Break the spring if close to target angle. - if (fabs(rot) > 0.1) - m_AngularVel -= rot * 0.5;//fabs(rot); - else if (fabs(m_AngularVel) > 0.3) - m_AngularVel *= 0.5; + else + { + float rotDiff = rot - (GetRotAngleTarget(m_MoveState) * GetFlipFactor()); + if (fabs(rotDiff) > 0.1F) { + m_AngularVel -= rotDiff * 0.5F; + } else if (fabs(m_AngularVel) > 0.3F) { + m_AngularVel *= 0.5F; + } } } // Keel over @@ -4422,210 +4338,34 @@ void AHuman::DrawThrowingReticule(BITMAP *pTargetBitmap, const Vector &targetPos release_bitmap(pTargetBitmap); } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveAnyRandomWounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specified amount of wounds from the actor and all standard attachables. - -int AHuman::RemoveAnyRandomWounds(int amount) -{ - float damage = 0; - - for (int i = 0; i < amount; i++) - { - // Fill the list of damaged bodyparts - std::vector bodyParts; - if (GetWoundCount() > 0) - bodyParts.push_back(this); - - if (m_pBGLeg && m_pBGLeg->GetWoundCount()) - bodyParts.push_back(m_pBGLeg); - if (m_pBGArm && m_pBGArm->GetWoundCount()) - bodyParts.push_back(m_pBGArm); - if (m_pJetpack && m_pJetpack->GetWoundCount()) - bodyParts.push_back(m_pJetpack); - if (m_pHead && m_pHead->GetWoundCount()) - bodyParts.push_back(m_pHead); - if (m_pFGLeg && m_pFGLeg->GetWoundCount()) - bodyParts.push_back(m_pFGLeg); - if (m_pFGArm && m_pFGArm->GetWoundCount()) - bodyParts.push_back(m_pFGArm); - - // Stop removing wounds if there are not any left - if (bodyParts.size() == 0) - break; - - int partIndex = RandomNum(0, bodyParts.size() - 1); - MOSRotating * part = bodyParts[partIndex]; - damage += part->RemoveWounds(1); - } - - return damage; -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound count of this actor and all vital attachables. - -int AHuman::GetTotalWoundCount() const -{ - int count = Actor::GetTotalWoundCount(); - - if (m_pBGLeg) - count += m_pBGLeg->GetWoundCount(); - if (m_pBGArm) - count += m_pBGArm->GetWoundCount(); - if (m_pJetpack) - count += m_pJetpack->GetWoundCount(); - if (m_pHead) - count += m_pHead->GetWoundCount(); - if (m_pFGLeg) - count += m_pFGLeg->GetWoundCount(); - if (m_pFGArm) - count += m_pFGArm->GetWoundCount(); - - return count; -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound limit of this actor and all vital attachables. - -int AHuman::GetTotalWoundLimit() const -{ - int count = Actor::GetGibWoundLimit(); - - if (m_pBGLeg) - count += m_pBGLeg->GetGibWoundLimit(); - if (m_pBGArm) - count += m_pBGArm->GetGibWoundLimit(); - if (m_pJetpack) - count += m_pJetpack->GetGibWoundLimit(); - if (m_pHead) - count += m_pHead->GetGibWoundLimit(); - if (m_pFGLeg) - count += m_pFGLeg->GetGibWoundLimit(); - if (m_pFGArm) - count += m_pFGArm->GetGibWoundLimit(); - - return count; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame - -void AHuman::UpdateChildMOIDs(vector &MOIDIndex, - MOID rootMOID, - bool makeNewMOID) -{ - if (m_pBGLeg) - m_pBGLeg->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - if (m_pBGArm) - m_pBGArm->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - if (m_pJetpack) - m_pJetpack->UpdateMOID(MOIDIndex, m_RootMOID, false); - if (m_pHead) - m_pHead->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - if (m_pFGLeg) - m_pFGLeg->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - if (m_pFGArm) - m_pFGArm->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - - Actor::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - -void AHuman::GetMOIDs(std::vector &MOIDs) const -{ - if (m_pBGLeg) - m_pBGLeg->GetMOIDs(MOIDs); - if (m_pBGArm) - m_pBGArm->GetMOIDs(MOIDs); - if (m_pJetpack) - m_pJetpack->GetMOIDs(MOIDs); - if (m_pHead) - m_pHead->GetMOIDs(MOIDs); - if (m_pFGLeg) - m_pFGLeg->GetMOIDs(MOIDs); - if (m_pFGArm) - m_pFGArm->GetMOIDs(MOIDs); - - Actor::GetMOIDs(MOIDs); -} - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Draw ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws this AHuman's current graphical representation to a // BITMAP of choice. -void AHuman::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const -{ - // Override color drawing with flash, if requested. - DrawMode realMode = (mode == g_DrawColor && m_FlashWhiteMS) ? g_DrawWhite : mode; - - if (m_pBGLeg) - m_pBGLeg->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); - if (m_pBGArm) - m_pBGArm->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); - if (m_pJetpack) - m_pJetpack->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); - if (m_pHead && !m_pHead->IsDrawnAfterParent()) - m_pHead->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); - +void AHuman::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { Actor::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pHead && m_pHead->IsDrawnAfterParent()) - m_pHead->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); - if (m_pFGLeg) - m_pFGLeg->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); - if (m_pFGArm) - { - m_pFGArm->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); - // Draw background Arm's hand after the HeldDevice of FGArm is drawn. - if (!onlyPhysical && mode == g_DrawColor && (m_pBGArm && m_pBGArm->DidReach() && m_pFGArm->HoldsHeldDevice() && !m_pFGArm->HoldsThrownDevice() && !m_pFGArm->GetHeldDevice()->IsReloading() && !m_pFGArm->GetHeldDevice()->IsShield())) - m_pBGArm->DrawHand(pTargetBitmap, targetPos, realMode); + DrawMode realMode = (mode == g_DrawColor && m_FlashWhiteMS) ? g_DrawWhite : mode; + // Note: For some reason the ordering of the attachables list can get messed up. The most important thing here is that the FGArm is on top of everything else. + if (m_pHead && m_pHead->IsDrawnAfterParent()) { m_pHead->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); } + if (m_pFGArm) { m_pFGArm->Draw(pTargetBitmap, targetPos, realMode, onlyPhysical); } + + //TODO simplify this complex if check when arm is cleaned up like turret so all it can hold are HeldDevices and children + // Draw background Arm's hand after the HeldDevice of FGArm is drawn if the FGArm is holding a weapon. + if (m_pFGArm && m_pBGArm && !onlyPhysical && mode == g_DrawColor && m_pBGArm->DidReach() && m_pFGArm->HoldsHeldDevice() && !m_pFGArm->HoldsThrownDevice() && !m_pFGArm->GetHeldDevice()->IsReloading() && !m_pFGArm->GetHeldDevice()->IsShield()) { + m_pBGArm->DrawHand(pTargetBitmap, targetPos, realMode); } #ifdef DEBUG_BUILD - if (mode == g_DrawDebug) - { - // Limbpath debug drawing + if (mode == g_DrawDebug) { m_Paths[m_HFlipped][WALK].Draw(pTargetBitmap, targetPos, 122); m_Paths[m_HFlipped][CRAWL].Draw(pTargetBitmap, targetPos, 122); m_Paths[m_HFlipped][ARMCRAWL].Draw(pTargetBitmap, targetPos, 13); m_Paths[m_HFlipped][CLIMB].Draw(pTargetBitmap, targetPos, 165); } - - if (mode == g_DrawColor && !onlyPhysical) - { - acquire_bitmap(pTargetBitmap); - putpixel(pTargetBitmap, std::floor(m_Pos.m_X), - std::floor(m_Pos.m_Y), - 64); - putpixel(pTargetBitmap, std::floor(m_Pos.m_X), - std::floor(m_Pos.m_Y), - 64); - release_bitmap(pTargetBitmap); - -// m_pAtomGroup->Draw(pTargetBitmap, targetPos, false, 122); + if (mode == g_DrawColor && !onlyPhysical) { m_pFGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); m_pBGFootGroup->Draw(pTargetBitmap, targetPos, true, 13); m_pFGHandGroup->Draw(pTargetBitmap, targetPos, true, 13); diff --git a/Entities/AHuman.h b/Entities/AHuman.h index 9806af505..6df16f708 100644 --- a/Entities/AHuman.h +++ b/Entities/AHuman.h @@ -15,6 +15,7 @@ // Inclusions of header files #include "Actor.h" +#include "Arm.h" #include "Leg.h" #include "LimbPath.h" @@ -23,11 +24,7 @@ struct BITMAP; namespace RTE { -class Attachable; -class Arm; -class Leg; class AEmitter; -//class LimbPath; ////////////////////////////////////////////////////////////////////////////////////////// @@ -76,7 +73,7 @@ enum ProneState PRONESTATECOUNT }; -enum +enum Layer { FGROUND = 0, BGROUND @@ -158,17 +155,6 @@ ClassInfoGetters void Destroy(bool notInherited = false) override; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this AHuman, including the mass of its -// currently attached body parts and inventory. -// Arguments: None. -// Return value: A float describing the mass value in Kilograms (kg). - - float GetMass() const override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetGoldCarried ////////////////////////////////////////////////////////////////////////////////////////// @@ -244,78 +230,101 @@ ClassInfoGetters Vector GetEyePos() const override; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetHead -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the head Attachable -// Arguments: None. -// Return value: A pointer to the head Attachable of this. Ownership is NOT transferred! - + /// + /// Gets the head of this AHuman. + /// + /// A pointer to the head of this AHuman. Ownership is NOT transferred. Attachable * GetHead() const { return m_pHead; } + /// + /// Sets the head for this AHuman. + /// + /// The new head to use. + void SetHead(Attachable *newHead); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFGArm -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the FG Arm as an Attachable. This is for Lua binding mostly. -// Arguments: None. -// Return value: A pointer to the FG Arm Attachable of this. Ownership is NOT transferred! + /// + /// Gets the jetpack of this AHuman. + /// + /// A pointer to the jetpack of this AHuman. Ownership is NOT transferred. + AEmitter * GetJetpack() const { return m_pJetpack; } - Attachable * GetFGArm() const { return (Attachable *)m_pFGArm; } + /// + /// Sets the jetpack for this AHuman. + /// + /// The new jetpack to use. + void SetJetpack(AEmitter *newJetpack); + /// + /// Gets the foreground Arm of this AHuman. + /// + /// A pointer to the foreground Arm of this AHuman. Ownership is NOT transferred. + Arm * GetFGArm() const { return m_pFGArm; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBGArm -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the BG Arm as an Attachable. This is for Lua binding mostly. -// Arguments: None. -// Return value: A pointer to the BG Arm Attachable of this. Ownership is NOT transferred! + /// + /// Sets the foreground Arm for this AHuman. + /// + /// The new Arm to use. + void SetFGArm(Arm *newArm); - Attachable * GetBGArm() const { return (Attachable *)m_pBGArm; } + /// + /// Gets the background arm of this AHuman. + /// + /// A pointer to the background arm of this AHuman. Ownership is NOT transferred. + Arm * GetBGArm() const { return m_pBGArm; } + /// + /// Sets the background Arm for this AHuman. + /// + /// The new Arm to use. + void SetBGArm(Arm *newArm); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetFGLeg -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the FG Leg as an Attachable. This is for Lua binding mostly. -// Arguments: None. -// Return value: A pointer to the FG Leg Attachable of this. Ownership is NOT transferred! + /// + /// Gets the foreground Leg of this AHuman. + /// + /// A pointer to the foreground Leg of this AHuman. Ownership is NOT transferred. + Leg * GetFGLeg() const { return m_pFGLeg; } - Attachable * GetFGLeg() const { return (Attachable *)m_pFGLeg; } + /// + /// Sets the foreground Leg for this AHuman. + /// + /// The new Leg to use. + void SetFGLeg(Leg *newLeg); + /// + /// Gets the background Leg of this AHuman. + /// + /// A pointer to the background Leg of this AHuman. Ownership is NOT transferred. + Leg * GetBGLeg() const { return m_pBGLeg; } + + /// + /// Sets the background Leg for this AHuman. + /// + /// The new Leg to use. + void SetBGLeg(Leg *newLeg); /// - /// Gets the FG foot attachable of this. + /// Gets the foot Attachable of this AHuman's foreground Leg. /// - /// A pointer to the FG foot attachable of this. Ownership is NOT transferred! - Attachable * GetFGFoot() const { if (m_pFGLeg) { return m_pFGLeg->GetFoot(); } else { return nullptr; } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBGLeg -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the BG Leg as an Attachable. This is for Lua binding mostly. -// Arguments: None. -// Return value: A pointer to the BG Leg Attachable of this. Ownership is NOT transferred! - - Attachable * GetBGLeg() const { return (Attachable *)m_pBGLeg; } + /// A pointer to the foot Attachable of this AHuman's foreground Leg. Ownership is NOT transferred! + Attachable * GetFGFoot() const { return m_pFGLeg ? m_pFGLeg->GetFoot() : nullptr; } + /// + /// Sets the foot Attachable of this AHuman's foreground Leg. + /// + /// The new foot for this AHuman's foreground Leg to use. + void SetFGFoot(Attachable *newFoot) { if (m_pFGLeg && m_pFGLeg->IsAttached()) { m_pFGLeg->SetFoot(newFoot); } } /// - /// Gets the BG foot attachable of this. + /// Gets the foot Attachable of this AHuman's background Leg. /// - /// A pointer to the BG foot attachable of this. Ownership is NOT transferred! - Attachable * GetBGFoot() const { if (m_pBGLeg) { return m_pBGLeg->GetFoot(); } else { return nullptr; } } - + /// A pointer to the foot Attachable of this AHuman's background Leg. Ownership is NOT transferred! + Attachable * GetBGFoot() const { return m_pBGLeg ? m_pBGLeg->GetFoot() : nullptr; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetJetpack -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the jetpack as an emitter. This is for Lua binding mostly. -// Arguments: None. -// Return value: A pointer to jetpack emitter. Ownership is NOT transferred! - - AEmitter * GetJetpack() const { return (AEmitter *)m_pJetpack; } + /// + /// Sets the foot Attachable of this AHuman's background Leg. + /// + /// The new foot for this AHuman's background Leg to use. + void SetBGFoot(Attachable *newFoot) { if (m_pBGLeg && m_pBGLeg->IsAttached()) { m_pBGLeg->SetFoot(newFoot); } } ////////////////////////////////////////////////////////////////////////////////////////// @@ -326,7 +335,7 @@ ClassInfoGetters // Return value: A pointer to the bitmap of with the head of this. Ownership is NOT // transferred! - BITMAP * GetHeadBitmap() const; + BITMAP *GetHeadBitmap() const; ////////////////////////////////////////////////////////////////////////////////////////// @@ -369,17 +378,6 @@ ClassInfoGetters void SetJetTimeLeft(float newValue) { m_JetTimeLeft = newValue < m_JetTimeTotal ? newValue : m_JetTimeTotal; } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. -// Arguments: A moid specifying the MOID that this MovableObject is -// assigned for this frame. -// Return value: None. - - void SetID(const MOID newID) override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: CollideAtPoint ////////////////////////////////////////////////////////////////////////////////////////// @@ -711,19 +709,6 @@ ClassInfoGetters MovableObject * LookForMOs(float FOVSpread = 45, unsigned char ignoreMaterial = 0, bool ignoreAllTerrain = false); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. -// Arguments: The impulse (kg * m/s) of the impact causing the gibbing to happen. -// The internal blast impulse which will push the gibs away from the center. -// A pointer to an MO which the gibs shuold not be colliding with! -// Return value: None. - - void GibThis(Vector impactImpulse = Vector(), float internalBlast = 10, MovableObject *pIgnoreMO = 0) override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetGraphicalIcon ////////////////////////////////////////////////////////////////////////////////////////// @@ -736,44 +721,6 @@ ClassInfoGetters BITMAP * GetGraphicalIcon() override { return GetHeadBitmap(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. -// Arguments: The point in absolute scene coordinates. -// Return value: Whether this' graphical rep overlaps the scene point. - - bool IsOnScenePoint(Vector &scenePoint) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveAnyRandomWounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specified amount of wounds from the actor and all standard attachables. -// Arguments: Amount of wounds to remove. -// Return value: Damage taken from removed wounds. - - int RemoveAnyRandomWounds(int amount) override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound count of this actor and all vital attachables. -// Arguments: None. -// Return value: Returns total number of wounds of this actor. - - int GetTotalWoundCount() const override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound limit of this actor and all vital attachables. -// Arguments: None. -// Return value: Returns total wound limit of this actor. - - int GetTotalWoundLimit() const override; - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: ResetAllTimers ////////////////////////////////////////////////////////////////////////////////////////// @@ -853,14 +800,13 @@ ClassInfoGetters void DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), int whichScreen = 0, bool playerControlled = false) override; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - - void GetMOIDs(std::vector &MOIDs) const override; + /// + /// Gets the LimbPath corresponding to the passed in Layer and MovementState values. + /// + /// Whether to get foreground or background LimbPath. + /// Which movement state to get the LimbPath for. + /// The LimbPath corresponding to the passed in Layer and MovementState values. + LimbPath * GetLimbPath(Layer layer, MovementState movementState) { return &m_Paths[layer][movementState]; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -904,6 +850,20 @@ ClassInfoGetters void SetLimbPathPushForce(float force); + /// + /// Gets the target rot angle for the given MovementState. + /// + /// The MovementState to get the rot angle target for. + /// The target rot angle for the given MovementState. + float GetRotAngleTarget(MovementState movementState) { return m_RotAngleTargets.at(movementState); } + + /// + /// Sets the target rot angle for the given MovementState. + /// + /// The MovementState to get the rot angle target for. + /// The new rot angle target to use. + void SetRotAngleTarget(MovementState movementState, float newRotAngleTarget) { m_RotAngleTargets.at(movementState) = newRotAngleTarget; } + /// /// Gets the duration it takes this AHuman to fully charge a throw. /// @@ -922,20 +882,6 @@ ClassInfoGetters protected: -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. -// Arguments: The MOID index to register itself and its children in. -// The MOID of the root MO of this MO, ie the highest parent of this MO. -// 0 means that this MO is the root, ie it is owned by MovableMan. -// Whether this MO should make a new MOID to use for itself, or to use -// the same as the last one in the index (presumably its parent), -// Return value: None. - - void UpdateChildMOIDs(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) override; - ////////////////////////////////////////////////////////////////////////////////////////// // Method: ChunkGold @@ -977,7 +923,9 @@ ClassInfoGetters AtomGroup *m_pFGHandGroup; AtomGroup *m_pBGHandGroup; AtomGroup *m_pFGFootGroup; + AtomGroup *m_BackupFGFootGroup; AtomGroup *m_pBGFootGroup; + AtomGroup *m_BackupBGFootGroup; // The sound of the actor taking a step (think robot servo) SoundContainer m_StrideSound; // Jetpack booster. @@ -1001,6 +949,7 @@ ClassInfoGetters // Limb paths for different movement states. // [0] is for the foreground limbs, and [1] is for BG. LimbPath m_Paths[2][MOVEMENTSTATECOUNT]; + std::array m_RotAngleTargets; //!< An array of rot angle targets for different movement states. // Whether was aiming during the last frame too. bool m_Aiming; // Whether the BG Arm is helping with locomotion or not. diff --git a/Entities/Actor.cpp b/Entities/Actor.cpp index a776f5089..f39ec3c2c 100644 --- a/Entities/Actor.cpp +++ b/Entities/Actor.cpp @@ -135,6 +135,8 @@ void Actor::Clear() m_StuckTimer.Reset(); m_FallTimer.Reset(); m_DigStrength = 1; + + m_DamageMultiplier = 1.0F; } @@ -506,22 +508,16 @@ int Actor::LoadScript(std::string const &scriptPath, bool loadAsEnabledScript) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this Actor, including the mass of its -// currently attached body parts and inventory. - -float Actor::GetMass() const -{ - float totalMass = MOSRotating::GetMass(); - for (deque::const_iterator itr = m_Inventory.begin(); itr != m_Inventory.end(); ++itr) - totalMass += (*itr)->GetMass(); - totalMass += m_GoldCarried * g_SceneMan.GetKgPerOz(); - return totalMass; +float Actor::GetInventoryMass() const { + float inventoryMass = 0.0F; + for (const MovableObject *inventoryItem : m_Inventory) { + inventoryMass += inventoryItem->GetMass(); + } + return inventoryMass; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// // Method: IsPlayerControlled @@ -742,19 +738,6 @@ void Actor::RestDetection() } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FacingAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adjusts an absolute aiming angle based on whether this Actor is facing -// left or right. - -float Actor::FacingAngle(float angle) const -{ - return (m_HFlipped ? c_PI : 0) + (angle * (m_HFlipped ? -1 : 1)); -// return (angle * m_HFlipped ? -1 : 1); -} - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: AddPieMenuSlices ////////////////////////////////////////////////////////////////////////////////////////// @@ -909,7 +892,7 @@ void Actor::DropAllInventory() velRange = 10.0F; // Randomize the offset from center to be within the original object - gibROffset.SetXY(m_MaxRadius * 0.35F * RandomNormalNum(), m_MaxRadius * 0.35F * RandomNormalNum()); + gibROffset.SetXY(m_SpriteRadius * 0.35F * RandomNormalNum(), m_SpriteRadius * 0.35F * RandomNormalNum()); // Set up its position and velocity according to the parameters of this AEmitter. pObject->SetPos(m_Pos + gibROffset/*Vector(m_Pos.m_X + 5 * NormalRand(), m_Pos.m_Y + 5 * NormalRand())*/); pObject->SetRotAngle(m_Rotation.GetRadAngle() + pObject->GetRotMatrix().GetRadAngle()); @@ -973,14 +956,14 @@ void Actor::DropAllInventory() // Description: Gibs this, effectively destroying it and creating multiple gibs or // pieces in its place. -void Actor::GibThis(Vector impactImpulse, float internalBlast, MovableObject *pIgnoreMO) +void Actor::GibThis(const Vector &impactImpulse, MovableObject *movableObjectToIgnore) { // Play death sound // TODO: Don't attenuate since death is pretty important.. maybe only make this happen for teh brains m_DeathSound.Play(m_Pos); // Gib all the regular gibs - MOSRotating::GibThis(impactImpulse, internalBlast, pIgnoreMO); + MOSRotating::GibThis(impactImpulse, movableObjectToIgnore); if (g_SettingsMan.EnableCrabBombs()) { unsigned short crabCount = 0; @@ -1007,11 +990,11 @@ void Actor::GibThis(Vector impactImpulse, float internalBlast, MovableObject *pI pObject = *gItr; // Generate the velocities procedurally - velMin = internalBlast / pObject->GetMass(); + velMin = m_GibBlastStrength / pObject->GetMass(); velRange = 10.0F; // Randomize the offset from center to be within the original object - gibROffset.SetXY(m_MaxRadius * 0.35F * RandomNormalNum(), m_MaxRadius * 0.35F * RandomNormalNum()); + gibROffset.SetXY(m_SpriteRadius * 0.35F * RandomNormalNum(), m_SpriteRadius * 0.35F * RandomNormalNum()); // Set up its position and velocity according to the parameters of this AEmitter. pObject->SetPos(m_Pos + gibROffset/*Vector(m_Pos.m_X + 5 * NormalRand(), m_Pos.m_Y + 5 * NormalRand())*/); pObject->SetRotAngle(m_Rotation.GetRadAngle() + pObject->GetRotMatrix().GetRadAngle()); @@ -1048,8 +1031,8 @@ void Actor::GibThis(Vector impactImpulse, float internalBlast, MovableObject *pI pObject->ResetAllTimers(); // Set the gib to not hit a specific MO - if (pIgnoreMO) - pObject->SetWhichMOToNotHit(pIgnoreMO); + if (movableObjectToIgnore) + pObject->SetWhichMOToNotHit(movableObjectToIgnore); // Detect whether we're dealing with a passenger and add it as Actor instead if (pPassenger = dynamic_cast(pObject)) @@ -1364,35 +1347,6 @@ void Actor::UpdateAI() } } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveAnyRandomWounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specified amount of wounds from the actor and all standard attachables. - -int Actor::RemoveAnyRandomWounds(int amount) -{ - float damage = 0; - - for (int i = 0; i < amount; i++) - { - // Fill the list of damaged bodyparts - std::vector bodyParts; - if (GetWoundCount() > 0) - bodyParts.push_back(this); - - // Stop removing wounds if there are not any left - if (bodyParts.size() == 0) - break; - - int partIndex = RandomNum(0, bodyParts.size() - 1); - MOSRotating * part = bodyParts[partIndex]; - damage += part->RemoveWounds(1); - } - - return damage; -} - ////////////////////////////////////////////////////////////////////////////////////////// // Method: VerifyMOIDs ////////////////////////////////////////////////////////////////////////////////////////// @@ -1493,10 +1447,14 @@ void Actor::Update() } ///////////////////////////////////// - // Detract damage caused by wounds from health - - for (list::iterator itr = m_Wounds.begin(); itr != m_Wounds.end(); ++itr) - m_Health -= (*itr)->CollectDamage() * m_DamageMultiplier; //Actors must apply DamageMultiplier effects to their main MO by themselves + // Take damage/heal from wounds and wounds on Attachables + for (AEmitter *wound : m_Wounds) { + m_Health -= wound->CollectDamage() * m_DamageMultiplier; + } + for (Attachable *attachable : m_Attachables) { + m_Health -= attachable->CollectDamage(); + } + m_Health = std::min(m_Health, m_MaxHealth); ///////////////////////////////////////////// // Take damage from large hits during travel @@ -2088,4 +2046,4 @@ void Actor::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichScr } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Entities/Actor.h b/Entities/Actor.h index 66a3f2aea..1abc117fc 100644 --- a/Entities/Actor.h +++ b/Entities/Actor.h @@ -147,16 +147,17 @@ ClassInfoGetters /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. int LoadScript(std::string const &scriptPath, bool loadAsEnabledScript = false) override; + /// + /// Gets the mass of this Actor's inventory. Does not include any equipped item (for actor subtypes that have that). + /// + /// The mass of this Actor's inventory. + float GetInventoryMass() const; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this Actor, including the mass of its -// currently attached body parts and inventory. -// Arguments: None. -// Return value: A float describing the mass value in Kilograms (kg). - - float GetMass() const override; + /// + /// Gets the mass of this Actor, including the mass of its Attachables, wounds and inventory. + /// + /// The mass of this Actor, its inventory and all its Attachables and wounds in Kilograms (kg). + float GetMass() const override { return MOSRotating::GetMass() + GetInventoryMass() + (m_GoldCarried * g_SceneMan.GetKgPerOz()); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -226,15 +227,6 @@ ClassInfoGetters void SetMaxHealth(int newValue) { m_MaxHealth = newValue; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: RemoveAnyRandomWounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specified amount of wounds from the actor and all standard attachables. -// Arguments: Amount of wounds to remove. -// Return value: Damage taken from removed wounds. - - virtual int RemoveAnyRandomWounds(int amount); - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetAimDistance ////////////////////////////////////////////////////////////////////////////////////////// @@ -596,18 +588,6 @@ ClassInfoGetters bool IsDead() const { return m_Status == DEAD; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: FacingAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adjusts an absolute aiming angle based on wether this Actor is facing -// left or right. -// Arguments: The input angle in radians. -// Return value: The output angle in radians, which will be unaltered if this Actor is -// facing right. - - float FacingAngle(float angle) const; - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: PieNeedsUpdate ////////////////////////////////////////////////////////////////////////////////////////// @@ -990,17 +970,13 @@ ClassInfoGetters void DrawWaypoints(bool drawWaypoints = true) { m_DrawWaypoints = drawWaypoints; } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. -// Arguments: The impulse (kg * m/s) of the impact causing the gibbing to happen. -// The internal blast impulse which will push the gibs away from the center. -// A pointer to an MO which the gibs shuold not be colliding with! -// Return value: None. - - void GibThis(Vector impactImpulse = Vector(), float internalBlast = 10, MovableObject *pIgnoreMO = 0) override; + /// + /// Destroys this MOSRotating and creates its specified Gibs in its place with appropriate velocities. + /// Any Attachables are removed and also given appropriate velocities. + /// + /// The impulse (kg * m/s) of the impact causing the gibbing to happen. + /// A pointer to an MO which the Gibs and Attachables should not be colliding with. + void GibThis(const Vector &impactImpulse = Vector(), MovableObject *movableObjectToIgnore = nullptr) override; ////////////////////////////////////////////////////////////////////////////////////////// @@ -1144,25 +1120,6 @@ ClassInfoGetters unsigned int GetDeploymentID() const { return m_DeploymentID; } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound count of this actor and all vital attachables. -// Arguments: None. -// Return value: Returns total number of wounds of this actor. - - virtual int GetTotalWoundCount() const { return this->GetWoundCount(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetTotalWoundLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns total wound limit of this actor and all vital attachables. -// Arguments: None. -// Return value: Returns total wound limit of this actor. - - virtual int GetTotalWoundLimit() const { return this->m_GibWoundLimit; } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: GetSightDistance diff --git a/Entities/Arm.cpp b/Entities/Arm.cpp index 1a0ccbdde..5ec5587d1 100644 --- a/Entities/Arm.cpp +++ b/Entities/Arm.cpp @@ -29,56 +29,60 @@ ConcreteClassInfo(Arm, Attachable, 50) void Arm::Clear() { - m_pHeldMO = 0; + m_pHeldMO = nullptr; + m_GripStrength = 0; m_HandFile.Reset(); - m_pHand = 0; + m_pHand = nullptr; m_MaxLength = 0; m_HandOffset.Reset(); - m_TargetPoint.Reset(); + m_TargetPosition.Reset(); m_IdleOffset.Reset(); m_MoveSpeed = 0; m_WillIdle = true; m_DidReach = false; } -/* + ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Create ////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Round object ready for use. +// Description: Makes the Arm object ready for use. -int Arm::Create() -{ - if (Attachable::Create() < 0) +int Arm::Create() { + if (Attachable::Create() < 0) { return -1; + } + + // Ensure Arms don't get flagged as inheriting RotAngle, since they never do and always set their RotAngle for themselves. + m_InheritsRotAngle = false; + + // Ensure Arms don't collide with terrain when attached since their expansion/contraction is frame based so atom group doesn't know how to account for it. + SetCollidesWithTerrainWhileAttached(false); return 0; } -*/ + ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// // Description: Creates a Arm to be identical to another, by deep copy. -int Arm::Create(const Arm &reference) -{ - Attachable::Create(reference); - +int Arm::Create(const Arm &reference) { if (reference.m_pHeldMO) { - m_pHeldMO = dynamic_cast(reference.m_pHeldMO->Clone()); - if (m_pHeldMO->IsDevice()) - dynamic_cast(m_pHeldMO)->Attach(this, - dynamic_cast(m_pHeldMO)->GetParentOffset()); + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pHeldMO->GetUniqueID()); + SetHeldMO(dynamic_cast(reference.m_pHeldMO->Clone())); } + Attachable::Create(reference); + m_GripStrength = reference.m_GripStrength; m_HandFile = reference.m_HandFile; m_pHand = m_HandFile.GetAsBitmap(); RTEAssert(m_pHand, "Failed to load hand bitmap in Arm::Create") m_MaxLength = reference.m_MaxLength; m_HandOffset = reference.m_HandOffset; - m_TargetPoint = reference.m_TargetPoint; + m_TargetPosition = reference.m_TargetPosition; m_IdleOffset = reference.m_IdleOffset; m_MoveSpeed = reference.m_MoveSpeed; @@ -94,35 +98,30 @@ int Arm::Create(const Arm &reference) // is called. If the property isn't recognized by any of the base classes, // false is returned, and the reader's position is untouched. -int Arm::ReadProperty(std::string propName, Reader &reader) -{ - if (propName == "HeldDevice") - { - const Entity *pEntity; - pEntity = g_PresetMan.GetEntityPreset(reader); - if (pEntity) - { - m_pHeldMO = dynamic_cast(pEntity->Clone()); - if (m_pHeldMO->IsDevice()) - dynamic_cast(m_pHeldMO)->Attach(this, dynamic_cast(m_pHeldMO)->GetParentOffset()); +int Arm::ReadProperty(std::string propName, Reader &reader) { + if (propName == "HeldDevice") { + RemoveAttachable(dynamic_cast(m_pHeldMO)); + const Entity *heldDeviceEntity = g_PresetMan.GetEntityPreset(reader); + if (heldDeviceEntity) { + m_pHeldMO = dynamic_cast(heldDeviceEntity->Clone()); + AddAttachable(dynamic_cast(m_pHeldMO)); } - pEntity = 0; - } - else if (propName == "Hand") - { + } else if (propName == "GripStrength") { + reader >> m_GripStrength; + } else if (propName == "Hand") { reader >> m_HandFile; m_pHand = m_HandFile.GetAsBitmap(); - } - else if (propName == "MaxLength") + } else if (propName == "MaxLength") { reader >> m_MaxLength; - else if (propName == "IdleOffset") + } else if (propName == "IdleOffset") { reader >> m_IdleOffset; - else if (propName == "WillIdle") + } else if (propName == "WillIdle") { reader >> m_WillIdle; - else if (propName == "MoveSpeed") + } else if (propName == "MoveSpeed") { reader >> m_MoveSpeed; - else + } else { return Attachable::ReadProperty(propName, reader); + } return 0; } @@ -140,6 +139,8 @@ int Arm::Save(Writer &writer) const writer.NewProperty("HeldDevice"); writer << m_pHeldMO; + writer.NewProperty("GripStrength"); + writer << m_GripStrength; writer.NewProperty("HandGroup"); writer << m_HandFile; writer.NewProperty("MaxLength"); @@ -164,7 +165,6 @@ void Arm::Destroy(bool notInherited) { // g_MovableMan.RemoveEntityPreset(this); - delete m_pHeldMO; // Not owned by this // destroy_bitmap(m_pHand); @@ -174,18 +174,6 @@ void Arm::Destroy(bool notInherited) } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this Arm, including the mass of any device it -// may be holding. - -float Arm::GetMass() const -{ - return m_Mass + (m_pHeldMO ? m_pHeldMO->GetMass() : 0); -} - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetHeldDevice ////////////////////////////////////////////////////////////////////////////////////////// @@ -204,19 +192,6 @@ HeldDevice * Arm::GetHeldDevice() const } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. - -void Arm::SetID(const MOID newID) -{ - MovableObject::SetID(newID); - if (m_pHeldMO) - m_pHeldMO->SetID(newID); -} - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetHeldMO ////////////////////////////////////////////////////////////////////////////////////////// @@ -224,28 +199,29 @@ void Arm::SetID(const MOID newID) // one. Ownership IS transferred. The currently held MovableObject // (if there is one) will be dropped and become a detached MovableObject, -void Arm::SetHeldMO(MovableObject *newHeldMO) -{ -// TODO: NEED TO REWORK THIS TO WORK WITH THROWNDEVICES!! - if (m_pHeldMO && m_pHeldMO->IsHeldDevice() && dynamic_cast(m_pHeldMO)->IsAttachedTo(this)) { - HeldDevice *pHeldDev = dynamic_cast(m_pHeldMO); - pHeldDev->Detach(); -// TODO: Refine throwing force to dropped device here?") - pHeldDev->SetVel(Vector(RandomNum(0.0F, 10.0F), -RandomNum(0.0F, 15.0F))); - pHeldDev->SetAngularVel(-RandomNum(0.0F, 10.0F)); - g_MovableMan.AddItem(pHeldDev); - m_pHeldMO = pHeldDev = 0; - } +void Arm::SetHeldMO(MovableObject *newHeldMO) { + if (newHeldMO == nullptr) { + Attachable *heldMOAsAttachable = dynamic_cast(m_pHeldMO); + if (heldMOAsAttachable && heldMOAsAttachable->IsAttached()) { RemoveAttachable(heldMOAsAttachable); } + m_pHeldMO = nullptr; + } else { + //TODO All this needs cleaning up, this should do the basics, some other method should be responsible for replacing held things + if (m_pHeldMO && m_pHeldMO->IsHeldDevice() && dynamic_cast(m_pHeldMO)->IsAttached()) { + RemoveAttachable(dynamic_cast(m_pHeldMO), true, false); + m_pHeldMO = nullptr; + } + + if (newHeldMO->IsHeldDevice()) { + Attachable *newHeldDevice = dynamic_cast(newHeldMO); + if (newHeldDevice->IsAttached()) { dynamic_cast(newHeldDevice->GetParent())->RemoveAttachable(newHeldDevice); } + AddAttachable(newHeldDevice); - if (newHeldMO && (newHeldMO->IsHeldDevice() || newHeldMO->IsThrownDevice())) { - Attachable *pNewDev = dynamic_cast(newHeldMO); - pNewDev->Detach(); - g_MovableMan.RemoveMO(pNewDev); - pNewDev->Attach(this, pNewDev->GetParentOffset()); - pNewDev->SetTeam(m_Team); - pNewDev = 0; + m_HardcodedAttachableUniqueIDsAndSetters.insert({newHeldDevice->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + dynamic_cast(parent)->SetHeldMO(attachable); + }}); + } + m_pHeldMO = newHeldMO; } - m_pHeldMO = newHeldMO; } @@ -269,11 +245,11 @@ MovableObject * Arm::ReleaseHeldMO() if (m_pHeldMO->IsDevice()) { // Once detached may have incorrect ID value. Detach will take care m_RootID. New ID will be assigned on next frame. - m_pHeldMO->SetID(g_NoMOID); - dynamic_cast(m_pHeldMO)->Detach(); + m_pHeldMO->SetAsNoID(); + RemoveAttachable(dynamic_cast(m_pHeldMO)); } } - m_pHeldMO = 0; + m_pHeldMO = nullptr; return pReturnMO; } @@ -290,8 +266,7 @@ MovableObject * Arm::DropEverything() MovableObject *pReturnMO = m_pHeldMO; if (m_pHeldMO && m_pHeldMO->IsDevice()) { - dynamic_cast(m_pHeldMO)->Detach(); - g_MovableMan.AddItem(m_pHeldMO); + RemoveAttachable(dynamic_cast(m_pHeldMO), true, false); } else if (m_pHeldMO) g_MovableMan.AddParticle(m_pHeldMO); @@ -311,13 +286,11 @@ MovableObject * Arm::DropEverything() MovableObject * Arm::SwapHeldMO(MovableObject *newMO) { MovableObject *oldMO = m_pHeldMO; - if (oldMO && oldMO->IsDevice()) - dynamic_cast(oldMO)->Detach(); + if (m_pHeldMO && m_pHeldMO->IsDevice()) { RemoveAttachable(dynamic_cast(m_pHeldMO)); } m_pHeldMO = newMO; if (newMO && newMO->IsDevice()) { - dynamic_cast(newMO)->Attach(this, - dynamic_cast(newMO)->GetParentOffset()); + AddAttachable(dynamic_cast(newMO)); } return oldMO; @@ -334,7 +307,7 @@ MovableObject * Arm::SwapHeldMO(MovableObject *newMO) void Arm::Reach(const Vector &scenePoint) { - m_TargetPoint = scenePoint; + m_TargetPosition = scenePoint; m_WillIdle = true; /* if (m_HFlipped) { @@ -344,7 +317,7 @@ void Arm::Reach(const Vector &scenePoint) else m_Pos += m_ParentOffset; - Vector reachVec(m_TargetPoint - m_Pos); + Vector reachVec(m_TargetPosition - m_Pos); return reachVec.GetMagnitude() <= m_MaxLength && reachVec.GetMagnitude() >= (m_MaxLength / 2); */ @@ -361,7 +334,7 @@ void Arm::Reach(const Vector &scenePoint) void Arm::ReachToward(const Vector &scenePoint) { - m_TargetPoint = scenePoint; + m_TargetPosition = scenePoint; m_WillIdle = false; /* if (m_HFlipped) { @@ -371,290 +344,128 @@ void Arm::ReachToward(const Vector &scenePoint) else m_Pos += m_ParentOffset; - Vector reachVec(m_TargetPoint - m_Pos); + Vector reachVec(m_TargetPosition - m_Pos); return reachVec.GetMagnitude() <= m_MaxLength && reachVec.GetMagnitude() >= (m_MaxLength / 2); */ } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ConstrainHand -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes sure the hand distance is constrained between the max length of -// this Arm and half the max length. - -bool Arm::ConstrainHand() -{ - float halfMax = m_MaxLength / 2; - if (m_HandOffset.GetMagnitude() > m_MaxLength) { - m_HandOffset.SetMagnitude(m_MaxLength); - return false; - } - else if (m_HandOffset.GetMagnitude() < halfMax) { - m_HandOffset.SetMagnitude(halfMax + 0.1); - return true; - } - else - return true; -} +void Arm::Update() { + if (!IsAttached()) { + RemoveAttachable(dynamic_cast(m_pHeldMO), true, false); + } else { + m_AngularVel = 0.0F; + } + UpdateCurrentHandOffset(); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. + HeldDevice *heldDevice = m_pHeldMO ? dynamic_cast(m_pHeldMO) : nullptr; + const ThrownDevice *thrownDevice = heldDevice ? dynamic_cast(heldDevice) : nullptr; -void Arm::GibThis(Vector impactImpulse, float internalBlast, MovableObject *pIgnoreMO) -{ - DropEverything(); - Attachable::GibThis(impactImpulse, internalBlast, pIgnoreMO); -} + // HeldDevices need to use the aim angle for their positioning and rotating, while ThrownDevices need to aim and position themselves based on the hand offset, so this done here for TDs and below for HDs. + if (thrownDevice || !heldDevice) { m_Rotation = m_HandOffset.GetAbsRadAngle() + (m_HFlipped ? c_PI : 0); } + if (heldDevice) { + // In order to keep the HeldDevice in the right place, we need to convert its offset (the hand offset) to work as the ParentOffset for the HeldDevice. + // The HeldDevice will then use this to set its JointPos when it's updated. Unfortunately UnRotateOffset doesn't work for this, since it's Vector/Matrix division, which isn't commutative. + Vector handOffsetAsParentOffset = RotateOffset(m_JointOffset) + m_HandOffset; + handOffsetAsParentOffset.RadRotate(-m_Rotation.GetRadAngle()).FlipX(m_HFlipped); + heldDevice->SetParentOffset(handOffsetAsParentOffset); + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this Arm. Supposed to be done every frame. - -void Arm::Update() -{ - // Update basic metrics from parent. Attachable::Update(); - if (!m_pParent) { - // When arm is detached, let go of whatever it is holding - if (m_pHeldMO) { - m_pHeldMO->SetVel(m_Vel + Vector(-RandomNum(0.0F, 10.0F), -RandomNum(0.0F, 15.0F))); - m_pHeldMO->SetAngularVel(-7); - if (m_pHeldMO->IsDevice()) - dynamic_cast(m_pHeldMO)->Detach(); - g_MovableMan.AddItem(m_pHeldMO); - m_pHeldMO = 0; - } - // Update hand - m_HandOffset.SetXY(m_MaxLength * 0.65, 0); - m_HandOffset.RadRotate((m_HFlipped ? c_PI : 0) + m_Rotation.GetRadAngle()); - } - else { - // Attached, so act like it - - // If a Firearm or Shield device is held, but not a throwable, update it and arm configuration accordingly. - HeldDevice *pHeldDev = dynamic_cast(m_pHeldMO); - if (pHeldDev && !dynamic_cast(m_pHeldMO)) - { - // Indicate that the arm wasn't reaching for anything this frame. - m_DidReach = false; - pHeldDev->SetHFlipped(m_HFlipped); - - Vector handTarget(pHeldDev->GetStanceOffset()); - handTarget *= m_Rotation/* + m_pParent->GetRotMatrix()*/; - // handTarget.RadRotate(m_pParent->GetRotMatrix()); - - // Predict where the new muzzle position will be if we don't try to clear the muzzle of terrain - Vector newMuzzlePos = (m_JointPos + handTarget) - RotateOffset(pHeldDev->GetJointOffset()) + RotateOffset(pHeldDev->GetMuzzleOffset()); - // Adjust the hand offset back if necessary so that the weapon's muzzle doesn't poke into terrain - Vector midToMuzzle(pHeldDev->GetRadius(), 0); - midToMuzzle = RotateOffset(midToMuzzle); - - // Figure out where the back butt of the device will be without adjustment - Vector midOfDevice = newMuzzlePos - midToMuzzle; - - // Trace from the back toward the muzzle, finding first pixel of impassable hardness - Vector freeMuzzlePos; - g_SceneMan.CastStrengthRay(midOfDevice, midToMuzzle, 5, freeMuzzlePos, 0, false); - Vector muzzleAdjustment = g_SceneMan.ShortestDistance(newMuzzlePos, freeMuzzlePos); - // Only apply if it's large enough - if (muzzleAdjustment.GetMagnitude() > 2.0F) - handTarget += muzzleAdjustment; - - // Interpolate the hand offset to the hand target - handTarget -= m_HandOffset; - m_HandOffset += handTarget * m_MoveSpeed; - - // Make sure the weapon cannot be extended beyond the reach of the arm. - ConstrainHand(); - - pHeldDev->SetJointPos(m_JointPos + m_HandOffset); - pHeldDev->SetRotAngle(m_Rotation.GetRadAngle()); - pHeldDev->Update(); - if (pHeldDev->IsRecoiled()) - m_pParent->AddImpulseForce(pHeldDev->GetRecoilForce()); - else - m_Recoiled = false; - - m_Rotation = (m_HFlipped ? c_PI : 0) + m_HandOffset.GetAbsRadAngle(); - - // Redo the positioning of the arm now since the rotation has changed and RotateOffset will return different results - if (!m_JointPos.IsZero()) - m_Pos = m_JointPos - RotateOffset(m_JointOffset); - else - m_Pos = m_pParent->GetPos() - RotateOffset(m_JointOffset); - - // Apply forces and detach if necessary - // OBSERVE the memeber pointer is what gets set to 0!$@#$@ - if (!ApplyAttachableForces(pHeldDev)) - m_pHeldMO = 0; - // If it blew up or whatever, releaes it from hand and put into scene so it'll be cleaned up properly - if (m_pHeldMO && m_pHeldMO->IsSetToDelete()) - g_MovableMan.AddItem(ReleaseHeldMO()); - } - // Adjust rotation and hand distance if reaching toward something. - else - { - // Not reaching toward anything - if (m_TargetPoint.IsZero()) - { - Vector moveVec(m_IdleOffset.GetXFlipped(m_HFlipped) - m_HandOffset); - m_HandOffset += moveVec * m_MoveSpeed; - m_DidReach = false; - } - else - { - Vector handTarget = g_SceneMan.ShortestDistance(m_JointPos, m_TargetPoint); - - // Check if handTarget is within arm's length. - if (handTarget.GetMagnitude() <= m_MaxLength || !m_WillIdle/* && handTarget.GetFloored() != m_HandOffset.GetFloored()*/) - { - Vector moveVec(handTarget - m_HandOffset); - m_HandOffset += moveVec * m_MoveSpeed; - m_DidReach = m_WillIdle; - } - else /*if (m_IdleOffset.GetXFlipped(m_HFlipped).GetFloored() != m_HandOffset.GetFloored())*/ - { - Vector moveVec(m_IdleOffset.GetXFlipped(m_HFlipped) - m_HandOffset); - m_HandOffset += moveVec * m_MoveSpeed; - m_DidReach = false; - } - } - // Cap hand distance to what the Arm allows - ConstrainHand(); - m_Rotation = (m_HFlipped ? c_PI : 0) + m_HandOffset.GetAbsRadAngle(); - - // Redo the positioning of the arm now since the rotation has changed and RotateOffset will return different results - if (!m_JointPos.IsZero()) - m_Pos = m_JointPos - RotateOffset(m_JointOffset); - else - m_Pos = m_pParent->GetPos() - RotateOffset(m_JointOffset); - - // If holding something other than a FireArm, then update it - if (m_pHeldMO) - { - Attachable *pHeldDev = dynamic_cast(m_pHeldMO); - if (pHeldDev) - pHeldDev->SetJointPos(m_JointPos + m_HandOffset); - else - m_pHeldMO->SetPos(m_JointPos + m_HandOffset); - m_pHeldMO->SetHFlipped(m_HFlipped); - m_pHeldMO->SetRotAngle(m_Rotation.GetRadAngle()); - m_pHeldMO->Update(); - // If it blew up or whatever, release it from hand and put into scene so it'll be cleaned up properly - if (m_pHeldMO->IsSetToDelete()) - g_MovableMan.AddItem(ReleaseHeldMO()); - } - } + m_Recoiled = heldDevice && heldDevice->IsRecoiled(); - // Set correct frame for arm bend. - float halfMax = m_MaxLength / 2.0F; - int newFrame = static_cast(((m_HandOffset.GetMagnitude()- halfMax) / halfMax) * static_cast(m_FrameCount)); - if (newFrame < 0) { - newFrame = 0; - } - RTEAssert(newFrame <= m_FrameCount, "Arm frame is out of bounds for "+ GetClassName()+": "+ GetPresetName() + "."); - if (newFrame == m_FrameCount) { - --newFrame; - } - m_Frame = newFrame; + if (heldDevice && !thrownDevice) { + m_Rotation = m_HandOffset.GetAbsRadAngle() + (m_HFlipped ? c_PI : 0); + m_Pos = m_JointPos - RotateOffset(m_JointOffset); } -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. - -void Arm::UpdateChildMOIDs(vector &MOIDIndex, - MOID rootMOID, - bool makeNewMOID) -{ - if (m_pHeldMO && m_pHeldMO->GetsHitByMOs()) - m_pHeldMO->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - - Attachable::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); + UpdateArmFrame(); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. +void Arm::UpdateCurrentHandOffset() { + if (IsAttached()) { + Vector targetOffset; + if (m_pHeldMO && !dynamic_cast(m_pHeldMO)) { + const HeldDevice *heldDevice = dynamic_cast(m_pHeldMO); + targetOffset = heldDevice->GetStanceOffset() * m_Rotation; -void Arm::GetMOIDs(std::vector &MOIDs) const -{ - if (m_pHeldMO) - m_pHeldMO->GetMOIDs(MOIDs); + // In order to keep the held device from clipping through terrain, we need to determine where its muzzle position will be, and use that to figure out where its midpoint will be, as well as the distance between the two. + Vector newMuzzlePos = (m_JointPos + targetOffset) - RotateOffset(heldDevice->GetJointOffset()) + RotateOffset(heldDevice->GetMuzzleOffset()); + Vector midToMuzzle = RotateOffset({heldDevice->GetRadius(), 0}); + Vector midOfDevice = newMuzzlePos - midToMuzzle; - Attachable::GetMOIDs(MOIDs); + Vector terrainOrMuzzlePosition; + g_SceneMan.CastStrengthRay(midOfDevice, midToMuzzle, 5, terrainOrMuzzlePosition, 0, false); + targetOffset += g_SceneMan.ShortestDistance(newMuzzlePos, terrainOrMuzzlePosition, g_SceneMan.SceneWrapsX()); + } else { + if (m_TargetPosition.IsZero()) { + targetOffset = m_IdleOffset.GetXFlipped(m_HFlipped); + } else { + targetOffset = g_SceneMan.ShortestDistance(m_JointPos, m_TargetPosition, g_SceneMan.SceneWrapsX()); + if (m_WillIdle && targetOffset.GetMagnitude() > m_MaxLength) { targetOffset = m_IdleOffset.GetXFlipped(m_HFlipped); } + } + } + + Vector distanceFromTargetOffsetToHandOffset(targetOffset - m_HandOffset); + m_HandOffset += distanceFromTargetOffsetToHandOffset * m_MoveSpeed; + m_HandOffset.ClampMagnitude(m_MaxLength, m_MaxLength / 2 + 0.1F); + } else { + m_HandOffset.SetXY(m_MaxLength * 0.65F, 0); + m_HandOffset.RadRotate((m_HFlipped ? c_PI : 0) + m_Rotation.GetRadAngle()); + } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Arm's current graphical representation to a -// BITMAP of choice. +void Arm::UpdateArmFrame() { + if (IsAttached()) { + float halfMax = m_MaxLength / 2.0F; + //TODO this should be replaced with floor I think. If I remember right, casting float to int always rounds to 0, which should function the same but is harder to remember than clearly flooring it. + int newFrame = static_cast(((m_HandOffset.GetMagnitude() - halfMax) / halfMax) * static_cast(m_FrameCount)); + RTEAssert(newFrame <= m_FrameCount, "Arm frame is out of bounds for " + GetClassName() + ": " + GetPresetName() + "."); + m_Frame = std::clamp(static_cast(newFrame), 0U, m_FrameCount - 1); + } +} -void Arm::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const -{ - if (m_pHeldMO && !m_pHeldMO->IsDrawnAfterParent()) - m_pHeldMO->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Arm::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { Attachable::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (!onlyPhysical && mode == g_DrawColor && (!m_DidReach && !m_pHeldMO)) - DrawHand(pTargetBitmap, targetPos, mode); - - if (m_pHeldMO && m_pHeldMO->IsHeldDevice() && m_pHeldMO->IsDrawnAfterParent()) - m_pHeldMO->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - if (!onlyPhysical && mode == g_DrawColor && (m_pHeldMO || !m_pParent)) + if (!onlyPhysical && (mode == g_DrawColor || mode == g_DrawWhite || mode == g_DrawTrans) && (!m_Parent || m_pHeldMO || (!m_pHeldMO && !m_DidReach))) { DrawHand(pTargetBitmap, targetPos, mode); - - if (m_pHeldMO && ((!m_pHeldMO->IsHeldDevice() && !m_pHeldMO->IsThrownDevice()) || m_pHeldMO->IsDrawnAfterParent())) - m_pHeldMO->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + if (m_pHeldMO && m_pHeldMO->IsDrawnAfterParent()) { m_pHeldMO->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } + } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawHand -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Arm's hand's graphical representation to a BITMAP of -// choice. +void Arm::DrawHand(BITMAP *targetBitmap, const Vector &targetPos, DrawMode mode) const { + Vector handPos(m_JointPos + m_HandOffset + (m_Recoiled ? m_RecoilOffset : Vector()) - targetPos); + handPos.m_X -= static_cast((m_pHand->w / 2) + 1); + handPos.m_Y -= static_cast((m_pHand->h / 2) + 1); -void Arm::DrawHand(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode) const -{ - - Vector handPos(m_JointPos + - m_HandOffset + - (m_Recoiled ? m_RecoilOffset : Vector()) - - targetPos); - handPos.m_X -= (m_pHand->w / 2) + 1; - handPos.m_Y -= (m_pHand->h / 2) + 1; - - if (!m_HFlipped) - draw_sprite(pTargetBitmap, m_pHand, handPos.GetFloorIntX(), handPos.GetFloorIntY()); - else - draw_sprite_h_flip(pTargetBitmap, m_pHand, handPos.GetFloorIntX(), handPos.GetFloorIntY()); + if (!m_HFlipped) { + if (mode == g_DrawWhite) { + draw_character_ex(targetBitmap, m_pHand, handPos.GetFloorIntX(), handPos.GetFloorIntY(), g_WhiteColor, -1); + } else { + draw_sprite(targetBitmap, m_pHand, handPos.GetFloorIntX(), handPos.GetFloorIntY()); + } + } else { + //TODO this draw_character_ex won't draw flipped. It should draw onto a temp bitmap and then draw that flipped. Maybe it can reuse a temp bitmap from MOSR, maybe not? + if (mode == g_DrawWhite) { + draw_character_ex(targetBitmap, m_pHand, handPos.GetFloorIntX(), handPos.GetFloorIntY(), g_WhiteColor, -1); + } else { + draw_sprite_h_flip(targetBitmap, m_pHand, handPos.GetFloorIntX(), handPos.GetFloorIntY()); + } + } } } // namespace RTE \ No newline at end of file diff --git a/Entities/Arm.h b/Entities/Arm.h index 9b52cac3f..9f1c1e266 100644 --- a/Entities/Arm.h +++ b/Entities/Arm.h @@ -65,6 +65,12 @@ ClassInfoGetters ~Arm() override { Destroy(true); } + /// + /// Makes the Arm object ready for use. + /// + /// An error return value signaling sucess or any particular failure. Anything below 0 is an error signal. + int Create() override; + ////////////////////////////////////////////////////////////////////////////////////////// // Method: Create ////////////////////////////////////////////////////////////////////////////////////////// @@ -98,17 +104,6 @@ ClassInfoGetters void Destroy(bool notInherited = false) override; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this Arm, including the mass of any device it -// may be holding. -// Arguments: None. -// Return value: A float describing the mass value in Kilograms (kg). - - float GetMass() const override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetHandPos ////////////////////////////////////////////////////////////////////////////////////////// @@ -175,6 +170,18 @@ ClassInfoGetters MovableObject * GetHeldMO() const { return m_pHeldMO; } + /// + /// Gets the the strength with which this Arm will grip its HeldDevice. + /// + /// The grip strength of this Arm. + float GetGripStrength() const { return m_GripStrength; } + + /// + /// Sets the strength with which this Arm will grip its HeldDevice. + /// + /// The new grip strength for this Arm to use. + void SetGripStrength(float newGripStrength) { m_GripStrength = newGripStrength; } + ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetMaxLength @@ -185,17 +192,13 @@ ClassInfoGetters float GetMaxLength() const { return m_MaxLength; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. -// Arguments: A moid specifying the MOID that this MovableObject is -// assigned for this frame. -// Return value: None. - - void SetID(const MOID newID) override; - + /// + /// Replaces the MovableObject currently held by this arm with a new one. Ownership IS transferred. + /// The currently held MovableObject (if there is one) will be dropped and become a detached MovableObject. + /// This is primarily used to support clone Create. At some point this should be refactored so Arm can only hold HeldDevices or Attachables anyway. + /// + /// A pointer to the new MovableObject to hold. Ownership IS transferred. + void SetHeldMO(Attachable *newHeldMO) { SetHeldMO(dynamic_cast(newHeldMO)); } ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetHeldMO @@ -311,7 +314,7 @@ ClassInfoGetters // Arguments: None. // Return value: Whether this Arm is holding anyhting. - bool IsReaching() { return !m_TargetPoint.IsZero(); } + bool IsReaching() { return !m_TargetPosition.IsZero(); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -355,27 +358,9 @@ ClassInfoGetters bool HoldsSomething() { return m_pHeldMO != 0; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. -// Arguments: The impulse (kg * m/s) of the impact causing the gibbing to happen. -// The internal blast impulse which will push the gibs away from the center. -// A pointer to an MO which the gibs shuold not be colliding with! -// Return value: None. - - void GibThis(Vector impactImpulse = Vector(), float internalBlast = 10, MovableObject *pIgnoreMO = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - + /// + /// Updates this Arm. Supposed to be done every frame. + /// void Update() override; @@ -393,28 +378,13 @@ ClassInfoGetters void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawHand -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Arm's hand's graphical representation to a BITMAP of -// choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Return value: None. - - void DrawHand(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor) const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - - void GetMOIDs(std::vector &MOIDs) const override; + /// + /// Draws this Arm's hand's graphical representation to a BITMAP of choice. + /// + /// A pointer to a BITMAP to draw on. + /// The absolute position of the target bitmap's upper left corner in the Scene. + /// Which mode to draw in. See the DrawMode enumeration for available modes. + void DrawHand(BITMAP *targetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor) const; ////////////////////////////////////////////////////////////////////////////////////////// // Protected member variable and method declarations @@ -433,21 +403,6 @@ ClassInfoGetters bool ConstrainHand(); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. -// Arguments: The MOID index to register itself and its children in. -// The MOID of the root MO of this MO, ie the highest parent of this MO. -// 0 means that this MO is the root, ie it is owned by MovableMan. -// Whether this MO should make a new MOID to use for itself, or to use -// the same as the last one in the index (presumably its parent), -// Return value: None. - - void UpdateChildMOIDs(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) override; - - // Member variables static Entity::ClassInfo m_sClass; // // The location of the 'hand' in relation to the MovableObject::m_Pos @@ -456,6 +411,7 @@ ClassInfoGetters MovableObject *m_pHeldMO; // Whether or not this arm is currently supporting something held in another hand bool m_Supporting; + float m_GripStrength; //!< The strength with which this Arm will grip its HeldDevice. Effectively supercedes the HeldDevice's JointStrength. // The file containing the hand bitmap. ContentFile m_HandFile; // The small bitmap holding the hand bitmap. @@ -466,7 +422,7 @@ ClassInfoGetters Vector m_HandOffset; // The target position that this Arm's hand is reaching after. // If (0, 0), the Arm is currently not reaching after anything. - Vector m_TargetPoint; + Vector m_TargetPosition; // The target offset relative to m_JointPos that this Arm's hand is moving to while not reaching for or doing anything else. Vector m_IdleOffset; // How fast the arm moves to a reach target, @@ -484,6 +440,19 @@ ClassInfoGetters private: +#pragma region Update Breakdown + /// + /// Updates the current hand offset for this Arm. Should only be called from Update. + /// If the Arm is attached, the current hand offset is based on the target offset and move speed, and whether the Arm should idle or not, otherwise it puts it in a reasonable position. + /// + void UpdateCurrentHandOffset(); + + /// + /// Updates the frame for this Arm. Should only be called from Update. + /// + void UpdateArmFrame(); +#pragma endregion + ////////////////////////////////////////////////////////////////////////////////////////// // Method: Clear ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Entities/AtomGroup.cpp b/Entities/AtomGroup.cpp index 10b2653d1..9ec7ff11c 100644 --- a/Entities/AtomGroup.cpp +++ b/Entities/AtomGroup.cpp @@ -202,10 +202,12 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float AtomGroup::GetMomentOfInertia() { - if (m_MomentOfInertia == 0.0F) { + float currentOwnerMass = m_OwnerMOSR->GetMass(); + if (m_MomentOfInertia == 0.0F || std::abs(m_StoredOwnerMass - currentOwnerMass) >= (m_StoredOwnerMass / 10.0F)) { RTEAssert(m_OwnerMOSR, "Tried to calculate moment of inertia for an AtomGroup with no parent!"); - float distMass = m_OwnerMOSR->GetMass() / static_cast(m_Atoms.size()); + m_StoredOwnerMass = currentOwnerMass; + float distMass = m_StoredOwnerMass / static_cast(m_Atoms.size()); float radius = 0.0F; for (const Atom *atom : m_Atoms) { radius = atom->GetOffset().GetMagnitude() * c_MPP; @@ -229,9 +231,11 @@ namespace RTE { atomToAdd->SetSubID(subgroupID); atomToAdd->SetOffset(offset + (atomToAdd->GetOriginalOffset() * offsetRotation)); atomToAdd->SetOwner(m_OwnerMOSR); + atomToAdd->SetIgnoreMOIDsByGroup(&m_IgnoreMOIDs); m_Atoms.push_back(atomToAdd); m_SubGroups.at(subgroupID).push_back(atomToAdd); } + if (!atomList.empty()) { m_MomentOfInertia = 0.0F; } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -253,6 +257,7 @@ namespace RTE { } } m_SubGroups.erase(removeID); + if (removedAny) { m_MomentOfInertia = 0.0F; } return removedAny; } @@ -1356,7 +1361,7 @@ namespace RTE { } // If the exit vector is too large, then avoid the jarring jump and report that we didn't make it out - if (totalExitVector.GetMagnitude() > m_OwnerMOSR->GetRadius()) { + if (totalExitVector.GetMagnitude() > m_OwnerMOSR->GetIndividualRadius()) { return false; } @@ -1484,7 +1489,7 @@ namespace RTE { } // Now actually apply the exit vectors to both, but only if the jump isn't too jarring - if (thisExit.GetMagnitude() < m_OwnerMOSR->GetRadius()) { position += thisExit; } + if (thisExit.GetMagnitude() < m_OwnerMOSR->GetIndividualRadius()) { position += thisExit; } if (!intersectedExit.IsZero() && intersectedExit.GetMagnitude() < intersectedMO->GetRadius()) { intersectedMO->SetPos(intersectedMO->GetPos() + intersectedExit); } if (m_OwnerMOSR->CanBeSquished() && RatioInTerrain() > 0.75F) /* && totalExitVector.GetMagnitude() > m_OwnerMOSR->GetDiameter()) */ { diff --git a/Entities/AtomGroup.h b/Entities/AtomGroup.h index 50795d8f4..6635281ba 100644 --- a/Entities/AtomGroup.h +++ b/Entities/AtomGroup.h @@ -179,10 +179,11 @@ namespace RTE { #pragma region Atom Management /// /// Adds a new Atom into the internal list that makes up this AtomGroup. Ownership of the Atom IS transferred! + /// Note, this resets the moment of inertia, which then has to be recalculated. /// /// A pointer to an Atom that will pushed onto the end of the list. Ownership IS transferred! /// The subgroup ID that the new Atom will have within the group. - void AddAtom(Atom *newAtom, long subgroupID = 0) { newAtom->SetSubID(subgroupID); m_Atoms.push_back(newAtom); } + void AddAtom(Atom *newAtom, long subgroupID = 0) { newAtom->SetSubID(subgroupID); m_Atoms.push_back(newAtom); m_MomentOfInertia = 0.0F; } /// /// Adds a list of new Atoms to the internal list that makes up this AtomGroup. Ownership of all Atoms in the list IS NOT transferred! @@ -200,6 +201,18 @@ namespace RTE { /// Whether any Atoms of that subgroup ID were found and removed. bool RemoveAtoms(long removeID); + /// + /// Removes all atoms in this AtomGroup, leaving it empty of Atoms. + /// + void RemoveAllAtoms() { m_Atoms.clear(); m_SubGroups.clear(); m_MomentOfInertia = 0.0F; m_StoredOwnerMass = 0.0F; } + + /// + /// Gets whether the AtomGroup contains a subgroup with the given subgroupID. + /// + /// The subgroupID to check for. + /// Whether this AtomGroup contains a subgroup with the given subgroupID. + bool ContainsSubGroup(long subgroupID) const { return m_SubGroups.count(subgroupID) != 0; } + /// /// Updates the offsets of a subgroup of Atoms in this AtomGroup. This allows repositioning a subgroup to match the position and rotation of the graphical representation of it's owner MOSR. /// @@ -351,6 +364,7 @@ namespace RTE { std::unordered_map> m_SubGroups; //!< Sub groupings of Atoms. Points to Atoms owned in m_Atoms. Not owned. MOSRotating *m_OwnerMOSR; //!< The owner of this AtomGroup. The owner is obviously not owned by this AtomGroup. + float m_StoredOwnerMass; //!< The stored mass for the owner MOSR. Used to figure out when the moment of inertia needs to be recalculated due to significant mass changes. const Material *m_Material; //!< Material of this AtomGroup. bool m_AutoGenerate; //!< Whether the Atoms in this AtomGroup were automatically generated based on a sprite, or manually defined. diff --git a/Entities/Attachable.cpp b/Entities/Attachable.cpp index 02d188636..c405a0b36 100644 --- a/Entities/Attachable.cpp +++ b/Entities/Attachable.cpp @@ -1,679 +1,474 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Attachable.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the Attachable class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "Attachable.h" #include "AtomGroup.h" #include "PresetMan.h" #include "MovableMan.h" #include "AEmitter.h" #include "Actor.h" -#include "ConsoleMan.h" namespace RTE { -ConcreteClassInfo(Attachable, MOSRotating, 100) - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Attachable, effectively -// resetting the members of this abstraction level only. - -void Attachable::Clear() -{ - m_pParent = 0; - m_ParentOffset.Reset(); - m_JointStrength = 10; - m_JointStiffness = 1.0; - m_pBreakWound = 0; - m_JointOffset.Reset(); - m_JointPos.Reset(); - m_RotTarget.Reset(); - m_AtomSubgroupID = -1; - m_DrawAfterParent = true; - m_DamageCount = 0; - m_OnlyLinForces = false; - m_InheritsRotAngle = true; - m_CanCollideWithTerrainWhenAttached = false; - m_IsCollidingWithTerrainWhileAttached = false; - m_DeleteWithParent = false; -} + ConcreteClassInfo(Attachable, MOSRotating, 0) +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the MOSParticle object ready for use. + void Attachable::Clear() { + m_Parent = nullptr; + m_ParentOffset.Reset(); + m_DrawAfterParent = true; + m_DrawnNormallyByParent = true; + m_DeleteWhenRemovedFromParent = false; + m_ApplyTransferredForcesAtOffset = true; -int Attachable::Create() -{ - if (MOSRotating::Create() < 0) - return -1; + m_GibWithParentChance = 0.0F; + m_ParentGibBlastStrengthMultiplier = 1; - return 0; -} + m_IsWound = false; + m_JointStrength = 10.0F; + m_JointStiffness = 1.0F; + m_JointOffset.Reset(); + m_JointPos.Reset(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Attachable to be identical to another, by deep copy. - -int Attachable::Create(const Attachable &reference) -{ - MOSRotating::Create(reference); - - m_pParent = reference.m_pParent; - m_ParentOffset = reference.m_ParentOffset; - m_JointStrength = reference.m_JointStrength; - m_JointStiffness = reference.m_JointStiffness; - m_pBreakWound = reference.m_pBreakWound; - m_JointOffset = reference.m_JointOffset; - m_JointPos = reference.m_JointPos; - m_RotTarget = reference.m_RotTarget; - m_AtomSubgroupID = reference.m_AtomSubgroupID; - m_DrawAfterParent = reference.m_DrawAfterParent; - m_DamageCount = reference.m_DamageCount; - m_OnlyLinForces = reference.m_OnlyLinForces; - m_InheritsRotAngle = reference.m_InheritsRotAngle; - m_CanCollideWithTerrainWhenAttached = reference.m_CanCollideWithTerrainWhenAttached; - m_DeleteWithParent = reference.m_DeleteWithParent; - - return 0; -} + m_DamageCount = 0.0F; + m_BreakWound = nullptr; + m_ParentBreakWound = nullptr; + m_InheritsHFlipped = 1; + m_InheritsRotAngle = true; + m_InheritedRotAngleOffset = 0.0F; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int Attachable::ReadProperty(std::string propName, Reader &reader) -{ - if (propName == "ParentOffset") - reader >> m_ParentOffset; - else if (propName == "JointStrength" || propName == "Strength") - reader >> m_JointStrength; - else if (propName == "JointStiffness" || propName == "Stiffness") - reader >> m_JointStiffness; - else if (propName == "BreakWound") - m_pBreakWound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); - else if (propName == "JointOffset") - reader >> m_JointOffset; - else if (propName == "InheritsRotAngle") - reader >> m_InheritsRotAngle; - else if (propName == "DrawAfterParent") - reader >> m_DrawAfterParent; - else if (propName == "CollidesWithTerrainWhenAttached") - reader >> m_CanCollideWithTerrainWhenAttached; - else if (propName == "DeleteWithParent") - reader >> m_DeleteWithParent; - else - return MOSRotating::ReadProperty(propName, reader); - - return 0; -} + m_AtomSubgroupID = -1L; + m_CollidesWithTerrainWhileAttached = true; + m_PrevRotAngleOffset = 0.0F; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this Attachable with a Writer for -// later recreation with Create(Reader &reader); - -int Attachable::Save(Writer &writer) const -{ - MOSRotating::Save(writer); - - writer.NewProperty("ParentOffset"); - writer << m_ParentOffset; - writer.NewProperty("JointStrength"); - writer << m_JointStrength; - writer.NewProperty("JointStiffness"); - writer << m_JointStiffness; - writer.NewProperty("BreakWound"); - writer << m_pBreakWound; - writer.NewProperty("JointOffset"); - writer << m_JointOffset; - writer.NewProperty("InheritsRotAngle"); - writer << m_InheritsRotAngle; - writer.NewProperty("DrawAfterParent"); - writer << m_DrawAfterParent; - writer.NewProperty("CollidesWithTerrainWhenAttached"); - writer << m_CanCollideWithTerrainWhenAttached; - writer.NewProperty("DeleteWithParent"); - writer << m_DeleteWithParent; - - return 0; -} +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int Attachable::Create() { + MOSRotating::Create(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the Attachable object. + m_AtomSubgroupID = GetUniqueID(); -void Attachable::Destroy(bool notInherited) -{ + return 0; + } - if (!notInherited) - MOSRotating::Destroy(); - Clear(); -} +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int Attachable::Create(const Attachable &reference) { + MOSRotating::Create(reference); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CollideAtPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the collision response when another MO's Atom collides with -// this MO's physical representation. The effects will be applied -// directly to this MO, and also represented in the passed in HitData. - -bool Attachable::CollideAtPoint(HitData &hd) -{ - return MOSRotating::CollideAtPoint(hd); -/* - // See if the impact created a force enough to detach from parent. - if (m_pParent && hd.ResImpulse[HITEE].GetMagnitude() > m_JointStrength) { - m_pParent->AddAbsImpulseForce(Vector(hd.ResImpulse[HITEE]).SetMagnitude(m_JointStrength), m_JointPos); - - Detach(); - } - else { - - } -*/ -} + m_ParentOffset = reference.m_ParentOffset; + m_DrawAfterParent = reference.m_DrawAfterParent; + m_DrawnNormallyByParent = reference.m_DrawnNormallyByParent; + m_DeleteWhenRemovedFromParent = reference.m_DeleteWhenRemovedFromParent; + m_ApplyTransferredForcesAtOffset = reference.m_ApplyTransferredForcesAtOffset; + m_GibWithParentChance = reference.m_GibWithParentChance; + m_ParentGibBlastStrengthMultiplier = reference.m_ParentGibBlastStrengthMultiplier; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ParticlePenetration -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Determines whether a particle which has hit this MO will penetrate, -// and if so, whether it gets lodged or exits on the other side of this -// MO. Appropriate effects will be determined and applied ONLY IF there -// was penetration! If not, nothing will be affected. - -bool Attachable::ParticlePenetration(HitData &hd) -{ - bool penetrated = MOSRotating::ParticlePenetration(hd); - - // Add damage points if MO is set to damage actors - if (hd.Body[HITOR]->DamageOnCollision() != 0) - AddDamage(hd.Body[HITOR]->DamageOnCollision()); - - // If penetrated, propogate an alarm up to the root parent, if it's an actor - if (penetrated && m_pParent) - { - // Add damage points if MO is set to damage actors on penetration - if (hd.Body[HITOR]->DamageOnPenetration() != 0) - AddDamage(hd.Body[HITOR]->DamageOnPenetration()); - - Actor *pParentActor = dynamic_cast(GetRootParent()); - if (pParentActor) - { - // Move the alarm point out a bit from the body so the reaction is better -// Vector extruded(g_SceneMan.ShortestDistance(pParentActor->GetPos(), hd.HitPoint)); - Vector extruded(hd.HitVel[HITOR]); - extruded.SetMagnitude(pParentActor->GetHeight()); - extruded = m_Pos - extruded; - g_SceneMan.WrapPosition(extruded); - pParentActor->AlarmPoint(extruded); - } - } - - return penetrated; -} + m_IsWound = reference.m_IsWound; + m_JointStrength = reference.m_JointStrength; + m_JointStiffness = reference.m_JointStiffness; + m_JointOffset = reference.m_JointOffset; + m_JointPos = reference.m_JointPos; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. - -void Attachable::GibThis(Vector impactImpulse, float internalBlast, MovableObject *pIgnoreMO) { - if(m_pParent){ - m_pParent->RemoveAttachable(this); - } - else{ - Detach(); - } - MOSRotating::GibThis(impactImpulse, internalBlast, pIgnoreMO); -} + m_DamageCount = reference.m_DamageCount; + m_BreakWound = reference.m_BreakWound; + m_ParentBreakWound = reference.m_ParentBreakWound; + m_InheritsHFlipped = reference.m_InheritsHFlipped; + m_InheritsRotAngle = reference.m_InheritsRotAngle; + m_InheritedRotAngleOffset = reference.m_InheritedRotAngleOffset; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Attach -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Attaches this Attachable to a host MOSprite + m_AtomSubgroupID = GetUniqueID(); + m_CollidesWithTerrainWhileAttached = reference.m_CollidesWithTerrainWhileAttached; -void Attachable::Attach(MOSRotating *pParent) -{ - m_pParent = pParent; + m_PrevRotAngleOffset = reference.m_PrevRotAngleOffset; - // Adopt the team of parent - if (pParent) - { - m_Team = pParent->GetTeam(); - } + return 0; + } - // Reset the attachables timers so things that have been sitting in inventory don't make backed up emissions - ResetAllTimers(); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int Attachable::ReadProperty(std::string propName, Reader &reader) { + if (propName == "ParentOffset") { + reader >> m_ParentOffset; + } else if (propName == "DrawAfterParent") { + reader >> m_DrawAfterParent; + } else if (propName == "DeleteWhenRemovedFromParent") { + reader >> m_DeleteWhenRemovedFromParent; + } else if (propName == "ApplyTransferredForcesAtOffset") { + reader >> m_ApplyTransferredForcesAtOffset; + } else if (propName == "GibWithParentChance") { + reader >> m_GibWithParentChance; + } else if (propName == "ParentGibBlastStrengthMultiplier") { + reader >> m_ParentGibBlastStrengthMultiplier; + } else if (propName == "JointStrength" || propName == "Strength") { + reader >> m_JointStrength; + } else if (propName == "JointStiffness" || propName == "Stiffness") { + reader >> m_JointStiffness; + } else if (propName == "JointOffset") { + reader >> m_JointOffset; + } else if (propName == "BreakWound") { + m_BreakWound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); + } else if (propName == "ParentBreakWound") { + m_ParentBreakWound = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); + } else if (propName == "InheritsHFlipped") { + reader >> m_InheritsHFlipped; + if (m_InheritsHFlipped != 0 && m_InheritsHFlipped != 1) { m_InheritsHFlipped = -1; } + } else if (propName == "InheritsRotAngle") { + reader >> m_InheritsRotAngle; + } else if (propName == "InheritedRotAngleRadOffset" || propName == "InheritedRotAngleOffset") { + reader >> m_InheritedRotAngleOffset; + } else if (propName == "InheritedRotAngleDegOffset") { + m_InheritedRotAngleOffset = DegreesToRadians(std::stof(reader.ReadPropValue())); + } else if (propName == "CollidesWithTerrainWhenAttached") { + reader >> m_CollidesWithTerrainWhileAttached; + } else { + return MOSRotating::ReadProperty(propName, reader); + } - if (m_pParent != NULL && GetRootParent()->HasEverBeenAddedToMovableMan()) { - RunScriptedFunctionInAppropriateScripts("OnAttach", false, false, {m_pParent}); - } -} + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int Attachable::Save(Writer &writer) const { + MOSRotating::Save(writer); + + writer.NewProperty("ParentOffset"); + writer << m_ParentOffset; + writer.NewProperty("DrawAfterParent"); + writer << m_DrawAfterParent; + writer.NewProperty("DeleteWhenRemovedFromParent"); + writer << m_DeleteWhenRemovedFromParent; + writer.NewProperty("ApplyTransferredForcesAtOffset"); + writer << m_ApplyTransferredForcesAtOffset; + + writer.NewProperty("JointStrength"); + writer << m_JointStrength; + writer.NewProperty("JointStiffness"); + writer << m_JointStiffness; + writer.NewProperty("JointOffset"); + writer << m_JointOffset; + + writer.NewProperty("BreakWound"); + writer << m_BreakWound; + writer.NewProperty("ParentBreakWound"); + writer << m_ParentBreakWound; + + writer.NewProperty("InheritsHFlipped"); + writer << ((m_InheritsHFlipped == 0 || m_InheritsHFlipped == 1) ? m_InheritsHFlipped : 2); + writer.NewProperty("InheritsRotAngle"); + writer << m_InheritsRotAngle; + writer.NewProperty("InheritedRotAngleOffset"); + writer << m_InheritedRotAngleOffset; + + writer.NewProperty("CollidesWithTerrainWhileAttached"); + writer << m_CollidesWithTerrainWhileAttached; + + return 0; + } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Detach -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Detaches this Attachable from its host MOSprite + bool Attachable::TransferJointForces(Vector &jointForces) { + if (!m_Parent) { + return false; + } + if (m_Forces.empty()) { + return true; + } -void Attachable::Detach() -{ - if (m_pParent) - { - // Attempt to remove any atoms of this that may have been added to the parent's AtomGroup before detaching - m_pParent->GetAtomGroup()->RemoveAtoms(m_AtomSubgroupID); - } + Vector totalForce; + for (const std::pair &force : m_Forces) { + totalForce += force.first; + } - m_Team = -1; - MOSRotating *temporaryParent = m_pParent; - m_pParent = 0; - // Since it's no longer atteched it should belong to itself - m_RootMOID = m_MOID; + jointForces += totalForce; + m_Forces.clear(); + return true; + } -#ifndef RELEASE_BUILD - RTEAssert(m_RootMOID == g_NoMOID || (m_RootMOID >= 0 && m_RootMOID < g_MovableMan.GetMOIDCount()), "MOID out of bounds!"); - RTEAssert(m_MOID == g_NoMOID || (m_MOID >= 0 && m_MOID < g_MovableMan.GetMOIDCount()), "MOID out of bounds!"); -#endif +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - m_RestTimer.Reset(); + bool Attachable::TransferJointImpulses(Vector &jointImpulses, float jointStiffnessValueToUse, float jointStrengthValueToUse, float gibImpulseLimitValueToUse) { + if (!m_Parent) { + return false; + } + if (m_ImpulseForces.empty()) { + return true; + } + jointStiffnessValueToUse = jointStiffnessValueToUse > 0 ? jointStiffnessValueToUse : m_JointStiffness; + jointStrengthValueToUse = jointStrengthValueToUse > 0 ? jointStrengthValueToUse : m_JointStrength; + gibImpulseLimitValueToUse = gibImpulseLimitValueToUse > 0 ? gibImpulseLimitValueToUse : m_GibImpulseLimit; + if (gibImpulseLimitValueToUse > 0) { gibImpulseLimitValueToUse = std::max(gibImpulseLimitValueToUse, jointStrengthValueToUse); } + + Vector totalImpulseForce; + for (const std::pair &impulseForce : m_ImpulseForces) { + totalImpulseForce += impulseForce.first; + } + totalImpulseForce *= jointStiffnessValueToUse; + + if (gibImpulseLimitValueToUse > 0 && totalImpulseForce.GetMagnitude() > gibImpulseLimitValueToUse) { + jointImpulses += totalImpulseForce.SetMagnitude(gibImpulseLimitValueToUse); + GibThis(); + return false; + } else if (jointStrengthValueToUse > 0 && totalImpulseForce.GetMagnitude() > jointStrengthValueToUse) { + jointImpulses += totalImpulseForce.SetMagnitude(jointStrengthValueToUse); + m_Parent->RemoveAttachable(this, true, true); + return false; + } else { + jointImpulses += totalImpulseForce; + } - if (temporaryParent != NULL && temporaryParent->GetRootParent()->HasEverBeenAddedToMovableMan()) { - RunScriptedFunctionInAppropriateScripts("OnDetach", false, false, {temporaryParent}); - } -} + // Rough explanation of what this is doing: + // The first part is getting the Dot/Scalar product of the perpendicular of the offset vector for the force onto the force vector itself (dot product is the amount two vectors are pointing in the same direction). + // The second part is dividing that Dot product by the moment of inertia, i.e. the torque needed to make it turn. All of this is multiplied by 1 - JointStiffness, because max stiffness joints transfer all force to parents and min stiffness transfer none. + if (!m_InheritsRotAngle) { + for (const std::pair &impulseForce : m_ImpulseForces) { + if (!impulseForce.second.IsZero()) { + m_AngularVel += (impulseForce.second.GetPerpendicular().Dot(impulseForce.first) / m_pAtomGroup->GetMomentOfInertia()) * (1.0F - jointStiffnessValueToUse); + } + } + } + + m_ImpulseForces.clear(); + return true; + } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/// -/// Turns on/off this Attachable's collisions, by adding/removing its atoms to/from its parent's AtomGroup. -/// -/// Adds this Attachable's atoms to the parent's AtomGroup if true, removes them if false. -void Attachable::EnableTerrainCollisions(bool enable) -{ - if (IsAttached() && CanCollideWithTerrainWhenAttached()) - { - if (!IsCollidingWithTerrainWhileAttached() && enable) - { - m_pParent->GetAtomGroup()->AddAtoms(GetAtomGroup()->GetAtomList(), GetAtomSubgroupID(), GetParentOffset() - GetJointOffset()); - SetIsCollidingWithTerrainWhileAttached(true); + float Attachable::CollectDamage() { + if (m_DamageMultiplier != 0) { + float totalDamage = m_DamageCount; + m_DamageCount = 0; + + for (AEmitter *wound : m_Wounds) { + totalDamage += wound->CollectDamage(); + } + for (Attachable *attachable : m_Attachables) { + totalDamage += attachable->CollectDamage(); + } + return totalDamage * m_DamageMultiplier; } - else if (IsCollidingWithTerrainWhileAttached() && !enable) - { - m_pParent->GetAtomGroup()->RemoveAtoms(GetAtomSubgroupID()); - SetIsCollidingWithTerrainWhileAttached(false); + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Attachable::SetCollidesWithTerrainWhileAttached(bool collidesWithTerrainWhileAttached) { + if (m_CollidesWithTerrainWhileAttached != collidesWithTerrainWhileAttached) { + AddOrRemoveAtomsFromRootParentAtomGroup(collidesWithTerrainWhileAttached); + m_CollidesWithTerrainWhileAttached = collidesWithTerrainWhileAttached; + } + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool Attachable::ParticlePenetration(HitData &hd) { + bool penetrated = MOSRotating::ParticlePenetration(hd); + + if (hd.Body[HITOR]->DamageOnCollision() != 0) { AddDamage(hd.Body[HITOR]->DamageOnCollision()); } + + if (penetrated && m_Parent) { + if (hd.Body[HITOR]->DamageOnPenetration() != 0) { AddDamage(hd.Body[HITOR]->DamageOnPenetration()); } + + // If the parent is an actor, generate an alarm point for them, moving it slightly away from the body (in the direction they got hit from) to get a good reaction. + Actor *parentAsActor = dynamic_cast(GetRootParent()); + if (parentAsActor) { + Vector extruded(hd.HitVel[HITOR]); + extruded.SetMagnitude(parentAsActor->GetHeight()); + extruded = m_Pos - extruded; + g_SceneMan.WrapPosition(extruded); + parentAsActor->AlarmPoint(extruded); + } } + + return penetrated; } - else if (IsAttached() && !CanCollideWithTerrainWhenAttached()) - { - if (enable || !enable) - { - g_ConsoleMan.PrintString("ERROR: Tried to toggle collisions for attachable that was not set to collide when initially created!"); + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Attachable::GibThis(const Vector &impactImpulse, MovableObject *movableObjectToIgnore) { + MOSRotating::GibThis(impactImpulse, movableObjectToIgnore); + if (m_Parent) { m_Parent->RemoveAttachable(this, true, true); } + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool Attachable::HandlePotentialRadiusAffectingAttachable(const Attachable *attachable) { + if (MOSRotating::HandlePotentialRadiusAffectingAttachable(attachable)) { + if (IsAttached()) { m_Parent->HandlePotentialRadiusAffectingAttachable(this); } + return true; } + return false; } -} +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Attachable::Update() { + if (m_Parent) { + UpdatePositionAndJointPositionBasedOnOffsets(); + if (m_ParentOffset != m_PrevParentOffset || m_JointOffset != m_PrevJointOffset) { m_Parent->HandlePotentialRadiusAffectingAttachable(this); } + m_Vel = m_Parent->GetVel(); + m_Team = m_Parent->GetTeam(); + if (InheritsHFlipped() != 0) { m_HFlipped = m_InheritsHFlipped == 1 ? m_Parent->IsHFlipped() : !m_Parent->IsHFlipped(); } + if (InheritsRotAngle()) { + SetRotAngle(m_Parent->GetRotAngle() + m_InheritedRotAngleOffset * static_cast(m_Parent->GetFlipFactor())); + m_AngularVel = 0.0F; + } + + MOSRotating *rootParentAsMOSR = dynamic_cast(GetRootParent()); + float currentRotAngleOffset = (GetRotAngle() * static_cast(GetFlipFactor())) - rootParentAsMOSR->GetRotAngle(); + if (rootParentAsMOSR && m_CollidesWithTerrainWhileAttached) { + // Note: This safety check exists to ensure the parent's AtomGroup contains this Attachable's Atoms in a subgroup. Hardcoded Attachables need this in order to work, since they're cloned before their parent's AtomGroup exists. + if (!rootParentAsMOSR->GetAtomGroup()->ContainsSubGroup(m_AtomSubgroupID)) { AddOrRemoveAtomsFromRootParentAtomGroup(true); } + + if (std::abs(currentRotAngleOffset - m_PrevRotAngleOffset) > 0.01745F) { // Update for 1 degree differences + Matrix atomRotationForSubgroup(rootParentAsMOSR->FacingAngle(GetRotAngle()) - rootParentAsMOSR->FacingAngle(rootParentAsMOSR->GetRotAngle())); + Vector atomOffsetForSubgroup = g_SceneMan.ShortestDistance(rootParentAsMOSR->GetPos(), m_Pos, g_SceneMan.SceneWrapsX()); + atomOffsetForSubgroup.FlipX(rootParentAsMOSR->IsHFlipped()); //TODO consolidate this into the line above once this returns the vector + atomOffsetForSubgroup /= Matrix(rootParentAsMOSR->GetRotAngle() * rootParentAsMOSR->GetFlipFactor()); + rootParentAsMOSR->GetAtomGroup()->UpdateSubAtoms(GetAtomSubgroupID(), atomOffsetForSubgroup, atomRotationForSubgroup); + } + } + m_DeepCheck = false; + m_PrevRotAngleOffset = currentRotAngleOffset; + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TransferJointForces -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Bundles up all the accumulated forces of this Attachable and calcs -// how they transfer to the joint, and therefore to the parent. + MOSRotating::Update(); -bool Attachable::TransferJointForces(Vector &jointForces) -{ - // Exit trivially if not attahced to anything - if (!m_pParent) - return false; + // If we're attached to something, MovableMan doesn't own us, and therefore isn't calling our UpdateScripts method (and neither is our parent), so we should here. + if (m_Parent != nullptr && GetRootParent()->HasEverBeenAddedToMovableMan()) { UpdateScripts(); } - Vector forces; - // Add up all the forces - for (deque >::iterator fItr = m_Forces.begin(); - fItr != m_Forces.end(); ++fItr) - forces += fItr->first; + m_PrevPos = m_Pos; + m_PrevVel = m_Vel; + m_PrevParentOffset = m_ParentOffset; + m_PrevJointOffset = m_JointOffset; + } - // Joint held up, so act accordingly -// TODO: Maybe not do this here, we might need the forces for other stuff?") - // Clear out forces after we've bundled them up. - m_Forces.clear(); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - jointForces += forces; - return true; -} + void Attachable::SetMass(const float newMass) { + float currentMass = GetMass(); + if (newMass != currentMass) { + float previousMassForUpdatingParent = m_Parent ? currentMass : 0.0F; + MovableObject::SetMass(newMass); + if (m_Parent) { m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); } + } + } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TransferJointImpulses -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Bundles up all the accumulated impulses of this Attachable and calcs -// how they transfer to the joint, and therefore to the parent. - -bool Attachable::TransferJointImpulses(Vector &jointImpulses) -{ - // Exit trivially if not attahced to anything - if (!m_pParent) - return false; - - Vector impulses; - // Add up the impulses - for (deque >::iterator iItr = m_ImpulseForces.begin(); iItr != m_ImpulseForces.end(); ++iItr) - impulses += iItr->first; - - // Factor in the stiffness, or lack thereof - impulses *= m_JointStiffness; - - // Check if joint breaks and act accordingly - if (impulses.GetMagnitude() > m_JointStrength) - { - impulses.SetMagnitude(m_JointStrength); - jointImpulses += impulses; -/* - float deltaTime = g_TimerMan.GetDeltaTimeSecs(); - m_ImpulseForces.push_back(make_pair(-impulses, m_JointOffset)); - for (iItr = m_ImpulseForces.begin(); iItr != m_ImpulseForces.end(); ++iItr) - { - // Impulse force application to the transformational velocity of this MO. - // Don't timescale these because they're already in kg * m/s (as opposed to kg * m/s^2). - m_Vel += iItr->first / m_Mass; - // Impulse force application to the rotational velocity of this MO. - if (!(*iItr).second.IsZero()) - m_AngularVel += iItr->second.GetPerpendicular().Dot(iItr->first) / - m_pAtomGroup->GetMomentOfInertia(); - } -*/ - if (m_pBreakWound) - { - // Add velocity and wound before detaching. - // The forces should be applied to this' vel next round when detached - AEmitter *pWound = dynamic_cast(m_pBreakWound->Clone()); - if (pWound) - { - pWound->SetEmitAngle(m_JointOffset.GetAbsRadAngle()); - AddWound(pWound, m_JointOffset, false); - pWound = 0; - } - } - - if (m_pParent) - { - m_pParent->RemoveAttachable(this); - } - else - { - Detach(); - } - g_MovableMan.AddParticle(this); - return false; - } - - //////////////////////////////////////////// - // Joint held up, so act accordingly - - // Apply the rotational effects of all the impulses accumulated. - for (deque >::iterator iItr = m_ImpulseForces.begin(); iItr != m_ImpulseForces.end(); ++iItr) - { - // Impulse force application to the rotational velocity of this MO. - if (!(*iItr).second.IsZero()) - m_AngularVel += ((*iItr).second.GetPerpendicular().Dot((*iItr).first) / - m_pAtomGroup->GetMomentOfInertia()) * (1.0 - m_JointStiffness); - } -// TODO: Maybe not do this here, we might need the forces for other stuff?") - // Clear out forces after we've bundled them up. - m_ImpulseForces.clear(); - - jointImpulses += impulses; - return true; -} + void Attachable::UpdateAttachableAndWoundMass(float oldAttachableOrWoundMass, float newAttachableOrWoundMass) { + float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; + MOSRotating::UpdateAttachableAndWoundMass(oldAttachableOrWoundMass, newAttachableOrWoundMass); + if (m_Parent) { m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); } + } -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: PostTravel -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does stuff that needs to be done after Update(). Always call after -// calling Update. +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void Attachable::PostTravel() -{ - MOSRotating::PostTravel(); -} -*/ + void Attachable::AddAttachable(Attachable *attachable, const Vector &parentOffsetToSet) { + float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; + MOSRotating::AddAttachable(attachable, parentOffsetToSet); + if (m_Parent) { m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: CollectDamage -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the number of damage points this Attachable has sustained and -// should cause its parent. Calling this will reset the damage count. -// This should normally be called AFTER Update() to get the correct -// damage for a given frame. +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -float Attachable::CollectDamage() -{ - float totalDamage = m_DamageCount; - m_DamageCount = 0; + bool Attachable::RemoveAttachable(Attachable *attachable, bool addToMovableMan, bool addBreakWounds) { + float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; + bool result = MOSRotating::RemoveAttachable(attachable, addToMovableMan, addBreakWounds); + if (m_Parent) { m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); } + return result; + } - for (list::iterator itr = m_Wounds.begin(); itr != m_Wounds.end(); ++itr) - totalDamage += (*itr)->CollectDamage(); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - return totalDamage * m_DamageMultiplier; -} + void Attachable::AddWound(AEmitter *woundToAdd, const Vector &parentOffsetToSet, bool checkGibWoundLimit) { + float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; + MOSRotating::AddWound(woundToAdd, parentOffsetToSet, checkGibWoundLimit); + if (m_Parent) { m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); } + } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: RemoveWounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specified amount of wounds and returns damage caoused by this wounds. -// Head multiplier is not used. + float Attachable::RemoveWounds(int numberOfWoundsToRemove, bool includeAttachablesWithAPositiveDamageMultiplier, bool includeAttachablesWithANegativeDamageMultiplier, bool includeAttachablesWithNoDamageMultiplier) { + float previousMassForUpdatingParent = m_Parent ? GetMass() : 0.0F; + float result = MOSRotating::RemoveWounds(numberOfWoundsToRemove, includeAttachablesWithAPositiveDamageMultiplier, includeAttachablesWithANegativeDamageMultiplier, includeAttachablesWithNoDamageMultiplier); + if (m_Parent) { m_Parent->UpdateAttachableAndWoundMass(previousMassForUpdatingParent, GetMass()); } + return result; + } -int Attachable::RemoveWounds(int amount) -{ - return MOSRotating::RemoveWounds(amount) * m_DamageMultiplier; -} +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this Attachable. Supposed to be done every frame. - -void Attachable::Update() -{ - if (!m_pParent) { -// This doesn't work so well, sinking problems -// m_DeepCheck = true; - m_JointPos = m_Pos + RotateOffset(m_JointOffset); - } - else { - // Save previous position and velocities before moving - m_PrevPos = m_Pos; - m_PrevVel = m_Vel; - // Attached, so get all metrics from parent and apply -// m_HFlipped = m_pParent->IsHFlipped(); not flexible enough - if (!m_JointPos.IsZero()) - m_Pos = m_JointPos - RotateOffset(m_JointOffset); - else - m_Pos = m_pParent->GetPos() - RotateOffset(m_JointOffset); -// m_Rotation = m_pParent->GetRotMatrix(); - m_Vel = m_pParent->GetVel(); -// m_AngularVel = m_pParent->GetAngularVel(); - m_Team = m_pParent->GetTeam(); -/* - /////////////////////////////////////// - // Rotation spring calc - // Get the rotation in radians. - float springDelta = m_Rotation.GetRadAngle() - m_RotTarget.GetRadAngle(); - // Eliminate full rotations - while (fabs(springDelta) > c_TwoPI) { - springDelta -= springDelta > 0 ? c_TwoPI : -c_TwoPI; - } - // Eliminate rotations over half a turn -// if (fabs(springDelta) > c_PI) -// springDelta = (springDelta > 0 ? -c_PI : c_PI) + (springDelta - (springDelta > 0 ? c_PI : -c_PI)); - // Break the spring if close to target angle. - if (fabs(springDelta) > 0.1) - m_AngularVel -= springDelta * fabs(springDelta);// * m_JointStiffness; - else if (fabs(m_AngularVel) > 0.1) - m_AngularVel *= 0.5; -// m_Rotation += springDelta * m_JointStiffness; - -// m_AngularVel -= springDelta * m_JointStiffness; - - // Apply the rotational velocity - float deltaTime = g_TimerMan.GetDeltaTimeSecs(); -// m_Rotation += m_AngularVel * deltaTime; -*/ - - m_DeepCheck = false; - } - - MOSRotating::Update(); - - // If we're attached to something, MoveableMan doesn't own us, and therefore isn't calling our ScriptUpdate (and our parent isn't calling it either), so we should here - if (IsAttached() && GetRootParent()->HasEverBeenAddedToMovableMan()) { UpdateScripts(); } -} + void Attachable::SetParent(MOSRotating *newParent) { + if (newParent == m_Parent) { + return; + } + MOSRotating *parentToUseForScriptCall = newParent ? newParent : m_Parent; + + //TODO Get rid of the need for calling ResetAllTimers, if something like inventory swapping needs timers reset it should do it itself! This blanket handling probably has side-effects. + // Timers are reset here as a precaution, so that if something was sitting in an inventory, it doesn't cause backed up emissions. + ResetAllTimers(); + + if (newParent) { + m_Parent = newParent; + m_Team = newParent->GetTeam(); + UpdatePositionAndJointPositionBasedOnOffsets(); + if (m_CollidesWithTerrainWhileAttached) { AddOrRemoveAtomsFromRootParentAtomGroup(true); } + } else { + m_RootMOID = m_MOID; + m_RestTimer.Reset(); + m_Team = -1; + m_IsWound = false; + if (m_pMOToNotHit && m_Parent && m_Parent->GetWhichMOToNotHit() == m_pMOToNotHit) { m_pMOToNotHit = nullptr; } + + if (m_CollidesWithTerrainWhileAttached) { AddOrRemoveAtomsFromRootParentAtomGroup(false); } + for (Attachable *attachable : m_Attachables) { + if (attachable->GetCollidesWithTerrainWhileAttached()) { attachable->AddOrRemoveAtomsFromRootParentAtomGroup(false); } + } + m_Parent = newParent; + for (Attachable *attachable : m_Attachables) { + if (attachable->GetCollidesWithTerrainWhileAttached()) { attachable->AddOrRemoveAtomsFromRootParentAtomGroup(true); } + } + } + if (parentToUseForScriptCall && parentToUseForScriptCall->GetRootParent()->HasEverBeenAddedToMovableMan()) { + RunScriptedFunctionInAppropriateScripts(newParent ? "OnAttach" : "OnDetach", false, false, {parentToUseForScriptCall}); + } + } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Attachable's current graphical representation to a -// BITMAP of choice. - -void Attachable::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const -{ - MOSRotating::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - -/* - Vector spritePos(m_Pos.GetFloored() - targetPos); - - // If attached to something, rotate about the joint, - // otherwise rotate about the center of mass -// if (m_pParent) -// spritePos += m_JointOffset; - - if (m_Recoiled) - spritePos += m_RecoilOffset; - - if (m_HFlipped) { - if (!m_pTempBitmapB) { - m_pTempBitmapB = new BITMAP(); - m_pTempBitmapB->Create(g_FrameMan.GetScreen(), - m_aSprite->GetTile()->GetBlockWidth(), - m_aSprite->GetTile()->GetBlockHeight(), - CDXMEM_SYSTEMONLY); - } - m_pTempBitmapB->Fill(g_MaskColor); - m_pTempBitmapB->SetColorKey(g_MaskColor); - m_aSprite->SetPos(0, 0); - - if (mode != g_DrawColor) { - if (!m_pTempBitmapA) { - m_pTempBitmapA = new BITMAP(); - m_pTempBitmapA->Create(g_FrameMan.GetScreen(), - m_aSprite->GetTile()->GetBlockWidth(), - m_aSprite->GetTile()->GetBlockHeight(), - CDXMEM_SYSTEMONLY); - } - m_pTempBitmapA->Fill(g_MaskColor); - m_pTempBitmapA->SetColorKey(g_MaskColor); - - if (mode == g_DrawMaterial) - DrawMaterial(m_aSprite, m_pTempBitmapA, GetSettleMaterial()); - else if (mode == g_DrawAir) - DrawMaterial(m_aSprite, m_pTempBitmapA, g_MaterialAir); - else if (mode == g_DrawMask) - DrawMaterial(m_aSprite, m_pTempBitmapA, g_MaskColor); - else if (mode == g_DrawMOID) - DrawMaterial(m_aSprite, m_pTempBitmapA, m_MOID); - else - RTEAbort("Unknown draw mode selected in Attachable:Draw()!"); - - m_pTempBitmapA->DrawTransHFlip(m_pTempBitmapB, 0, 0); - } - else - m_aSprite->Draw(m_pTempBitmapB, 0, 0, CDXBLT_TRANSHFLIP); - - Vector newSpriteCent(m_SpriteCenter); - newSpriteCent.m_X = -newSpriteCent.m_X; - newSpriteCent.RadRotate(m_Rotation); - spritePos += newSpriteCent; - - // Hack to correct shitty cdx rotozoom - spritePos.m_X += 2.0; - - // This guy wants the center of the sprite, not the top left corner like Draw(). - m_pTempBitmapB->DrawTransRotoZoom(pTargetBitmap, - spritePos.m_X, - spritePos.m_Y, - m_pTempBitmapB->GetClipRect(), - m_Rotation, - m_Scale); - } - else { - spritePos += m_SpriteOffset; - spritePos += (m_SpriteCenter * m_Rotation - m_SpriteCenter); - // Hack to correct shitty cdx rotozoom - spritePos.m_X += 1.0; - - m_aSprite->SetPos(spritePos.m_X, spritePos.m_Y); - - if (mode == g_DrawMaterial) - DrawMaterialRotoZoomed(m_aSprite, pTargetBitmap, GetSettleMaterial()); - else if (mode == g_DrawAir) - DrawMaterialRotoZoomed(m_aSprite, pTargetBitmap, g_MaterialAir); - else if (mode == g_DrawMask) - DrawMaterialRotoZoomed(m_aSprite, pTargetBitmap, g_MaskColor); - else if (mode == g_DrawMOID) - DrawMaterialRotoZoomed(m_aSprite, pTargetBitmap, m_MOID); - else - m_aSprite->Draw(pTargetBitmap, 0, 0, CDXBLT_TRANSROTOZOOM); - } - -// TODO: Clean up the drawing hierarchy!#@!") - // Finally draw all the attached emitters, and only if the mode is g_DrawColor - if (mode == g_DrawColor || mode == g_DrawMaterial) { - for (list::iterator itr = m_Wounds.begin(); itr != m_Wounds.end(); ++itr) - (*itr)->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - } -*/ -/* -#ifdef DEBUG_BUILD - pTargetBitmap->Lock(); - pTargetBitmap->PutPixel(m_Pos.GetFloorIntX() - targetPos.m_X, - m_Pos.GetFloorIntY() - targetPos.m_Y, - 64); - pTargetBitmap->UnLock(); -#endif -*/ -} +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -} // namespace RTE + void Attachable::UpdatePositionAndJointPositionBasedOnOffsets() { + if (m_Parent) { + m_JointPos = m_Parent->GetPos() + m_Parent->RotateOffset(GetParentOffset()); + m_Pos = m_JointPos - RotateOffset(m_JointOffset); + } else { + m_JointPos = m_Pos + RotateOffset(m_JointOffset); + } + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Attachable::AddOrRemoveAtomsFromRootParentAtomGroup(bool addAtoms) { + MOSRotating *rootParentAsMOSR = dynamic_cast(GetRootParent()); + if (rootParentAsMOSR && IsAttached()) { + AtomGroup *rootParentAtomGroup = rootParentAsMOSR->GetAtomGroup(); + if (rootParentAtomGroup) { + if (addAtoms && !rootParentAtomGroup->ContainsSubGroup(GetAtomSubgroupID())) { + Vector atomOffsetForSubgroup = g_SceneMan.ShortestDistance(rootParentAsMOSR->GetPos(), m_Pos, g_SceneMan.SceneWrapsX()); + atomOffsetForSubgroup.FlipX(rootParentAsMOSR->IsHFlipped()); + Matrix atomRotationForSubgroup(rootParentAsMOSR->FacingAngle(GetRotAngle()) - rootParentAsMOSR->FacingAngle(rootParentAsMOSR->GetRotAngle())); + rootParentAtomGroup->AddAtoms(GetAtomGroup()->GetAtomList(), GetAtomSubgroupID(), atomOffsetForSubgroup, atomRotationForSubgroup); + } else if (!addAtoms && rootParentAtomGroup->ContainsSubGroup(GetAtomSubgroupID())) { + rootParentAtomGroup->RemoveAtoms(GetAtomSubgroupID()); + } + } + } + } +} diff --git a/Entities/Attachable.h b/Entities/Attachable.h index 6fc04abf3..0d255c635 100644 --- a/Entities/Attachable.h +++ b/Entities/Attachable.h @@ -1,660 +1,558 @@ #ifndef _RTEATTACHABLE_ #define _RTEATTACHABLE_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Attachable.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the Attachable class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "MOSRotating.h" -namespace RTE -{ - -class AEmitter; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: Attachable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: An articulated, detachable part of an Actor's body. -// Parent(s): MOSRotating. -// Class history: 05/30/2002 Attachable created. - -class Attachable: - public MOSRotating -{ - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(Attachable) -AddScriptFunctionNames(MOSRotating, "OnAttach", "OnDetach") -SerializableOverrideMethods -ClassInfoGetters - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: Attachable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a Attachable object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - Attachable() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~Attachable -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a Attachable object before deletion -// from system memory. -// Arguments: None. - - ~Attachable() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Attachable object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Attachable to be identical to another, by deep copy. -// Arguments: A reference to the Attachable to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const Attachable &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire Attachable, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); MOSRotating::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - - /// - /// Gets the MO which is the parent of this Attachable. - /// - /// A pointer to the parent of this Attachable. - MovableObject * GetParent() override { return m_pParent; } - - /// - /// Gets the MO which is the parent of this Attachable. - /// - /// A pointer to the parent of this Attachable. - const MovableObject * GetParent() const override { return m_pParent; } - - /// - /// Gets the MO which is the ultimate root parent of this Attachable and its parent. - /// - /// A pointer to the highest root parent of this Attachable. - MovableObject * GetRootParent() override { return m_pParent ? m_pParent->GetRootParent() : this; } - - /// - /// Gets the MO which is the ultimate root parent of this Attachable and its parent. - /// - /// A pointer to the highest root parent of this Attachable. - const MovableObject * GetRootParent() const override { return m_pParent ? m_pParent->GetRootParent() : this; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetParentOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the stored offset between this Attachable's parent's Pos and the -// joint position. This should be maintained by the parent. -// Arguments: None. -// Return value: A const reference Vector describing the offset from the parent's pos -// to the joint point. - - const Vector & GetParentOffset() const { return m_ParentOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetJointOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the offset to the joint (the point around which this Attachable -// and its parent hinge) from this Attachable's center of mass/origin. -// Arguments: None. -// Return value: A const reference Vector describing the offset of the joint relative -// to the this Attachable's origin/center of mass position. - - const Vector & GetJointOffset() const { return m_JointOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetJointOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the offset to the joint (the point around which this Attachable -// and its parent hinge) from this Attachable's center of mass/origin. -// Arguments: Vector describing the offset of the joint relative -// to the this Attachable's origin/center of mass position. -// Return value: None. - - void SetJointOffset(Vector offset) { m_JointOffset = offset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetJointStrength -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the amount of impulse force the joint of this Attachable can -// handle before breaking. -// Arguments: None. -// Return value: A float with the max tolerated impulse force in kg * m/s. - - float GetJointStrength() const { return m_JointStrength; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetJointStiffness -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the normalized stiffness scalar of the join of this Attachable. -// Arguments: None. -// Return value: A float between 0 and 1.0. 1.0 means 100% of all forces imposed on this -// attachable will be transferred through the joint to the parent. 0.5 -// means 50%, and so on. - - float GetJointStiffness() const { return m_JointStiffness; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetBreakWound -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the AEmitter that represents the wound created when this -// Attachable gets detached from its parent. -// Arguments: None. -// Return value: A const pointer to the break wound AEmitter. - - const AEmitter * GetBreakWound() const { return m_pBreakWound; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRotTarget -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the target rotation that this Attachable should be striving to -// match its real rotation with, if it is attached. The joint stiffness -// will determine how strong the scalar spring is between the current -// rotation and the target set here. -// Arguments: None. -// Return value: A float for the current target angle in radians. - - float GetRotTarget() const { return m_RotTarget.GetRadAngle(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAtomSubgroupID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the subgroup ID of this' Atoms. -// Arguments: None. -// Return value: The the subgroup ID of this' Atoms. - - long int GetAtomSubgroupID() const { return m_AtomSubgroupID; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetOnlyLinearForces -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this AEmitter only cares about linear forces that it -// creates through emissions, ie no torquing due to the parent offset. -// Arguments: None. -// Return value: Whether only using linear forces or not. - - bool GetOnlyLinearForces() const { return m_OnlyLinForces; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetParentOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the stored offset between this Attachable's parent's Pos and the -// joint position. This should be maintained by the parent. -// Arguments: A const reference to the new parent offset. -// Return value: None. - - void SetParentOffset(const Vector &newParOff) { m_ParentOffset = newParOff; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetJointPos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the position of this Attachable by defining where the joint is. -// Upon Update(), this will be translated into what the actual position -// of the Attachable origin/center of mass is, depending on its set -// rotational angle and joint offset. -// Arguments: A const reference to the new joint position to set the position with. -// Return value: None. - - void SetJointPos(const Vector &newJointPos) { m_JointPos = newJointPos; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetJointStrength -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the amount of impulse force the joint of this Attachable can -// handle before breaking. -// Arguments: A float with the max tolerated impulse force in kg * m/s. -// Return value: None. - - void SetJointStrength(float newJointStrength) { m_JointStrength = newJointStrength; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetJointStiffness -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the normalized stiffness scalar of the join of this Attachable. -// Arguments: A float between 0 and 1.0. 1.0 means 100% of all forces imposed on this -// attachable will be transferred through the joint to the parent. 0.5 -// means 50%, and so on. -// Return value: None. - - void SetJointStiffness(float newJointStiff) { m_JointStiffness = newJointStiff; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetRotTarget -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the target rotation that this Attachable should be striving to -// match its real rotation with, if it is attached. The joint stiffness -// will determine how strong the scalar spring is between the current -// rotation and the target set here. -// Arguments: A float for the new target angle in radians. -// Return value: None. - - void SetRotTarget(float newRotTarget) { m_RotTarget.SetRadAngle(newRotTarget); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetAtomSubgroupID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the subgroup ID of this' Atoms -// Arguments: The new subgroup id of this' Atoms -// Return value: None. - - void SetAtomSubgroupID(long int newID = 0) { m_AtomSubgroupID = newID; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetOnlyLinearForces -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets whether this AEmitter should only care about linear forces that it -// creates through emissions, ie no torquing due to the parent offset. -// Arguments: Whether only use linear forces or not. -// Return value: None. - - void SetOnlyLinearForces(bool linOnly) { m_OnlyLinForces = linOnly; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsAttached -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this Attachable is attached to an MOSprite or not. -// Arguments: None. -// Return value: Whether it's attached or not. - - bool IsAttached() const { return m_pParent != 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsAttachedTo -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this Attachable is attached to a specific actor or -// not. -// Arguments: A pointer to which MOSprite you want to check if this is attached to. -// Return value: Whether it's attached or not. - - bool IsAttachedTo(const MOSprite *pparent) const { return m_pParent == pparent; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsDrawnAfterParent -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this Attachable is to be drawn after (in front of) or -// before (behind) the parent. -// Arguments: None. -// Return value: Whether it's to be drawn after parent or not. - - bool IsDrawnAfterParent() const override { return m_DrawAfterParent; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CollideAtPoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Calculates the collision response when another MO's Atom collides with -// this MO's physical representation. The effects will be applied -// directly to this MO, and also represented in the passed in HitData. -// Arguments: Reference to the HitData struct which describes the collision. This -// will be modified to represent the results of the collision. -// Return value: Whether the collision has been deemed valid. If false, then disregard -// any impulses in the Hitdata. - - bool CollideAtPoint(HitData &hitData) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ParticlePenetration -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Determines whether a particle which has hit this MO will penetrate, -// and if so, whether it gets lodged or exits on the other side of this -// MO. Appropriate effects will be determined and applied ONLY IF there -// was penetration! If not, nothing will be affected. -// Arguments: The HitData describing the collision in detail, the impulses have to -// have been filled out! -// Return value: Whether the particle managed to penetrate into this MO or not. If -// somehting but a MOPixel or MOSParticle is being passed in as hitor, -// false will trivially be returned here. - - bool ParticlePenetration(HitData &hd) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. -// Arguments: The impulse (kg * m/s) of the impact causing the gibbing to happen. -// The internal blast impulse which will push the gibs away from the center. -// A pointer to an MO which the gibs shuold not be colliding with! -// Return value: None. - - void GibThis(Vector impactImpulse = Vector(), float internalBlast = 10, MovableObject *pIgnoreMO = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Attach -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Attaches this Attachable to a host MOSprite -// Arguments: Pointer to the MOSprite to attach to. Ownership is NOT transferred! -// Return value: None. - - virtual void Attach(MOSRotating *pParent); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Attach -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Attaches this Attachable to a host MOSprite -// Arguments: Pointer to the MOSprite to attach to. Ownership is NOT transferred! -// The offset from the parent's Pos to the joint position. -// Return value: None. - - void Attach(MOSRotating *pParent, const Vector &parOffset) { Attach(pParent); m_ParentOffset = parOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Detach -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Detaches this Attachable from its host MOSprite -// Arguments: None. -// Return value: None. - - virtual void Detach(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TransferJointForces -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Bundles up all the accumulated forces of this Attachable and calcs -// how they transfer to the joint, and therefore to the parent. -// Arguments: A vector that will have with the forces affecting the joint ADDED to it. -// Return value: If the accumulated forces exceed the strength of the joint, the -// attachable will only fill out the forces up to the strength threshold -// and then detach itself and return false, and the parent should react -// accordingly in that case (null out pointers to the Attachable). - - bool TransferJointForces(Vector &jointForces); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: TransferJointImpulses -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Bundles up all the accumulated impulses of this Attachable and calcs -// how they transfer to the joint, and therefore to the parent. -// Arguments: A vector that will have with the imps affecting the joint ADDED to it. -// Return value: If the accumulated impulses exceed the strength of the joint, the -// attachable will only fill out the impulses up to the strength threshold -// and then detach itself and return false, and the parent should react -// accordingly in that case (null out pointers to the Attachable). - - bool TransferJointImpulses(Vector &jointImpulses); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: CollectDamage -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the amount of damage points this Attachable has sustained and -// should cause its parent. Calling this will reset the damage count. -// This should normally be called AFTER Update() to get the correct -// damage for a given frame. -// Arguments: None. -// Return value: A float with the damage points accumulated since the last time -// this method was called. - - float CollectDamage(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Attachable's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: RemoveWounds -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Removes a specified amount of wounds. -// Arguments: Amount of wounds to remove. -// Return value: Amount of damage, caused by these wounds. - - int RemoveWounds(int amount) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: AddDamage -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Adds specified number of damage points to this attachable. -// Arguments: Amount of damage to add. -// Return value: None. - - void AddDamage(float amount) { m_DamageCount += amount; }; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: InheritsRotAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: If true (default old behavior) the parent MOSRotating resets attachables or emitters RotAngle every frame. False to avoid that. -// Arguments: None. -// Return value: Whether parent MOSRotating should change this RotAngle to match it's own during MOSRotating::Update - - bool InheritsRotAngle() const { return m_InheritsRotAngle; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetInheritsRotAngle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets Whether parent MOSRotating should change this RotAngle to match it's own during MOSRotating::Update. -// Arguments: Whether to inherit RotAngle or not. -// Return value: None. - - void SetInheritsRotAngle(bool inherit) { m_InheritsRotAngle = inherit; } +namespace RTE { + class AEmitter; /// - /// Whether this attachable is capable of having terrain collisions enabled/disabled when attached to a parent. + /// An articulated, detachable part of an Actor's body. /// - /// If true, can have terrain collisions enabled/disabled when attached. - bool CanCollideWithTerrainWhenAttached() const { return m_CanCollideWithTerrainWhenAttached; } - - - /// - /// Sets whether this attachable is capable of having terrain collisions enabled/disabled when attached to a parent. - /// - /// Whether this attachable can have terrain collisions enabled/disabled when attached. - void SetCanCollideWithTerrainWhenAttached(bool canCollide) { m_CanCollideWithTerrainWhenAttached = canCollide; } - - - /// - /// Whether this attachable currently has terrain collisions enabled and it's atoms are present in the parent AtomGroup. - /// - /// If true, terrain collisions while attached are enabled and atoms are present in parent AtomGroup. - bool IsCollidingWithTerrainWhileAttached() const { return m_IsCollidingWithTerrainWhileAttached; } - - - /// - /// Sets whether this attachable currently has terrain collisions enabled and it's atoms are present in the parent AtomGroup. - /// - /// Whether this attachable currently has terrain collisions enabled and it's atoms are present in the parent AtomGroup. - void SetIsCollidingWithTerrainWhileAttached(bool isColliding) { m_IsCollidingWithTerrainWhileAttached = isColliding; } - - - /// - /// Turns on/off this Attachable's terrain collisions while it is attached by adding/removing its atoms to/from its parent AtomGroup. - /// - void EnableTerrainCollisions(bool enable); - - - /// - /// Gets whether this attachable is marked be deleted along with it's parent when it's being deleted or not. - /// - /// Whether this attachable is marked to be deleted along with it's parent or not. - bool ToDeleteWithParent() const { return m_DeleteWithParent; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - // Member variables - static Entity::ClassInfo m_sClass; - // Pointer to the MOSRotating this attachable is attached to. - MOSRotating *m_pParent; - // The offset from the PARENT's Pos to the joint point this Attachable is attached. - Vector m_ParentOffset; - // The amount of impulse force needed on this to deatch it from the host Actor. In kg * m/s - float m_JointStrength; - - // The normalized joint stiffness scalar. 1.0 = all forces are trasferred - // to parent through joint. 0.5 = half are. - float m_JointStiffness; - - // The wound this Attachable will cause when it breaks from its parent. - const AEmitter *m_pBreakWound; - - // The offset to the joint (the point around which this Attachable and its - // parent hinge) from its center of mass/origin. - Vector m_JointOffset; - // The absolute position of the joint that the parent sets upon Update() if this - // Attachable is attached to it. This position is used to determine the actual - // position/center of mass of this Attachable on its Update(), using the JointOff - // and RotAngle of this attachable. - Vector m_JointPos; - - // This is the desired rotation of this Attachable, the angle that the attachable - // is striving to achieve through angle springs. - Matrix m_RotTarget; - - // The Atom ID's this' atoms will have when attached and added to a parent's AtomGroup - long int m_AtomSubgroupID; - - // Whether to draw this Attachable after (in front of) or before (behind) the parent. - bool m_DrawAfterParent; - - // The number of damage points that this Attachable has accumulated since the - // last time CollectDamage() was called. - float m_DamageCount; - - // Whether to only record linear forces, ie no torquing due to the parent offset - bool m_OnlyLinForces; - - // If true inherits Parent's rot angle, which is set MOSRotating::Update. Default is true to maintain maybe awkward but default behavior - bool m_InheritsRotAngle; - - // Whether this attachable is capable of having terrain collisions enabled/disabled when attached to a parent. - bool m_CanCollideWithTerrainWhenAttached; - - // Whether this attachable currently has terrain collisions enabled while it's attached to a parent. - bool m_IsCollidingWithTerrainWhileAttached; - - bool m_DeleteWithParent; //!< Whether this attachable is marked to be deleted along with it's parent when it's being deleted or not. - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Attachable, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - Attachable(const Attachable &reference) = delete; - Attachable & operator=(const Attachable &rhs) = delete; - -}; - -} // namespace RTE - -#endif // File \ No newline at end of file + class Attachable : public MOSRotating { + friend class MOSRotating; + + public: + + EntityAllocation(Attachable) + AddScriptFunctionNames(MOSRotating, "OnAttach", "OnDetach") + SerializableOverrideMethods + ClassInfoGetters + +#pragma region Creation + /// + /// Constructor method used to instantiate a Attachable object in system memory. Create() should be called before using the object. + /// + Attachable() { Clear(); } + + /// + /// Makes the Attachable object ready for use. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create() override; + + /// + /// Creates an Attachable to be identical to another, by deep copy. + /// + /// A reference to the Attachable to deep copy. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create(const Attachable &reference); +#pragma endregion + +#pragma region Destruction + /// + /// Destructor method used to clean up an Attachable object before deletion from system memory. + /// + ~Attachable() override { Destroy(true); } + + /// + /// Destroys and resets (through Clear()) the Attachable object. + /// + /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + void Destroy(bool notInherited = false) override { if (!notInherited) { MOSRotating::Destroy(); } Clear(); } + + /// + /// Resets the entire Attachable, including its inherited members, to their default settings or values. + /// + void Reset() override { Clear(); MOSRotating::Reset(); } +#pragma endregion + +#pragma region Parent Getters and Setters + /// + /// Gets the MO which is the parent of this Attachable. + /// + /// A pointer to the parent of this Attachable. + MovableObject * GetParent() override { return m_Parent; } + + /// + /// Gets the MO which is the parent of this Attachable. + /// + /// A pointer to the parent of this Attachable. + const MovableObject * GetParent() const override { return m_Parent; } + + /// + /// Indicates whether this Attachable is attached to an MOSRotating parent or not. + /// + /// Whether it's attached or not. + bool IsAttached() const { return m_Parent != nullptr; } + + /// + /// Indicates whether this Attachable is attached to the specified MOSRotating or not. + /// + /// A pointer to which MOSRotating you want to check for. + /// Whether it's attached or not. + bool IsAttachedTo(const MOSRotating *parentToCheck) const { return m_Parent == parentToCheck; } + + /// + /// Gets the MO which is the ultimate root parent of this Attachable and its parent. + /// + /// A pointer to the highest root parent of this Attachable. + MovableObject * GetRootParent() override { return m_Parent ? m_Parent->GetRootParent() : this; } + + /// + /// Gets the MO which is the ultimate root parent of this Attachable and its parent. + /// + /// A pointer to the highest root parent of this Attachable. + const MovableObject * GetRootParent() const override { return m_Parent ? m_Parent->GetRootParent() : this; } + + /// + /// Gets the stored offset between this Attachable's parent's position and the joint position. This should be maintained by the parent. + /// + /// A const reference Vector describing the offset from the parent's position to the joint position. + const Vector & GetParentOffset() const { return m_ParentOffset; } + + /// + /// Sets the stored offset between this Attachable's parent's Pos and the joint position. This should be maintained by the parent. + /// + /// A const reference to the new parent offset. + void SetParentOffset(const Vector &newParentOffset) { m_ParentOffset = newParentOffset; } + + /// + /// Gets whether this Attachable is to be drawn after (in front of) or before (behind) its parent. + /// + /// Whether this Attachable is to be drawn after its parent or not. + bool IsDrawnAfterParent() const override { return m_DrawAfterParent; } + + /// + /// Sets whether this Attachable is to be drawn after (in front of) or before (behind) its parent. + /// + /// Whether this Attachable is to be drawn after its parent. + void SetDrawnAfterParent(bool drawAfterParent) { m_DrawAfterParent = drawAfterParent; } + + /// + /// Gets whether this Attachable should be drawn normally by its parent. + /// Some attachables (e.g. AEmitter flashes) require custom handling for when they should or shouldn't draw, to be done by the specific parent class. + /// + /// Whether this Attachable should be drawn normally by its parent. + bool IsDrawnNormallyByParent() const { return m_DrawnNormallyByParent; } + + /// + /// Sets whether this Attachable should be drawn normally by its parent. + /// Some attachables (e.g. AEmitter flashes) require custom handling for when they should or shouldn't draw, to be done by the specific parent class. + /// + /// Whether this Attachable should be drawn normally by its parent. + void SetDrawnNormallyByParent(bool drawnNormallyByParent) { m_DrawnNormallyByParent = drawnNormallyByParent; } + + /// + /// Gets whether this Attachable will be deleted when it's removed from its parent. Has no effect until the Attachable is added to a parent. + /// + /// Whether this Attachable is marked to be deleted when it's removed from its parent or not. + bool GetDeleteWhenRemovedFromParent() const { return m_DeleteWhenRemovedFromParent; } + + /// + /// Sets whether this Attachable will be deleted when it's removed from its parent. + /// + /// Whether this Attachable should be deleted when it's removed from its parent. + virtual void SetDeleteWhenRemovedFromParent(bool deleteWhenRemovedFromParent) { m_DeleteWhenRemovedFromParent = deleteWhenRemovedFromParent; } + + /// + /// Gets whether forces transferred from this Attachable should be applied at its parent's offset (rotated to match the parent) where they will produce torque, or directly at its parent's position. + /// + /// Whether forces transferred from this Attachable should be applied at an offset. + bool GetApplyTransferredForcesAtOffset() const { return m_ApplyTransferredForcesAtOffset; } + + /// + /// Sets whether forces transferred from this Attachable should be applied at its parent's offset (rotated to match the parent) where they will produce torque, or directly at its parent's position. + /// + /// Whether forces transferred from this Attachable should be applied at an offset. + void SetApplyTransferredForcesAtOffset(bool appliesTransferredForcesAtOffset) { m_ApplyTransferredForcesAtOffset = appliesTransferredForcesAtOffset; } +#pragma endregion + +#pragma region Parent Gib Handling Getters and Setters + /// + /// Gets the percentage chance that this Attachable will gib when its parent does. 0 means never, 1 means always. + /// + /// A float with the percentage chance this Attachable will gib when its parent gibs. + float GetGibWithParentChance() const { return m_GibWithParentChance; } + + /// + /// Sets the percentage chance that this Attachable will gib when its parent does. 0 means never, 1 means always. + /// + /// A float describing the percentage chance this Attachable will gib when its parent gibs. + void SetGibWithParentChance(float gibWithParentChance) { m_GibWithParentChance = gibWithParentChance; } + + /// + /// Gets the multiplier this Attachable will apply to its parent's gib blast strength when the parent gibs. + /// + /// A float with the gib blast strength multiplier of this Attachable. + float GetParentGibBlastStrengthMultiplier() const { return m_ParentGibBlastStrengthMultiplier; } + + /// + /// Sets the multiplier this Attachable will apply to its parent's gib blast strength when the parent gibs. + /// + /// A float describing the gib blast strength multiplier of this Attachable. + void SetParentGibBlastStrengthMultiplier(float parentGibBlastStrengthMultiplier) { m_ParentGibBlastStrengthMultiplier = parentGibBlastStrengthMultiplier; } +#pragma endregion + +#pragma region Temporary Handling for Wounds, to be Replaced by a Wound Object in Future + /// + /// Gets whether or not this Attachable is a wound, as determined by its parent MOSR. + /// + /// Whether or not this Attachable is a wound. + bool IsWound() const { return m_IsWound; } + + /// + /// Sets whether or not this Attachable is a wound, to be done by its parent MOSR. + /// + /// Whether or not this Attachable should be a wound. + void SetIsWound(bool isWound) { m_IsWound = isWound; } +#pragma endregion + +#pragma region Joint Getters and Setters + /// + /// Gets the amount of impulse force the joint of this Attachable can handle before breaking. + /// + /// A float with the max tolerated impulse force in kg * m/s. + float GetJointStrength() const { return m_JointStrength; } + + /// + /// Sets the amount of impulse force the joint of this Attachable can handle before breaking. + /// + /// A float describing the max tolerated impulse force in Newtons (kg * m/s). + void SetJointStrength(float jointStrength) { m_JointStrength = jointStrength; } + + /// + /// Gets the stiffness scalar of the joint of this Attachable, normalized between 0 and 1.0. + /// 1.0 means impulse forces on this attachable will be transferred to the parent with 100% strength, 0 means they will not transfer at all. + /// + /// The normalized stiffness scalar of this Attachable's joint. + float GetJointStiffness() const { return m_JointStiffness; } + + /// + /// Sets the stiffness scalar of the joint of this Attachable, normalized between 0 and 1.0. + /// 1.0 means impulse forces on this attachable will be transferred to the parent with 100% strength, 0 means they will not transfer at all. + /// + /// A float describing the normalized stiffness scalar of this Attachable's joint. It will automatically be limited between 0 and 1.0. + void SetJointStiffness(float jointStiffness) { m_JointStiffness = Limit(jointStiffness, 1.0F, 0.0F); } + + /// + /// Gets the offset of the joint (the point around which this Attachable and its parent hinge) from this Attachable's center of mass/origin. + /// + /// A const reference Vector describing the offset of the joint relative to this Attachable's origin/center of mass position. + const Vector & GetJointOffset() const { return m_JointOffset; } + + /// + /// Sets the offset of the joint (the point around which this Attachable and its parent hinge) from this Attachable's center of mass/origin. + /// + /// A Vector describing the offset of the joint relative to the this Attachable's origin/center of mass position. + void SetJointOffset(const Vector &newJointOffset) { m_JointOffset = newJointOffset; } +#pragma endregion + +#pragma region Force Transferral + /// + /// Bundles up all the accumulated forces of this Attachable and calculates how they transfer to the joint, and therefore to the parent. + /// If the accumulated forces exceed the joint strength of this Attachable, the jointForces Vector will be filled to the limit and false will be returned. + /// Additionally, in this case, the Attachable will remove itself from its parent. + /// + /// A vector that will have the forces affecting the joint ADDED to it. + /// False if the Attachable has no parent or its accumulated forces are greater than its joint strength, otherwise true. + bool TransferJointForces(Vector &jointForces); + + /// + /// Bundles up all the accumulated impulse forces of this Attachable and calculates how they transfer to the joint, and therefore to the parent. + /// If the accumulated impulse forces exceed the joint strength or gib impulse limit of this Attachable, the jointImpulses Vector will be filled up to that limit and false will be returned. + /// Additionally, in this case, the Attachable will remove itself from its parent and gib itself if appropriate. + /// + /// A vector that will have the impulse forces affecting the joint ADDED to it. + /// An optional override for the Attachable's joint stiffness for this function call. Primarily used to allow subclasses to perform special behavior. + /// An optional override for the Attachable's joint strength for this function call. Primarily used to allow subclasses to perform special behavior. + /// An optional override for the Attachable's gib impulse limit for this function call. Primarily used to allow subclasses to perform special behavior. + /// False if the Attachable has no parent or its accumulated forces are greater than its joint strength or gib impulse limit, otherwise true. + virtual bool TransferJointImpulses(Vector &jointImpulses, float jointStiffnessValueToUse = -1, float jointStrengthValueToUse = -1, float gibImpulseLimitValueToUse = -1); +#pragma endregion + +#pragma region Damage and Wound Management + /// + /// Adds the specified number of damage points to this attachable. + /// + /// The amount of damage to add. + void AddDamage(float damageAmount) { m_DamageCount += damageAmount; } + + /// + /// Calculates the amount of damage this Attachable has sustained since the last time this method was called and returns it, modified by the Attachable's damage multiplier. + /// This should normally be called AFTER updating this Attachable in order to get the correct damage for a given frame. + /// + /// A float with the damage accumulated, multiplied by the Attachable's damage multiplier, since the last time this method was called. + float CollectDamage(); + + /// + /// Gets the AEmitter that represents the wound added to this Attachable when it gets detached from its parent. OWNERSHIP IS NOT TRANSFERRED! + /// + /// A const pointer to the break wound AEmitter. + const AEmitter * GetBreakWound() const { return m_BreakWound; } + + /// + /// Sets the AEmitter that represents the wound added to this Attachable when it gets detached from its parent. OWNERSHIP IS NOT TRANSFERRED! + /// + /// The AEmitter to use for this Attachable's breakwound. + void SetBreakWound(AEmitter *breakWound) { m_BreakWound = breakWound; } + + /// + /// Gets the AEmitter that represents the wound added to this Attachable's parent when this Attachable gets detached from its parent. OWNERSHIP IS NOT TRANSFERRED! + /// + /// A const pointer to the parent break wound AEmitter. + const AEmitter * GetParentBreakWound() const { return m_ParentBreakWound; } + + /// + /// Sets the AEmitter that represents the wound added to this Attachable's parent when this Attachable gets detached from its parent. OWNERSHIP IS NOT TRANSFERRED! + /// + /// The AEmitter to use for the parent's breakwound. + void SetParentBreakWound(AEmitter *breakWound) { m_ParentBreakWound = breakWound; } +#pragma endregion + +#pragma region Inherited Value Getters and Setters + /// + /// Gets whether or not this Attachable inherits its parent's HFlipped value, i.e. whether it has its HFlipped value reset to match/reverse its parent's every frame, if attached. + /// -1 (or technically any value that's not 0 or 1) means reversed inheritance (i.e. if the parent's HFlipped value is true, this Attachable's HFlipped value will be false), 0 means no inheritance, 1 means normal inheritance. + /// + /// Whether or not this Attachable inherits its parent's HFlipped value. + int InheritsHFlipped() const { return m_InheritsHFlipped; } + + /// + /// Sets whether or not this Attachable inherits its parent's HFlipped value, i.e. whether it has its HFlipped value reset to match/reverse its parent's every frame, if attached. + /// -1 (or technically any value that's not 0 or 1) means reversed inheritance (i.e. if the parent's HFlipped value is true, this Attachable's HFlipped value will be false), 0 means no inheritance, 1 means normal inheritance. + /// + /// Whether or not to inherit its parent's HFlipped value. + void SetInheritsHFlipped(int inheritsHFlipped) { m_InheritsHFlipped = inheritsHFlipped; } + + /// + /// Gets whether or not this Attachable inherits its RotAngle from its parent, i.e. whether it has its RotAngle reset to match its parent every frame, if attached. + /// + /// Whether or not this Attachable inherits its parent's RotAngle. + bool InheritsRotAngle() const { return m_InheritsRotAngle; } + + /// + /// Sets whether or not this Attachable inherits its RotAngle from its parent, i.e. whether it has its RotAngle reset to match its parent every frame, if attached. + /// + /// Whether or not to inherit its parent's RotAngle. + void SetInheritsRotAngle(bool inheritsRotAngle) { m_InheritsRotAngle = inheritsRotAngle; } + + /// + /// Gets the offset of this Attachable's rotation angle from its parent. Only actually applied if the Attachable is set to inherit its parent's rotation angle. + /// + /// This Attachable's inherited rotation angle offset. + float GetInheritedRotAngleOffset() const { return m_InheritedRotAngleOffset; } + + /// + /// Sets the offset of this Attachable's rotation angle from its parent. Only actually applied if the Attachable is set to inherit its parent's rotation angle. + /// + /// Thee new rotation angle offset for this Attachable. + void SetInheritedRotAngleOffset(float inheritedRotAngleOffset) { m_InheritedRotAngleOffset = inheritedRotAngleOffset; } +#pragma endregion + +#pragma region Collision Management + /// + /// Gets the subgroup ID of this' Atoms. + /// + /// The subgroup ID of this' Atoms. + long GetAtomSubgroupID() const { return m_AtomSubgroupID; } + + /// + /// Sets the subgroup ID of this' Atoms + /// + /// A long describing the new subgroup id of this' Atoms. + void SetAtomSubgroupID(long subgroupID = 0) { m_AtomSubgroupID = subgroupID; } + + /// + /// Whether this attachable currently has terrain collisions enabled and it's atoms are present in the parent AtomGroup. + /// + /// If true, terrain collisions while attached are enabled and atoms are present in parent AtomGroup. + bool GetCollidesWithTerrainWhileAttached() const { return m_CollidesWithTerrainWhileAttached; } + + /// + /// Sets whether this attachable currently has terrain collisions enabled and it's atoms are present in the parent AtomGroup. + /// + /// Whether this attachable currently has terrain collisions enabled and it's atoms are present in the parent AtomGroup. + void SetCollidesWithTerrainWhileAttached(bool collidesWithTerrainWhileAttached); +#pragma endregion + +#pragma region Override Methods + /// + /// Determines whether a particle which has hit this MO will penetrate, and if so, whether it gets lodged or exits on the other side of this MO. + /// Appropriate effects will be determined and applied ONLY IF there was penetration! If not, nothing will be affected. + /// + /// The HitData describing the collision in detail, the impulses have to have been filled out! + /// + /// Whether the particle managed to penetrate into this MO or not. + /// If something other than an MOPixel or MOSParticle is being passed in as the hitor, false will trivially be returned here. + /// + bool ParticlePenetration(HitData &hitData) override; + + /// + /// Destroys this Attachable and creates its specified Gibs in its place with appropriate velocities. + /// Any Attachables are removed and also given appropriate velocities. + /// + /// The impulse (kg * m/s) of the impact causing the gibbing to happen. + /// A pointer to an MO which the Gibs and Attachables should not be colliding with. + void GibThis(const Vector &impactImpulse = Vector(), MovableObject *movableObjectToIgnore = nullptr) override; + + /// + /// Checks if the given Attachable should affect radius, and handles it if it should. + /// + /// The Attachable to check. + /// Whether the radius affecting Attachable changed as a result of this call. + bool HandlePotentialRadiusAffectingAttachable(const Attachable *attachable) override; + + /// + /// Updates this Attachable. Supposed to be done every frame. + /// + void Update() override; +#pragma endregion + +#pragma region Override Methods for Handling Mass + /// + /// Sets the mass of this Attachable. + /// + /// A float specifying the new mass value in Kilograms (kg). + void SetMass(const float newMass) final; + + /// + /// Updates the total mass of Attachables and wounds for this Attachable, intended to be used when Attachables' masses get modified. Simply subtracts the old mass and adds the new one. + /// + /// The mass the Attachable or wound had before its mass was modified. + /// The up-to-date mass of the Attachable or wound after its mass was modified. + void UpdateAttachableAndWoundMass(float oldAttachableOrWoundMass, float newAttachableOrWoundMass) final; + + /// + /// Adds the passed in Attachable the list of Attachables and sets its parent to this Attachable. + /// + /// The Attachable to add. + void AddAttachable(Attachable *attachable) final { MOSRotating::AddAttachable(attachable); } + + /// + /// Adds the passed in Attachable the list of Attachables, changes its parent offset to the passed in Vector, and sets its parent to this Attachable. + /// + /// The Attachable to add. + /// The Vector to set as the Attachable's parent offset. + void AddAttachable(Attachable *attachable, const Vector &parentOffsetToSet) final; + + /// + /// Removes the Attachable corresponding to the passed in UniqueID and sets its parent to nullptr. Does not add it to MovableMan or add break wounds. + /// + /// The UniqueID of the Attachable to remove. + /// False if the Attachable is invalid, otherwise true. + bool RemoveAttachable(long attachableUniqueID) final { return MOSRotating::RemoveAttachable(attachableUniqueID); } + + /// + /// Removes the Attachable corresponding to the passed in UniqueID and sets its parent to nullptr. Optionally adds it to MovableMan and/or adds break wounds. + /// If the Attachable is not set to delete or delete when removed from its parent, and addToMovableMan is false, the caller must hang onto a pointer to the Attachable ahead of time to avoid memory leaks. + /// + /// The UniqueID of the Attachable to remove. + /// Whether or not to add the Attachable to MovableMan once it has been removed. + /// Whether or not to add break wounds to the removed Attachable and this Attachable. + /// False if the Attachable is invalid, otherwise true. + bool RemoveAttachable(long attachableUniqueID, bool addToMovableMan, bool addBreakWounds) final { return MOSRotating::RemoveAttachable(attachableUniqueID, addToMovableMan, addBreakWounds); } + + /// + /// Removes the passed in Attachable and sets its parent to nullptr. Does not add it to MovableMan or add break wounds. + /// + /// The Attachable to remove. + /// False if the Attachable is invalid, otherwise true. + bool RemoveAttachable(Attachable *attachable) final { return MOSRotating::RemoveAttachable(attachable); } + + /// + /// Removes the passed in Attachable and sets its parent to nullptr. Optionally adds it to MovableMan and/or adds break wounds. + /// If the Attachable is not set to delete or delete when removed from its parent, and addToMovableMan is false, the caller must hang onto a pointer to the Attachable ahead of time to avoid memory leaks. + /// + /// The Attachable to remove. + /// Whether or not to add the Attachable to MovableMan once it has been removed. + /// Whether or not to add break wounds to the removed Attachable and this Attachable. + /// False if the Attachable is invalid, otherwise true. + bool RemoveAttachable(Attachable *attachable, bool addToMovableMan, bool addBreakWounds) final; + + /// + /// Adds the passed in wound AEmitter to the list of wounds and changes its parent offset to the passed in Vector. + /// + /// The wound AEmitter to add. + /// The vector to set as the wound AEmitter's parent offset. + /// Whether to gib this Attachable if adding this wound raises its wound count past its gib wound limit. Defaults to true. + void AddWound(AEmitter *woundToAdd, const Vector &parentOffsetToSet, bool checkGibWoundLimit = true) final; + + /// + /// Removes the specified number of wounds from this Attachable, and returns damage caused by these removed wounds. + /// Includes any Attachables (and their Attachables, etc.) that have a positive damage multiplier. + /// + /// The number of wounds that should be removed. + /// The amount of damage caused by these wounds, taking damage multipliers into account. + float RemoveWounds(int numberOfWoundsToRemove) final { return MOSRotating::RemoveWounds(numberOfWoundsToRemove); } + + /// + /// Removes the specified number of wounds from this Attachable, and returns damage caused by these removed wounds. + /// Optionally removes wounds from Attachables (and their Attachables, etc.) that match the conditions set by the provided inclusion parameters. + /// + /// The number of wounds that should be removed. + /// Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this Attachable) when wounded. + /// Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this Attachable) when wounded. + /// Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this Attachable) when wounded. + /// The amount of damage caused by these wounds, taking damage multipliers into account. + float RemoveWounds(int numberOfWoundsToRemove, bool includeAttachablesWithAPositiveDamageMultiplier, bool includeAttachablesWithANegativeDamageMultiplier, bool includeAttachablesWithNoDamageMultiplier) override; +#pragma endregion + + protected: + + static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. + + MOSRotating *m_Parent; //!< Pointer to the MOSRotating this attachable is attached to. + Vector m_ParentOffset; //!< The offset from the parent's Pos to the joint point this Attachable is attached with. + bool m_DrawAfterParent; //!< Whether to draw this Attachable after (in front of) or before (behind) the parent. + bool m_DrawnNormallyByParent; //!< Whether this Attachable will be drawn normally when attached, or will require special handling by some non-MOSR parent type. + bool m_DeleteWhenRemovedFromParent; //!< Whether this Attachable should be deleted when it's removed from its parent. + bool m_ApplyTransferredForcesAtOffset; //!< Whether forces transferred from this Attachable should be applied at the rotated parent offset (which will produce torque), or directly at the parent's position. Mostly useful to make jetpacks and similar emitters viable. + + float m_GibWithParentChance; //!< The percentage chance that this Attachable will gib when its parent does. 0 means never, 1 means always. + float m_ParentGibBlastStrengthMultiplier; //!< The multiplier this Attachable will apply to its parent's gib blast strength when the parent gibs. + + //TODO This is a stopgap for a dedicated Wound class, that would be helpful to simplify things like this and default damage multiplier handling. + bool m_IsWound; //!< Whether or not this Attachable has been added as a wound. Only set and applied for Attachables with parents. + + float m_JointStrength; //!< The amount of impulse force needed on this to detach it from the host Actor, in kg * m/s. A value of 0 means the join is infinitely strong and will never break naturally. + float m_JointStiffness; //!< The normalized joint stiffness scalar. 1.0 means impulse forces on this attachable will be transferred to the parent with 100% strength, 0 means they will not transfer at all. + Vector m_JointOffset; //!< The offset to the joint (the point around which this Attachable and its parent hinge) from its center of mass/origin. + Vector m_JointPos; //!< The absolute position of the joint that the parent sets upon Update() if this Attachable is attached to it. + + float m_DamageCount; //!< The number of damage points that this Attachable has accumulated since the last time CollectDamage() was called. + const AEmitter *m_BreakWound; //!< The wound this Attachable will receive when it breaks from its parent. + const AEmitter *m_ParentBreakWound; //!< The wound this Attachable's parent will receive when the Attachable breaks from its parent. + + int m_InheritsHFlipped; //!< Whether this Attachable should inherit its parent's HFlipped. Defaults to 1 (normal inheritance). + bool m_InheritsRotAngle; //!< Whether this Attachable should inherit its parent's RotAngle. Defaults to true. + float m_InheritedRotAngleOffset; //!< The offset by which this Attachable should be rotated when it's set to inherit its parent's rotation angle. Defaults to 0. + + long m_AtomSubgroupID; //!< The Atom IDs this' atoms will have when attached and added to a parent's AtomGroup. + bool m_CollidesWithTerrainWhileAttached; //!< Whether this attachable currently has terrain collisions enabled while it's attached to a parent. + + Vector m_PrevParentOffset; //!< The previous frame's parent offset. + Vector m_PrevJointOffset; //!< The previous frame's joint offset. + float m_PrevRotAngleOffset; //!< The previous frame's difference between this Attachable's RotAngle and it's root parent's RotAngle. + + /// + /// Sets this Attachable's parent MOSRotating, and also sets its Team based on its parent and, if the Attachable is set to collide, adds/removes Atoms to its new/old parent. + /// + /// A pointer to the MOSRotating to set as the new parent. Ownership is NOT transferred! + virtual void SetParent(MOSRotating *newParent); + + private: + + /// + /// Updates the position of this Attachable based on its parent offset and joint offset. Used during update and when something sets these offsets through setters. + /// + void UpdatePositionAndJointPositionBasedOnOffsets(); + + /// + /// Turns on/off this Attachable's terrain collisions while it is attached by adding/removing its Atoms to/from its root parent's AtomGroup. + /// + /// Whether to add this Attachable's Atoms to the root parent's AtomGroup or remove them. + void AddOrRemoveAtomsFromRootParentAtomGroup(bool addAtoms); + + /// + /// Clears all the member variables of this Attachable, effectively resetting the members of this abstraction level only. + /// + void Clear(); + + // Disallow the use of some implicit methods. + Attachable(const Attachable &reference) = delete; + Attachable & operator=(const Attachable &rhs) = delete; + }; +} +#endif \ No newline at end of file diff --git a/Entities/Gib.h b/Entities/Gib.h index 76eaa8127..73d0aac5c 100644 --- a/Entities/Gib.h +++ b/Entities/Gib.h @@ -49,7 +49,7 @@ namespace RTE { /// Gets the reference particle to be used as a Gib. Ownership is NOT transferred! /// /// A pointer to the particle to be used as a Gib. - const MovableObject * GetParticlePreset() { return m_GibParticle; } + const MovableObject * GetParticlePreset() const { return m_GibParticle; } /// /// Gets the spawn offset of this Gib from the parent's position. diff --git a/Entities/HDFirearm.cpp b/Entities/HDFirearm.cpp index 942c4c54b..f57b64fab 100644 --- a/Entities/HDFirearm.cpp +++ b/Entities/HDFirearm.cpp @@ -51,7 +51,6 @@ void HDFirearm::Clear() m_ReloadTime = 0; m_FullAuto = false; m_FireIgnoresThis = true; - m_RecoilTransmission = 1.0; m_ShakeRange = 0; m_SharpShakeRange = 0; m_NoSupportFactor = 0; @@ -97,22 +96,18 @@ int HDFirearm::Create() ////////////////////////////////////////////////////////////////////////////////////////// // Description: Creates a HDFirearm to be identical to another, by deep copy. -int HDFirearm::Create(const HDFirearm &reference) -{ +int HDFirearm::Create(const HDFirearm &reference) { + if (reference.m_pMagazine) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pMagazine->GetUniqueID()); + SetMagazine(dynamic_cast(reference.m_pMagazine->Clone())); + } + if (reference.m_pFlash) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_pFlash->GetUniqueID()); + SetFlash(dynamic_cast(reference.m_pFlash->Clone())); + } HeldDevice::Create(reference); m_pMagazineReference = reference.m_pMagazineReference; - - if (reference.m_pMagazine) - { - m_pMagazine = dynamic_cast(reference.m_pMagazine->Clone()); - AddAttachable(m_pMagazine, m_pMagazine->GetParentOffset(), true); - } - if (reference.m_pFlash) - { - m_pFlash = dynamic_cast(reference.m_pFlash->Clone()); - m_pFlash->Attach(this, m_pFlash->GetParentOffset()); - } m_PreFireSound = reference.m_PreFireSound; m_FireSound = reference.m_FireSound; m_FireEchoSound = reference.m_FireEchoSound; @@ -129,7 +124,6 @@ int HDFirearm::Create(const HDFirearm &reference) m_ReloadTime = reference.m_ReloadTime; m_FullAuto = reference.m_FullAuto; m_FireIgnoresThis = reference.m_FireIgnoresThis; - m_RecoilTransmission = reference.m_RecoilTransmission; m_ShakeRange = reference.m_ShakeRange; m_SharpShakeRange = reference.m_SharpShakeRange; m_NoSupportFactor = reference.m_NoSupportFactor; @@ -154,27 +148,24 @@ int HDFirearm::Create(const HDFirearm &reference) // is called. If the property isn't recognized by any of the base classes, // false is returned, and the reader's position is untouched. -int HDFirearm::ReadProperty(std::string propName, Reader &reader) -{ - if (propName == "Magazine") - { - const Entity *pObj = g_PresetMan.GetEntityPreset(reader); - if (pObj) - { - m_pMagazineReference = dynamic_cast(pObj); - - delete m_pMagazine; - m_pMagazine = dynamic_cast(m_pMagazineReference->Clone()); +int HDFirearm::ReadProperty(std::string propName, Reader &reader) { + if (propName == "Magazine") { + RemoveAttachable(m_pMagazine); + const Entity *magazineEntity = g_PresetMan.GetEntityPreset(reader); + if (magazineEntity) { + m_pMagazineReference = dynamic_cast(magazineEntity); + m_pMagazine = dynamic_cast(magazineEntity->Clone()); + AddAttachable(m_pMagazine); } - } - else if (propName == "Flash") - { - const Entity *pObj = g_PresetMan.GetEntityPreset(reader); - if (pObj) - { - m_pFlash = dynamic_cast(pObj->Clone()); - if (m_pFlash) - m_pFlash->Attach(this); + } else if (propName == "Flash") { + RemoveAttachable(m_pFlash); + const Entity *flashEntity = g_PresetMan.GetEntityPreset(reader); + if (flashEntity) { + m_pFlash = dynamic_cast(flashEntity->Clone()); + AddAttachable(m_pFlash); + m_pFlash->SetDrawnNormallyByParent(false); + m_pFlash->SetDeleteWhenRemovedFromParent(true); + m_pFlash->SetCollidesWithTerrainWhileAttached(false); } } else if (propName == "PreFireSound") { reader >> m_PreFireSound; @@ -189,61 +180,52 @@ int HDFirearm::ReadProperty(std::string propName, Reader &reader) } else if (propName == "DeactivationSound") { reader >> m_DeactivationSound; m_DeactivationSound.SetSoundOverlapMode(SoundContainer::SoundOverlapMode::IGNORE_PLAY); - } else if (propName == "EmptySound") + } else if (propName == "EmptySound") { reader >> m_EmptySound; - else if (propName == "ReloadStartSound") + } else if (propName == "ReloadStartSound") { reader >> m_ReloadStartSound; - else if (propName == "ReloadEndSound") + } else if (propName == "ReloadEndSound") { reader >> m_ReloadEndSound; - else if (propName == "RateOfFire") + } else if (propName == "RateOfFire") { reader >> m_RateOfFire; - else if (propName == "ActivationDelay") + } else if (propName == "ActivationDelay") { reader >> m_ActivationDelay; - else if (propName == "DeactivationDelay") + } else if (propName == "DeactivationDelay") { reader >> m_DeactivationDelay; - else if (propName == "ReloadTime") + } else if (propName == "ReloadTime") { reader >> m_ReloadTime; - else if (propName == "FullAuto") + } else if (propName == "FullAuto") { reader >> m_FullAuto; - else if (propName == "FireIgnoresThis") + } else if (propName == "FireIgnoresThis") { reader >> m_FireIgnoresThis; - else if (propName == "RecoilTransmission") - reader >> m_RecoilTransmission; - else if (propName == "IsAnimatedManually") + } else if (propName == "RecoilTransmission") { + reader >> m_JointStiffness; + } else if (propName == "IsAnimatedManually") { reader >> m_IsAnimatedManually; - else if (propName == "ShakeRange") - { + } else if (propName == "ShakeRange") { reader >> m_ShakeRange; m_ShakeRange /= 2; - } - else if (propName == "SharpShakeRange") - { + } else if (propName == "SharpShakeRange") { reader >> m_SharpShakeRange; m_SharpShakeRange /= 2; - } - else if (propName == "NoSupportFactor") + } else if (propName == "NoSupportFactor") { reader >> m_NoSupportFactor; - else if (propName == "ParticleSpreadRange") - { + } else if (propName == "ParticleSpreadRange") { reader >> m_ParticleSpreadRange; m_ParticleSpreadRange /= 2; - } - else if (propName == "ShellSpreadRange") - { + } else if (propName == "ShellSpreadRange") { reader >> m_ShellSpreadRange; m_ShellSpreadRange /= 2; - } - else if (propName == "ShellAngVelRange") - { + } else if (propName == "ShellAngVelRange") { reader >> m_ShellAngVelRange; m_ShellAngVelRange /= 2; - } - else if (propName == "MuzzleOffset") + } else if (propName == "MuzzleOffset") { reader >> m_MuzzleOff; - else if (propName == "EjectionOffset") + } else if (propName == "EjectionOffset") { reader >> m_EjectOff; - else + } else { return HeldDevice::ReadProperty(propName, reader); + } return 0; } @@ -292,7 +274,7 @@ int HDFirearm::Save(Writer &writer) const writer.NewProperty("FireIgnoresThis"); writer << m_FireIgnoresThis; writer.NewProperty("RecoilTransmission"); - writer << m_RecoilTransmission; + writer << m_JointStiffness; writer.NewProperty("IsAnimatedManually"); writer << m_IsAnimatedManually; writer.NewProperty("ShakeRange"); @@ -323,37 +305,57 @@ int HDFirearm::Save(Writer &writer) const void HDFirearm::Destroy(bool notInherited) { - delete m_pMagazine; - delete m_pFlash; m_PreFireSound.Stop(); m_FireSound.Stop(); m_FireEchoSound.Stop(); m_ActiveSound.Stop(); m_DeactivationSound.Stop(); + m_EmptySound.Stop(); + m_ReloadStartSound.Stop(); + m_ReloadEndSound.Stop(); if (!notInherited) HeldDevice::Destroy(); Clear(); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void HDFirearm::SetMagazine(Magazine *newMagazine) { + if (newMagazine == nullptr) { + if (m_pMagazine && m_pMagazine->IsAttached()) { RemoveAttachable(m_pMagazine); } + m_pMagazine = nullptr; + } else { + if (m_pMagazine && m_pMagazine->IsAttached()) { RemoveAttachable(m_pMagazine); } + m_pMagazine = newMagazine; + AddAttachable(newMagazine); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newMagazine->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + Magazine *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetMagazine"); + dynamic_cast(parent)->SetMagazine(castedAttachable); + }}); + } +} -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this HDFirearm, including the mass of Magazine -// may have inserted. +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -float HDFirearm::GetMass() const -{ - return m_pMagazine ? m_Mass + m_pMagazine->GetMass() : m_Mass; +void HDFirearm::SetFlash(Attachable *newFlash) { + if (newFlash == nullptr) { + if (m_pFlash && m_pFlash->IsAttached()) { RemoveAttachable(m_pFlash); } + m_pFlash = nullptr; + } else { + if (m_pFlash && m_pFlash->IsAttached()) { RemoveAttachable(m_pFlash); } + m_pFlash = newFlash; + AddAttachable(newFlash); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newFlash->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + dynamic_cast(parent)->SetFlash(attachable); + }}); + } } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetNextMagazineName -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the Preset name of the next Magazine that will be loaded into -// this gun. This changes all future mags that will be reloaded. +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool HDFirearm::SetNextMagazineName(string magName) { @@ -551,19 +553,6 @@ void HDFirearm::RestDetection() } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. - -void HDFirearm::SetID(const MOID newID) -{ - MovableObject::SetID(newID); - if (m_pMagazine) - m_pMagazine->SetID(newID); -} - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Activate ////////////////////////////////////////////////////////////////////////////////////////// @@ -629,13 +618,9 @@ void HDFirearm::Reload() if (m_pMagazine) { m_pMagazine->SetVel(m_Vel + Vector(m_HFlipped ? -3 : 3, 0.3)); - m_pMagazine->SetAngularVel(6.0F + (-RandomNum(0.0F, 6.0F))); - m_pMagazine->Detach(); - // Whether the magazine is ok to release into scene - if (m_pMagazine->IsDiscardable()) - g_MovableMan.AddParticle(m_pMagazine); - else - delete m_pMagazine; + m_pMagazine->SetAngularVel(6.0F + (-RandomNum(0.0F, 6.0F))); + if (!m_pMagazine->IsDiscardable()) { m_pMagazine->SetToDelete(); } + RemoveAttachable(m_pMagazine, m_pMagazine->IsDiscardable(), false); m_pMagazine = 0; } @@ -774,9 +759,9 @@ void HDFirearm::Update() int player = -1; Controller * pController = 0; - if (m_pParent) + if (m_Parent) { - Actor * pActor = dynamic_cast(m_pParent); + Actor * pActor = dynamic_cast(m_Parent); if (pActor) { pController = pActor->GetController(); @@ -828,7 +813,7 @@ void HDFirearm::Update() Attachable *pAttachable = dynamic_cast(pParticle); if (pAttachable) { - pAttachable->Detach(); + if (pAttachable->IsAttached()) { dynamic_cast(pAttachable->GetParent())->RemoveAttachable(pAttachable); } // Activate if it is some kind of grenade or whatnot. ThrownDevice *pTD = dynamic_cast(pAttachable); if (pTD) @@ -837,7 +822,7 @@ void HDFirearm::Update() // Set the fired particle to not hit this HeldDevice's parent, if applicable if (m_FireIgnoresThis) - pParticle->SetWhichMOToNotHit(pRootParent, 1.0f); + pParticle->SetWhichMOToNotHit(this, 1.0f); // Set the team so alarm events that happen if these gib won't freak out the guy firing pParticle->SetTeam(m_Team); @@ -935,7 +920,7 @@ void HDFirearm::Update() m_pMagazine = dynamic_cast(m_pMagazineReference->Clone()); if (m_pMagazine) { - m_pMagazine->Attach(this); + AddAttachable(m_pMagazine); m_ReloadEndSound.Play(m_Pos); m_ActivationTimer.Reset(); @@ -971,7 +956,7 @@ void HDFirearm::Update() // Set up the recoil force and shake offsets if (m_Recoiled) { - m_RecoilForce.SetXY(totalFireForce * m_RecoilTransmission, 0); + m_RecoilForce.SetXY(totalFireForce * m_JointStiffness, 0); m_RecoilForce = RotateOffset(m_RecoilForce); m_RecoilForce = -m_RecoilForce; @@ -980,11 +965,7 @@ void HDFirearm::Update() m_RecoilOffset.SetMagnitude(1.25); } -// TODO: This is broken, revise") - if (m_pParent) - m_pParent->SetRecoil(m_RecoilForce, m_RecoilOffset, m_Recoiled); - else - m_ImpulseForces.push_back(make_pair(m_RecoilForce, m_RecoilOffset)); + AddImpulseForce(m_RecoilForce, m_RecoilOffset); // Display gun animation if (!m_IsAnimatedManually) @@ -995,11 +976,8 @@ void HDFirearm::Update() // Display gun flame frame. if (m_pFlash) { - m_pFlash->SetHFlipped(m_HFlipped); - m_pFlash->SetJointPos(m_Pos + (m_MuzzleOff.GetXFlipped(m_HFlipped) * m_Rotation)); - m_pFlash->SetRotAngle(m_Rotation.GetRadAngle()); - m_pFlash->SetFrame(RandomNum(0, m_pFlash->GetFrameCount() - 1)); - m_pFlash->Update(); + m_pFlash->SetParentOffset(m_MuzzleOff); + m_pFlash->SetFrame(RandomNum(0, m_pFlash->GetFrameCount() - 1)); } // Play firing sound @@ -1061,54 +1039,14 @@ void HDFirearm::Update() // Update fitted Magazine. if (m_pMagazine) { - m_pMagazine->SetHFlipped(m_HFlipped); - m_pMagazine->SetJointPos(m_Pos + RotateOffset(m_pMagazine->GetParentOffset())); - m_pMagazine->SetRotAngle(m_Rotation.GetRadAngle()); - m_pMagazine->Update(); // Recoil offset has to be applied after the Update or it'll get reset within the update m_pMagazine->SetRecoil(m_RecoilForce, m_RecoilOffset, m_Recoiled); } - if (!ApplyAttachableForces(m_pMagazine)) - m_pMagazine = 0; - m_FiredLastFrame = m_FireFrame; } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. - -void HDFirearm::UpdateChildMOIDs(vector &MOIDIndex, - MOID rootMOID, - bool makeNewMOID) -{ - if (m_pMagazine && m_pMagazine->GetsHitByMOs()) - m_pMagazine->UpdateMOID(MOIDIndex, m_RootMOID, false); - - HeldDevice::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - -void HDFirearm::GetMOIDs(std::vector &MOIDs) const -{ - if (m_pMagazine && m_pMagazine->GetsHitByMOs()) - m_pMagazine->GetMOIDs(MOIDs); - - HeldDevice::GetMOIDs(MOIDs); -} - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: EstimateDigStrenght ////////////////////////////////////////////////////////////////////////////////////////// @@ -1129,32 +1067,25 @@ float HDFirearm::EstimateDigStrenght() // Description: Draws this HDFirearm's current graphical representation to a // BITMAP of choice. -void HDFirearm::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const -{ - if (m_pMagazine && !m_pMagazine->IsDrawnAfterParent()) - m_pMagazine->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - if (m_pFlash && m_FireFrame && !m_pFlash->IsDrawnAfterParent() && mode == g_DrawColor && !onlyPhysical) +void HDFirearm::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { + if (m_pFlash && m_FireFrame && !m_pFlash->IsDrawnAfterParent() && mode == g_DrawColor && !onlyPhysical) { m_pFlash->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + } HeldDevice::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - if (m_pMagazine && m_pMagazine->IsDrawnAfterParent()) - m_pMagazine->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - if (m_pFlash && m_FireFrame && m_pFlash->IsDrawnAfterParent() && mode == g_DrawColor && !onlyPhysical) + if (m_pFlash && m_FireFrame && m_pFlash->IsDrawnAfterParent() && mode == g_DrawColor && !onlyPhysical) { m_pFlash->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + } // Fudge the muzzle pos forward a little bit so the glow aligns nicely Vector muzzlePos = m_MuzzleOff; muzzlePos.m_X += 4; muzzlePos = m_Pos + RotateOffset(muzzlePos); // Set the screen flash effect to draw at the final post processing stage - if (m_FireFrame && m_pFlash && m_pFlash->GetScreenEffect() && mode == g_DrawColor && !onlyPhysical && !g_SceneMan.ObscuredPoint(muzzlePos)) - g_PostProcessMan.RegisterPostEffect(muzzlePos, m_pFlash->GetScreenEffect(), m_pFlash->GetScreenEffectHash(), 55.0F + RandomNum(0.0F,200.0F), m_pFlash->GetEffectRotAngle()); + if (m_FireFrame && m_pFlash && m_pFlash->GetScreenEffect() && mode == g_DrawColor && !onlyPhysical && !g_SceneMan.ObscuredPoint(muzzlePos)) { + g_PostProcessMan.RegisterPostEffect(muzzlePos, m_pFlash->GetScreenEffect(), m_pFlash->GetScreenEffectHash(), 55.0F + RandomNum(0.0F, 200.0F), m_pFlash->GetEffectRotAngle()); + } } @@ -1179,7 +1110,7 @@ void HDFirearm::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whic HeldDevice::DrawHUD(pTargetBitmap, targetPos, whichScreen); // Don't bother if the aim distance is really short, or not held - if (!m_pParent || m_SharpAim < 0.15) + if (!m_Parent || m_SharpAim < 0.15) return; float sharpLength = m_MaxSharpLength * m_SharpAim; @@ -1264,4 +1195,4 @@ void HDFirearm::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whic } } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Entities/HDFirearm.h b/Entities/HDFirearm.h index b2efaf430..d13d03983 100644 --- a/Entities/HDFirearm.h +++ b/Entities/HDFirearm.h @@ -109,17 +109,6 @@ ClassInfoGetters void Destroy(bool notInherited = false) override; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this HDFirearm, including the mass of Magazine -// may have inserted. -// Arguments: None. -// Return value: A float describing the mass value in Kilograms (kg). - - float GetMass() const override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetRateOfFire ////////////////////////////////////////////////////////////////////////////////////////// @@ -142,14 +131,29 @@ ClassInfoGetters void SetRateOfFire(int newRate) { m_RateOfFire = newRate; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMagazine -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the currently attached Magazine, if any. -// Arguments: None. -// Return value: The Magazine, if any is attached. + /// + /// Gets the Magazine of this HDFirearm. + /// + /// A pointer to Magazine of this HDFirearm. Ownership is NOT transferred! + Magazine * GetMagazine() const { return m_pMagazine; } + + /// + /// Sets the Magazine for this HDFirearm. Ownership IS transferred! + /// + /// The new Magazine to use. + void SetMagazine(Magazine *newMagazine); - Magazine * GetMagazine() const { return m_pMagazine; } + /// + /// Gets the flash of this HDFirearm. + /// + /// A pointer to flash of this HDFirearm. Ownership is NOT transferred! + Attachable * GetFlash() const { return m_pFlash; } + + /// + /// Sets the flash for this HDFirearm. Ownership IS transferred! + /// + /// The new flash to use. + void SetFlash(Attachable *newFlash); ////////////////////////////////////////////////////////////////////////////////////////// @@ -424,37 +428,6 @@ ClassInfoGetters void SetMuzzleOffset(Vector newOffset) override { m_MuzzleOff = newOffset; } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. -// Arguments: An moid specifying the MOID that this MovableObject is -// assigned for this frame. -// Return value: None. - - void SetID(const MOID newID) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Attach -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Attaches this Attachable to a host MOSprite -// Arguments: Pointer to the MOSprite to attach to. Ownership is NOT transferred! -// Return value: None. - - void Attach(MOSRotating *pParent) override { HeldDevice::Attach(pParent); m_Reloading = false; m_ReloadTmr.Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Detach -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Detaches this Attachable from its host MOSprite -// Arguments: None. -// Return value: None. - - void Detach() override { HeldDevice::Detach(); m_Activated = m_Reloading = false; m_ReloadTmr.Reset(); } - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: ResetAllTimers ////////////////////////////////////////////////////////////////////////////////////////// @@ -680,55 +653,17 @@ ClassInfoGetters void SetAnimatedManually(bool newValue) { m_IsAnimatedManually = newValue; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetRecoilTransmission -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the calar of how much of the fire recoil force is transmitted to -// who/whatever is holding this weapon -// Arguments: New transmission value. -// Return value: None. - - void SetRecoilTransmission(float recoilTransmission) { m_RecoilTransmission = recoilTransmission; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetRecoilTransmission -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the calar of how much of the fire recoil force is transmitted to -// who/whatever is holding this weapon -// Arguments: None. -// Return value: Transmission value. - - float GetRecoilTransmission() const { return m_RecoilTransmission; } - - ////////////////////////////////////////////////////////////////////////////////////////// // Protected member variable and method declarations protected: -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. -// Arguments: The MOID index to register itself and its children in. -// The MOID of the root MO of this MO, ie the highest parent of this MO. -// 0 means that this MO is the root, ie it is owned by MovableMan. -// Whether this MO should make a new MOID to use for itself, or to use -// the same as the last one in the index (presumably its parent), -// Return value: None. - - void UpdateChildMOIDs(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - - void GetMOIDs(std::vector &MOIDs) const override; + /// + /// Sets this Attachable's parent MOSRotating, and also sets its Team based on its parent and, if the Attachable is set to collide, adds/removes Atoms to its new/old parent. + /// Additionally, sets this HDFirearm as not firing or reloading, and resets its reload timer. + /// + /// A pointer to the MOSRotating to set as the new parent. Ownership is NOT transferred! + void SetParent(MOSRotating *newParent) override { HeldDevice::SetParent(newParent); Deactivate(); m_Reloading = false; m_ReloadTmr.Reset(); } // Member variables. static Entity::ClassInfo m_sClass; @@ -773,8 +708,6 @@ ClassInfoGetters // Whether particles fired from this HDFirearm will ignore hits with itself, // and the root parent of this HDFirearm, regardless if they are set to hit MOs. bool m_FireIgnoresThis; - // Scalar of how much of the fire recoil force is transmitted to who/whatever is holding this weapon - float m_RecoilTransmission; // Timer for timing how long ago the last round was fired. Timer m_LastFireTmr; diff --git a/Entities/HeldDevice.cpp b/Entities/HeldDevice.cpp index a7f60ae58..905545c0c 100644 --- a/Entities/HeldDevice.cpp +++ b/Entities/HeldDevice.cpp @@ -14,6 +14,8 @@ #include "HeldDevice.h" #include "MovableMan.h" #include "AtomGroup.h" +#include "Arm.h" +#include "Actor.h" #include "GUI/GUI.h" #include "GUI/AllegroBitmap.h" @@ -23,7 +25,6 @@ namespace RTE { ConcreteClassInfo(HeldDevice, Attachable, 50) - ////////////////////////////////////////////////////////////////////////////////////////// // Method: Clear ////////////////////////////////////////////////////////////////////////////////////////// @@ -40,10 +41,13 @@ void HeldDevice::Clear() m_DualWieldable = false; m_StanceOffset.Reset(); m_SharpStanceOffset.Reset(); - m_SharpAim = 0.0f; + m_SharpAim = 0.0F; m_MaxSharpLength = 0; m_Supported = false; m_SupportOffset.Reset(); + m_IsUnPickupable = false; + m_PickupableByPresetNames.clear(); + m_GripStrengthMultiplier = 1.0F; m_BlinkTimer.Reset(); m_PieSlices.clear(); m_Loudness = -1; @@ -92,6 +96,12 @@ int HeldDevice::Create() else m_Loudness = 1.0; } + + // Make it so held devices are dropped gently when their parent gibs + m_ParentGibBlastStrengthMultiplier = 0.0F; + + // Make it so users can't accidentally set this to true for HeldDevices, since it'll cause crashes when swapping inventory items around. + m_DeleteWhenRemovedFromParent = false; // All HeldDevice:s by default avoid hitting and getting physically hit by AtomGoups when they are at rest m_IgnoresAGHitsWhenSlowerThan = 1.0; @@ -125,6 +135,11 @@ int HeldDevice::Create(const HeldDevice &reference) m_StanceOffset = reference.m_StanceOffset; m_SharpStanceOffset = reference.m_SharpStanceOffset; m_SupportOffset = reference.m_SupportOffset; + m_IsUnPickupable = reference.m_IsUnPickupable; + for (std::string referenceActorWhoCanPickThisUp : reference.m_PickupableByPresetNames) { + m_PickupableByPresetNames.insert(referenceActorWhoCanPickThisUp); + } + m_GripStrengthMultiplier = reference.m_GripStrengthMultiplier; m_SharpAim = reference.m_SharpAim; m_MaxSharpLength = reference.m_MaxSharpLength; @@ -160,7 +175,29 @@ int HeldDevice::ReadProperty(std::string propName, Reader &reader) reader >> m_SharpStanceOffset; else if (propName == "SupportOffset") reader >> m_SupportOffset; - else if (propName == "SharpLength") + else if (propName == "PickupableBy") { + std::string pickupableByValue = reader.ReadPropValue(); + if (pickupableByValue == "PickupableByEntries") { + while (reader.NextProperty()) { + std::string pickupableByEntryType = reader.ReadPropName(); + if (pickupableByEntryType == "AddPresetNameEntry") { + m_PickupableByPresetNames.insert(reader.ReadPropValue()); + } else if (pickupableByEntryType == "AddClassNameEntry ") { + reader.ReportError("AddClassNameEntry is not yet supported."); + } else if (pickupableByEntryType == "AddGroupEntry") { + reader.ReportError("AddGroupEntry is not yet supported."); + } else if (pickupableByEntryType == "AddDataModuleEntry ") { + reader.ReportError("AddDataModuleEntry is not yet supported."); + } else { + break; + } + } + } else if (pickupableByValue == "None") { + SetUnPickupable(true); + } + } else if (propName == "GripStrengthMultiplier") { + reader >> m_GripStrengthMultiplier; + } else if (propName == "SharpLength") reader >> m_MaxSharpLength; else if (propName == "Loudness") reader >> m_Loudness; @@ -201,6 +238,8 @@ int HeldDevice::Save(Writer &writer) const writer << m_SharpStanceOffset; writer.NewProperty("SupportOffset"); writer << m_SupportOffset; + writer.NewProperty("GripStrengthMultiplier"); + writer << m_GripStrengthMultiplier; writer.NewProperty("SharpLength"); writer << m_MaxSharpLength; writer.NewProperty("Loudness"); @@ -274,6 +313,14 @@ Vector HeldDevice::GetMagazinePos() const return m_Pos; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void HeldDevice::RemovePickupableByPresetName(const std::string &actorPresetName) { + std::unordered_set::iterator pickupableByPresetNameEntry = m_PickupableByPresetNames.find(actorPresetName); + if (pickupableByPresetNameEntry != m_PickupableByPresetNames.end()) { m_PickupableByPresetNames.erase(pickupableByPresetNameEntry); } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: AddPieMenuSlices @@ -347,6 +394,33 @@ bool HeldDevice::OnMOHit(MovableObject *pOtherMO) return false; } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool HeldDevice::TransferJointImpulses(Vector &jointImpulses, float jointStiffnessValueToUse, float jointStrengthValueToUse, float gibImpulseLimitValueToUse) { + MovableObject *parent = m_Parent; + if (!parent) { + return false; + } + if (m_ImpulseForces.empty()) { + return true; + } + const Arm *parentAsArm = dynamic_cast(parent); + if (parentAsArm && parentAsArm->GetGripStrength() > 0 && jointStrengthValueToUse < 0) { + jointStrengthValueToUse = parentAsArm->GetGripStrength() * m_GripStrengthMultiplier; + } + bool intact = Attachable::TransferJointImpulses(jointImpulses, jointStiffnessValueToUse, jointStrengthValueToUse, gibImpulseLimitValueToUse); + if (!intact) { + Actor *rootParentAsActor = dynamic_cast(parent->GetRootParent()); + if (rootParentAsActor && rootParentAsActor->GetStatus() == Actor::STABLE) { + rootParentAsActor->SetStatus(Actor::UNSTABLE); + } + } + return intact; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /* ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Travel @@ -371,7 +445,7 @@ void HeldDevice::Update() Attachable::Update(); // Remove loose items that have completely disappeared into the terrain, unless they're pinned - if (!m_pParent && m_PinStrength <= 0 && m_RestTimer.IsPastSimMS(20000) && m_CanBeSquished && m_pAtomGroup->RatioInTerrain() > 0.9) + if (!m_Parent && m_PinStrength <= 0 && m_RestTimer.IsPastSimMS(20000) && m_CanBeSquished && m_pAtomGroup->RatioInTerrain() > 0.9) GibThis(); if (m_Activated) @@ -389,7 +463,7 @@ void HeldDevice::Update() } } - if (!m_pParent) { + if (!m_Parent) { } else { @@ -457,7 +531,7 @@ void HeldDevice::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whi Attachable::DrawHUD(pTargetBitmap, targetPos, whichScreen); - if (!m_pParent) + if (!m_Parent && !IsUnPickupable()) { // Only draw if the team viewing this has seen the space where this is located int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen)); diff --git a/Entities/HeldDevice.h b/Entities/HeldDevice.h index 5c18bb3f5..2250e7402 100644 --- a/Entities/HeldDevice.h +++ b/Entities/HeldDevice.h @@ -15,6 +15,7 @@ // Inclusions of header files #include "Attachable.h" +#include "Actor.h" #include "PieMenuGUI.h" namespace RTE @@ -281,6 +282,56 @@ ClassInfoGetters void SetSupportOffset(Vector newOffset) { m_SupportOffset = newOffset; } + /// + /// Gets whether this HeldDevice has any limitations on what can pick it up. + /// + /// Whether this HeldDevice has any limitations on what can pick it up. + bool HasPickupLimitations() const { return IsUnPickupable() || !m_PickupableByPresetNames.empty(); } + + /// + /// Gets whether this HeldDevice cannot be picked up at all. + /// + /// Whether this HeldDevice cannot be picked up at all. + bool IsUnPickupable() const { return m_IsUnPickupable; } + + /// + /// Sets whether this HeldDevice cannot be picked up at all. + /// + /// Whether this HeldDevice cannot be picked up at all. True means it cannot, false means any other limitations will apply normally. + void SetUnPickupable(bool shouldBeUnPickupable) { m_IsUnPickupable = shouldBeUnPickupable; } + + /// + /// Checks whether the given Actor can pick up this HeldDevice. + /// + /// The Actor to check. Ownership is NOT transferred. + /// Whether the given Actor can pick up this HeldDevice. + bool IsPickupableBy(const Actor *actor) const { return !HasPickupLimitations() || m_PickupableByPresetNames.find(actor->GetPresetName()) != m_PickupableByPresetNames.end(); } + + /// + /// Specify that objects with the given PresetName can pick up this HeldDevice. + /// + /// The PresetName of an object that should be able to pick up this HeldDevice. + void AddPickupableByPresetName(const std::string &presetName) { SetUnPickupable(false); m_PickupableByPresetNames.insert(presetName); } + + /// + /// Remove allowance for objects with the given PresetName to pick up this HeldDevice. + /// Note that if the last allowance is removed, the HeldDevice will no longer have pickup limitations, rather than setting itself as unpickupable. + /// + /// The PresetName of an object that should no longer be able to pick up this HeldDevice. + void RemovePickupableByPresetName(const std::string &actorPresetName); + + /// + /// Gets the multiplier for how well this HeldDevice can be gripped by Arms. + /// + /// The grip strength multiplier for this HeldDevice. + float GetGripStrengthMultiplier() const { return m_GripStrengthMultiplier; } + + /// + /// Sets the multiplier for how well this HeldDevice can be gripped by Arms. + /// + /// The new grip strength multiplier for this HeldDevice. + void SetGripStrengthMultiplier(float gripStrengthMultiplier) { m_GripStrengthMultiplier = gripStrengthMultiplier; } + ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetSharpAim @@ -530,6 +581,20 @@ ClassInfoGetters /// void ResetAllTimers() override { Attachable::ResetAllTimers(); m_ActivationTimer.Reset(); } +#pragma region Force Transferral + /// + /// Bundles up all the accumulated impulse forces of this HeldDevice and calculates how they transfer to the joint, and therefore to the parent. + /// If the accumulated impulse forces exceed the joint strength or gib impulse limit of this HeldDevice, the jointImpulses Vector will be filled up to that limit and false will be returned. + /// Additionally, in this case, the HeldDevice will remove itself from its parent, destabilizing said parent if it's an Actor, and gib itself if appropriate. + /// + /// A vector that will have the impulse forces affecting the joint ADDED to it. + /// An optional override for the HeldDevice's joint stiffness for this function call. Primarily used to allow subclasses to perform special behavior. + /// An optional override for the HeldDevice's joint strength for this function call. Primarily used to allow subclasses to perform special behavior. + /// An optional override for the HeldDevice's gib impulse limit for this function call. Primarily used to allow subclasses to perform special behavior. + /// False if the HeldDevice has no parent or its accumulated forces are greater than its joint strength or gib impulse limit, otherwise true. + bool TransferJointImpulses(Vector &jointImpulses, float jointStiffnessValueToUse = -1, float jointStrengthValueToUse = -1, float gibImpulseLimitValueToUse = -1) override; +#pragma endregion + ////////////////////////////////////////////////////////////////////////////////////////// // Protected member variable and method declarations @@ -562,6 +627,9 @@ ClassInfoGetters float m_MaxSharpLength; // If this HeldDevice is currently being supported by a second hand. bool m_Supported; + bool m_IsUnPickupable; //!< Whether or not this HeldDevice should be able to be picked up at all. + std::unordered_set m_PickupableByPresetNames; //!< The unordered set of PresetNames that can pick up this HeldDevice if it's dropped. An empty set means there are no PresetName limitations. + float m_GripStrengthMultiplier; //!< The multiplier for how well this HeldDevice can be gripped by Arms. // Blink timer for the icon Timer m_BlinkTimer; // Extra pie menu options that this should add to any actor who holds this device diff --git a/Entities/Leg.cpp b/Entities/Leg.cpp index 28e964adf..60d072566 100644 --- a/Entities/Leg.cpp +++ b/Entities/Leg.cpp @@ -1,459 +1,232 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Leg.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the Leg class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "Leg.h" #include "PresetMan.h" namespace RTE { -ConcreteClassInfo(Leg, Attachable, 50) - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Leg, effectively -// resetting the members of this abstraction level only. - -void Leg::Clear() -{ -// m_AnkleOffset.Reset(); - m_pFoot = 0; - m_ContractedOffset.Reset(); - m_ExtendedOffset.Reset(); - m_MinExtension = 0; - m_MaxExtension = 0; - m_CurrentNormalizedExtension = 0; - m_AnkleOffset.Reset(); - m_TargetOffset.Reset(); - m_IdleOffset.Reset(); - m_MoveSpeed = 0; - m_WillIdle = false; - m_DidReach = false; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Round object ready for use. - -int Leg::Create() -{ - if (Attachable::Create() < 0) - return -1; - - // Make sure the contracted offset is the one closer to the joint - if (m_ContractedOffset.GetMagnitude() > m_ExtendedOffset.GetMagnitude()) - { - Vector tempOffset = m_ContractedOffset; - m_ContractedOffset = m_ExtendedOffset; - m_ExtendedOffset = tempOffset; - } - - m_MinExtension = m_ContractedOffset.GetMagnitude(); - m_MaxExtension = m_ExtendedOffset.GetMagnitude(); - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Leg to be identical to another, by deep copy. - -int Leg::Create(const Leg &reference) -{ - Attachable::Create(reference); - - if (reference.m_pFoot) { - m_pFoot = dynamic_cast(reference.m_pFoot->Clone()); - AddAttachable(m_pFoot, true); - } - m_ContractedOffset = reference.m_ContractedOffset; - m_ExtendedOffset = reference.m_ExtendedOffset; - m_MinExtension = reference.m_MinExtension; - m_MaxExtension = reference.m_MaxExtension; - m_CurrentNormalizedExtension = reference.m_CurrentNormalizedExtension; - m_AnkleOffset = reference.m_AnkleOffset; - m_TargetOffset = reference.m_TargetOffset; - m_IdleOffset = reference.m_IdleOffset; - m_WillIdle = reference.m_WillIdle; - m_MoveSpeed = reference.m_MoveSpeed; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int Leg::ReadProperty(std::string propName, Reader &reader) -{ - if (propName == "Foot") - { - const Entity *pObj = g_PresetMan.GetEntityPreset(reader); - if (pObj) - { - m_pFoot = dynamic_cast(pObj->Clone()); - } - } - else if (propName == "ContractedOffset") - { - reader >> m_ContractedOffset; - m_MinExtension = m_ContractedOffset.GetMagnitude(); - } - else if (propName == "ExtendedOffset") - { - reader >> m_ExtendedOffset; - m_MaxExtension = m_ExtendedOffset.GetMagnitude(); - } - else if (propName == "MaxLength") - { - // For backward compatibiltiy with before - float maxLength; - reader >> maxLength; - - m_MinExtension = maxLength / 2; - m_ContractedOffset.SetXY(m_MinExtension, 0); - m_MaxExtension = maxLength; - m_ExtendedOffset.SetXY(m_MaxExtension, 0); - } - else if (propName == "IdleOffset") - reader >> m_IdleOffset; - else if (propName == "WillIdle") - reader >> m_WillIdle; - else if (propName == "MoveSpeed") - reader >> m_MoveSpeed; - else - return Attachable::ReadProperty(propName, reader); - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this Leg with a Writer for -// later recreation with Create(Reader &reader); - -int Leg::Save(Writer &writer) const -{ - Attachable::Save(writer); - - writer.NewProperty("Foot"); - writer << m_pFoot; - writer.NewProperty("ContractedOffset"); - writer << m_ContractedOffset; - writer.NewProperty("ExtendedOffset"); - writer << m_ExtendedOffset; - writer.NewProperty("IdleOffset"); - writer << m_IdleOffset; - writer.NewProperty("WillIdle"); - writer << m_WillIdle; - writer.NewProperty("MoveSpeed"); - writer << m_MoveSpeed; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the Leg object. - -void Leg::Destroy(bool notInherited) -{ - delete m_pFoot; - if (!notInherited) - Attachable::Destroy(); - Clear(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. - -void Leg::SetID(const MOID newID) -{ - MovableObject::SetID(newID); - if (m_pFoot) - m_pFoot->SetID(newID); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReachToward -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Rotates the leg so that it reaches after a point in scene coordinates. - -void Leg::ReachToward(const Vector &scenePoint) -{ - m_TargetOffset = scenePoint; -/* - if (m_HFlipped) { - m_Pos.m_X -= m_ParentOffset.m_X; - m_Pos.m_Y += m_ParentOffset.m_Y; - } - else - m_Pos += m_ParentOffset; - - Vector reachVec(m_TargetOffset - m_Pos); - return reachVec.GetMagnitude() <= m_MaxExtension && - reachVec.GetMagnitude() >= (m_MaxExtension / 2); -*/ -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BendLeg -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Bends the leg to the appropriate position depending on the ankle -// offset. - -void Leg::BendLeg() -{ - if (m_FrameCount == 1) - { - m_Frame = 0; - return; - } - - // Set correct frame for leg bend. - float range = m_MaxExtension - m_MinExtension; - m_CurrentNormalizedExtension = (m_AnkleOffset.GetMagnitude() - (m_MaxExtension - range)) / range; - - if (m_CurrentNormalizedExtension < 0) - m_CurrentNormalizedExtension = 0; - else if (m_CurrentNormalizedExtension > 1.0) - m_CurrentNormalizedExtension = 1.0; - - m_Frame = std::floor(m_CurrentNormalizedExtension * m_FrameCount); - - // Clamp - if (m_Frame >= m_FrameCount) - m_Frame = m_FrameCount - 1; - - RTEAssert(m_Frame >= 0 && m_Frame < m_FrameCount, "Frame is out of bounds!"); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. - -void Leg::GibThis(Vector impactImpulse, float internalBlast, MovableObject *pIgnoreMO) -{ - // Detach foot and let loose - if (m_pFoot) - { - RemoveAttachable(m_pFoot); - g_MovableMan.AddParticle(m_pFoot); - m_pFoot = 0; - } - - Attachable::GibThis(impactImpulse, internalBlast, pIgnoreMO); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this Leg. Supposed to be done every frame. - -void Leg::Update() -{ - // Update basic metrics from parent. - Attachable::Update(); - - if (!m_pParent) - { - if (m_pFoot) - { - m_AnkleOffset.SetXY(m_MaxExtension * 0.60, 0); - m_AnkleOffset.RadRotate((m_HFlipped ? c_PI : 0) + m_Rotation.GetRadAngle()); - BendLeg(); - m_pFoot->SetJointPos(m_JointPos.GetFloored() + m_AnkleOffset); - m_pFoot->SetRotAngle(m_Rotation.GetRadAngle() + c_PI / 2); - m_pFoot->Update(); - } - } - else - { - // Attached, so act like it - - Vector target = g_SceneMan.ShortestDistance(m_JointPos, m_TargetOffset);// m_Pos + RotateOffset(m_JointOffset)); - // Check if target is within leg's length. - if (target.GetMagnitude() <= m_MaxExtension && target.m_Y >= -3/* && target.GetFloored() != m_AnkleOffset.GetFloored()*/) - { - Vector moveVec(target - m_AnkleOffset); - m_AnkleOffset += moveVec * m_MoveSpeed; - m_DidReach = true; - } - else /*if (m_IdleOffset.GetXFlipped(m_HFlipped).GetFloored() != m_AnkleOffset.GetFloored())*/ - { - if (target.m_Y < -3 && m_WillIdle) - { - Vector moveVec(m_IdleOffset.GetXFlipped(m_HFlipped) - m_AnkleOffset); - m_AnkleOffset += moveVec * m_MoveSpeed; -// m_AnkleOffset = m_IdleOffset.GetXFlipped(m_HFlipped); - } - else - m_AnkleOffset = target; - m_DidReach = false; - } - - // Cap foot distance to what the Leg allows - ConstrainFoot(); - - // Set the basic rotation - m_Rotation = (m_HFlipped ? c_PI : 0) + m_AnkleOffset.GetAbsRadAngle(); - - // Apply the extra rotation needed to line up the sprite with the leg extension line - - // Get normalized scalar for how much of the difference in angle between the contracted and extrended offsets should be applied - // EaseOut is used to get the sine effect needed - float extraRotationRatio = (EaseOut(m_MinExtension, m_MaxExtension, m_CurrentNormalizedExtension) - m_MinExtension) / (m_MaxExtension - m_MinExtension); - // The contracted offset's inverse angle is the base for the rotation correction - float extraRotation = -(m_ContractedOffset.GetAbsRadAngle()); - // Get the actual amount of extra rotation correction needed from the ratio, somewhere on the arc between contracted and extended angles - // This is negative because it's a correction, the bitmap needs to rotate back to align the ankle with where it's supposed to be in the sprite - extraRotation -= (m_ExtendedOffset.GetAbsRadAngle() - m_ContractedOffset.GetAbsRadAngle()) * extraRotationRatio; - // Apply the extra rotation - m_Rotation.SetRadAngle(m_Rotation.GetRadAngle() + (m_HFlipped ? -extraRotation : extraRotation)); - -// Don't apply state changes to BITMAP anywhere else than Draw(). -// m_aSprite->SetAngle(m_Rotation); -// m_aSprite->SetScale(m_Scale); - - BendLeg(); - - if (m_pFoot) - { - m_pFoot->SetHFlipped(m_HFlipped); -// Vector rotatedOff(m_pFoot->GetParentOffset()); -// if (m_HFlipped) -// rotatedOff.m_X = -rotatedOff.m_X; -// rotatedOff.RadRotate(m_Rotation); -// m_pFoot->SetPos(m_Pos + rotatedOff.GetFloored()); -// m_pFoot->SetJointPos(m_Pos.GetFloored() + RotateOffset(m_JointOffset) + m_AnkleOffset); - m_pFoot->SetJointPos(m_JointPos + m_AnkleOffset); - if (!m_HFlipped && target.m_X < -10 || - m_HFlipped && target.m_X > 10) { - m_pFoot->SetFrame(3); - } - else if (!m_HFlipped && target.m_X < -6 || - m_HFlipped && target.m_X > 6) { - m_pFoot->SetFrame(2); - m_pFoot->SetRotAngle(0); - } - else if (!m_HFlipped && target.m_X > 6 || - m_HFlipped && target.m_X < -6) { - m_pFoot->SetFrame(1); - m_pFoot->SetRotAngle(0); -// m_pFoot->SetRotAngle(m_Rotation + (m_HFlipped ? -c_HalfPI : c_HalfPI)); - } - else { - m_pFoot->SetFrame(0); - m_pFoot->SetRotAngle(0.0); - } - m_pFoot->Update(); - } - } -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. - -void Leg::UpdateChildMOIDs(vector &MOIDIndex, - MOID rootMOID, - bool makeNewMOID) -{ -// if (m_pFoot) -// m_pFoot->UpdateMOID(MOIDIndex, false); - - Attachable::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - -void Leg::GetMOIDs(std::vector &MOIDs) const -{ - Attachable::GetMOIDs(MOIDs); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Leg's current graphical representation to a -// BITMAP of choice. - -void Leg::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const -{ - if (m_pFoot && mode != g_DrawMOID && !m_pFoot->IsDrawnAfterParent()) - m_pFoot->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - Attachable::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - if (m_pFoot && mode != g_DrawMOID && m_pFoot->IsDrawnAfterParent()) - m_pFoot->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ConstrainFoot -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes sure the foot distance is constrained between the mix and max -// extension of this Leg. - -bool Leg::ConstrainFoot() -{ - if (m_AnkleOffset.GetMagnitude() > m_MaxExtension) - { - m_AnkleOffset.SetMagnitude(m_MaxExtension); - return false; - } - else if (m_AnkleOffset.GetMagnitude() < m_MinExtension) { - m_AnkleOffset.SetMagnitude(m_MinExtension + 0.1); - return true; - } - else - return true; -} - -} // namespace RTE \ No newline at end of file + ConcreteClassInfo(Leg, Attachable, 50) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Leg::Clear() { + m_Foot = nullptr; + + m_ContractedOffset.Reset(); + m_ExtendedOffset.Reset(); + + m_MinExtension = 0; + m_MaxExtension = 0; + m_NormalizedExtension = 0; + + m_TargetPosition.Reset(); + m_IdleOffset.Reset(); + + m_AnkleOffset.Reset(); + + m_WillIdle = false; + m_MoveSpeed = 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int Leg::Create() { + if (Attachable::Create() < 0) { + return -1; + } + + // Ensure Legs don't get flagged as inheriting RotAngle, since they never do and always set their RotAngle for themselves. + m_InheritsRotAngle = false; + + // Ensure Legs don't collide with terrain when attached since their expansion/contraction is frame based so atom group doesn't know how to account for it. + SetCollidesWithTerrainWhileAttached(false); + + if (m_ContractedOffset.GetMagnitude() > m_ExtendedOffset.GetMagnitude()) { std::swap(m_ContractedOffset, m_ExtendedOffset); } + + m_MinExtension = m_ContractedOffset.GetMagnitude(); + m_MaxExtension = m_ExtendedOffset.GetMagnitude(); + + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int Leg::Create(const Leg &reference) { + if (reference.m_Foot) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_Foot->GetUniqueID()); + SetFoot(dynamic_cast(reference.m_Foot->Clone())); + } + Attachable::Create(reference); + + m_ContractedOffset = reference.m_ContractedOffset; + m_ExtendedOffset = reference.m_ExtendedOffset; + + m_MinExtension = reference.m_MinExtension; + m_MaxExtension = reference.m_MaxExtension; + m_NormalizedExtension = reference.m_NormalizedExtension; + + m_TargetPosition = reference.m_TargetPosition; + m_IdleOffset = reference.m_IdleOffset; + + m_AnkleOffset = reference.m_AnkleOffset; + + m_WillIdle = reference.m_WillIdle; + m_MoveSpeed = reference.m_MoveSpeed; + + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int Leg::ReadProperty(std::string propName, Reader &reader) { + if (propName == "Foot") { + RemoveAttachable(m_Foot); + const Entity *footEntity = g_PresetMan.GetEntityPreset(reader); + if (footEntity) { + m_Foot = dynamic_cast(footEntity->Clone()); + AddAttachable(m_Foot); + m_Foot->SetInheritsRotAngle(false); + m_Foot->SetParentGibBlastStrengthMultiplier(0.0F); + m_Foot->SetCollidesWithTerrainWhileAttached(false); + } + } else if (propName == "ContractedOffset") { + reader >> m_ContractedOffset; + m_MinExtension = m_ContractedOffset.GetMagnitude(); + } else if (propName == "ExtendedOffset") { + reader >> m_ExtendedOffset; + m_MaxExtension = m_ExtendedOffset.GetMagnitude(); + } else if (propName == "IdleOffset") { + reader >> m_IdleOffset; + } else if (propName == "WillIdle") { + reader >> m_WillIdle; + } else if (propName == "MoveSpeed") { + reader >> m_MoveSpeed; + } else { + return Attachable::ReadProperty(propName, reader); + } + + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int Leg::Save(Writer &writer) const { + Attachable::Save(writer); + + writer.NewProperty("Foot"); + writer << m_Foot; + writer.NewProperty("ContractedOffset"); + writer << m_ContractedOffset; + writer.NewProperty("ExtendedOffset"); + writer << m_ExtendedOffset; + writer.NewProperty("IdleOffset"); + writer << m_IdleOffset; + writer.NewProperty("WillIdle"); + writer << m_WillIdle; + writer.NewProperty("MoveSpeed"); + writer << m_MoveSpeed; + + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Leg::SetFoot(Attachable *newFoot) { + if (newFoot == nullptr) { + if (m_Foot && m_Foot->IsAttached()) { RemoveAttachable(m_Foot); } + m_Foot = nullptr; + } else { + if (m_Foot && m_Foot->IsAttached()) { RemoveAttachable(m_Foot); } + m_Foot = newFoot; + AddAttachable(newFoot); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newFoot->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + dynamic_cast(parent)->SetFoot(attachable); + }}); + } + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Leg::Update() { + UpdateCurrentAnkleOffset(); + + if (m_Foot) { + // In order to keep the foot in the right place, we need to convert its offset (the ankle offset) to work as the ParentOffset for the foot. + // The foot will then use this to set its JointPos when it's updated. Unfortunately UnRotateOffset doesn't work for this, since it's Vector/Matrix division, which isn't commutative. + Vector ankleOffsetAsParentOffset = RotateOffset(m_JointOffset) + m_AnkleOffset; + ankleOffsetAsParentOffset.RadRotate(-m_Rotation.GetRadAngle()).FlipX(m_HFlipped); + m_Foot->SetParentOffset(ankleOffsetAsParentOffset); + } + + Attachable::Update(); + + UpdateLegRotation(); + + if (m_FrameCount == 1) { + m_Frame = 0; + } else { + m_NormalizedExtension = std::clamp((m_AnkleOffset.GetMagnitude() - m_MinExtension) / (m_MaxExtension - m_MinExtension), 0.0F, 1.0F); + m_Frame = std::min(m_FrameCount - 1, static_cast(std::floor(m_NormalizedExtension * static_cast(m_FrameCount)))); + } + + UpdateFootFrameAndRotation(); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Leg::UpdateCurrentAnkleOffset() { + if (IsAttached()) { + Vector targetOffset = g_SceneMan.ShortestDistance(m_JointPos, m_TargetPosition, g_SceneMan.SceneWrapsX()); + if (m_WillIdle && targetOffset.m_Y < -3) { targetOffset = m_IdleOffset.GetXFlipped(m_HFlipped); } + + Vector distanceFromTargetOffsetToAnkleOffset(targetOffset - m_AnkleOffset); + m_AnkleOffset += distanceFromTargetOffsetToAnkleOffset * m_MoveSpeed; + m_AnkleOffset.ClampMagnitude(m_MaxExtension, m_MinExtension + 0.1F); + } else { + m_AnkleOffset.SetXY(m_MaxExtension * 0.60F, 0); + m_AnkleOffset.RadRotate((m_HFlipped ? c_PI : 0) + m_Rotation.GetRadAngle()); + } + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Leg::UpdateLegRotation() { + if (IsAttached()) { + m_Rotation = m_AnkleOffset.GetAbsRadAngle() + (m_HFlipped ? c_PI : 0); + + // Get a normalized scalar for where the Leg should be rotated to between the contracted and extended offsets. EaseOut is used to get the sine effect needed. + float extraRotationRatio = (EaseOut(m_MinExtension, m_MaxExtension, m_NormalizedExtension) - m_MinExtension) / (m_MaxExtension - m_MinExtension); + + // The contracted offset's inverse angle is the base for the rotation correction. + float extraRotation = -(m_ContractedOffset.GetAbsRadAngle()); + + // Get the actual amount of extra rotation correction needed from the ratio, somewhere on the arc between contracted and extended angles. + // This is negative because it's a correction, the bitmap needs to rotate back to align the ankle with where it's supposed to be in the sprite. + extraRotation -= (m_ExtendedOffset.GetAbsRadAngle() - m_ContractedOffset.GetAbsRadAngle()) * extraRotationRatio; + + m_Rotation.SetRadAngle(m_Rotation.GetRadAngle() + extraRotation * static_cast(GetFlipFactor())); + m_AngularVel = 0.0F; + } + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Leg::UpdateFootFrameAndRotation() { + if (m_Foot) { + if (IsAttached()) { + float ankleOffsetHorizontalDistanceAccountingForFlipping = m_AnkleOffset.GetXFlipped(m_HFlipped).GetX(); + if (ankleOffsetHorizontalDistanceAccountingForFlipping < -10) { + m_Foot->SetFrame(3); + } else if (ankleOffsetHorizontalDistanceAccountingForFlipping < -6) { + m_Foot->SetFrame(2); + } else if (ankleOffsetHorizontalDistanceAccountingForFlipping > 6) { + m_Foot->SetFrame(1); + } else { + m_Foot->SetFrame(0); + } + m_Foot->SetRotAngle(0.0F); + } else { + m_Foot->SetRotAngle(m_Rotation.GetRadAngle() + c_PI / 2); + } + } + } +} \ No newline at end of file diff --git a/Entities/Leg.h b/Entities/Leg.h index 4a478a382..470c86076 100644 --- a/Entities/Leg.h +++ b/Entities/Leg.h @@ -1,342 +1,156 @@ #ifndef _RTELEG_ #define _RTELEG_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Leg.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the Leg class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "Attachable.h" -namespace RTE -{ - -class HeldDevice; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: Leg -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A detatchable leg that will be controlled by LimbPath:s. -// Parent(s): Attachable. -// Class history: 05/30/2002 Leg created. - -class Leg: - public Attachable -{ - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(Leg) -SerializableOverrideMethods -ClassInfoGetters - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: Leg -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a Leg object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - Leg() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~Leg -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a Leg object before deletion -// from system memory. -// Arguments: None. - - ~Leg() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Leg object ready for use. -// Arguments: None. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Leg to be identical to another, by deep copy. -// Arguments: A reference to the Leg to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const Leg &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire Leg, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Attachable::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetAnklePos -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the position of the ankle of this Leg as an absolute scene coord. -// Arguments: None. -// Return value: Vector with the current absolute scene ankle position. - - Vector GetAnklePos() { return m_Pos + m_AnkleOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMaxLength -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the max length this leg can reach from its socket to the foot. -// Arguments: None. -// Return value: The max length of reach, in pixels, of this leg. - - float GetMaxLength() const { return m_MaxExtension; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. -// Arguments: A MOID specifying the MOID that this MovableObject is -// assigned for this frame. -// Return value: None. - - void SetID(const MOID newID) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetIdleOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the offset from the position of this Leg to which its foot will -// go when not holding a device and not able to reach a certain target. -// Arguments: Vector with the new idle offset relative to the position of this Leg. -// Return value: None. - - void SetIdleOffset(const Vector &newIdleOffset) { m_IdleOffset = newIdleOffset; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: EnableIdle -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this to go into idle offset mode if the target appears to be above -// the joint of the leg. -// Arguments: Whetehr to enable the idling if the target is above the joint -// Return value: None. - - void EnableIdle(bool idle = true) { m_WillIdle = idle; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReachToward -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Rotates the leg so that it reaches after a point in scene coordinates. -// Must be called AFTER SetPos for this frame if the return value is to -// be accurate. -// Arguments: The point to reach after. If (0, 0), reaching is deactivated. -// Return value: None. - - void ReachToward(const Vector &scenePoint); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DidReach -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this Leg actually reached the reach target last -// Update(). -// Arguments: None. -// Return value: Whether.the Leg was able to actually reach the point or not. - - bool DidReach() { return m_DidReach; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: BendLeg -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Bends the leg to the appropriate position depending on the ankle -// offset. -// Arguments: None. -// Return value: None. - - void BendLeg(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. -// Arguments: The impulse (kg * m/s) of the impact causing the gibbing to happen. -// The internal blast impulse which will push the gibs away from the center. -// A pointer to an MO which the gibs shuold not be colliding with! -// Return value: None. - - void GibThis(Vector impactImpulse = Vector(), float internalBlast = 10, MovableObject *pIgnoreMO = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Leg's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - - void GetMOIDs(std::vector &MOIDs) const override; +namespace RTE { + class HeldDevice; /// - /// Gets the foot Attachable of this. + /// A detachable Leg that will be controlled by LimbPaths. /// - /// A pointer to the foot Attachable of this. Ownership is NOT transferred! - Attachable * GetFoot() const { return m_pFoot; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ConstrainFoot -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes sure the foot distance is constrained between the mix and max -// extension of this Leg. -// Arguments: None. -// Return value: Whether.the Foot already was within the reach range of this Leg or not. - - bool ConstrainFoot(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. -// Arguments: The MOID index to register itself and its children in. -// The MOID of the root MO of this MO, ie the highest parent of this MO. -// 0 means that this MO is the root, ie it is owned by MovableMan. -// Whether this MO should make a new MOID to use for itself, or to use -// the same as the last one in the index (presumably its parent), -// Return value: None. - - void UpdateChildMOIDs(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) override; - - - // Member variables - static Entity::ClassInfo m_sClass; -// // The location of the 'foot' in relation to the MovableObject::m_Pos -// Vector m_FootPos; - // The foot attachable. - Attachable *m_pFoot; - // The offset from the joint where the ankle contracts to in the sprite - Vector m_ContractedOffset; - // The offset from the joint where the ankle extends to in the sprite - Vector m_ExtendedOffset; - // Precalculated min and max extensions of the leg (from the joint) based ont he contracted and extended offsets - float m_MinExtension; - float m_MaxExtension; - // normalized scalar of where the ankle offset's magnitude is between the min and max extensions - float m_CurrentNormalizedExtension; - // Current offset from the joint to the ankle where the foot should be - Vector m_AnkleOffset; - // The target offset that this Leg's foot is reaching after. - // If (0, 0), the Leg is currently not reaching after anything. - Vector m_TargetOffset; - // The target offset from m_Pos that this Leg's foot is reaching after when - // not reaching for or holding anything else. - Vector m_IdleOffset; - // How fast the leg moves to a reach target, - // on a scale from 0.0 (frozen) to 1.0 (instantly there). - float m_MoveSpeed; - // Whether this will go to idle position if the target is above the joint - bool m_WillIdle; - // Whether this Leg reaached the reach target last Update. - bool m_DidReach; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Leg, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - Leg(const Leg &reference) = delete; - Leg & operator=(const Leg &rhs) = delete; - -}; - -} // namespace RTE - -#endif // File \ No newline at end of file + class Leg : public Attachable { + + public: + EntityAllocation(Leg) + SerializableOverrideMethods + ClassInfoGetters + +#pragma region Creation + /// + /// Constructor method used to instantiate a Leg object in system memory. Create() should be called before using the object. + /// + Leg() { Clear(); } + + /// + /// Makes the Leg object ready for use. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create() override; + + /// + /// Creates a Leg to be identical to another, by deep copy. + /// + /// A reference to the Leg to deep copy. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create(const Leg &reference); +#pragma endregion + +#pragma region Destruction + /// + /// Destructor method used to clean up a Leg object before deletion from system memory. + /// + ~Leg() override { Destroy(true); } + + /// + /// Destroys and resets (through Clear()) the Leg object. + /// + /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + void Destroy(bool notInherited = false) override { if (!notInherited) { Attachable::Destroy(); } Clear(); } + + /// + /// Resets the entire Leg, including its inherited members, to their default settings or values. + /// + void Reset() override { Clear(); Attachable::Reset(); } +#pragma endregion + +#pragma region Getters and Setters + /// + /// Gets the foot of this Leg. + /// + /// A pointer to foot of this Leg. Ownership is NOT transferred! + Attachable * GetFoot() const { return m_Foot; } + + /// + /// Sets the foot for this Leg. Ownership IS transferred! + /// + /// The new foot to use. + void SetFoot(Attachable *newFoot); + + /// + /// Gets the min length this of Leg, the minimum allowed length from its joint to its ankle's position. + /// + /// The min length, in pixels, of this Leg. + float GetMinLength() const { return m_MinExtension; } + + /// + /// Gets the max length this Leg, the maximum allowed length from its joint to its ankle's position. + /// + /// The max length, in pixels, of this Leg. + float GetMaxLength() const { return m_MaxExtension; } + + /// + /// Sets the position this Leg should move towards, in absolute coordinates. + /// + /// The position the Leg should move towards. + void SetTargetPosition(const Vector &targetPosition) { m_TargetPosition = targetPosition; } + + /// + /// Sets whether this Leg will go into idle offset mode if the target appears to be above the joint of the Leg. + /// + /// Whether to enable idling if the target offset is above the joint. + void EnableIdle(bool idle = true) { m_WillIdle = idle; } +#pragma endregion + +#pragma region Override Methods + /// + /// Updates this Leg. Supposed to be done every frame. + /// + void Update() override; +#pragma endregion + + protected: + + static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. + + Attachable *m_Foot; //!< Pointer to the foot attachable of this Leg. + + Vector m_ContractedOffset; //!< The offset from the joint where the ankle contracts to in the sprite. + Vector m_ExtendedOffset; //!< The offset from the joint where the ankle extends to in the sprite. + + float m_MinExtension; //!< Precalculated min extension of the Leg (from the joint) based on the contracted offset. + float m_MaxExtension; //!< Precalculated max extension of the Leg (from the joint) based on the extended offset. + float m_NormalizedExtension; //!< Normalized scalar of where the ankle offset's magnitude is between the min and max extensions. + + Vector m_TargetPosition; //!< The absolute position that this Leg's foot is moving towards. + Vector m_IdleOffset; //!< The target offset from m_Pos that this Leg's foot is moving towards when allowed to idle and the target position is not acceptable. + + Vector m_AnkleOffset; //!< Current offset from the joint to the ankle where the foot should be. + + bool m_WillIdle; //!< Whether the Leg will go to idle position if the target position is above the Leg's joint position. + float m_MoveSpeed; //!< How fast the Leg moves to a reach target, 0 means it doesn't and 1 means it moves instantly. + + private: + +#pragma region Update Breakdown + /// + /// Updates the current ankle offset for this Leg. Should only be called from Update. + /// If the Leg is attached, the current ankle offset is based on the target offset and move speed, and whether the Leg should idle or not, otherwise it puts it in a reasonable position. + /// + void UpdateCurrentAnkleOffset(); + + /// + /// Updates the rotation of the Leg. Should only be called from Update. + /// If the Leg is attached, this applies extra rotation to line up the Leg's sprite with its extension line, otherwise it does nothing. + /// + void UpdateLegRotation(); + + /// + /// Updates the frame and rotation of the Leg's foot Attachable. Should only be called from Update. + /// If the Leg is attached, the foot's rotation and frame depend on the ankle offset, otherwise the foot's rotation is set to be perpendicular to the Leg's rotation. + /// + void UpdateFootFrameAndRotation(); +#pragma endregion + + /// + /// Clears all the member variables of this Leg, effectively resetting the members of this abstraction level only. + /// + void Clear(); + + // Disallow the use of some implicit methods. + Leg(const Leg &reference) = delete; + Leg & operator=(const Leg &rhs) = delete; + }; +} +#endif \ No newline at end of file diff --git a/Entities/LimbPath.cpp b/Entities/LimbPath.cpp index 3ed325cf4..efde4a0c5 100644 --- a/Entities/LimbPath.cpp +++ b/Entities/LimbPath.cpp @@ -32,6 +32,7 @@ void LimbPath::Clear() m_StartSegCount = 0; m_Segments.clear(); // m_CurrentSegment = 0; + m_FootCollisionsDisabledSegment = -1; m_SegProgress = 0.0; for (int i = 0; i < SPEEDCOUNT; ++i) m_TravelSpeed[i] = 0.0; @@ -120,6 +121,8 @@ int LimbPath::Create(const LimbPath &reference) else m_CurrentSegment = m_Segments.end(); + m_FootCollisionsDisabledSegment = reference.m_FootCollisionsDisabledSegment; + m_SegProgress = reference.m_SegProgress; for (int i = 0; i < SPEEDCOUNT; ++i) m_TravelSpeed[i] = reference.m_TravelSpeed[i]; @@ -158,6 +161,8 @@ int LimbPath::ReadProperty(std::string propName, Reader &reader) m_TotalLength += segment.GetMagnitude(); if (m_Segments.size() >= m_StartSegCount) m_RegularLength += segment.GetMagnitude(); + } else if (propName == "EndSegCount") { + reader >> m_FootCollisionsDisabledSegment; } else if (propName == "SlowTravelSpeed") { @@ -417,7 +422,7 @@ void LimbPath::ReportProgress(const Vector &limbPos) // Description: Gets a value representing the total progress that has been made on // this entire path. If the path has ended, 0.0 is returned. -float LimbPath::GetTotalProgress() +float LimbPath::GetTotalProgress() const { if (m_Ended || IsStaticPoint()) return 0.0; @@ -439,7 +444,7 @@ float LimbPath::GetTotalProgress() // If progress has not been made past the starting segments, < 0 will // be returned. If the path has ended, 0.0 is returned. -float LimbPath::GetRegularProgress() +float LimbPath::GetRegularProgress() const { if (m_Ended || IsStaticPoint()) return 0.0; @@ -453,6 +458,21 @@ float LimbPath::GetRegularProgress() } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int LimbPath::GetCurrentSegmentNumber() const { + int progress = 0; + if (!m_Ended && !IsStaticPoint()) { + for (deque::const_iterator itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) { + progress++; + } + } + return progress; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetSpeed ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Entities/LimbPath.h b/Entities/LimbPath.h index e7ba324d1..90f46eeaa 100644 --- a/Entities/LimbPath.h +++ b/Entities/LimbPath.h @@ -139,16 +139,17 @@ ClassInfoGetters void Destroy(bool notInherited = false) override; + /// + /// Gets the coordinates where the limb should start at the start of the LimbPath cycle, relative to the owning AtomGroup's local origin. + /// + /// A Vector with the start position. + const Vector & GetStartOffset() const { return m_Start; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetStartOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the coordinates where the limb should start at the start of the -// LimbPath cycle, relative to the owning AtomGroup's local origin. -// Arguments: None. -// Return value: A Vector with the start position. - - Vector GetStartOffset() const { return m_Start; } + /// + /// Sets the coordinates where the limb should start at the start of the LimbPath cycle, relative to the owning AtomGroup's local origin. + /// + /// A Vector with the new start offset. + void SetStartOffset(const Vector &newStartOffset) { m_Start = newStartOffset; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -162,14 +163,18 @@ ClassInfoGetters unsigned int GetSegCount() const { return m_Segments.size(); } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetSegments -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the array of 'waypoints' or segments of this LimbPath. -// Arguments: None. -// Return value: An array of Vectors defining the path of this LimbPath. + /// + /// Gets a pointer to the segment at the given index. Ownership is NOT transferred. + /// + /// The index of the segment to get. + /// A pointer to the segment at the given index. Ownership is NOT transferred. + Vector *GetSegment(int segmentIndex) { if (segmentIndex >= 0 && segmentIndex < m_Segments.size()) { return &m_Segments.at(segmentIndex); } return nullptr;} -// const Vector const * GetSegArray() const { return m_aSegments; } + /// + /// Gets whether or not foot collisions should be disabled, i.e. the limbpath's progress is greater than the FootCollisionsDisabledSegment value. + /// + /// Whether or not foot collisions should be disabled for this limbpath at its current progress. + bool FootCollisionsShouldBeDisabled() const { return m_FootCollisionsDisabledSegment >= 0 && GetSegCount() - GetCurrentSegmentNumber() <= m_FootCollisionsDisabledSegment; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -343,7 +348,7 @@ ClassInfoGetters // Return value: A float indicating the total progress made on the entire path, from // 0.0 to 1.0. If the path has ended, 0.0 is returned. - float GetTotalProgress(); + float GetTotalProgress() const; ////////////////////////////////////////////////////////////////////////////////////////// @@ -357,7 +362,13 @@ ClassInfoGetters // Return value: A float indicating the total progress made on the regular path, from // 0.0 to 1.0. If the path has ended, 0.0 is returned. - float GetRegularProgress(); + float GetRegularProgress() const; + + /// + /// Gets the current segment as a number, rather than an iterator. + /// + /// The current segment as a number. + int GetCurrentSegmentNumber() const; ////////////////////////////////////////////////////////////////////////////////////////// @@ -573,7 +584,7 @@ ClassInfoGetters // Arguments: None. // Return value: Whether this has been Create:ed yet. - bool IsInitialized() { return !m_Start.IsZero() || !m_Segments.empty(); } + bool IsInitialized() const { return !m_Start.IsZero() || !m_Segments.empty(); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -585,7 +596,7 @@ ClassInfoGetters // Arguments: None. // Return value: None. - bool IsStaticPoint() { return m_Segments.empty(); } + bool IsStaticPoint() const { return m_Segments.empty(); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -622,6 +633,8 @@ ClassInfoGetters // The iterator to the segment of the path that the limb ended up on the end of std::deque::iterator m_CurrentSegment; + int m_FootCollisionsDisabledSegment; //!< The segment after which foot collisions will be disabled for this limbpath, if it's for legs. + // Normalized measure of how far the limb has progressed toward the // current segment's target. 0.0 means its farther away than the // magnitude of the entire segment. 0.5 means it's half the mag of the segment diff --git a/Entities/MOPixel.cpp b/Entities/MOPixel.cpp index fd8accd30..d5abc3897 100644 --- a/Entities/MOPixel.cpp +++ b/Entities/MOPixel.cpp @@ -143,10 +143,10 @@ namespace RTE { // Set the atom to ignore a certain MO, if set and applicable. if (m_HitsMOs && m_pMOToNotHit && g_MovableMan.ValidMO(m_pMOToNotHit) && !m_MOIgnoreTimer.IsPastSimTimeLimit()) { - MOID root = m_pMOToNotHit->GetID(); - int footprint = m_pMOToNotHit->GetMOIDFootprint(); - for (int i = 0; i < footprint; ++i) { - m_Atom->AddMOIDToIgnore(root + i); + std::vector MOIDsNotToHit; + m_pMOToNotHit->GetMOIDs(MOIDsNotToHit); + for (const MOID &MOIDNotToHit : MOIDsNotToHit) { + m_Atom->AddMOIDToIgnore(MOIDNotToHit); } } // Do static particle bounce calculations. diff --git a/Entities/MOSParticle.cpp b/Entities/MOSParticle.cpp index 6f5ddd1c2..4c81a82f1 100644 --- a/Entities/MOSParticle.cpp +++ b/Entities/MOSParticle.cpp @@ -111,10 +111,10 @@ namespace RTE { // Set the atom to ignore a certain MO, if set and applicable. if (m_HitsMOs && m_pMOToNotHit && g_MovableMan.ValidMO(m_pMOToNotHit) && !m_MOIgnoreTimer.IsPastSimTimeLimit()) { - MOID root = m_pMOToNotHit->GetID(); - int footprint = m_pMOToNotHit->GetMOIDFootprint(); - for (int i = 0; i < footprint; ++i) { - m_Atom->AddMOIDToIgnore(root + i); + std::vector MOIDsNotToHit; + m_pMOToNotHit->GetMOIDs(MOIDsNotToHit); + for (const MOID &MOIDNotToHit : MOIDsNotToHit) { + m_Atom->AddMOIDToIgnore(MOIDNotToHit); } } // Do static particle bounce calculations. diff --git a/Entities/MOSRotating.cpp b/Entities/MOSRotating.cpp index 54ea12e61..13e69078b 100644 --- a/Entities/MOSRotating.cpp +++ b/Entities/MOSRotating.cpp @@ -19,6 +19,7 @@ #include "MOSParticle.h" #include "AEmitter.h" #include "Attachable.h" +#include "HDFirearm.h" #include "RTEError.h" @@ -62,10 +63,15 @@ void MOSRotating::Clear() m_RecoilOffset.Reset(); m_Wounds.clear(); m_Attachables.clear(); - m_AllAttachables.clear(); + m_ReferenceHardcodedAttachableUniqueIDs.clear(); + m_HardcodedAttachableUniqueIDsAndSetters.clear(); + m_RadiusAffectingAttachable = nullptr; + m_FarthestAttachableDistanceAndRadius = 0.0F; + m_AttachableAndWoundMass = 0.0F; m_Gibs.clear(); m_GibImpulseLimit = 0; m_GibWoundLimit = 0; + m_GibBlastStrength = 10.0F; m_GibSound.Reset(); m_EffectOnGib = true; m_pFlipBitmap = 0; @@ -73,8 +79,8 @@ void MOSRotating::Clear() m_pTempBitmap = 0; m_pTempBitmapS = 0; m_LoudnessOnGib = 1; - m_DamageMultiplier = 1; - m_DamageMultiplierRedefined = false; + m_DamageMultiplier = 0; + m_NoSetDamageMultiplier = true; m_StringValueMap.clear(); m_NumberValueMap.clear(); m_ObjectValueMap.clear(); @@ -101,16 +107,9 @@ int MOSRotating::Create() else if (m_pDeepGroup) m_pDeepGroup->SetOwner(this); - // Set up the sprite center Vector. - m_SpriteCenter.SetXY(m_aSprite[m_Frame]->w / 2, - m_aSprite[m_Frame]->h / 2); + m_SpriteCenter.SetXY(m_aSprite[m_Frame]->w / 2, m_aSprite[m_Frame]->h / 2); m_SpriteCenter += m_SpriteOffset; -// Now done in MOSprite::Create, based on the sprite - // Calc radius based on the atomgroup -// m_MaxRadius = m_pAtomGroup->CalculateMaxRadius() + 4; -// m_MaxDiameter = m_MaxRadius * 2; - /* Allocated in lazy fashion as needed when drawing flipped if (!m_pFlipBitmap && m_aSprite[0]) m_pFlipBitmap = create_bitmap_ex(8, m_aSprite[0]->w, m_aSprite[0]->h); @@ -149,27 +148,27 @@ int MOSRotating::Create() m_spTempBitmapS512 = create_bitmap_ex(c_MOIDLayerBitDepth, 512, 512); // Choose an appropriate size for this' diameter - if (m_MaxDiameter >= 256) + if (m_SpriteDiameter >= 256) { m_pTempBitmap = m_spTempBitmap512; m_pTempBitmapS = m_spTempBitmapS512; } - else if (m_MaxDiameter >= 128) + else if (m_SpriteDiameter >= 128) { m_pTempBitmap = m_spTempBitmap256; m_pTempBitmapS = m_spTempBitmapS256; } - else if (m_MaxDiameter >= 64) + else if (m_SpriteDiameter >= 64) { m_pTempBitmap = m_spTempBitmap128; m_pTempBitmapS = m_spTempBitmapS128; } - else if (m_MaxDiameter >= 32) + else if (m_SpriteDiameter >= 32) { m_pTempBitmap = m_spTempBitmap64; m_pTempBitmapS = m_spTempBitmapS64; } - else if (m_MaxDiameter >= 16) + else if (m_SpriteDiameter >= 16) { m_pTempBitmap = m_spTempBitmap32; m_pTempBitmapS = m_spTempBitmapS32; @@ -197,7 +196,6 @@ int MOSRotating::Create(ContentFile spriteFile, const unsigned long lifetime) { MOSprite::Create(spriteFile, frameCount, mass, position, velocity, lifetime); - return 0; } @@ -207,26 +205,21 @@ int MOSRotating::Create(ContentFile spriteFile, ////////////////////////////////////////////////////////////////////////////////////////// // Description: Creates a MOSRotating to be identical to another, by deep copy. -int MOSRotating::Create(const MOSRotating &reference) -{ +int MOSRotating::Create(const MOSRotating &reference) { MOSprite::Create(reference); - if (!reference.m_pAtomGroup) + if (!reference.m_pAtomGroup) { return -1; + } - { - // THESE ATOMGROUP COPYING ARE A TIME SINK! - m_pAtomGroup = new AtomGroup(); - m_pAtomGroup->Create(*reference.m_pAtomGroup, true); - if (m_pAtomGroup) - m_pAtomGroup->SetOwner(this); + // THESE ATOMGROUP COPYING ARE A TIME SINK! + m_pAtomGroup = new AtomGroup(); + m_pAtomGroup->Create(*reference.m_pAtomGroup, true); + if (m_pAtomGroup) { m_pAtomGroup->SetOwner(this); } - if (reference.m_pDeepGroup) - { - m_pDeepGroup = dynamic_cast(reference.m_pDeepGroup->Clone()); - if (m_pDeepGroup) - m_pDeepGroup->SetOwner(this); - } + if (reference.m_pDeepGroup) { + m_pDeepGroup = dynamic_cast(reference.m_pDeepGroup->Clone()); + if (m_pDeepGroup) { m_pDeepGroup->SetOwner(this); } } m_DeepCheck = reference.m_DeepCheck; @@ -237,29 +230,19 @@ int MOSRotating::Create(const MOSRotating &reference) m_RecoilForce = reference.m_RecoilForce; m_RecoilOffset = reference.m_RecoilOffset; - // Wound emitter copies - AEmitter *pWound = 0; - for (list::const_iterator itr = reference.m_Wounds.begin(); itr != reference.m_Wounds.end(); ++itr) - { - pWound = dynamic_cast((*itr)->Clone()); - AddWound(pWound, pWound->GetParentOffset()); - pWound = 0; + for (const AEmitter *wound : reference.m_Wounds) { + AddWound(dynamic_cast(wound->Clone()), wound->GetParentOffset(), false); } - // Attachable copies - m_AllAttachables.clear(); - Attachable *pAttachable = 0; - for (list::const_iterator aItr = reference.m_Attachables.begin(); aItr != reference.m_Attachables.end(); ++aItr) - { - pAttachable = dynamic_cast((*aItr)->Clone()); - AddAttachable(pAttachable, pAttachable->GetParentOffset()); - pAttachable = 0; + for (const Attachable *referenceAttachable : reference.m_Attachables) { + if (m_ReferenceHardcodedAttachableUniqueIDs.find(referenceAttachable->GetUniqueID()) == m_ReferenceHardcodedAttachableUniqueIDs.end()) { + AddAttachable(dynamic_cast(referenceAttachable->Clone())); + } } + m_ReferenceHardcodedAttachableUniqueIDs.clear(); - // Gib copies - for (list::const_iterator gItr = reference.m_Gibs.begin(); gItr != reference.m_Gibs.end(); ++gItr) - { - m_Gibs.push_back(*gItr); + for (const Gib &gib : reference.m_Gibs) { + m_Gibs.push_back(gib); } m_StringValueMap = reference.m_StringValueMap; @@ -268,12 +251,13 @@ int MOSRotating::Create(const MOSRotating &reference) m_GibImpulseLimit = reference.m_GibImpulseLimit; m_GibWoundLimit = reference.m_GibWoundLimit; + m_GibBlastStrength = reference.m_GibBlastStrength; m_GibSound = reference.m_GibSound; m_EffectOnGib = reference.m_EffectOnGib; m_LoudnessOnGib = reference.m_LoudnessOnGib; m_DamageMultiplier = reference.m_DamageMultiplier; - m_DamageMultiplierRedefined = reference.m_DamageMultiplierRedefined; + m_NoSetDamageMultiplier = reference.m_NoSetDamageMultiplier; /* Allocated in lazy fashion as needed when drawing flipped if (!m_pFlipBitmap && m_aSprite[0]) @@ -338,7 +322,9 @@ int MOSRotating::ReadProperty(std::string propName, Reader &reader) reader >> m_GibImpulseLimit; else if (propName == "GibWoundLimit" || propName == "WoundLimit") reader >> m_GibWoundLimit; - else if (propName == "GibSound") + else if (propName == "GibBlastStrength") { + reader >> m_GibBlastStrength; + } else if (propName == "GibSound") reader >> m_GibSound; else if (propName == "EffectOnGib") reader >> m_EffectOnGib; @@ -346,7 +332,7 @@ int MOSRotating::ReadProperty(std::string propName, Reader &reader) reader >> m_LoudnessOnGib; else if (propName == "DamageMultiplier") { reader >> m_DamageMultiplier; - m_DamageMultiplierRedefined = true; + m_NoSetDamageMultiplier = false; } else if (propName == "AddCustomValue") { ReadCustomValueProperty(reader); } else @@ -426,59 +412,124 @@ int MOSRotating::Save(Writer &writer) const return 0; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/// -/// Attaches the passed in wound AEmitter and adds it to the list of wounds, changing its parent offset to the passed in Vector. -/// -/// The wound AEmitter to add -/// The vector to set as the wound AEmitter's parent offset -void MOSRotating::AddWound(AEmitter *pWound, const Vector & parentOffsetToSet, bool checkGibWoundLimit) -{ - if (pWound) - { - if (checkGibWoundLimit && !ToDelete() && m_GibWoundLimit && m_Wounds.size() + 1 > m_GibWoundLimit) - { - // Indicate blast in opposite direction of emission - // TODO: don't hardcode here, get some data from the emitter - Vector blast(-5, 0); - blast.RadRotate(pWound->GetEmitAngle()); - GibThis(blast); - return; - } - else - { - pWound->Attach(this, parentOffsetToSet); - m_Wounds.push_back(pWound); - } - } +int MOSRotating::GetGibWoundLimit(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const { + int gibWoundLimit = m_GibWoundLimit; + if (includePositiveDamageAttachables || includeNegativeDamageAttachables || includeNoDamageAttachables) { + for (const Attachable *attachable : m_Attachables) { + bool attachableSatisfiesConditions = (includePositiveDamageAttachables && attachable->GetDamageMultiplier() > 0) || + (includeNegativeDamageAttachables && attachable->GetDamageMultiplier() < 0) || + (includeNoDamageAttachables && attachable->GetDamageMultiplier() == 0); + + if (attachableSatisfiesConditions) { + gibWoundLimit += attachable->GetGibWoundLimit(includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); + } + } + } + return gibWoundLimit; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/// -/// Removes a specified amount of wounds and returns damage caused by this wounds. Head multiplier is not used. -/// -/// Amount of wounds to remove. -/// Amount of damage caused by these wounds. -int MOSRotating::RemoveWounds(int amount) -{ - int deleted = 0; - float damage = 0; +int MOSRotating::GetWoundCount(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const { + int woundCount = m_Wounds.size(); + if (includePositiveDamageAttachables || includeNegativeDamageAttachables || includeNoDamageAttachables) { + for (const Attachable *attachable : m_Attachables) { + bool attachableSatisfiesConditions = (includePositiveDamageAttachables && attachable->GetDamageMultiplier() > 0) || + (includeNegativeDamageAttachables && attachable->GetDamageMultiplier() < 0) || + (includeNoDamageAttachables && attachable->GetDamageMultiplier() == 0); - for (list::iterator itr = m_Wounds.begin(); itr != m_Wounds.end();) - { - damage += (*itr)->GetBurstDamage(); - delete (*itr); - (*itr) = 0; - itr = m_Wounds.erase(itr); - deleted++; + if (attachableSatisfiesConditions) { + woundCount += attachable->GetWoundCount(includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); + } + } + } + return woundCount; +} - if (deleted >= amount) - break; - } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void MOSRotating::AddWound(AEmitter *woundToAdd, const Vector &parentOffsetToSet, bool checkGibWoundLimit) { + if (woundToAdd) { + if (checkGibWoundLimit && !ToDelete() && m_GibWoundLimit && m_Wounds.size() + 1 > m_GibWoundLimit) { + // Indicate blast in opposite direction of emission + // TODO: don't hardcode here, get some data from the emitter + Vector blast(-5, 0); + blast.RadRotate(woundToAdd->GetEmitAngle()); + GibThis(blast); + return; + } else { + woundToAdd->SetCollidesWithTerrainWhileAttached(false); + woundToAdd->SetParentOffset(parentOffsetToSet); + woundToAdd->SetInheritsHFlipped(false); + woundToAdd->SetParent(this); + woundToAdd->SetIsWound(true); + if (woundToAdd->HasNoSetDamageMultiplier()) { woundToAdd->SetDamageMultiplier(1.0F); } + m_AttachableAndWoundMass += woundToAdd->GetMass(); + m_Wounds.push_back(woundToAdd); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +float MOSRotating::RemoveWounds(int numberOfWoundsToRemove, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) { + float damage = 0; + int woundCount = GetWoundCount(includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); + + std::vector> woundedParts; + if (woundCount > 0) { woundedParts.push_back({this, woundCount}); } + + for (Attachable *attachable : m_Attachables) { + bool attachableSatisfiesConditions = (includePositiveDamageAttachables && attachable->GetDamageMultiplier() > 0) || + (includeNegativeDamageAttachables && attachable->GetDamageMultiplier() < 0) || + (includeNoDamageAttachables && attachable->GetDamageMultiplier() == 0); + int attachableWoundCount = attachable->GetWoundCount(includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); + + if (attachableSatisfiesConditions && attachableWoundCount > 0) { woundedParts.push_back({attachable, attachableWoundCount}); } + } + + if (woundedParts.empty()) { + return damage; + } + + /// + /// Internal lambda function to remove the first wound emitter from this MOSRotating. + /// + auto removeFirstWoundEmitter = [this]() { + if (m_Wounds.empty()) { + return 0.0F; + } + float woundDamage = m_Wounds.front()->GetBurstDamage(); + AEmitter *wound = m_Wounds.front(); + m_AttachableAndWoundMass -= wound->GetMass(); + m_Wounds.pop_front(); + delete wound; + return woundDamage; + }; + + for (int i = 0; i < numberOfWoundsToRemove; i++) { + if (woundedParts.empty()) { + break; + } + + int woundedPartIndex = RandomNum(0, static_cast(woundedParts.size()) - 1); + MOSRotating *woundedPart = woundedParts[woundedPartIndex].first; + if (woundedPart == this) { + damage += removeFirstWoundEmitter() * GetDamageMultiplier(); + } else { + //TODO This is less efficient than it should be. We already collected all wounded parts and their wounds above, we should pass that in (make another function overload) instead of collecting everything again. It might be wise to use a tree for this purpose. + damage += woundedPart->RemoveWounds(1, includePositiveDamageAttachables, includeNegativeDamageAttachables, includeNoDamageAttachables); + } + if (woundedParts[woundedPartIndex].second-- <= 0) { woundedParts.erase(woundedParts.begin() + woundedPartIndex); } + } return damage; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// // Method: Destroy ////////////////////////////////////////////////////////////////////////////////////////// @@ -505,23 +556,16 @@ void MOSRotating::Destroy(bool notInherited) Clear(); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this ACDropShip, including the mass of its -// currently attached body parts and inventory. - -float MOSRotating::GetMass() const -{ - float totalMass = MOSprite::GetMass(); - - for (list::const_iterator aItr = m_Attachables.begin(); aItr != m_Attachables.end(); ++aItr) - totalMass += (*aItr)->GetMass(); - - return totalMass; +void MOSRotating::SetAsNoID() { + MovableObject::SetAsNoID(); + for (Attachable *attachable : m_Attachables) { + attachable->SetAsNoID(); + } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: GetMaterial @@ -850,7 +894,8 @@ bool MOSRotating::ParticlePenetration(HitData &hd) // Add entry wound AEmitter to actor where the particle penetrated. AEmitter *pEntryWound = dynamic_cast(m_pEntryWound->Clone()); pEntryWound->SetEmitAngle(dir.GetXFlipped(m_HFlipped).GetAbsRadAngle() + c_PI); - pEntryWound->SetDamageMultiplier(hd.Body[HITOR]->WoundDamageMultiplier()); + float damageMultiplier = pEntryWound->HasNoSetDamageMultiplier() ? 1.0F : pEntryWound->GetDamageMultiplier(); + pEntryWound->SetDamageMultiplier(damageMultiplier * hd.Body[HITOR]->WoundDamageMultiplier()); // Adjust position so that it looks like the hole is actually *on* the Hitee. entryPos[dom] += increment[dom] * (pEntryWound->GetSpriteFrame()->w / 2); AddWound(pEntryWound, entryPos + m_SpriteOffset); @@ -869,7 +914,8 @@ bool MOSRotating::ParticlePenetration(HitData &hd) // Adjust position so that it looks like the hole is actually *on* the Hitee. exitPos[dom] -= increment[dom] * (pExitWound->GetSpriteFrame()->w / 2); pExitWound->SetEmitAngle(dir.GetXFlipped(m_HFlipped).GetAbsRadAngle()); - pExitWound->SetDamageMultiplier(hd.Body[HITOR]->WoundDamageMultiplier()); + float damageMultiplier = pExitWound->HasNoSetDamageMultiplier() ? 1.0F : pExitWound->GetDamageMultiplier(); + pExitWound->SetDamageMultiplier(damageMultiplier * hd.Body[HITOR]->WoundDamageMultiplier()); AddWound(pExitWound, exitPos + m_SpriteOffset); pExitWound = 0; } @@ -904,176 +950,103 @@ bool MOSRotating::ParticlePenetration(HitData &hd) return false; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. - -void MOSRotating::GibThis(Vector impactImpulse, float internalBlast, MovableObject *pIgnoreMO) -{ - // Can't, or is already gibbed, so don't do anything - if (m_MissionCritical || m_ToDelete) +void MOSRotating::GibThis(const Vector &impactImpulse, MovableObject *movableObjectToIgnore) { + if (m_MissionCritical || m_ToDelete) { return; + } - MovableObject *pGib = 0; - float velMin, velRange, spread, angularVel; - Vector gibROffset, gibVel; - for (list::iterator gItr = m_Gibs.begin(); gItr != m_Gibs.end(); ++gItr) - { - // Throwing out gibs - for (int i = 0; i < (*gItr).GetCount(); ++i) - { - // Make a copy after the preset particle - // THIS IS A TIME SINK, takes up the vast bulk of time of GibThis - { - // Create gibs - pGib = dynamic_cast((*gItr).GetParticlePreset()->Clone()); - } - - // Generate the velocities procedurally - if ((*gItr).GetMinVelocity() == 0 && (*gItr).GetMaxVelocity() == 0) - { - velMin = internalBlast / pGib->GetMass(); - velRange = 10.0f; - } - // Use the ones defined already - else - { - velMin = (*gItr).GetMinVelocity(); - velRange = (*gItr).GetMaxVelocity() - (*gItr).GetMinVelocity(); - } - spread = (*gItr).GetSpread(); - gibROffset = RotateOffset((*gItr).GetOffset()); - // Put variation on the lifetime, if it's not set to be endless - if (pGib->GetLifetime() != 0) - pGib->SetLifetime(pGib->GetLifetime() * (1.0F + ((*gItr).GetLifeVariation() * RandomNormalNum()))); - // Set up its position and velocity according to the parameters of this AEmitter. - pGib->SetPos(m_Pos + gibROffset/*Vector(m_Pos.m_X + 5 * NormalRand(), m_Pos.m_Y + 5 * NormalRand())*/); - pGib->SetRotAngle(m_Rotation.GetRadAngle() + pGib->GetRotMatrix().GetRadAngle()); - // Rotational angle - pGib->SetAngularVel((pGib->GetAngularVel() * 0.35F) + (pGib->GetAngularVel() * 0.65F / pGib->GetMass()) * RandomNum()); - // Make it rotate away in the appropriate direction depending on which side of the object it is on - // If the object is far to the relft or right of the center, make it always rotate outwards to some degree - if (gibROffset.m_X > m_aSprite[0]->w / 3) - { - float offCenterRatio = gibROffset.m_X / (m_aSprite[0]->w / 2); - angularVel = fabs(pGib->GetAngularVel() * 0.5); - angularVel += fabs(pGib->GetAngularVel() * 0.5 * offCenterRatio); - pGib->SetAngularVel(angularVel * (gibROffset.m_X > 0 ? -1 : 1)); - } - // Gib is too close to center to always make it rotate in one direction, so give it a baseline rotation and then randomize - else - { - pGib->SetAngularVel((pGib->GetAngularVel() * 0.5F + pGib->GetAngularVel() * RandomNum()) * (RandomNormalNum() > 0.0F ? 1.0F : -1.0F)); - } - -// TODO: Optimize making the random angles!") - { - // Pretty much always zero - gibVel = gibROffset; - if (gibVel.IsZero()) - gibVel.SetXY(velMin + RandomNum(0.0F, velRange), 0); - else - gibVel.SetMagnitude(velMin + RandomNum(0.0F, velRange)); - gibVel.RadRotate(impactImpulse.GetAbsRadAngle() + spread * RandomNormalNum()); -// Don't! the offset was already rotated! -// gibVel = RotateOffset(gibVel); - // Distribute any impact implse out over all the gibs -// gibVel += (impactImpulse / m_Gibs.size()) / pGib->GetMass(); - } + CreateGibsWhenGibbing(impactImpulse, movableObjectToIgnore); - // Only add the velocity of the parent if it's suppposed to - if ((*gItr).InheritsVelocity()) - pGib->SetVel(m_Vel + gibVel); - else - pGib->SetVel(gibVel); + RemoveAttachablesWhenGibbing(impactImpulse, movableObjectToIgnore); - // Set the gib to not hit a specific MO - if (pIgnoreMO) - pGib->SetWhichMOToNotHit(pIgnoreMO); + m_GibSound.Play(m_Pos); - // Add the gib to the scene - g_MovableMan.AddParticle(pGib); - pGib = 0; - } + if (m_pScreenEffect && m_EffectOnGib && (m_EffectAlwaysShows || !g_SceneMan.ObscuredPoint(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY()))) { + g_PostProcessMan.RegisterPostEffect(m_Pos, m_pScreenEffect, m_ScreenEffectHash, 255, m_EffectRotAngle); } - // Throw out all the attachables - Attachable *pAttachable = 0; - for (list::iterator aItr = m_Attachables.begin(); aItr != m_Attachables.end(); ) //NOTE: No increment to handle RemoveAttachable removing the object - { - RTEAssert((*aItr), "Broken Attachable!"); - if (!(*aItr)) - continue; + if (m_LoudnessOnGib > 0) { g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, m_Team, m_LoudnessOnGib)); } - // Get handy handle to the object we're putting - pAttachable = *aItr; + m_ToDelete = true; +} - // TODO: Rework this whole system - // Generate the velocities procedurally - velMin = internalBlast / (1 + pAttachable->GetMass()); - velRange = 10.0f; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Rotational angle velocity - pAttachable->SetAngularVel((pAttachable->GetAngularVel() * 0.35F) + (pAttachable->GetAngularVel() * 0.65F / pAttachable->GetMass()) * RandomNum()); - // Make it rotate away in the appropriate direction depending on which side of the object it is on - // If the object is far to the relft or right of the center, make it always rotate outwards to some degree - if (pAttachable->GetParentOffset().m_X > m_aSprite[0]->w / 3) - { - float offCenterRatio = pAttachable->GetParentOffset().m_X / (m_aSprite[0]->w / 2); - angularVel = fabs(pAttachable->GetAngularVel() * 0.5); - angularVel += fabs(pAttachable->GetAngularVel() * 0.5 * offCenterRatio); - pAttachable->SetAngularVel(angularVel * (pAttachable->GetParentOffset().m_X > 0 ? -1 : 1)); +void MOSRotating::CreateGibsWhenGibbing(const Vector &impactImpulse, MovableObject *movableObjectToIgnore) { + for (const Gib &gibSettingsObject : m_Gibs) { + if (gibSettingsObject.GetCount() == 0) { + continue; } - // Gib is too close to center to always make it rotate in one direction, so give it a baseline rotation and then randomize - else - { - pAttachable->SetAngularVel((pAttachable->GetAngularVel() * 0.5F + pAttachable->GetAngularVel() * RandomNum()) * (RandomNormalNum() > 0.0F ? 1.0F : -1.0F)); + MovableObject *gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); + + float minVelocity = gibSettingsObject.GetMinVelocity(); + float velocityRange = gibSettingsObject.GetMaxVelocity() - gibSettingsObject.GetMinVelocity(); + if (gibSettingsObject.GetMinVelocity() == 0 && gibSettingsObject.GetMaxVelocity() == 0) { + minVelocity = m_GibBlastStrength / gibParticleClone->GetMass(); + velocityRange = 10.0F; } + Vector rotatedGibOffset = RotateOffset(gibSettingsObject.GetOffset()); + for (int i = 0; i < gibSettingsObject.GetCount(); i++) { + gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); -// TODO: Optimize making the random angles!") - gibVel = pAttachable->GetParentOffset(); - if (gibVel.IsZero()) - gibVel.SetXY(velMin + RandomNum(0.0F, velRange), 0); - else - gibVel.SetMagnitude(velMin + RandomNum(0.0F, velRange)); - gibVel.RadRotate(impactImpulse.GetAbsRadAngle()); - pAttachable->SetVel(m_Vel + gibVel); - - // Set the gib to not hit a specific MO - if (pIgnoreMO) - pAttachable->SetWhichMOToNotHit(pIgnoreMO); - - // Safely remove attachable and add it to the scene - ++aItr; - RemoveAttachable(pAttachable); - g_MovableMan.AddParticle(pAttachable); - pAttachable = 0; - } - // Clear the attachables list, all the attachables ownership have been handed to the movableman - m_Attachables.clear(); - m_AllAttachables.clear(); + if (gibParticleClone->GetLifetime() != 0) { + gibParticleClone->SetLifetime(static_cast(static_cast(gibParticleClone->GetLifetime()) * (1.0F + (gibSettingsObject.GetLifeVariation() * RandomNormalNum())))); + } - // Play the gib sound - m_GibSound.Play(m_Pos); + gibParticleClone->SetRotAngle(GetRotAngle() + gibParticleClone->GetRotAngle()); + gibParticleClone->SetAngularVel((gibParticleClone->GetAngularVel() * 0.35F) + (gibParticleClone->GetAngularVel() * 0.65F / gibParticleClone->GetMass()) * RandomNum()); + if (rotatedGibOffset.GetRoundIntX() > m_aSprite[0]->w / 3) { + float offCenterRatio = rotatedGibOffset.m_X / (static_cast(m_aSprite[0]->w) / 2.0F); + float angularVel = fabs(gibParticleClone->GetAngularVel() * 0.5F) + std::fabs(gibParticleClone->GetAngularVel() * 0.5F * offCenterRatio); + gibParticleClone->SetAngularVel(angularVel * (rotatedGibOffset.m_X > 0 ? -1 : 1)); + } else { + gibParticleClone->SetAngularVel((gibParticleClone->GetAngularVel() * 0.5F + (gibParticleClone->GetAngularVel() * RandomNum())) * (RandomNormalNum() > 0.0F ? 1.0F : -1.0F)); + } - // Flash post effect if it is defined - if (m_pScreenEffect && m_EffectOnGib && (m_EffectAlwaysShows || !g_SceneMan.ObscuredPoint(m_Pos.GetFloorIntX(), m_Pos.GetFloorIntY()))) - { - // Set the screen effect to draw at the final post processing stage - g_PostProcessMan.RegisterPostEffect(m_Pos, m_pScreenEffect, m_ScreenEffectHash, 255, m_EffectRotAngle); + gibParticleClone->SetPos(m_Pos + rotatedGibOffset); + Vector gibVelocity = rotatedGibOffset.IsZero() ? Vector(minVelocity + RandomNum(0.0F, velocityRange), 0.0F) : rotatedGibOffset.SetMagnitude(minVelocity + RandomNum(0.0F, velocityRange)); + gibVelocity.RadRotate(impactImpulse.GetAbsRadAngle() + (gibSettingsObject.GetSpread() * RandomNormalNum())); + gibParticleClone->SetVel(gibVelocity + (gibSettingsObject.InheritsVelocity() ? m_Vel : Vector())); + + if (movableObjectToIgnore) { gibParticleClone->SetWhichMOToNotHit(movableObjectToIgnore); } + + g_MovableMan.AddParticle(gibParticleClone); + } } +} - // Things breaking apart makes alarming noises! - if (m_LoudnessOnGib > 0) - g_MovableMan.RegisterAlarmEvent(AlarmEvent(m_Pos, m_Team, m_LoudnessOnGib)); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Mark this for deletion! - m_ToDelete = true; +void MOSRotating::RemoveAttachablesWhenGibbing(const Vector &impactImpulse, MovableObject *movableObjectToIgnore) { + Attachable *attachable; + for (std::list::iterator attachableIterator = m_Attachables.begin(); attachableIterator != m_Attachables.end();) { + RTEAssert((*attachableIterator), "Broken Attachable!"); + attachable = *attachableIterator; + + if (RandomNum() < attachable->GetGibWithParentChance()) { + ++attachableIterator; + attachable->GibThis(); + continue; + } + + if (!attachable->GetDeleteWhenRemovedFromParent()) { + float attachableGibBlastStrength = (attachable->GetParentGibBlastStrengthMultiplier() == 0 ? 1 : attachable->GetParentGibBlastStrengthMultiplier() * m_GibBlastStrength) / (1 + attachable->GetMass()); + attachable->SetAngularVel((attachable->GetAngularVel() * 0.5F) + (attachable->GetAngularVel() * 0.5F * attachableGibBlastStrength * RandomNormalNum())); + Vector gibBlastVel = Vector(attachable->GetParentOffset()).SetMagnitude(attachableGibBlastStrength * 0.5F + (attachableGibBlastStrength * RandomNum())); + attachable->SetVel(m_Vel + gibBlastVel + impactImpulse); + + if (movableObjectToIgnore) { attachable->SetWhichMOToNotHit(movableObjectToIgnore); } + } + + ++attachableIterator; + RemoveAttachable(attachable, true, true); + } + m_Attachables.clear(); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: MoveOutOfTerrain @@ -1190,100 +1163,48 @@ void MOSRotating::RestDetection() if (fabs(m_Rotation.GetRadAngle() - m_PrevRotation.GetRadAngle()) >= 0.01) m_RestTimer.Reset(); - // If we seem to be about to settle, make sure we're not flying in the air still + // If we seem to be about to settle, make sure we're not flying in the air still. + // Note that this uses sprite radius to avoid possibly settling when it shouldn't (e.g. if there's a lopsided attachable enlarging the radius, using GetRadius might make it settle in the air). if (m_ToSettle || IsAtRest()) { - if (g_SceneMan.OverAltitude(m_Pos, m_MaxRadius + 4, 3)) + if (g_SceneMan.OverAltitude(m_Pos, m_SpriteRadius + 4, 3)) { m_RestTimer.Reset(); m_ToSettle = false; } -// TODO: REMOVE -// bool KUK = g_SceneMan.OverAltitude(m_Pos, m_MaxRadius + 4, 3); } m_PrevRotation = m_Rotation; m_PrevAngVel = m_AngularVel; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. - -bool MOSRotating::IsOnScenePoint(Vector &scenePoint) const -{ - if (!m_aSprite[m_Frame]) +bool MOSRotating::IsOnScenePoint(Vector &scenePoint) const { + if (!m_aSprite[m_Frame]) { return false; -// TODO: TAKE CARE OF WRAPPING -/* - // Take care of wrapping situations - bitmapPos = m_Pos + m_BitmapOffset; - Vector aScenePoint[4]; - aScenePoint[0] = scenePoint; - int passes = 1; - - // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap - if (targetPos.IsZero()) - { - if (g_SceneMan.SceneWrapsX()) - { - if (bitmapPos.m_X < m_pFGColor->w) - { - aScenePoint[passes] = aScenePoint[0]; - aScenePoint[passes].m_X += g_SceneMan.GetSceneWidth(); - passes++; - } - else if (aScenePoint[0].m_X > pTargetBitmap->w - m_pFGColor->w) - { - aScenePoint[passes] = aScenePoint[0]; - aScenePoint[passes].m_X -= g_SceneMan.GetSceneWidth(); - passes++; - } - } - if (g_SceneMan.SceneWrapsY()) - { - - } } - // Check all the passes needed - for (int i = 0; i < passes; ++i) - { - if (IsWithinBox(aScenePoint[i], m_Pos + m_BitmapOffset, m_pFGColor->w, m_pFGColor->h)) - { - if (getpixel(m_pFGColor, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaskColor || - (m_pBGColor && getpixel(m_pBGColor, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaskColor) || - (m_pMaterial && getpixel(m_pMaterial, aScenePoint[i].m_X, aScenePoint[i].m_Y) != g_MaterialAir)) - return true; - } - } -*/ - if (WithinBox(scenePoint, m_Pos.m_X - m_MaxRadius, m_Pos.m_Y - m_MaxRadius, m_Pos.m_X + m_MaxRadius, m_Pos.m_Y + m_MaxRadius)) - { - // Get scene point in object's relative space + //TODO this should really use GetRadius() instead of sprite radius, then check attachable's sprites directly here. It'd save some computation but I didn't wanna deal with it. + if (WithinBox(scenePoint, m_Pos.m_X - m_SpriteRadius, m_Pos.m_Y - m_SpriteRadius, m_Pos.m_X + m_SpriteRadius, m_Pos.m_Y + m_SpriteRadius)) { Vector spritePoint = scenePoint - m_Pos; - // Rotate it back to correspond with the unrotated bitmap spritePoint = UnRotateOffset(spritePoint); - // Check for overlap on the local rotated relative point. subtract spriteoffset to get into sprite bitmap's space - int pixel = getpixel(m_aSprite[m_Frame], spritePoint.m_X - m_SpriteOffset.m_X, spritePoint.m_Y - m_SpriteOffset.m_Y); - // Check that it isn't outside the bitmap, and not of the key color - if (pixel != -1 && pixel != g_MaskColor) - return true; + int pixel = getpixel(m_aSprite[m_Frame], static_cast(spritePoint.m_X - m_SpriteOffset.m_X), static_cast(spritePoint.m_Y - m_SpriteOffset.m_Y)); + if (pixel != -1 && pixel != g_MaskColor) { + return true; + } } - // Check the attachables too, backward since the latter ones tend to be larger, and therefore more likeyl to be on the point - for (list::const_reverse_iterator aItr = m_Attachables.rbegin(); aItr != m_Attachables.rend(); ++aItr) - { - if ((*aItr)->IsOnScenePoint(scenePoint)) + for (const Attachable *attachable : m_Attachables) { + if (attachable->IsOnScenePoint(scenePoint)) { return true; + } } return false; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: EraseFromTerrain @@ -1441,12 +1362,12 @@ void MOSRotating::Travel() } // Set the atom to ignore a certain MO, if set and applicable. - if (m_HitsMOs && m_pMOToNotHit && g_MovableMan.ValidMO(m_pMOToNotHit) && !m_MOIgnoreTimer.IsPastSimTimeLimit()) - { - MOID root = m_pMOToNotHit->GetID(); - int footprint = m_pMOToNotHit->GetMOIDFootprint(); - for (int i = 0; i < footprint; ++i) - m_pAtomGroup->AddMOIDToIgnore(root + i); + if (m_HitsMOs && m_pMOToNotHit && g_MovableMan.ValidMO(m_pMOToNotHit) && !m_MOIgnoreTimer.IsPastSimTimeLimit()) { + std::vector MOIDsNotToHit; + m_pMOToNotHit->GetMOIDs(MOIDsNotToHit); + for (const MOID &MOIDNotToHit : MOIDsNotToHit) { + m_pAtomGroup->AddMOIDToIgnore(MOIDNotToHit); + } } ///////////////////////////////// @@ -1494,79 +1415,64 @@ void MOSRotating::PostTravel() ////////////////////////////////////////////////////////////////////////////////////////// // Description: Updates this MOSRotating. Supposed to be done every frame. -void MOSRotating::Update() -{ - +void MOSRotating::Update() { #ifndef RELEASE_BUILD RTEAssert(m_MOID == g_NoMOID || (m_MOID >= 0 && m_MOID < g_MovableMan.GetMOIDCount()), "MOID out of bounds!"); #endif MOSprite::Update(); - if (m_InheritEffectRotAngle) - m_EffectRotAngle = m_Rotation.GetRadAngle(); - - // Align the orienatation with the velocity vector, if any - if (m_OrientToVel > 0 && m_Vel.GetLargest() > 5.0) - { - // Clamp - if (m_OrientToVel > 1.0) - m_OrientToVel = 1.0; + if (m_InheritEffectRotAngle) { m_EffectRotAngle = m_Rotation.GetRadAngle(); } - // Velocity influence - float velInfluence = m_OrientToVel < 1.0 ? m_Vel.GetMagnitude() / 100 : 1.0f; - if (velInfluence > 1.0) - velInfluence = 1.0; + if (m_OrientToVel > 0 && m_Vel.GetLargest() > 5.0F) { + m_OrientToVel = std::clamp(m_OrientToVel, 0.0F, 1.0F); - // Figure the difference in current velocity vector and + float velInfluence = std::clamp(m_OrientToVel < 1.0F ? m_Vel.GetMagnitude() / 100.0F : 1.0F, 0.0F, 1.0F); float radsToGo = m_Rotation.GetRadAngleTo(m_Vel.GetAbsRadAngle()); - m_Rotation += (radsToGo * m_OrientToVel * velInfluence); + m_Rotation += radsToGo * m_OrientToVel * velInfluence; } - // Update all the attached wound emitters - for (list::iterator itr = m_Wounds.begin(); - itr != m_Wounds.end(); ++itr) - { - if ((*itr)) - { - (*itr)->SetJointPos(m_Pos + RotateOffset((*itr)->GetParentOffset())); - if ((*itr)->InheritsRotAngle()) - (*itr)->SetRotAngle(m_Rotation.GetRadAngle()); -// (*itr)->SetEmitAngle(m_Rotation); - (*itr)->Update(); + AEmitter *wound = nullptr; + for (std::list::iterator woundIterator = m_Wounds.begin(); woundIterator != m_Wounds.end(); ) { + wound = *woundIterator; + RTEAssert(wound && wound->IsAttachedTo(this), "Broken wound AEmitter"); + ++woundIterator; + wound->Update(); + + if (wound->IsSetToDelete() || (wound->GetLifetime() > 0 && wound->GetAge() > wound->GetLifetime())) { + m_Wounds.remove(wound); + m_AttachableAndWoundMass -= wound->GetMass(); + delete wound; + } else { + Vector totalImpulseForce; + for (const std::pair &impulseForce : wound->GetImpulses()) { + totalImpulseForce += impulseForce.first; + } + totalImpulseForce *= wound->GetJointStiffness(); + + if (!totalImpulseForce.IsZero()) { AddImpulseForce(totalImpulseForce, wound->GetApplyTransferredForcesAtOffset() ? wound->GetParentOffset() * m_Rotation * c_MPP : Vector()); } + + wound->ClearImpulseForces(); } - else - RTEAbort("Broken emitter!!"); } - // Update all the attachables - Attachable *pAttachable = 0; - for (list::iterator aItr = m_Attachables.begin(); aItr != m_Attachables.end(); ) // NOTE NO INCCREMENT! - { - RTEAssert((*aItr), "Broken Attachable!"); - if (!(*aItr)) - continue; + Attachable *attachable = nullptr; + for (std::list::iterator attachableIterator = m_Attachables.begin(); attachableIterator != m_Attachables.end(); ) { + attachable = *attachableIterator; + RTEAssert(attachable, "Broken Attachable!"); + ++attachableIterator; - pAttachable = *aItr; - ++aItr; + attachable->Update(); - pAttachable->SetHFlipped(m_HFlipped); - pAttachable->SetJointPos(m_Pos + RotateOffset((pAttachable)->GetParentOffset())); - if (pAttachable->InheritsRotAngle()) - { - pAttachable->SetRotAngle(m_Rotation.GetRadAngle()); + if (attachable->IsAttachedTo(this) && attachable->IsSetToDelete()) { + RemoveAttachable(attachable, true, true); + } else if (!attachable->IsSetToDelete()) { + TransferForcesFromAttachable(attachable); } - pAttachable->Update(); - - ApplyAttachableForces(pAttachable); } - // Create intermediate flipping bitmap if there isn't one yet - if (m_HFlipped && !m_pFlipBitmap && m_aSprite[0]) - m_pFlipBitmap = create_bitmap_ex(8, m_aSprite[0]->w, m_aSprite[0]->h); - - if (m_HFlipped && !m_pFlipBitmapS && m_aSprite[0]) - m_pFlipBitmapS = create_bitmap_ex(c_MOIDLayerBitDepth, m_aSprite[0]->w, m_aSprite[0]->h); + if (m_HFlipped && !m_pFlipBitmap && m_aSprite[0]) { m_pFlipBitmap = create_bitmap_ex(8, m_aSprite[0]->w, m_aSprite[0]->h); } + if (m_HFlipped && !m_pFlipBitmapS && m_aSprite[0]) { m_pFlipBitmapS = create_bitmap_ex(c_MOIDLayerBitDepth, m_aSprite[0]->w, m_aSprite[0]->h); } } @@ -1580,7 +1486,7 @@ bool MOSRotating::DrawMOIDIfOverlapping(MovableObject *pOverlapMO) { if (pOverlapMO != this && m_GetsHitByMOs) { - float combinedRadii = m_MaxRadius + pOverlapMO->GetRadius(); + float combinedRadii = GetRadius() + pOverlapMO->GetRadius(); Vector otherPos = pOverlapMO->GetPos(); // Quick check @@ -1588,8 +1494,7 @@ bool MOSRotating::DrawMOIDIfOverlapping(MovableObject *pOverlapMO) return false; // Check if the offset is within the combined radii of the two object, and therefore might be overlapping - Vector offset = otherPos - m_Pos; - if (offset.GetMagnitude() < combinedRadii) + if (g_SceneMan.ShortestDistance(m_Pos, otherPos, g_SceneMan.SceneWrapsX()).GetMagnitude() < combinedRadii) { // They may be overlapping, so draw the MOID rep of this to the MOID layer Draw(g_SceneMan.GetMOIDBitmap(), Vector(), g_DrawMOID, true); @@ -1600,167 +1505,137 @@ bool MOSRotating::DrawMOIDIfOverlapping(MovableObject *pOverlapMO) return false; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. +//TODO This should just be defined in MOSR instead of having an empty definition in MO. MOSR would need to override UpdateMOID accordingly, but this would clean things up a little. +void MOSRotating::UpdateChildMOIDs(vector &MOIDIndex, MOID rootMOID, bool makeNewMOID) { + MOSprite::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); -void MOSRotating::UpdateChildMOIDs(vector &MOIDIndex, - MOID rootMOID, - bool makeNewMOID) -{ - // Register all the eligible attachables - for (list::iterator aItr = m_Attachables.begin(); aItr != m_Attachables.end(); ++aItr) - { -// TODO: Which should it be, don't register at all, or register as same as parent?? - if ((*aItr)->GetsHitByMOs()) - (*aItr)->UpdateMOID(MOIDIndex, m_RootMOID, (*aItr)->GetsHitByMOs()); + for (Attachable *attachable : m_Attachables) { + // Anything that doesn't get hit by MOs doesn't need an ID, since that's only actually used for collision stuff. + if (attachable->GetsHitByMOs()) { attachable->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); } } - - MOSprite::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/// -/// Attaches the passed in Attachable and adds it to the list of attachables, not changing its parent offset and not treating it as hardcoded. -/// -/// The Attachable to attach. -void MOSRotating::AddAttachable(Attachable *pAttachable) -{ - if (pAttachable) - { - AddAttachable(pAttachable, pAttachable->GetParentOffset()); - } +void MOSRotating::AddAttachable(Attachable *attachable) { + if (attachable) { AddAttachable(attachable, attachable->GetParentOffset()); } } - -/// -/// Attaches the passed in Attachable and adds it to the list of attachables, changing its parent offset to the passed in Vector but not treating it as hardcoded. -/// -/// The Attachable to add. -/// The vector to set as the Attachable's parent offset. -void MOSRotating::AddAttachable(Attachable *pAttachable, const Vector& parentOffsetToSet) -{ - AddAttachable(pAttachable, parentOffsetToSet, false); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void MOSRotating::AddAttachable(Attachable *attachable, const Vector& parentOffsetToSet) { + if (attachable) { + RTEAssert(!attachable->IsAttached(), "Tried to add Attachable " + attachable->GetModuleAndPresetName() + " but it already has a parent, " + (attachable->GetParent() ? attachable->GetParent()->GetModuleAndPresetName() : "ERROR") + "."); + if (g_MovableMan.ValidMO(attachable)) { g_MovableMan.RemoveMO(attachable); } + attachable->SetParentOffset(parentOffsetToSet); + attachable->SetParent(this); + m_AttachableAndWoundMass += attachable->GetMass(); + HandlePotentialRadiusAffectingAttachable(attachable); + m_Attachables.push_back(attachable); + } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/// -/// Attaches the passed in Attachable and adds it to the list of attachables, not changing its parent offset but treating it as hardcoded depending on the passed in boolean. -/// -/// The Attachable to add. -/// Whether or not the Attachable should be treated as hardcoded. -void MOSRotating::AddAttachable(Attachable *pAttachable, bool isHardcodedAttachable) -{ - if (pAttachable) - { - AddAttachable(pAttachable, pAttachable->GetParentOffset(), isHardcodedAttachable); - } +bool MOSRotating::RemoveAttachable(long attachableUniqueID, bool addToMovableMan, bool addBreakWounds) { + MovableObject *attachableAsMovableObject = g_MovableMan.FindObjectByUniqueID(attachableUniqueID); + if (attachableAsMovableObject) { + return RemoveAttachable(dynamic_cast(attachableAsMovableObject), addToMovableMan, addBreakWounds); + } + return false; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/// -/// Attaches the passed in Attachable and adds it to the list of attachables, changing its parent offset to the passed in Vector and treating it as hardcoded depending on the passed in boolean. -/// -/// The Attachable to add. -/// The vector to set as the Attachable's parent offset. -/// Whether or not the Attachable should be treated as hardcoded. -void MOSRotating::AddAttachable(Attachable *pAttachable, const Vector & parentOffsetToSet, bool isHardcodedAttachable) -{ - if (pAttachable) - { - pAttachable->Attach(this, parentOffsetToSet); - // Set the attachable's subgroup ID to it's Unique ID to avoid any possible conflicts when adding atoms to parent group. - pAttachable->SetAtomSubgroupID(pAttachable->GetUniqueID()); +bool MOSRotating::RemoveAttachable(Attachable *attachable, bool addToMovableMan, bool addBreakWounds) { + if (!attachable || !attachable->IsAttached()) { + return false; + } + RTEAssert(attachable->IsAttachedTo(this), "Tried to remove Attachable " + attachable->GetModuleAndPresetName() + " from presumed parent " + GetModuleAndPresetName() + ", but it had a different parent (" + (attachable->GetParent() ? attachable->GetParent()->GetModuleAndPresetName() : "ERROR") + "). This should never happen!"); - if (pAttachable->CanCollideWithTerrainWhenAttached()) - { - pAttachable->EnableTerrainCollisions(true); - } + if (!m_Attachables.empty()) { m_Attachables.remove(attachable); } + attachable->SetParent(nullptr); + m_AttachableAndWoundMass -= attachable->GetMass(); - if (!isHardcodedAttachable) - { - m_Attachables.push_back(pAttachable); - } - m_AllAttachables.push_back(pAttachable); - } -} + std::unordered_map>::iterator hardcodedAttachableMapEntry = m_HardcodedAttachableUniqueIDsAndSetters.find(attachable->GetUniqueID()); + if (hardcodedAttachableMapEntry != m_HardcodedAttachableUniqueIDsAndSetters.end()) { + hardcodedAttachableMapEntry->second(this, nullptr); + m_HardcodedAttachableUniqueIDsAndSetters.erase(hardcodedAttachableMapEntry); + } + + if (addBreakWounds) { + if (!m_ToDelete && attachable->GetParentBreakWound()) { + AEmitter *parentBreakWound = dynamic_cast(attachable->GetParentBreakWound()->Clone()); + if (parentBreakWound) { + parentBreakWound->SetEmitAngle((attachable->GetParentOffset() * m_Rotation).GetAbsRadAngle()); + AddWound(parentBreakWound, attachable->GetParentOffset(), false); + parentBreakWound = nullptr; + } + } + if (!attachable->IsSetToDelete() && attachable->GetBreakWound()) { + AEmitter *childBreakWound = dynamic_cast(attachable->GetBreakWound()->Clone()); + if (childBreakWound) { + childBreakWound->SetEmitAngle(attachable->GetJointOffset().GetAbsRadAngle()); + attachable->AddWound(childBreakWound, attachable->GetJointOffset()); + childBreakWound = nullptr; + } + } + } + if (attachable->GetDeleteWhenRemovedFromParent()) { attachable->SetToDelete(); } + if (addToMovableMan || attachable->IsSetToDelete()) { g_MovableMan.AddMO(attachable); } + if (attachable == m_RadiusAffectingAttachable) { + m_RadiusAffectingAttachable = nullptr; + m_FarthestAttachableDistanceAndRadius = 0; + std::for_each(m_Attachables.begin(), m_Attachables.end(), [this](const Attachable *attachableToCheck) { HandlePotentialRadiusAffectingAttachable(attachableToCheck); }); -/// -/// Detaches the Attachable corresponding to the passed in UniqueId, and removes it from the appropriate attachable lists -/// -/// The UniqueId of the the attachable to remove -/// False if the attachable is invalid, otherwise true -bool MOSRotating::RemoveAttachable(long attachableUniqueId) -{ - MovableObject *attachableAsMovableObject = g_MovableMan.FindObjectByUniqueID(attachableUniqueId); - if (attachableAsMovableObject) - { - return RemoveAttachable((Attachable *)attachableAsMovableObject); - } - return false; -} - + Attachable *thisAsAttachable = dynamic_cast(this); + if (thisAsAttachable && m_Attachables.empty() && thisAsAttachable->IsAttached()) { + thisAsAttachable->m_Parent->HandlePotentialRadiusAffectingAttachable(thisAsAttachable); + } + } -/// -/// Detaches the passed in Attachable and removes it from the appropriate attachable lists -/// -/// The attachable to remove -/// False if the attachable is invalid, otherwise true -bool MOSRotating::RemoveAttachable(Attachable *pAttachable) { - if (pAttachable) { - if (m_Attachables.size() > 0) { - m_Attachables.remove(pAttachable); - } - if (m_AllAttachables.size() > 0) { - m_AllAttachables.remove(pAttachable); - } - pAttachable->ToDeleteWithParent() ? pAttachable->SetToDelete() : pAttachable->Detach(); - return true; - } - return false; + return true; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/// -/// Either detaches or deletes all of this MOSRotating's attachables -/// -/// Whether to detach or delete the attachables. Setting this to true deletes them, setting it to false detaches them -void MOSRotating::DetachOrDestroyAll(bool destroy) -{ - for (list::const_iterator aItr = m_AllAttachables.begin(); aItr != m_AllAttachables.end(); ++aItr) - { - if (destroy) - delete (*aItr); - else - (*aItr)->Detach(); - } +void MOSRotating::RemoveOrDestroyAllAttachables(bool destroy) { + Attachable *attachable; + for (std::list::iterator attachableIterator = m_Attachables.begin(); attachableIterator != m_Attachables.end(); ) { + attachable = *attachableIterator; + RTEAssert(attachable, "Broken Attachable!"); + ++attachableIterator; - m_AllAttachables.clear(); + if (destroy) { + delete attachable; + } else { + RemoveAttachable(attachable); + } + } + m_Attachables.clear(); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. +void MOSRotating::GetMOIDs(std::vector &MOIDs) const { + MOSprite::GetMOIDs(MOIDs); + for (const Attachable *attachable : m_Attachables) { + if (attachable->GetsHitByMOs()) { attachable->GetMOIDs(MOIDs); } + } +} -void MOSRotating::GetMOIDs(std::vector &MOIDs) const -{ - // Get MOIDs all the eligible attachables - for (list::const_iterator aItr = m_Attachables.begin(); aItr != m_Attachables.end(); ++aItr) - (*aItr)->GetMOIDs(MOIDs); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Get self MOID - MOSprite::GetMOIDs(MOIDs); +void MOSRotating::SetWhichMOToNotHit(MovableObject *moToNotHit, float forHowLong) { + MOSprite::SetWhichMOToNotHit(moToNotHit, forHowLong); + for (Attachable *attachable : m_Attachables) { attachable->SetWhichMOToNotHit(moToNotHit, forHowLong); } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Draw @@ -1837,13 +1712,13 @@ void MOSRotating::Draw(BITMAP *pTargetBitmap, // See if need to double draw this across the scene seam if we're being drawn onto a scenewide bitmap if (targetPos.IsZero() && m_WrapDoubleDraw) { - if (spritePos.m_X < m_MaxDiameter) + if (spritePos.m_X < m_SpriteDiameter) { aDrawPos[passes] = spritePos; aDrawPos[passes].m_X += pTargetBitmap->w; passes++; } - else if (spritePos.m_X > pTargetBitmap->w - m_MaxDiameter) + else if (spritePos.m_X > pTargetBitmap->w - m_SpriteDiameter) { aDrawPos[passes] = spritePos; aDrawPos[passes].m_X -= pTargetBitmap->w; @@ -1868,24 +1743,18 @@ void MOSRotating::Draw(BITMAP *pTargetBitmap, } } - // Draw all the attached wound emitters, and only if the mode is g_DrawColor and not onlyphysical // Only draw attachables and emitters which are not drawn after parent, so we draw them before - if (mode == g_DrawColor || (!onlyPhysical && mode == g_DrawMaterial)) - { - for (list::const_iterator itr = m_Wounds.begin(); itr != m_Wounds.end(); ++itr) - { - if (!(*itr)->IsDrawnAfterParent()) - (*itr)->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - } + if (mode == g_DrawColor || (!onlyPhysical && mode == g_DrawMaterial)) { + for (const AEmitter *woundToDraw : m_Wounds) { + if (!woundToDraw->IsDrawnAfterParent()) { woundToDraw->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } + } } // Draw all the attached attachables - for (list::const_iterator aItr = m_Attachables.begin(); aItr != m_Attachables.end(); ++aItr) - { - if (!(*aItr)->IsDrawnAfterParent()) - (*aItr)->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - } + for (const Attachable *attachableToDraw : m_Attachables) { + if (!attachableToDraw->IsDrawnAfterParent() && attachableToDraw->IsDrawnNormallyByParent()) { attachableToDraw->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } + } ////////////////// @@ -1940,7 +1809,7 @@ void MOSRotating::Draw(BITMAP *pTargetBitmap, // Register potential MOID drawing if (mode == g_DrawMOID) - g_SceneMan.RegisterMOIDDrawing(aDrawPos[i].GetFloored(), m_MaxRadius + 2); + g_SceneMan.RegisterMOIDDrawing(aDrawPos[i].GetFloored(), m_SpriteRadius + 2); } } } @@ -1988,97 +1857,72 @@ void MOSRotating::Draw(BITMAP *pTargetBitmap, // Register potential MOID drawing if (mode == g_DrawMOID) - g_SceneMan.RegisterMOIDDrawing(aDrawPos[i].GetFloored(), m_MaxRadius + 2); + g_SceneMan.RegisterMOIDDrawing(aDrawPos[i].GetFloored(), m_SpriteRadius + 2); } } } // Draw all the attached wound emitters, and only if the mode is g_DrawColor and not onlyphysical - if (mode == g_DrawColor || (!onlyPhysical && mode == g_DrawMaterial)) - { - for (list::const_iterator itr = m_Wounds.begin(); itr != m_Wounds.end(); ++itr) - { - if ((*itr)->IsDrawnAfterParent()) - (*itr)->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - } + // Only draw attachables and emitters which are not drawn after parent, so we draw them before + if (mode == g_DrawColor || (!onlyPhysical && mode == g_DrawMaterial)) { + for (const AEmitter *woundToDraw : m_Wounds) { + if (woundToDraw->IsDrawnAfterParent()) { woundToDraw->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } + } } // Draw all the attached attachables - for (list::const_iterator aItr = m_Attachables.begin(); aItr != m_Attachables.end(); ++aItr) - { - if ((*aItr)->IsDrawnAfterParent()) - (*aItr)->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - } -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyAttachableForces -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does the joint force transfer stuff for an attachable. Convencinece -// method. If this returns false, it means the attachable has been knocked -// off and has been passed to MovableMan. In either case, if false is -// returned just set the pointer to 0 and be done with it. - -bool MOSRotating::ApplyAttachableForces(Attachable *pAttachable, bool isCritical) -{ - bool intact = true; - Vector forces, impulses; + for (const Attachable *attachableToDraw : m_Attachables) { + if (attachableToDraw->IsDrawnAfterParent() && attachableToDraw->IsDrawnNormallyByParent()) { attachableToDraw->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } + } - if (pAttachable) - { - // If the attahcable is set to be deleted (probably by it having been gibbed), do so - // Note that is done regardless of whether it's attached anymore or not - if (pAttachable->IsSetToDelete()) - { - intact = false; - RemoveAttachable(pAttachable); - // If set to delete, then add to the movableman, and it'll delete it when it's safe to! - g_MovableMan.AddParticle(pAttachable); - } - // If still attached, see what forces are being transferred to this, and if they attachable can withstand them. - // If they rip the thing off, it will add itself to the Movableman, transferring ownership. - else if (pAttachable->IsAttached()) - { - pAttachable->PostTravel(); - intact = pAttachable->TransferJointForces(forces); - intact = intact && pAttachable->TransferJointImpulses(impulses); - - if (!forces.IsZero()) - AddForce(forces, pAttachable->GetOnlyLinearForces() ? Vector() : pAttachable->GetParentOffset() * m_Rotation); - if (!impulses.IsZero()) - AddImpulseForce(impulses, pAttachable->GetOnlyLinearForces() ? Vector() : pAttachable->GetParentOffset() * m_Rotation); - } - // If not attached, then we're not intact - else - intact = false; +#ifdef DEBUG_BUILD + if (mode == g_DrawColor && !onlyPhysical && m_pAtomGroup && GetRootParent() == this) { + m_pAtomGroup->Draw(pTargetBitmap, targetPos, false, 122); + //m_pDeepGroup->Draw(pTargetBitmap, targetPos, false, 13); + } +#endif +} - // If the attachable was ripped off or gibbed, handle it - if (!intact) - { - // Get and attach the wound to this if the attachable has one and it was ripped off or gibbed - if (pAttachable->GetBreakWound()) - { - AEmitter *pWound = dynamic_cast(pAttachable->GetBreakWound()->Clone()); - if (pWound) - { - pWound->SetEmitAngle((pAttachable->GetParentOffset() * m_Rotation).GetAbsRadAngle()); - if (isCritical && !IsInGroup("Brains")) - { - pWound->SetEmitCountLimit(1000000); - pWound->SetEmitDamage(0.5); - pWound->SetBurstDamage(35); - } - // IMPORTANT to pass false here so the added wound doesn't potentially gib this and cause the Attachables list to get f'd up while we're iterating through it in MOSRotating::Update! - AddWound(pWound, pAttachable->GetParentOffset(), false); - pWound = 0; - } - } +bool MOSRotating::HandlePotentialRadiusAffectingAttachable(const Attachable *attachable) { + if (!attachable->IsAttachedTo(this) && !attachable->IsWound()) { + return false; + } + const HDFirearm *thisAsFirearm = dynamic_cast(this); + const AEmitter *thisAsEmitter = dynamic_cast(this); + if ((thisAsFirearm && attachable == thisAsFirearm->GetFlash()) || (thisAsEmitter && attachable == thisAsEmitter->GetFlash())) { + return false; + } + float distanceAndRadiusFromParent = g_SceneMan.ShortestDistance(m_Pos, attachable->m_Pos, g_SceneMan.SceneWrapsX()).GetMagnitude() + attachable->GetRadius(); + if (attachable == m_RadiusAffectingAttachable && distanceAndRadiusFromParent < m_FarthestAttachableDistanceAndRadius) { + m_FarthestAttachableDistanceAndRadius = distanceAndRadiusFromParent; + if (m_Attachables.size() > 1) { + std::for_each(m_Attachables.begin(), m_Attachables.end(), [this](const Attachable *attachableToCheck) { HandlePotentialRadiusAffectingAttachable(attachableToCheck); }); } + return true; + } else if (distanceAndRadiusFromParent > m_FarthestAttachableDistanceAndRadius) { + m_FarthestAttachableDistanceAndRadius = distanceAndRadiusFromParent; + m_RadiusAffectingAttachable = attachable; + return true; } - else - intact = false; + return false; +} +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool MOSRotating::TransferForcesFromAttachable(Attachable *attachable) { + bool intact = false; + if (attachable) { + RTEAssert(attachable->IsAttached(), "Tried to transfer forces from Attachable (" + attachable->GetModuleAndPresetName() + ") with no parent, this should never happen!"); + RTEAssert(attachable->IsAttachedTo(this), "Tried to transfer forces from another parent's (" + attachable->GetParent()->GetModuleAndPresetName() + ") Attachable (" + attachable->GetModuleAndPresetName() + "), this should never happen!"); + + attachable->PostTravel(); + Vector forces; + Vector impulses; + intact = attachable->TransferJointForces(forces) && attachable->TransferJointImpulses(impulses); + + if (!forces.IsZero()) { AddForce(forces, attachable->GetApplyTransferredForcesAtOffset() ? attachable->GetParentOffset() * m_Rotation * c_MPP : Vector()); } + if (!impulses.IsZero()) { AddImpulseForce(impulses, attachable->GetApplyTransferredForcesAtOffset() ? attachable->GetParentOffset() * m_Rotation * c_MPP : Vector()); } + } return intact; } @@ -2221,4 +2065,4 @@ bool MOSRotating::ObjectValueExists(std::string key) return false; } -} // namespace RTE \ No newline at end of file +} // namespace RTE diff --git a/Entities/MOSRotating.h b/Entities/MOSRotating.h index 5fcfc3ddf..3186bdae5 100644 --- a/Entities/MOSRotating.h +++ b/Entities/MOSRotating.h @@ -136,16 +136,73 @@ ClassInfoGetters void Destroy(bool notInherited = false) override; + /// + /// Gets the radius of this MOSRotating, not including any Attachables. + /// + /// + float GetIndividualRadius() const { return m_SpriteRadius; } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this ACDropShip, including the mass of its -// currently attached body parts and inventory. -// Arguments: None. -// Return value: A float describing the mass value in Kilograms (kg). + /// + /// Gets the radius of this MOSRotating, including any Attachables. + /// + /// The radius of this MOSRotating, including any Attachables. + float GetRadius() const override { return std::max(m_SpriteRadius, m_FarthestAttachableDistanceAndRadius); } - float GetMass() const override; + /// + /// Gets the diameter of this MOSRotating, not including any Attachables. + /// + /// + float GetIndividualDiameter() const { return m_SpriteDiameter; } + + /// + /// Gets the diameter of this MOSRotating, including any Attachables. + /// + /// The diameter of this MOSRotating, including any Attachables. + float GetDiameter() const override { return GetRadius() * 2.0F; } + + /// + /// Checks if the given Attachable should affect radius, and handles it if it should. + /// + /// The Attachable to check. + /// Whether the radius affecting Attachable changed as a result of this call. + virtual bool HandlePotentialRadiusAffectingAttachable(const Attachable *attachable); + + /// + /// Gets the mass value of this MOSRotating, not including any Attachables or wounds. + /// + /// The mass of this MOSRotating. + float GetIndividualMass() const { return MovableObject::GetMass(); } + + /// + /// Gets the mass value of this MOSRotating, including the mass of all its Attachables and wounds, and their Attachables and so on. + /// + /// The mass of this MOSRotating and all of its Attachables and wounds in Kilograms (kg). + float GetMass() const override { return MovableObject::GetMass() + m_AttachableAndWoundMass; } + + /// + /// Updates the total mass of Attachables and wounds for this MOSRotating, intended to be used when Attachables' masses get modified. Simply subtracts the old mass and adds the new one. + /// + /// The mass the Attachable or wound had before its mass was modified. + /// The up-to-date mass of the Attachable or wound after its mass was modified. + virtual void UpdateAttachableAndWoundMass(float oldAttachableOrWoundMass, float newAttachableOrWoundMass) { m_AttachableAndWoundMass += newAttachableOrWoundMass - oldAttachableOrWoundMass; } + + /// + /// Gets the MOIDs of this MOSRotating and all its Attachables and Wounds, putting them into the MOIDs vector. + /// + /// The vector that will store all the MOIDs of this MOSRotating. + void GetMOIDs(std::vector &MOIDs) const override; + + /// + /// Sets the MOID of this MOSRotating and any Attachables on it to be g_NoMOID (255) for this frame. + /// + void SetAsNoID() override; + + /// + /// Sets this MOSRotating to not hit a specific other MO and all its children even though MO hitting is enabled on this MOSRotating. + /// + /// A pointer to the MO to not be hitting. Null pointer means don't ignore anything. Ownership is NOT transferred! + /// How long, in seconds, to ignore the specified MO. A negative number means forever. + void SetWhichMOToNotHit(MovableObject *moToNotHit = nullptr, float forHowLong = -1) override; ////////////////////////////////////////////////////////////////////////////////////////// @@ -324,17 +381,12 @@ ClassInfoGetters virtual bool ParticlePenetration(HitData &hd); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. -// Arguments: The impulse (kg * m/s) of the impact causing the gibbing to happen. -// The internal blast impulse which will push the gibs away from the center. -// A pointer to an MO which the gibs shuold not be colliding with! -// Return value: None. - - virtual void GibThis(Vector impactImpulse = Vector(), float internalBlast = 10, MovableObject *pIgnoreMO = 0); + /// + /// Destroys this MOSRotating and creates its specified Gibs in its place with appropriate velocities. Any Attachables are removed and also given appropriate velocities. + /// + /// The impulse (kg * m/s) of the impact causing the gibbing to happen. + /// A pointer to an MO which the Gibs and Attachables should not be colliding with. + virtual void GibThis(const Vector &impactImpulse = Vector(), MovableObject *movableObjectToIgnore = nullptr); ////////////////////////////////////////////////////////////////////////////////////////// @@ -374,20 +426,59 @@ ClassInfoGetters void ApplyImpulses() override; + /// + /// Adds the passed in Attachable the list of Attachables and sets its parent to this MOSRotating. + /// + /// The Attachable to add. + virtual void AddAttachable(Attachable *attachable); - void AddAttachable(Attachable *pAttachable); - - void AddAttachable(Attachable *pAttachable, const Vector& parentOffsetToSet); + /// + /// Adds the passed in Attachable the list of Attachables, changes its parent offset to the passed in Vector, and sets its parent to this MOSRotating. + /// + /// The Attachable to add. + /// The Vector to set as the Attachable's parent offset. + virtual void AddAttachable(Attachable *attachable, const Vector &parentOffsetToSet); - void AddAttachable(Attachable *pAttachable, bool isHardcodedAttachable); + //TODO All RemoveAttachable methods should return the removed attachable (if it's not deleted) so there's no potential memory leaks or other safety problems. Very little cares about whether this actually succeeded or failed anyway, so returning a boolean here is kind of pointless. This should probably be done as part of Arm cleanup. Also, worth noting, dinosaurs are/were neat. + /// + /// Removes the Attachable corresponding to the passed in UniqueID and sets its parent to nullptr. Does not add it to MovableMan or add break wounds. + /// + /// The UniqueID of the Attachable to remove. + /// False if the Attachable is invalid, otherwise true. + virtual bool RemoveAttachable(long attachableUniqueID) { return RemoveAttachable(attachableUniqueID, false, false); } - void AddAttachable(Attachable *pAttachable, const Vector& parentOffsetToSet, bool isHardcodedAttachable); + /// + /// Removes the Attachable corresponding to the passed in UniqueID and sets its parent to nullptr. Optionally adds it to MovableMan and/or adds break wounds. + /// If the Attachable is not set to delete or delete when removed from its parent, and addToMovableMan is false, the caller must hang onto a pointer to the Attachable ahead of time to avoid memory leaks. + /// + /// The UniqueID of the Attachable to remove. + /// Whether or not to add the Attachable to MovableMan once it has been removed. + /// Whether or not to add break wounds to the removed Attachable and this MOSRotating. + /// False if the Attachable is invalid, otherwise true. + virtual bool RemoveAttachable(long attachableUniqueID, bool addToMovableMan, bool addBreakWounds); - bool RemoveAttachable(long attachableUniqueId); + /// + /// Removes the passed in Attachable and sets its parent to nullptr. Does not add it to MovableMan or add break wounds. + /// + /// The Attachable to remove. + /// False if the Attachable is invalid, otherwise true. + virtual bool RemoveAttachable(Attachable *attachable) { return RemoveAttachable(attachable, false, false); } - bool RemoveAttachable(Attachable *pAttachable); + /// + /// Removes the passed in Attachable and sets its parent to nullptr. Optionally adds it to MovableMan and/or adds break wounds. + /// If the Attachable is not set to delete or delete when removed from its parent, and addToMovableMan is false, the caller must hang onto a pointer to the Attachable ahead of time to avoid memory leaks. + /// + /// The Attachable to remove. + /// Whether or not to add the Attachable to MovableMan once it has been removed. + /// Whether or not to add break wounds to the removed Attachable and this MOSRotating. + /// False if the Attachable is invalid, otherwise true. + virtual bool RemoveAttachable(Attachable *attachable, bool addToMovableMan, bool addBreakWounds); - void DetachOrDestroyAll(bool destroy); + /// + /// Either removes or deletes all of this MOSRotating's Attachables. + /// + /// Whether to remove or delete the Attachables. Setting this to true deletes them, setting it to false removes them. + void RemoveOrDestroyAllAttachables(bool destroy); ////////////////////////////////////////////////////////////////////////////////////////// @@ -414,14 +505,11 @@ ClassInfoGetters void RestDetection() override; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: IsOnScenePoint -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this' current graphical representation overlaps -// a point in absolute scene coordinates. -// Arguments: The point in absolute scene coordinates. -// Return value: Whether this' graphical rep overlaps the scene point. - + /// + /// Indicates whether this MOSRotating's current graphical representation, including its Attachables, overlaps a point in absolute scene coordinates. + /// + /// The point in absolute scene coordinates to check for overlap with. + /// Whether or not this MOSRotating's graphical representation overlaps the given scene point. bool IsOnScenePoint(Vector &scenePoint) const override; @@ -517,71 +605,96 @@ ClassInfoGetters void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGibWoundLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Return wound limit for this object. -// Arguments: None. -// Return value: Wound limit of the object. - - int GetGibWoundLimit() const { return m_GibWoundLimit; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetGibImpulseLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Set new impulse limit. -// Arguments: New impulse limit. -// Return value: None. - - void SetGibImpulseLimit(int newLimit) { m_GibImpulseLimit = newLimit; } - + /// + /// Gets the gib impulse limit for this MOSRotating, i.e. the amount of impulse force required in a frame to gib this MOSRotating. + /// + /// The gib impulse limit of this MOSRotating. + float GetGibImpulseLimit() const { return m_GibImpulseLimit; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetGibImpulseLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Return impulse limit for this object. -// Arguments: None. -// Return value: Impulse limit of the object. + /// + /// Sets the gib impulse limit for this MOSRotating, i.e. the amount of impulse force required in a frame to gib this MOSRotating. + /// + /// The new gib impulse limit to use. + void SetGibImpulseLimit(float newGibImpulseLimit) { m_GibImpulseLimit = newGibImpulseLimit; } - int GetGibImpulseLimit() const { return m_GibImpulseLimit; } + /// + /// Gets the gib wound limit for this MOSRotating, i.e. the total number of wounds required to gib this MOSRotating. Does not include any Attachables. + /// + /// + int GetGibWoundLimit() const { return GetGibWoundLimit(false, false, false); } + /// + /// Gets the gib wound limit for this MOSRotating, i.e. the total number of wounds required to gib this MOSRotating. + /// Optionally adds the gib wound limits of Attachables (and their Attachables, etc.) that match the conditions set by the provided parameters. + /// + /// Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. + /// Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. + /// Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. + /// The wound limit of this MOSRotating and, optionally, its Attachables. + int GetGibWoundLimit(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const; -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetGibWoundLimit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Set new wound limit, current wounds are not affected. -// Arguments: New wound limit. -// Return value: None. + /// + /// Sets the gib wound limit for this MOSRotating, i.e. the total number of wounds required to gib this MOSRotating. + /// This will not directly trigger gibbing, even if the limit is lower than the current number of wounds. + /// + /// The new gib wound limit to use. + void SetGibWoundLimit(int newGibWoundLimit) { m_GibWoundLimit = newGibWoundLimit; } - void SetGibWoundLimit(int newLimit) { m_GibWoundLimit = newLimit; } + /// + /// Gets the gib blast strength this MOSRotating, i.e. the strength with which Gibs and Attachables will be launched when this MOSRotating is gibbed. + /// + /// The gib blast strength of this MOSRotating. + float GetGibBlastStrength() const { return m_GibBlastStrength; } + /// + /// Sets the gib blast strength this MOSRotating, i.e. the strength with which Gibs and Attachables will be launched when this MOSRotating is gibbed. + /// + /// The new gib blast strength to use. + void SetGibBlastStrength(float newGibBlastStrength) { m_GibBlastStrength = newGibBlastStrength; } - /// - /// Attaches the passed in wound AEmitter and adds it to the list of wounds, changing its parent offset to the passed in Vector. - /// - /// The wound AEmitter to add - /// The vector to set as the wound AEmitter's parent offset - void AddWound(AEmitter *pWound, const Vector& parentOffsetToSet, bool checkGibWoundLimit = true); + /// + /// Gets the number of wounds attached to this MOSRotating. + /// Includes any Attachables (and their Attachables, etc.) that have a positive damage multiplier. + /// The number of wounds on this MOSRotating. + /// + int GetWoundCount() const { return GetWoundCount(true, false, false); } + /// + /// Gets the number of wounds attached to this MOSRotating. + /// Optionally adds the wound counts of Attachables (and their Attachables, etc.) that match the conditions set by the provided parameters. + /// Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. + /// Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. + /// Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. + /// The number of wounds on this MOSRotating and, optionally, its Attachables. + /// + int GetWoundCount(bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables) const; /// - /// Removes a specified amount of wounds and returns damage caused by these wounds. Head multiplier is not used. + /// Adds the passed in wound AEmitter to the list of wounds and changes its parent offset to the passed in Vector. /// - /// Amount of wounds to remove. - /// Amount of damage caused by these wounds. - virtual int RemoveWounds(int amount); - + /// The wound AEmitter to add. + /// The vector to set as the wound AEmitter's parent offset. + /// Whether to gib this MOSRotating if adding this wound raises its wound count past its gib wound limit. Defaults to true. + virtual void AddWound(AEmitter *woundToAdd, const Vector &parentOffsetToSet, bool checkGibWoundLimit = true); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetWoundCount -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Returns the amount of wound attached to this. -// Arguments: Key to retrieve value. -// Return value: Wound amount. - - int GetWoundCount() const { return m_Wounds.size(); }; + /// + /// Removes the specified number of wounds from this MOSRotating, and returns damage caused by these removed wounds. + /// Includes any Attachables (and their Attachables, etc.) that have a positive damage multiplier. + /// + /// The number of wounds that should be removed. + /// The amount of damage caused by these wounds, taking damage multipliers into account. + virtual float RemoveWounds(int numberOfWoundsToRemove) { return RemoveWounds(numberOfWoundsToRemove, true, false, false); } + /// + /// Removes the specified number of wounds from this MOSRotating, and returns damage caused by these removed wounds. + /// Optionally removes wounds from Attachables (and their Attachables, etc.) that match the conditions set by the provided inclusion parameters. + /// + /// The number of wounds that should be removed. + /// Whether to count wounds from Attachables that have a positive damage multiplier, i.e. those that damage their parent (this MOSRotating) when wounded. + /// Whether to count wounds from Attachables that have a negative damage multiplier, i.e. those that heal their parent (this MOSRotating) when wounded. + /// Whether to count wounds from Attachables that a zero damage multiplier, i.e. those that do not affect their parent (this MOSRotating) when wounded. + /// The amount of damage caused by these wounds, taking damage multipliers into account. + virtual float RemoveWounds(int numberOfWoundsToRemove, bool includePositiveDamageAttachables, bool includeNegativeDamageAttachables, bool includeNoDamageAttachables); ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetStringValue @@ -691,15 +804,6 @@ ClassInfoGetters bool ObjectValueExists(std::string key); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - - void GetMOIDs(std::vector &MOIDs) const override; - ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetDamageMultiplier ////////////////////////////////////////////////////////////////////////////////////////// @@ -707,7 +811,7 @@ ClassInfoGetters // Arguments: New multiplier value. // Return value: None. - void SetDamageMultiplier(float newValue) { m_DamageMultiplier = newValue; } + void SetDamageMultiplier(float newValue) { m_DamageMultiplier = newValue; m_NoSetDamageMultiplier = false; } ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetDamageMultiplier @@ -718,15 +822,11 @@ ClassInfoGetters float GetDamageMultiplier() const { return m_DamageMultiplier; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsDamageMultiplierRedefined -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether the damage multiplier was altered in the .INI definition. -// If not, CC will apply default values during actor construction. -// Arguments: None. -// Return value: Current multiplier value. - - bool IsDamageMultiplierRedefined() const { return m_DamageMultiplierRedefined; } + /// + /// Gets whether the damage multiplier for this MOSRotating has been directly set, or is at its default value. + /// + /// Whether the damage multiplier for this MOSRotating has been set. + bool HasNoSetDamageMultiplier() const { return m_NoSetDamageMultiplier; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -754,23 +854,12 @@ ClassInfoGetters protected: - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ApplyAttachableForces -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Does the joint force transfer stuff for an attachable. Convencinece -// method. If this returns false, it means the attachable has been knocked -// off and has been passed to MovableMan OR deleted. In either case, -// if false is returned just set the pointer to 0 and be done with it. -// Arguments: A pointer to the attachable to mess with. Ownership isn't transferred, -// but if the return is false, then the object has been deleted! -// If isCritical is true, then if attachable is gibbed created break wound -// emits indefenitely to guarantee actor's death. -// Return value: Whether or not the joint held up to the forces and impulses which had -// accumulated on the Attachable during this Update(). If false, the passed -// in instance is now deleted and invalid! - - bool ApplyAttachableForces(Attachable *pAttachable, bool isCritical = false); + /// + /// Transfers forces and impulse forces from the given Attachable to this MOSRotating or remove the Attachable if needed. + /// + /// A pointer to the Attachable to apply forces from. Ownership is NOT transferred! + /// Whether or not the Attachable has been removed, in which case it'll usually be passed to MovableMan or deleted. + bool TransferForcesFromAttachable(Attachable *attachable); ////////////////////////////////////////////////////////////////////////////////////////// @@ -787,6 +876,20 @@ ClassInfoGetters void UpdateChildMOIDs(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) override; + /// + /// Creates the particles specified by this MOSRotating's list of Gibs and adds them to MovableMan with appropriately randomized velocities, based on this MOSRotating's gib blast strength. + /// + /// The impulse (kg * m/s) of the impact that caused the gibbing to happen. + /// A pointer to an MO which the Attachables should not be colliding with. + void CreateGibsWhenGibbing(const Vector &impactImpulse, MovableObject *movableObjectToIgnore); + + /// + /// Removes all Attachables from this MOSR, deleting them or adding them to MovableMan as appropriate, and giving them randomized velocities based on their properties and this MOSRotating's gib blast strength. + /// + /// The impulse (kg * m/s) of the impact that caused the gibbing to happen. + /// A pointer to an MO which the Attachables should not be colliding with. + void RemoveAttachablesWhenGibbing(const Vector &impactImpulse, MovableObject *movableObjectToIgnore); + // Member variables static Entity::ClassInfo m_sClass; // float m_Torque; // In kg * r/s^2 (Newtons). @@ -813,18 +916,22 @@ ClassInfoGetters Vector m_RecoilForce; // The vector that the recoil offsets the sprite when m_Recoiled is true. Vector m_RecoilOffset; - // The list of wound AEmitters currently attached to this MOSRotating, and owned here as well + // The list of wound AEmitters currently attached to this MOSRotating, and owned here as well. std::list m_Wounds; - // The list of general Attachables currently attached and Owned by this. + // The list of Attachables currently attached and Owned by this. std::list m_Attachables; - // The list of all Attachables, including both hardcoded attachables and those added through ini or lua - std::list m_AllAttachables; + std::unordered_set m_ReferenceHardcodedAttachableUniqueIDs; //!< An unordered set is filled with the Unique IDs of all of the reference object's hardcoded Attachables when using the copy Create. + std::unordered_map> m_HardcodedAttachableUniqueIDsAndSetters; //!< An unordered map of Unique IDs to setter lambda functions, used to call the appropriate hardcoded Attachable setter when a hardcoded Attachable is removed. + const Attachable *m_RadiusAffectingAttachable; //!< A pointer to the Attachable that is currently affecting the radius. Used for some efficiency benefits. + float m_FarthestAttachableDistanceAndRadius; //!< The distance + radius of the radius affecting Attachable. + float m_AttachableAndWoundMass; //!< The mass of all Attachables and wounds on this MOSRotating. Used in combination with its actual mass and any other affecting factors to get its total mass. // The list of Gib:s this will create when gibbed std::list m_Gibs; // The amount of impulse force required to gib this, in kg * (m/s). 0 means no limit float m_GibImpulseLimit; // The number of wound emitters allowed before this gets gibbed. 0 means this can't get gibbed int m_GibWoundLimit; + float m_GibBlastStrength; //!< The strength with which Gibs and Attachables will get launched when this MOSRotating is gibbed. // Gib sound effect SoundContainer m_GibSound; // Whether to flash effect on gib @@ -838,10 +945,8 @@ ClassInfoGetters // Map to store any object pointers std::map m_ObjectValueMap; - // Damage mutliplier for this attachable - float m_DamageMultiplier; - // Whether damage multiplier for this attachable was redefined in .ini - bool m_DamageMultiplierRedefined; + float m_DamageMultiplier; //!< Damage multiplier for this MOSRotating. + bool m_NoSetDamageMultiplier; //!< Whether or not the damage multiplier for this MOSRotating was set. // Intermediary drawing bitmap used to flip rotating bitmaps. Owned! BITMAP *m_pFlipBitmap; diff --git a/Entities/MOSprite.cpp b/Entities/MOSprite.cpp index 22a55383e..da8360172 100644 --- a/Entities/MOSprite.cpp +++ b/Entities/MOSprite.cpp @@ -38,8 +38,8 @@ void MOSprite::Clear() m_SpriteAnimTimer.Reset(); m_SpriteAnimIsReversingFrames = false; m_HFlipped = false; - m_MaxRadius = 1; - m_MaxDiameter = 2; + m_SpriteRadius = 1.0F; + m_SpriteDiameter = 2.0F; m_Rotation.Reset(); m_PrevRotation.Reset(); m_AngularVel = 0; @@ -76,8 +76,8 @@ int MOSprite::Create() // Calc maximum dimensions from the Pos, based on the sprite float maxX = MAX(fabs(m_SpriteOffset.m_X), fabs(m_aSprite[0]->w + m_SpriteOffset.m_X)); float maxY = MAX(fabs(m_SpriteOffset.m_Y), fabs(m_aSprite[0]->h + m_SpriteOffset.m_Y)); - m_MaxRadius = sqrt((float)(maxX * maxX) + (maxY * maxY)); - m_MaxDiameter = m_MaxRadius * 2; + m_SpriteRadius = sqrt((float)(maxX * maxX) + (maxY * maxY)); + m_SpriteDiameter = m_SpriteRadius * 2.0F; } else return -1; @@ -111,8 +111,8 @@ int MOSprite::Create(ContentFile spriteFile, // Calc maximum dimensions from the Pos, based on the sprite float maxX = MAX(fabs(m_SpriteOffset.m_X), fabs(m_aSprite[0]->w + m_SpriteOffset.m_X)); float maxY = MAX(fabs(m_SpriteOffset.m_Y), fabs(m_aSprite[0]->h + m_SpriteOffset.m_Y)); - m_MaxRadius = sqrt((float)(maxX * maxX) + (maxY * maxY)); - m_MaxDiameter = m_MaxRadius * 2; + m_SpriteRadius = sqrt((float)(maxX * maxX) + (maxY * maxY)); + m_SpriteDiameter = m_SpriteRadius * 2.0F; return 0; } @@ -147,8 +147,8 @@ int MOSprite::Create(const MOSprite &reference) m_SpriteAnimMode = reference.m_SpriteAnimMode; m_SpriteAnimDuration = reference.m_SpriteAnimDuration; m_HFlipped = reference.m_HFlipped; - m_MaxRadius = reference.m_MaxRadius; - m_MaxDiameter = reference.m_MaxDiameter; + m_SpriteRadius = reference.m_SpriteRadius; + m_SpriteDiameter = reference.m_SpriteDiameter; m_Rotation = reference.m_Rotation; m_AngularVel = reference.m_AngularVel; @@ -407,7 +407,7 @@ bool MOSprite::IsOnScenePoint(Vector &scenePoint) const } } */ - if (WithinBox(scenePoint, m_Pos.m_X - m_MaxRadius, m_Pos.m_Y - m_MaxRadius, m_Pos.m_X + m_MaxRadius, m_Pos.m_Y + m_MaxRadius)) + if (WithinBox(scenePoint, m_Pos.m_X - m_SpriteRadius, m_Pos.m_Y - m_SpriteRadius, m_Pos.m_X + m_SpriteRadius, m_Pos.m_Y + m_SpriteRadius)) { // Get scene point in object's relative space Vector spritePoint = scenePoint - m_Pos; diff --git a/Entities/MOSprite.h b/Entities/MOSprite.h index f16255667..bd65f8842 100644 --- a/Entities/MOSprite.h +++ b/Entities/MOSprite.h @@ -147,7 +147,7 @@ class MOSprite: // Arguments: None. // Return value: The radius from its center to the edge of its graphical representation. - float GetRadius() const override { return m_MaxRadius; } + float GetRadius() const override { return m_SpriteRadius; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -157,7 +157,7 @@ class MOSprite: // Arguments: None. // Return value: The largest diameter across its graphical representation. - float GetDiameter() const override { return m_MaxDiameter; } + float GetDiameter() const override { return m_SpriteDiameter; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -167,7 +167,7 @@ class MOSprite: // Arguments: None. // Return value: A Vector with the absolute position of this' HUD stack top point. - Vector GetAboveHUDPos() const override { return m_Pos + Vector(0, -m_MaxRadius); } + Vector GetAboveHUDPos() const override { return m_Pos + Vector(0, -GetRadius()); } // TODO: Improve this one! Really crappy fit ////////////////////////////////////////////////////////////////////////////////////////// @@ -180,7 +180,7 @@ class MOSprite: // Return value: A Box which is guaranteed to contain this. Does nto take wrapping into // account, and parts of this box may be out of bounds! - Box GetBoundingBox() const { return Box(m_Pos + Vector(-m_MaxRadius, -m_MaxRadius), m_MaxDiameter, m_MaxDiameter); } + Box GetBoundingBox() const { return Box(m_Pos + Vector(-GetRadius(), -GetRadius()), GetDiameter(), GetDiameter()); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -404,7 +404,7 @@ class MOSprite: // Arguments: None. // Return value: Whether this is either moving or rotating too fast. - bool IsTooFast() const override { return m_Vel.GetLargest() > 500.0f || fabs(m_AngularVel) > (2000.0f / (m_MaxRadius + 1.0f)); } + bool IsTooFast() const override { return m_Vel.GetLargest() > 500.0f || fabs(m_AngularVel) > (2000.0f / (GetRadius() + 1.0f)); } //bool IsTooFast() const override { return m_Vel.GetLargest() > 500 || fabs(m_AngularVel) > 100.0f; } @@ -453,6 +453,13 @@ class MOSprite: Vector UnRotateOffset(const Vector &offset) const; + /// + /// Adjusts an absolute angle based on wether this MOSprite is flipped. + /// + /// The input angle in radians. + /// The output angle in radians, which will be unaltered if this MOSprite is not flipped. + float FacingAngle(float angle) const { return (m_HFlipped ? c_PI : 0) + (angle * static_cast(GetFlipFactor())); } + ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: SetEntryWound @@ -578,8 +585,8 @@ class MOSprite: // Whether flipped horizontally or not. bool m_HFlipped; // The precalculated maximum possible radius and diameter of this, in pixels - float m_MaxRadius; - float m_MaxDiameter; + float m_SpriteRadius; + float m_SpriteDiameter; // A counter to count the oscillations in rotation, in order to detect settling. int m_AngOscillations; // Whether to disable the settle material ID when this gets drawn as material diff --git a/Entities/Magazine.cpp b/Entities/Magazine.cpp index 00a0d24bf..5101ce6cf 100644 --- a/Entities/Magazine.cpp +++ b/Entities/Magazine.cpp @@ -38,6 +38,9 @@ void Magazine::Clear() m_AIAimMaxDistance = -1; m_AIAimPenetration = 0; m_AIBlastRadius = -1; + + // NOTE: This special override of a parent class member variable avoids needing an extra variable to avoid overwriting INI values. + m_CollidesWithTerrainWhileAttached = false; } diff --git a/Entities/Magazine.h b/Entities/Magazine.h index a756304aa..daf3e17c7 100644 --- a/Entities/Magazine.h +++ b/Entities/Magazine.h @@ -94,7 +94,7 @@ ClassInfoGetters // Arguments: None. // Return value: None. - void Reset() override { Clear(); Attachable::Reset(); } + void Reset() override { Clear(); Attachable::Reset(); m_CollidesWithTerrainWhileAttached = false; } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Entities/MovableObject.cpp b/Entities/MovableObject.cpp index 3b7cb98c6..e274fe235 100644 --- a/Entities/MovableObject.cpp +++ b/Entities/MovableObject.cpp @@ -1023,27 +1023,17 @@ void MovableObject::GetMOIDs(std::vector &MOIDs) const // itself and its children for this frame. // BITMAP of choice. -void MovableObject::RegMOID(vector &MOIDIndex, - MOID rootMOID, - bool makeNewMOID) -{ - // Make a new MOID for itself - if (makeNewMOID) - { - // Skip g_NoMOID item - if (MOIDIndex.size() == g_NoMOID) - MOIDIndex.push_back(0); +void MovableObject::RegMOID(vector &MOIDIndex, MOID rootMOID, bool makeNewMOID) { + if (!makeNewMOID && GetParent()) { + m_MOID = GetParent()->GetID(); + } else { + if (MOIDIndex.size() == g_NoMOID) { MOIDIndex.push_back(0); } m_MOID = MOIDIndex.size(); MOIDIndex.push_back(this); } - // Use the parent's MOID instead (the two are considered the same MO) - else - m_MOID = MOIDIndex.size() - 1; - // Assign the root MOID m_RootMOID = (rootMOID == g_NoMOID ? m_MOID : rootMOID); - } } // namespace RTE \ No newline at end of file diff --git a/Entities/MovableObject.h b/Entities/MovableObject.h index f9eda5a85..0c70f839f 100644 --- a/Entities/MovableObject.h +++ b/Entities/MovableObject.h @@ -327,7 +327,7 @@ friend class Atom; // Arguments: None. // Return value: The largest diameter across its graphical representation. - virtual float GetDiameter() const { return 2.0f; } + virtual float GetDiameter() const { return 2.0F; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -778,15 +778,10 @@ friend class Atom; void SetAge(double newAge = 0) { m_AgeTimer.SetElapsedSimTimeMS(newAge); } -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. -// Arguments: An int specifying the MOID that this MovableObject is -// assigned for this frame. -// Return value: None. - - virtual void SetID(const MOID newID) { m_MOID = newID; } + /// + /// Sets the MOID of this MovableObject to be g_NoMOID (255) for this frame. + /// + virtual void SetAsNoID() { m_MOID = g_NoMOID; } /// /// Sets this object as having been added to MovableMan. Should only really be done in MovableMan::AddObject. @@ -827,17 +822,18 @@ friend class Atom; void SetToGetHitByMOs(bool getHitByMOs = true) { m_GetsHitByMOs = getHitByMOs; } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetWhichMOToNotHit -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets this MO to not hit a specific other MO and all its children even -// though MO hitting is enabled on this MovableObject -// Arguments: A pointer to the MO to not be hitting. 0 means don't ignore anyhting. -// Ownership is not transferred! -// For how long, in S, to ignore the above. Negative number means forever. -// Return value: None. + /// + /// Gets the MO this MO is set not to hit even when MO hitting is enabled on this MO. + /// + /// The MO this MO is set not to hit. + const MovableObject * GetWhichMOToNotHit() const { return m_pMOToNotHit; } - void SetWhichMOToNotHit(MovableObject *moToNotHit = 0, float forHowLong = -1) { m_pMOToNotHit = moToNotHit; m_MOIgnoreTimer.Reset(); m_MOIgnoreTimer.SetSimTimeLimitS(forHowLong); } + /// + /// Sets this MO to not hit a specific other MO and all its children even when MO hitting is enabled on this MO. + /// + /// A pointer to the MO to not be hitting. Null pointer means don't ignore anyhting. Ownership is NOT transferred! + /// How long, in seconds, to ignore the specified MO. A negative number means forever. + virtual void SetWhichMOToNotHit(MovableObject *moToNotHit = nullptr, float forHowLong = -1) { m_pMOToNotHit = moToNotHit; m_MOIgnoreTimer.Reset(); m_MOIgnoreTimer.SetSimTimeLimitS(forHowLong); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1099,7 +1095,7 @@ friend class Atom; RTEAssert(offset.GetLargest() < 5000, "HUEG IMPULSE FORCE OFFSET"); #endif - m_ImpulseForces.push_back(std::make_pair(impulse, offset)); + m_ImpulseForces.push_back({impulse, offset}); } @@ -1431,6 +1427,11 @@ friend class Atom; void SetForceOffset(int n, Vector v) { if (n > 0 && n < m_Forces.size()) m_Forces[n].second = v; } + /// + /// Gets the pairs of impulse forces and their offsets that have to be applied. + /// + /// A constant reference to the deque of impulses for this MovableObject. + const std::deque > &GetImpulses() { return m_ImpulseForces; } ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: GetImpulsesCount() diff --git a/Entities/SLTerrain.cpp b/Entities/SLTerrain.cpp index 31f55b1cd..9ccc9fa87 100644 --- a/Entities/SLTerrain.cpp +++ b/Entities/SLTerrain.cpp @@ -1103,8 +1103,7 @@ void SLTerrain::ApplyMovableObject(MovableObject *pMObject) // Temporary bitmap holder, doesn't own BITMAP *pSprite = pMOSprite->GetSpriteFrame(); -// TODO: Make the diameter more accurate.. now we have to double it because it's not taking into account anything attached to the MO - float diameter = pMOSprite->GetDiameter() * 2; + float diameter = pMOSprite->GetDiameter(); // Choose an appropriate size if (diameter >= 256) pTempBitmap = m_spTempBitmap512; diff --git a/Entities/TDExplosive.cpp b/Entities/TDExplosive.cpp index 5a98a8b46..74e6d2885 100644 --- a/Entities/TDExplosive.cpp +++ b/Entities/TDExplosive.cpp @@ -12,15 +12,23 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int TDExplosive::Create(const TDExplosive &reference) { - ThrownDevice::Create(reference); + int TDExplosive::Create() { + if (ThrownDevice::Create() < 0) { + return -1; + } - m_IsAnimatedManually = reference.m_IsAnimatedManually; + if (IsInGroup("Bombs - Payloads")) { m_HUDVisible = false; } - // All Explosives should hit against other objects etc, like grenades flying and hitting actors etc EXCEPT when they are laying on the ground etc - m_IgnoresAGHitsWhenSlowerThan = 1.0F; + return 0; + } - if (IsInGroup("Bombs - Payloads")) { m_HUDVisible = false; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int TDExplosive::Create(const TDExplosive &reference) { + if (ThrownDevice::Create(reference) < 0) { + return -1; + } + m_IsAnimatedManually = reference.m_IsAnimatedManually; return 0; } @@ -54,25 +62,16 @@ namespace RTE { ThrownDevice::Update(); if (m_Activated) { - // If not animated manually, play 'fuse lit' animation m_SpriteAnimMode = !m_IsAnimatedManually ? ALWAYSLOOP : NOANIM; m_RestTimer.Reset(); m_ToSettle = false; } - // Blow up if the activation timer has reached the trigger delay limit - if (m_Activated && m_ActivationTimer.GetElapsedSimTimeMS() >= m_TriggerDelay) { - m_Activated = false; - GibThis(); - } + if (m_Activated && m_ActivationTimer.GetElapsedSimTimeMS() >= m_TriggerDelay) { GibThis(); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TDExplosive::DrawHUD(BITMAP *targetBitmap, const Vector &targetPos, int whichScreen, bool playerControlled) { - if (!m_HUDVisible) { - return; - } - // Only draw the pickup HUD if not activated - if (!m_Activated) { ThrownDevice::DrawHUD(targetBitmap, targetPos, whichScreen); } + if (m_HUDVisible && !m_Activated) { ThrownDevice::DrawHUD(targetBitmap, targetPos, whichScreen); } } } \ No newline at end of file diff --git a/Entities/TDExplosive.h b/Entities/TDExplosive.h index 68100dbbe..2da76d3dc 100644 --- a/Entities/TDExplosive.h +++ b/Entities/TDExplosive.h @@ -22,6 +22,12 @@ namespace RTE { /// TDExplosive() { Clear(); } + /// + /// Makes the TDExplosive object ready for use. + /// + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create() override; + /// /// Creates a TDExplosive to be identical to another, by deep copy. /// diff --git a/Entities/Turret.cpp b/Entities/Turret.cpp index 3e6c12b93..a9fa64285 100644 --- a/Entities/Turret.cpp +++ b/Entities/Turret.cpp @@ -1,395 +1,94 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Turret.cpp -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Source file for the Turret class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - #include "Turret.h" -#include "HeldDevice.h" #include "PresetMan.h" namespace RTE { -ConcreteClassInfo(Turret, Attachable, 20) - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Turret, effectively -// resetting the members of this abstraction level only. - -void Turret::Clear() -{ - m_pMountedMO = 0; - m_MountedRotOffset = 0; -} - -/* -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes the Round object ready for use. - -int Turret::Create() -{ - if (Attachable::Create() < 0) - return -1; - - return 0; -} -*/ - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Turret to be identical to another, by deep copy. - -int Turret::Create(const Turret &reference) -{ - Attachable::Create(reference); - - if (reference.m_pMountedMO) { - m_pMountedMO = dynamic_cast(reference.m_pMountedMO->Clone()); - if (m_pMountedMO->IsDevice()) - dynamic_cast(m_pMountedMO)->Attach(this, - dynamic_cast(m_pMountedMO)->GetParentOffset()); - } - - m_MountedRotOffset = reference.m_MountedRotOffset; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ReadProperty -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reads a property value from a reader stream. If the name isn't -// recognized by this class, then ReadProperty of the parent class -// is called. If the property isn't recognized by any of the base classes, -// false is returned, and the reader's position is untouched. - -int Turret::ReadProperty(std::string propName, Reader &reader) -{ - if (propName == "MountedMO") - { - const Entity *pEntity = g_PresetMan.GetEntityPreset(reader); - if (pEntity) - { - m_pMountedMO = dynamic_cast(pEntity->Clone()); - if (m_pMountedMO->IsDevice()) - dynamic_cast(m_pMountedMO)->Attach(this, dynamic_cast(m_pMountedMO)->GetParentOffset()); - } - pEntity = 0; - } - else - return Attachable::ReadProperty(propName, reader); - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Save -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Saves the complete state of this Turret with a Writer for -// later recreation with Create(Reader &reader); - -int Turret::Save(Writer &writer) const -{ - Attachable::Save(writer); - - writer.NewProperty("MountedMO"); - writer << m_pMountedMO; - - return 0; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the Turret object. - -void Turret::Destroy(bool notInherited) -{ -// g_MovableMan.RemoveEntityPreset(this); - - delete m_pMountedMO; - - if (!notInherited) - Attachable::Destroy(); - Clear(); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this Turret, including the mass of any device it -// may be holding. - -float Turret::GetMass() const -{ - return m_Mass + (m_pMountedMO ? m_pMountedMO->GetMass() : 0); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMountedDevice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the HeldDevice currently held by this Turret, IF the thing held is -// a HeldDevice, that is. Ownership is NOT transferred. -// Arguments: None. -// Return value: A pointer to the currently held HeldDevice. 0 is returned if no -// HeldDevice is currently held (even though an MO may be held). - -HeldDevice * Turret::GetMountedDevice() const -{ - HeldDevice *pRetDev = 0; - if (m_pMountedMO && m_pMountedMO->IsHeldDevice()) - pRetDev = dynamic_cast(m_pMountedMO); - return pRetDev; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. - -void Turret::SetID(const MOID newID) -{ - MovableObject::SetID(newID); - if (m_pMountedMO) - m_pMountedMO->SetID(newID); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMountedMO -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Replaces the MovableObject currently held by this Turret with a new -// one. Ownership IS transferred. The currently held MovableObject -// (if there is one) will be dropped and become a detached MovableObject, - -void Turret::SetMountedMO(MovableObject *newHeldMO) -{ - if (m_pMountedMO && m_pMountedMO->IsHeldDevice() && dynamic_cast(m_pMountedMO)->IsAttachedTo(this)) { - HeldDevice *pHeldDev = dynamic_cast(m_pMountedMO); - pHeldDev->Detach(); -// TODO: Refine throwing force to dropped device here?") - pHeldDev->SetVel(Vector(RandomNum(0.0F, 10.0F), -RandomNum(0.0F, 15.0F))); - pHeldDev->SetAngularVel(-RandomNum(0.0F, 10.0F)); - g_MovableMan.AddItem(pHeldDev); - m_pMountedMO = pHeldDev = 0; - } - - if (newHeldMO && newHeldMO->IsHeldDevice()) { - HeldDevice *pNewDev = dynamic_cast(newHeldMO); - pNewDev->Detach(); - g_MovableMan.RemoveMO(pNewDev); - pNewDev->Attach(this, pNewDev->GetParentOffset()); - pNewDev = 0; - } - m_pMountedMO = newHeldMO; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReleaseMountedMO -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this arm let go of the HeldDevice currently held. Ownership IS -// transferred! -// Arguments: None. -// Return value: A pointer to the up to this point held HeldDevice. 0 is returned if no -// HeldDevice is currently held. Ownership IS transferred! - -MovableObject * Turret::ReleaseMountedMO() -{ - MovableObject *pReturnMO = m_pMountedMO; - if (m_pMountedMO && m_pMountedMO->IsDevice()) - dynamic_cast(m_pMountedMO)->Detach(); - m_pMountedMO = 0; - return pReturnMO; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DropEverything -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this arm let go of anyhthing it holds and give it to the -// MovableMan. Ownership is transferred to MovableMan. -// Arguments: None. - -MovableObject * Turret::DropEverything() -{ - MovableObject *pReturnMO = m_pMountedMO; - - if (m_pMountedMO && m_pMountedMO->IsDevice()) { - dynamic_cast(m_pMountedMO)->Detach(); - g_MovableMan.AddItem(m_pMountedMO); - } - else if (m_pMountedMO) - g_MovableMan.AddParticle(m_pMountedMO); - - m_pMountedMO = 0; - - return pReturnMO; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SwapMountedMO -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Replaces the MovableObject currently held by this Turret with a new -// one, and returns the replaced one. Ownership IS transferred both ways. - -MovableObject * Turret::SwapMountedMO(MovableObject *newMO) -{ - MovableObject *oldMO = m_pMountedMO; - if (oldMO && oldMO->IsDevice()) - dynamic_cast(oldMO)->Detach(); - - m_pMountedMO = newMO; - if (newMO && newMO->IsDevice()) { - dynamic_cast(newMO)->Attach(this, - dynamic_cast(newMO)->GetParentOffset()); - } - - return oldMO; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. - -void Turret::GibThis(Vector impactImpulse, float internalBlast, MovableObject *pIgnoreMO) -{ - Attachable::GibThis(impactImpulse, internalBlast, pIgnoreMO); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this Turret. Supposed to be done every frame. - -void Turret::Update() -{ - // Update basic metrics from parent. - Attachable::Update(); - - if (!m_pParent) - { - if (m_pMountedMO) - { -/* - m_pMountedMO->SetVel(m_Vel + Vector(-10 * RandomNum(), -15 * RandomNum())); - m_pMountedMO->SetAngularVel(-7); - if (m_pMountedMO->IsDevice()) - dynamic_cast(m_pMountedMO)->Detach(); - g_MovableMan.AddItem(m_pMountedMO); - m_pMountedMO = 0; -*/ - HeldDevice *pHeldDev = dynamic_cast(m_pMountedMO); - if (pHeldDev && pHeldDev->IsHeldDevice()) - { - pHeldDev->SetJointPos(m_Pos + pHeldDev->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - pHeldDev->SetRotAngle(m_Rotation.GetRadAngle() + m_MountedRotOffset); - pHeldDev->Update(); - } - } - } - else - { - // Attached, so act like it - - // If a device is held, update it and arm configuration accordingly. - HeldDevice *pHeldDev = dynamic_cast(m_pMountedMO); - if (m_pMountedMO && m_pMountedMO->IsHeldDevice() && pHeldDev) - { - pHeldDev->SetHFlipped(m_HFlipped); - - pHeldDev->SetJointPos(m_Pos + pHeldDev->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation); - pHeldDev->SetRotAngle(m_Rotation.GetRadAngle() + m_MountedRotOffset); - pHeldDev->Update(); -// Looks strange -// if (pHeldDev->IsRecoiled()) -// m_pParent->AddImpulseForce(pHeldDev->GetRecoilForce()); -// else - m_Recoiled = false; - - // Apply forces and detach if necessary - // OBSERVE the memeber pointer is what gets set to 0!$@#$@ - if (!ApplyAttachableForces(pHeldDev)) - m_pMountedMO = 0; - } - } -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame. - -void Turret::UpdateChildMOIDs(vector &MOIDIndex, - MOID rootMOID, - bool makeNewMOID) -{ - if (m_pMountedMO && m_pMountedMO->GetsHitByMOs()) - m_pMountedMO->UpdateMOID(MOIDIndex, m_RootMOID, makeNewMOID); - - Attachable::UpdateChildMOIDs(MOIDIndex, m_RootMOID, makeNewMOID); -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - -void Turret::GetMOIDs(std::vector &MOIDs) const -{ - if (m_pMountedMO && m_pMountedMO->GetsHitByMOs()) - m_pMountedMO->GetMOIDs(MOIDs); - - Attachable::GetMOIDs(MOIDs); -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Turret's current graphical representation to a -// BITMAP of choice. - -void Turret::Draw(BITMAP *pTargetBitmap, - const Vector &targetPos, - DrawMode mode, - bool onlyPhysical) const -{ - if (m_pMountedMO && !m_pMountedMO->IsDrawnAfterParent()) - m_pMountedMO->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - Attachable::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); - - if (m_pMountedMO && m_pMountedMO->IsDrawnAfterParent()) - m_pMountedMO->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); -} - -} // namespace RTE \ No newline at end of file + ConcreteClassInfo(Turret, Attachable, 20) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Turret::Clear() { + m_MountedDevice = nullptr; + m_MountedDeviceRotOffset = 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int Turret::Create(const Turret &reference) { + if (reference.m_MountedDevice) { + m_ReferenceHardcodedAttachableUniqueIDs.insert(reference.m_MountedDevice->GetUniqueID()); + SetMountedDevice(dynamic_cast(reference.m_MountedDevice->Clone())); + } + Attachable::Create(reference); + + m_MountedDeviceRotOffset = reference.m_MountedDeviceRotOffset; + + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int Turret::ReadProperty(std::string propName, Reader &reader) { + if (propName == "MountedDevice") { + RemoveAttachable(m_MountedDevice); + const Entity *mountedDeviceEntity = g_PresetMan.GetEntityPreset(reader); + if (mountedDeviceEntity) { + m_MountedDevice = dynamic_cast(mountedDeviceEntity->Clone()); + AddAttachable(m_MountedDevice); + m_MountedDevice->SetInheritsRotAngle(false); + m_MountedDevice->SetUnPickupable(true); + //Force weapons mounted on turrets to never be removed due to forces. This doesn't affect them gibbing from hitting their impulse limits though. + m_MountedDevice->SetJointStrength(0.0F); + } + } else { + return Attachable::ReadProperty(propName, reader); + } + + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int Turret::Save(Writer &writer) const { + Attachable::Save(writer); + + writer.NewProperty("MountedDevice"); + writer << m_MountedDevice; + + return 0; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Turret::SetMountedDevice(HeldDevice *newMountedDevice) { + if (newMountedDevice == nullptr) { + if (m_MountedDevice && m_MountedDevice->IsAttached()) { RemoveAttachable(m_MountedDevice); } + m_MountedDevice = nullptr; + } else { + if (m_MountedDevice && m_MountedDevice->IsAttached()) { RemoveAttachable(m_MountedDevice); } + m_MountedDevice = newMountedDevice; + AddAttachable(newMountedDevice); + + m_HardcodedAttachableUniqueIDsAndSetters.insert({newMountedDevice->GetUniqueID(), [](MOSRotating *parent, Attachable *attachable) { + HeldDevice *castedAttachable = dynamic_cast(attachable); + RTEAssert(!attachable || castedAttachable, "Tried to pass incorrect Attachable subtype " + (attachable ? attachable->GetClassName() : "") + " to SetMountedDevice"); + dynamic_cast(parent)->SetMountedDevice(castedAttachable); + }}); + } + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Turret::Update() { + if (m_MountedDevice) { m_MountedDevice->SetRotAngle(m_Rotation.GetRadAngle() + m_MountedDeviceRotOffset); } + Attachable::Update(); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void Turret::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { + Attachable::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); + //TODO replace this with a relative draw order property or something that lets you organize attachable drawing so it doesn't need special hardcoding crap. Use this for ahuman limbs and arm held mo if possible. + if (m_MountedDevice && m_MountedDevice->IsDrawnAfterParent()) { m_MountedDevice->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } + } +} \ No newline at end of file diff --git a/Entities/Turret.h b/Entities/Turret.h index 0a9be67dc..f4386f7e4 100644 --- a/Entities/Turret.h +++ b/Entities/Turret.h @@ -1,364 +1,121 @@ #ifndef _RTETURRET_ #define _RTETURRET_ -////////////////////////////////////////////////////////////////////////////////////////// -// File: Turret.h -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Header file for the Turret class. -// Project: Retro Terrain Engine -// Author(s): Daniel Tabar -// data@datarealms.com -// http://www.datarealms.com - - -////////////////////////////////////////////////////////////////////////////////////////// -// Inclusions of header files - -#include "Attachable.h" - -namespace RTE -{ - -class HeldDevice; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Class: Turret -////////////////////////////////////////////////////////////////////////////////////////// -// Description: A detatchable turret pod that can hold HeldDevices (weapons, tools) -// Parent(s): Attachable. -// Class history: 10/24/2007 Turret created. - -class Turret: - public Attachable -{ - - -////////////////////////////////////////////////////////////////////////////////////////// -// Public member variable, method and friend function declarations - -public: - - -// Concrete allocation and cloning definitions -EntityAllocation(Turret) -SerializableOverrideMethods -ClassInfoGetters - - -////////////////////////////////////////////////////////////////////////////////////////// -// Constructor: Turret -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Constructor method used to instantiate a Turret object in system -// memory. Create() should be called before using the object. -// Arguments: None. - - Turret() { Clear(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Destructor: ~Turret -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destructor method used to clean up a Turret object before deletion -// from system memory. -// Arguments: None. - - ~Turret() override { Destroy(true); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Create -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Creates a Turret to be identical to another, by deep copy. -// Arguments: A reference to the Turret to deep copy. -// Return value: An error return value signaling sucess or any particular failure. -// Anything below 0 is an error signal. - - int Create(const Turret &reference); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Reset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Resets the entire Turret, including its inherited members, to their -// default settings or values. -// Arguments: None. -// Return value: None. - - void Reset() override { Clear(); Attachable::Reset(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Destroy -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Destroys and resets (through Clear()) the SceneLayer object. -// Arguments: Whether to only destroy the members defined in this derived class, or -// to destroy all inherited members also. -// Return value: None. - - void Destroy(bool notInherited = false) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMass -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the mass value of this Turret, including the mass of any device it -// may be holding. -// Arguments: None. -// Return value: A float describing the mass value in Kilograms (kg). - - float GetMass() const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMountedDevice -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the HeldDevice currently held by this Turret, IF the thing held is -// a HeldDevice, that is. Ownership is NOT transferred. -// Arguments: None. -// Return value: A pointer to the currently held HeldDevice. 0 is returned if no -// HeldDevice is currently held (even though an MO may be held). - - HeldDevice * GetMountedDevice() const; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsDeviceMounted -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether a device is being held or not. Faster than using -// GetMountedDevice() for the purpose. -// Arguments: None. -// Return value: A bool indicating whether any device is held by this Turret. - - bool IsDeviceMounted() const { return m_pMountedMO && m_pMountedMO->IsDevice(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsHeldDeviceMounted -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether a HeldDevice is held or not. Faster than using -// GetMountedDevice() for the purpose. -// Arguments: None. -// Return value: A bool indicating whether a HeldDevice is held by this Turret. - - bool IsHeldDeviceMounted() const { return m_pMountedMO && m_pMountedMO->IsHeldDevice(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsThrownDeviceMounted -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether a ThrownDevice is held or not. Faster than using -// GetMountedDevice() for the purpose. -// Arguments: None. -// Return value: A bool indicating whether a ThrownDevice is held by this Turret. - - bool IsThrownDeviceMounted() const { return m_pMountedMO && m_pMountedMO->IsThrownDevice(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: GetMountedMO -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gets the MovableObject currently held by this Turret. Ownership is NOT -// transferred. -// Arguments: None. -// Return value: A pointer to the currently held MovableObject. 0 is returned if no -// MovableObject is currently held. - - MovableObject * GetMountedMO() const { return m_pMountedMO; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetID -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the MOID of this MovableObject for this frame. -// Arguments: A MOID specifying the MOID that this MovableObject is -// assigned for this frame. -// Return value: None. - - void SetID(const MOID newID) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SetMountedMO -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Replaces the MovableObject currently held by this Turret with a new -// one. Ownership IS transferred. The currently held MovableObject -// (if there is one) will be dropped and become a detached MovableObject, -// Arguments: A pointer to the new MovableObject to hold. Ownership IS transferred. -// Return value: None. - - void SetMountedMO(MovableObject *newHeldMO); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ReleaseMountedMO -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this arm let go of the MovableObject currently held. Ownership -// IS transferred! -// Arguments: None. -// Return value: A pointer to the up to this point held MovableObject. 0 is returned -// if no MovableObject is currently held. Ownership IS transferred! - - MovableObject * ReleaseMountedMO(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DropEverything -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this arm let go of anyhthing it holds and give it to the -// MovableMan. Ownership is transferred to MovableMan. -// Arguments: None. -// Return value: A pointer to the up to this point held anything. 0 is returned if -// nothing is currently held. Ownership is NOT transferred, but given -// to MovableMan. - - MovableObject * DropEverything(); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: SwapMountedMO -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Replaces the MovableObject currently held by this Turret with a new -// one, and returns the replaced one. Ownership IS transferred both ways. -// Arguments: A pointer to the new MovableObject to hold. Ownership IS transferred. -// Return value: A pointer to the previously held MO. Ownership IS transferred. - - MovableObject * SwapMountedMO(MovableObject *newMO); - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsDeviceMounted -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this Turret is holding a HeldDevice or not. -// Arguments: None. -// Return value: Whether this Turret is holding a HeldDevice or not. - - bool IsDeviceMounted() { return m_pMountedMO && m_pMountedMO->IsDevice(); } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: IsSomethingMounted -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Indicates whether this Turret is holding an MO or not. -// Arguments: None. -// Return value: Whether this Turret is holding anyhting. - - bool IsSomethingMounted() { return m_pMountedMO != 0; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: SetMountedRotOffset -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Sets the current rotational offset of the mounted device from the rest -// of the turret -// Arguments: The new offset angle in radians, relative from the rest of the turret. -// Return value: None. - - void SetMountedRotOffset(float newOffsetAngle) { m_MountedRotOffset = newOffsetAngle; } - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GibThis -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Gibs this, effectively destroying it and creating multiple gibs or -// pieces in its place. -// Arguments: The impulse (kg * m/s) of the impact causing the gibbing to happen. -// The internal blast impulse which will push the gibs away from the center. -// A pointer to an MO which the gibs shuold not be colliding with! -// Return value: None. - - void GibThis(Vector impactImpulse = Vector(), float internalBlast = 10, MovableObject *pIgnoreMO = 0) override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Update -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Updates this MovableObject. Supposed to be done every frame. -// Arguments: None. -// Return value: None. - - void Update() override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: Draw -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws this Turret's current graphical representation to a -// BITMAP of choice. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// In which mode to draw in. See the DrawMode enumeration for the modes. -// Whether to not draw any extra 'ghost' items of this MovableObject, -// indicator arrows or hovering HUD text and so on. -// Return value: None. - - void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; - - - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: GetMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Puts all MOIDs associated with this MO and all it's descendants into MOIDs vector -// Arguments: Vector to store MOIDs -// Return value: None. - - void GetMOIDs(std::vector &MOIDs) const override; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Protected member variable and method declarations - -protected: - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: UpdateChildMOIDs -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Makes this MO register itself and all its attached children in the -// MOID register and get ID:s for itself and its children for this frame, -// and then finally draws its and its childrens' MOID silhouette onto a -// BITMAP of choice. -// Arguments: The MOID index to register itself and its children in. -// The MOID of the root MO of this MO, ie the highest parent of this MO. -// 0 means that this MO is the root, ie it is owned by MovableMan. -// Whether this MO should make a new MOID to use for itself, or to use -// the same as the last one in the index (presumably its parent), -// Return value: None. - - void UpdateChildMOIDs(std::vector &MOIDIndex, MOID rootMOID = g_NoMOID, bool makeNewMOID = true) override; - - - // Member variables - static Entity::ClassInfo m_sClass; - // The MovableObject held, updated and held by this Turret, if any. - MovableObject *m_pMountedMO; - // The relative offset in angle (radians) of the mounted device from this' rotation - float m_MountedRotOffset; - - -////////////////////////////////////////////////////////////////////////////////////////// -// Private member variable and method declarations - -private: - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: Clear -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Clears all the member variables of this Turret, effectively -// resetting the members of this abstraction level only. -// Arguments: None. -// Return value: None. - - void Clear(); - - - // Disallow the use of some implicit methods. - Turret(const Turret &reference) = delete; - Turret & operator=(const Turret &rhs) = delete; - -}; - -} // namespace RTE - -#endif // File \ No newline at end of file +#include "HeldDevice.h" + +namespace RTE { + + class HeldDevice; + + /// + /// A detachable turret pod that can hold HeldDevices. + /// + class Turret : public Attachable { + + public: + + EntityAllocation(Turret) + SerializableOverrideMethods + ClassInfoGetters + +#pragma region Creation + /// + /// Constructor method used to instantiate a Turret object in system memory. Create() should be called before using the object. + /// + Turret() { Clear(); } + + /// + /// Creates a Turret to be identical to another, by deep copy. + /// + /// A reference to the Turret to deep copy. + /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int Create(const Turret &reference); +#pragma endregion + +#pragma region Destruction + /// + /// Destructor method used to clean up a Turret object before deletion from system memory. + /// + ~Turret() override { Destroy(true); } + + /// + /// Destroys and resets (through Clear()) the Turret object. + /// + /// Whether to only destroy the members defined in this derived class, or to destroy all inherited members also. + void Destroy(bool notInherited = false) override { if (!notInherited) { Attachable::Destroy(); } Clear(); } + + /// + /// Resets the entire Turret, including its inherited members, to their default settings or values. + /// + void Reset() override { Clear(); Attachable::Reset(); } +#pragma endregion + +#pragma region Getters and Setters + /// + /// Indicates whether a HeldDevice is mounted or not. + /// + /// Whether or not a HeldDevice is mounted on this Turret. + bool HasMountedDevice() const { return m_MountedDevice != nullptr; } + + /// + /// Gets the mounted HeldDevice of this Turret. + /// + /// A pointer to mounted HeldDevice of this Turret. Ownership is NOT transferred! + HeldDevice * GetMountedDevice() const { return m_MountedDevice; } + + /// + /// Sets the mounted HeldDevice for this Turret. Ownership IS transferred! + /// The currently mounted HeldDevice (if there is one) will be dropped and added to MovableMan. + /// + /// The new HeldDevice to use. + void SetMountedDevice(HeldDevice *newMountedDevice); + + /// + /// Indicates whether a ThrownDevice is mounted or not. + /// + /// Whether or not a ThrownDevice is mounted on this Turret. + bool IsThrownDeviceMounted() const { return m_MountedDevice && m_MountedDevice->IsThrownDevice(); } + + /// + /// Sets the current rotational offset of the mounted HeldDevice from the rest of the Turret. + /// + /// The new offset angle in radians, relative from the rest of the turret. + void SetMountedDeviceRotOffset(float newOffsetAngle) { m_MountedDeviceRotOffset = newOffsetAngle; } +#pragma endregion + +#pragma region Override Methods + /// + /// Updates this MovableObject. Supposed to be done every frame. + /// + void Update() override; + + /// + /// Draws this Turret's current graphical representation to a BITMAP of choice. + /// + /// A pointer to a BITMAP to draw on. + /// The absolute position of the target bitmap's upper left corner in the Scene. + /// In which mode to draw in. See the DrawMode enumeration for the modes. + /// Whether to not draw any extra 'ghost' items of this MovableObject, indicator arrows or hovering HUD text and so on. + void Draw(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), DrawMode mode = g_DrawColor, bool onlyPhysical = false) const override; +#pragma endregion + + protected: + + static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. + + HeldDevice *m_MountedDevice; //!< Pointer to the mounted HeldDevice of this Turret, if any. + float m_MountedDeviceRotOffset; //!< The relative offset angle (in radians) of the mounted HeldDevice from this Turret's rotation. + + private: + + /// + /// Clears all the member variables of this Turret, effectively resetting the members of this abstraction level only. + /// + void Clear(); + + // Disallow the use of some implicit methods. + Turret(const Turret &reference) = delete; + Turret & operator=(const Turret &rhs) = delete; + }; +} +#endif \ No newline at end of file diff --git a/Managers/LuaMan.cpp b/Managers/LuaMan.cpp index 5dda4a2bc..e60f09162 100644 --- a/Managers/LuaMan.cpp +++ b/Managers/LuaMan.cpp @@ -53,6 +53,7 @@ // LuaBind #include "luabind/luabind.hpp" #include "luabind/operator.hpp" +#include "luabind/copy_policy.hpp" #include "luabind/adopt_policy.hpp" #include "luabind/out_value_policy.hpp" #include "luabind/iterator_policy.hpp" @@ -188,6 +189,8 @@ struct enum_wrapper { // These are expanded by the preprocessor to all the different cloning function definitions. LUAENTITYCREATE(SoundContainer) LUAENTITYCREATE(Attachable) +LUAENTITYCREATE(Arm) +LUAENTITYCREATE(Leg) LUAENTITYCREATE(AEmitter) LUAENTITYCREATE(Turret) LUAENTITYCREATE(Actor) @@ -234,6 +237,9 @@ LUAENTITYCLONE(SoundContainer) LUAENTITYCLONE(SceneObject) LUAENTITYCLONE(MovableObject) LUAENTITYCLONE(Attachable) +LUAENTITYCLONE(Arm) +LUAENTITYCLONE(Leg) +LUAENTITYCLONE(Emission) LUAENTITYCLONE(AEmitter) LUAENTITYCLONE(Turret) LUAENTITYCLONE(Actor) @@ -383,6 +389,30 @@ void AddParticle(MovableMan &This, MovableObject *pParticle) double NormalRand() { return RandomNormalNum(); } double PosRand() { return RandomNum(); } +/* +These methods are needed to specially handling removing attachables with Lua in order to avoid memory leaks. They have silly names cause luabind otherwise makes it difficult to pass values to them properly. +Eventually RemoveAttachable should return the removed attachable, making this whole thing no longer unsafe and these methods unnecessary (there's a TODO in MOSRotating.h for it). +*/ +bool RemoveAttachableLuaSafe4(MOSRotating *luaSelfObject, Attachable *attachable, bool addToMovableMan, bool addBreakWounds) { + if (!addToMovableMan && !attachable->IsSetToDelete()) { + attachable->SetToDelete(); + } + return luaSelfObject->RemoveAttachable(attachable, addToMovableMan, addBreakWounds); +} +bool RemoveAttachableLuaSafe3(MOSRotating *luaSelfObject, Attachable *attachable) { + return RemoveAttachableLuaSafe4(luaSelfObject, attachable, false, false); +} +bool RemoveAttachableLuaSafe2(MOSRotating *luaSelfObject, long attachableUniqueID, bool addToMovableMan, bool addBreakWounds) { + MovableObject *attachableAsMovableObject = g_MovableMan.FindObjectByUniqueID(attachableUniqueID); + if (attachableAsMovableObject) { + return RemoveAttachableLuaSafe4(luaSelfObject, dynamic_cast(attachableAsMovableObject), addToMovableMan, addBreakWounds); + } + return false; +} +bool RemoveAttachableLuaSafe1(MOSRotating *luaSelfObject, long attachableUniqueID) { + return RemoveAttachableLuaSafe2(luaSelfObject, attachableUniqueID, false, false); +} + /* ////////////////////////////////////////////////////////////////////////////////////////// // Wrapper for the GAScripted so we can derive new classes from it purely in lua: @@ -498,6 +528,7 @@ int LuaMan::Create() { .property("AbsRadAngle", &Vector::GetAbsRadAngle) .property("AbsDegAngle", &Vector::GetAbsDegAngle) .def("CapMagnitude", &Vector::CapMagnitude) + .def("ClampMagnitude", &Vector::ClampMagnitude) .def("FlipX", &Vector::FlipX) .def("FlipY", &Vector::FlipY) .def("IsZero", &Vector::IsZero) @@ -618,6 +649,11 @@ int LuaMan::Create() { .def("AddSound", (void (SoundSet:: *)(std::string const &soundFilePath, const Vector &offset, float minimumAudibleDistance, float attenuationStartDistance)) &SoundSet::AddSound) .def("AddSoundSet", &SoundSet::AddSoundSet), + class_("LimbPath") + .property("StartOffset", &LimbPath::GetStartOffset, &LimbPath::SetStartOffset) + .property("SegmentCount", &LimbPath::GetSegCount) + .def("GetSegment", &LimbPath::GetSegment), + ABSTRACTLUABINDING(SceneObject, Entity) .property("Pos", &SceneObject::GetPos, &SceneObject::SetPos) .property("HFlipped", &SceneObject::IsHFlipped, &SceneObject::SetHFlipped) @@ -670,6 +706,7 @@ int LuaMan::Create() { .property("IgnoresTeamHits", &MovableObject::IgnoresTeamHits, &MovableObject::SetIgnoresTeamHits) .property("IgnoresWhichTeam", &MovableObject::IgnoresWhichTeam) .property("IgnoreTerrain", &MovableObject::IgnoreTerrain, &MovableObject::SetIgnoreTerrain) + .def("GetWhichMOToNotHit", &MovableObject::GetWhichMOToNotHit) .def("SetWhichMOToNotHit", &MovableObject::SetWhichMOToNotHit) .property("ToSettle", &MovableObject::ToSettle, &MovableObject::SetToSettle) .property("ToDelete", &MovableObject::ToDelete, &MovableObject::SetToDelete) @@ -766,6 +803,7 @@ int LuaMan::Create() { .def("IsOnScenePoint", &MOSprite::IsOnScenePoint) .def("RotateOffset", &MOSprite::RotateOffset) .def("UnRotateOffset", &MOSprite::UnRotateOffset) + .def("FacingAngle", &MOSprite::FacingAngle) .def("GetSpriteWidth", &MOSprite::GetSpriteWidth) .def("GetSpriteHeight", &MOSprite::GetSpriteHeight) .def("SetEntryWound", &MOSprite::SetEntryWound) @@ -777,13 +815,16 @@ int LuaMan::Create() { CONCRETELUABINDING(MOSRotating, MOSprite) /*.property("Material", &MOSRotating::GetMaterial)*/ + .property("IndividualRadius", &MOSRotating::GetIndividualRadius) + .property("IndividualDiameter", &MOSRotating::GetIndividualDiameter) + .property("IndividualMass", &MOSRotating::GetIndividualMass) .property("RecoilForce", &MOSRotating::GetRecoilForce) .property("RecoilOffset", &MOSRotating::GetRecoilOffset) .property("TravelImpulse", &MOSRotating::GetTravelImpulse, &MOSRotating::SetTravelImpulse) - .property("GibWoundLimit", &MOSRotating::GetGibWoundLimit, &MOSRotating::SetGibWoundLimit) + .property("GibWoundLimit", (int (MOSRotating:: *)() const) &MOSRotating::GetGibWoundLimit, &MOSRotating::SetGibWoundLimit) .property("GibImpulseLimit", &MOSRotating::GetGibImpulseLimit, &MOSRotating::SetGibImpulseLimit) .property("DamageMultiplier", &MOSRotating::GetDamageMultiplier, &MOSRotating::SetDamageMultiplier) - .property("WoundCount", &MOSRotating::GetWoundCount) + .property("WoundCount", (int (MOSRotating:: *)() const) &MOSRotating::GetWoundCount) .def("AddRecoil", &MOSRotating::AddRecoil) .def("SetRecoil", &MOSRotating::SetRecoil) .def("IsRecoiled", &MOSRotating::IsRecoiled) @@ -793,10 +834,13 @@ int LuaMan::Create() { // Free function bound as member function to emulate default variables .def("GibThis", &GibThis) .def("MoveOutOfTerrain", &MOSRotating::MoveOutOfTerrain) - .def("ApplyForces", &MOSRotating::ApplyForces) - .def("ApplyImpulses", &MOSRotating::ApplyImpulses) + .def("GetGibWoundLimit", (int (MOSRotating:: *)() const) &MOSRotating::GetGibWoundLimit) + .def("GetGibWoundLimit", (int (MOSRotating:: *)(bool positiveDamage, bool negativeDamage, bool noDamage) const) &MOSRotating::GetGibWoundLimit) + .def("GetWoundCount", (int (MOSRotating:: *)() const) &MOSRotating::GetWoundCount) + .def("GetWoundCount", (int (MOSRotating:: *)(bool positiveDamage, bool negativeDamage, bool noDamage) const) &MOSRotating::GetWoundCount) .def("AddWound", &MOSRotating::AddWound, adopt(_2)) - .def("RemoveWounds", &MOSRotating::RemoveWounds) + .def("RemoveWounds", (float (MOSRotating:: *)(int numberOfWoundsToRemove)) &MOSRotating::RemoveWounds) + .def("RemoveWounds", (float (MOSRotating:: *)(int numberOfWoundsToRemove, bool positiveDamage, bool negativeDamage, bool noDamage)) &MOSRotating::RemoveWounds) .def("IsOnScenePoint", &MOSRotating::IsOnScenePoint) .def("EraseFromTerrain", &MOSRotating::EraseFromTerrain) .def("GetStringValue", &MOSRotating::GetStringValue) @@ -813,32 +857,39 @@ int LuaMan::Create() { .def("ObjectValueExists", &MOSRotating::ObjectValueExists) .def("AddAttachable", (void (MOSRotating::*)(Attachable *attachableToAdd))&MOSRotating::AddAttachable, adopt(_2)) .def("AddAttachable", (void (MOSRotating::*)(Attachable *attachableToAdd, const Vector &parentOffset))&MOSRotating::AddAttachable, adopt(_2)) + .def("RemoveAttachable", &RemoveAttachableLuaSafe1) + .def("RemoveAttachable", &RemoveAttachableLuaSafe2) + .def("RemoveAttachable", &RemoveAttachableLuaSafe3) + .def("RemoveAttachable", &RemoveAttachableLuaSafe4) + /* + .def("RemoveAttachable", (bool (MOSRotating:: *)(long uniqueIDOfAttachableToRemove)) &MOSRotating::RemoveAttachable) + .def("RemoveAttachable", (bool (MOSRotating:: *)(long uniqueIDOfAttachableToRemove, bool addToMovableMan, bool addBreakWounds)) &MOSRotating::RemoveAttachable) .def("RemoveAttachable", (bool (MOSRotating::*)(Attachable *attachableToRemove))&MOSRotating::RemoveAttachable) - .def("RemoveAttachable", (bool (MOSRotating::*)(long uniqueIDOfAttachableToRemove))&MOSRotating::RemoveAttachable) + .def("RemoveAttachable", (bool (MOSRotating:: *)(Attachable *attachableToRemove, bool addToMovableMan, bool addBreakWounds)) &MOSRotating::RemoveAttachable) + */ .def("AddEmitter", (void (MOSRotating::*)(Attachable *attachableToAdd))&MOSRotating::AddAttachable, adopt(_2)) .def("AddEmitter", (void (MOSRotating::*)(Attachable *attachableToAdd, const Vector &parentOffset))&MOSRotating::AddAttachable, adopt(_2)) .def("RemoveEmitter", (bool (MOSRotating::*)(Attachable *attachableToRemove))&MOSRotating::RemoveAttachable) .def("RemoveEmitter", (bool (MOSRotating::*)(long uniqueIDOfAttachableToRemove))&MOSRotating::RemoveAttachable) - .def_readonly("Attachables", &MOSRotating::m_AllAttachables, return_stl_iterator) + .def_readonly("Attachables", &MOSRotating::m_Attachables, return_stl_iterator) .def_readonly("Wounds", &MOSRotating::m_Wounds, return_stl_iterator), CONCRETELUABINDING(Attachable, MOSRotating) - .property("ParentOffset", &Attachable::GetParentOffset, &Attachable::SetParentOffset) - .property("JointOffset", &Attachable::GetJointOffset, &Attachable::SetJointOffset) - .property("JointStiffness", &Attachable::GetJointStiffness, &Attachable::SetJointStiffness) - .property("JointStrength", &Attachable::GetJointStrength, &Attachable::SetJointStrength) - .property("RotTarget", &Attachable::GetRotTarget, &Attachable::SetRotTarget) - .property("AtomSubgroupID", &Attachable::GetAtomSubgroupID, &Attachable::SetAtomSubgroupID) - .property("OnlyLinearForces", &Attachable::GetOnlyLinearForces, &Attachable::SetOnlyLinearForces) .def("IsAttached", &Attachable::IsAttached) .def("IsAttachedTo", &Attachable::IsAttachedTo) + .property("ParentOffset", &Attachable::GetParentOffset, &Attachable::SetParentOffset) .def("IsDrawnAfterParent", &Attachable::IsDrawnAfterParent) - .def("TransferJointForces", &Attachable::TransferJointForces) - .def("TransferJointImpulses", &Attachable::TransferJointImpulses) - .def("CollectDamage", &Attachable::CollectDamage) + .property("JointStrength", &Attachable::GetJointStrength, &Attachable::SetJointStrength) + .property("JointStiffness", &Attachable::GetJointStiffness, &Attachable::SetJointStiffness) + .property("JointOffset", &Attachable::GetJointOffset, &Attachable::SetJointOffset) + .property("ApplyTransferredForcesAtOffset", &Attachable::GetApplyTransferredForcesAtOffset, &Attachable::SetApplyTransferredForcesAtOffset) + .property("BreakWound", &Attachable::GetBreakWound, &Attachable::SetBreakWound, detail::null_type(), adopt(_2)) + .property("ParentBreakWound", &Attachable::GetParentBreakWound, &Attachable::SetParentBreakWound, detail::null_type(), adopt(_2)) + .property("InheritsHFlipped", &Attachable::InheritsHFlipped, &Attachable::SetInheritsHFlipped) .property("InheritsRotAngle", &Attachable::InheritsRotAngle, &Attachable::SetInheritsRotAngle) - .property("IsCollidingWithTerrainWhileAttached", &Attachable::IsCollidingWithTerrainWhileAttached) - .def("EnableTerrainCollisions", &Attachable::EnableTerrainCollisions), + .property("InheritedRotAngleOffset", &Attachable::GetInheritedRotAngleOffset, &Attachable::SetInheritedRotAngleOffset) + .property("AtomSubgroupID", &Attachable::GetAtomSubgroupID) + .property("CollidesWithTerrainWhileAttached", &Attachable::GetCollidesWithTerrainWhileAttached, &Attachable::SetCollidesWithTerrainWhileAttached), ABSTRACTLUABINDING(Emission, Entity) .property("ParticlesPerMinute", &Emission::GetRate, &Emission::SetRate) @@ -863,6 +914,7 @@ int LuaMan::Create() { .property("EmitterDamageMultiplier", &AEmitter::GetEmitterDamageMultiplier, &AEmitter::SetEmitterDamageMultiplier) .property("EmitCountLimit", &AEmitter::GetEmitCountLimit, &AEmitter::SetEmitCountLimit) .property("EmitDamage", &AEmitter::GetEmitDamage, &AEmitter::SetEmitDamage) + .property("Flash", &AEmitter::GetFlash, &AEmitter::SetFlash, detail::null_type(), adopt(_2)) .property("FlashScale", &AEmitter::GetFlashScale, &AEmitter::SetFlashScale) .def("GetEmitVector", &AEmitter::GetEmitVector) .def("GetRecoilVector", &AEmitter::GetRecoilVector) @@ -956,6 +1008,7 @@ int LuaMan::Create() { .property("Status", &Actor::GetStatus, &Actor::SetStatus) .property("Health", &Actor::GetHealth, &Actor::SetHealth) .property("MaxHealth", &Actor::GetMaxHealth, &Actor::SetMaxHealth) + .property("InventoryMass", &Actor::GetInventoryMass) .property("GoldCarried", &Actor::GetGoldCarried, &Actor::SetGoldCarried) .property("AimRange", &Actor::GetAimRange, &Actor::SetAimRange) .def("GetAimAngle", &Actor::GetAimAngle) @@ -970,7 +1023,6 @@ int LuaMan::Create() { .def("AddHealth", &Actor::AddHealth) .def("IsStatus", &Actor::IsStatus) .def("IsDead", &Actor::IsDead) - .def("FacingAngle", &Actor::FacingAngle) .property("AIMode", &Actor::GetAIMode, &Actor::SetAIMode) .property("DeploymentID", &Actor::GetDeploymentID) .property("PassengerSlots", &Actor::GetPassengerSlots, &Actor::SetPassengerSlots) @@ -1006,9 +1058,6 @@ int LuaMan::Create() { .def("GetAlarmPoint", &Actor::GetAlarmPoint) .property("AimDistance", &Actor::GetAimDistance, &Actor::SetAimDistance) .property("SightDistance", &Actor::GetSightDistance, &Actor::SetSightDistance) - .property("TotalWoundCount", &Actor::GetTotalWoundCount) - .property("TotalWoundLimit", &Actor::GetTotalWoundLimit) - .def("RemoveAnyRandomWounds", &Actor::RemoveAnyRandomWounds) .property("DeathSound", &Actor::GetDeathSound, &Actor::SetDeathSound), CONCRETELUABINDING(ADoor, Actor) @@ -1019,19 +1068,21 @@ int LuaMan::Create() { value("CLOSING", ADoor::DoorState::CLOSING), value("STOPPED", ADoor::DoorState::STOPPED) ] - .property("Door", &ADoor::GetDoor) + .property("Door", &ADoor::GetDoor, &ADoor::SetDoor, detail::null_type(), adopt(_2)) .def("GetDoorState", &ADoor::GetDoorState) .def("OpenDoor", &ADoor::OpenDoor) .def("CloseDoor", &ADoor::CloseDoor) .def("StopDoor", &ADoor::StopDoor) .def("SetClosedByDefault", &ADoor::SetClosedByDefault), - ABSTRACTLUABINDING(Arm, Attachable) + CONCRETELUABINDING(Arm, Attachable) + .property("HeldDevice", &Arm::GetHeldMO) .property("IdleOffset", &Arm::GetIdleOffset, &Arm::SetIdleOffset) + .property("GripStrength", &Arm::GetGripStrength, &Arm::SetGripStrength) .property("HandPos", &Arm::GetHandPos, &Arm::SetHandPos), - ABSTRACTLUABINDING(Leg, Attachable) - .property("Foot", &Leg::GetFoot), + CONCRETELUABINDING(Leg, Attachable) + .property("Foot", &Leg::GetFoot, &Leg::SetFoot, detail::null_type(), adopt(_2)), CONCRETELUABINDING(AHuman, Actor) // These are all private/protected so they can't be bound, need to consider making them public. @@ -1101,14 +1152,14 @@ int LuaMan::Create() { value("LANDJUMP", 5 /*AHuman::JumpState::LANDJUMP*/) ] .def(constructor<>()) - .property("Head", &AHuman::GetHead) - .property("FGArm", &AHuman::GetFGArm) - .property("BGArm", &AHuman::GetBGArm) - .property("FGLeg", &AHuman::GetFGLeg) - .property("FGFoot", &AHuman::GetFGFoot) - .property("BGLeg", &AHuman::GetBGLeg) - .property("BGFoot", &AHuman::GetBGFoot) - .property("Jetpack", &AHuman::GetJetpack) + .property("Head", &AHuman::GetHead, &AHuman::SetHead, detail::null_type(), adopt(_2)) + .property("Jetpack", &AHuman::GetJetpack, &AHuman::SetJetpack, detail::null_type(), adopt(_2)) + .property("FGArm", &AHuman::GetFGArm, &AHuman::SetFGArm, detail::null_type(), adopt(_2)) + .property("BGArm", &AHuman::GetBGArm, &AHuman::SetBGArm, detail::null_type(), adopt(_2)) + .property("FGLeg", &AHuman::GetFGLeg, &AHuman::SetFGLeg, detail::null_type(), adopt(_2)) + .property("BGLeg", &AHuman::GetBGLeg, &AHuman::SetBGLeg, detail::null_type(), adopt(_2)) + .property("FGFoot", &AHuman::GetFGFoot, &AHuman::SetFGFoot, detail::null_type(), adopt(_2)) + .property("BGFoot", &AHuman::GetBGFoot, &AHuman::SetBGFoot, detail::null_type(), adopt(_2)) .property("JetTimeTotal", &AHuman::GetJetTimeTotal, &AHuman::SetJetTimeTotal) .property("JetTimeLeft", &AHuman::GetJetTimeLeft, &AHuman::SetJetTimeLeft) .property("ThrowPrepTime", &AHuman::GetThrowPrepTime, &AHuman::SetThrowPrepTime) @@ -1135,9 +1186,12 @@ int LuaMan::Create() { .def("LookForGold", &AHuman::LookForGold) .def("LookForMOs", &AHuman::LookForMOs) .def("IsOnScenePoint", &AHuman::IsOnScenePoint) + .def("GetLimbPath", &AHuman::GetLimbPath) .property("LimbPathPushForce", &AHuman::GetLimbPathPushForce, &AHuman::SetLimbPathPushForce) .def("GetLimbPathSpeed", &AHuman::GetLimbPathSpeed) - .def("SetLimbPathSpeed", &AHuman::SetLimbPathSpeed), + .def("SetLimbPathSpeed", &AHuman::SetLimbPathSpeed) + .def("GetRotAngleTarget", &AHuman::GetRotAngleTarget) + .def("SetRotAngleTarget", &AHuman::SetRotAngleTarget), CONCRETELUABINDING(ACrab, Actor) // These are all private/protected so they can't be bound, need to consider making them public. @@ -1192,12 +1246,12 @@ int LuaMan::Create() { value("LANDJUMP", 5 /*ACrab::JumpState::LANDJUMP*/) ] .def(constructor<>()) - .property("Turret", &ACrab::GetTurret) - .property("LFGLeg", &ACrab::GetLFGLeg) - .property("LBGLeg", &ACrab::GetLBGLeg) - .property("RFGLeg", &ACrab::GetRFGLeg) - .property("RBGLeg", &ACrab::GetRBGLeg) - .property("Jetpack", &ACrab::GetJetpack) + .property("Turret", &ACrab::GetTurret, &ACrab::SetTurret, detail::null_type(), adopt(_2)) + .property("Jetpack", &ACrab::GetJetpack, &ACrab::SetJetpack, detail::null_type(), adopt(_2)) + .property("LeftFGLeg", &ACrab::GetLeftFGLeg, &ACrab::SetLeftFGLeg, detail::null_type(), adopt(_2)) + .property("LeftBGLeg", &ACrab::GetLeftBGLeg, &ACrab::SetLeftBGLeg, detail::null_type(), adopt(_2)) + .property("RightFGLeg", &ACrab::GetRightFGLeg, &ACrab::SetRightFGLeg, detail::null_type(), adopt(_2)) + .property("RightBGLeg", &ACrab::GetRightBGLeg, &ACrab::SetRightBGLeg, detail::null_type(), adopt(_2)) .property("JetTimeTotal", &ACrab::GetJetTimeTotal, &ACrab::SetJetTimeTotal) .property("JetTimeLeft", &ACrab::GetJetTimeLeft) .property("EquippedItem", &ACrab::GetEquippedItem) @@ -1211,11 +1265,13 @@ int LuaMan::Create() { .def("Look", &ACrab::Look) .def("LookForMOs", &ACrab::LookForMOs) .def("IsOnScenePoint", &ACrab::IsOnScenePoint) + .def("GetLimbPath", &ACrab::GetLimbPath) .property("LimbPathPushForce", &ACrab::GetLimbPathPushForce, &ACrab::SetLimbPathPushForce) .def("GetLimbPathSpeed", &ACrab::GetLimbPathSpeed) .def("SetLimbPathSpeed", &ACrab::SetLimbPathSpeed), - ABSTRACTLUABINDING(Turret, Attachable), + CONCRETELUABINDING(Turret, Attachable) + .property("MountedDevice", &Turret::GetMountedDevice, &Turret::SetMountedDevice, detail::null_type(), adopt(_2)), ABSTRACTLUABINDING(ACraft, Actor) .enum_("HatchState")[ @@ -1251,12 +1307,12 @@ int LuaMan::Create() { .property("DeliveryDelayMultiplier", &ACraft::GetDeliveryDelayMultiplier), CONCRETELUABINDING(ACDropShip, ACraft) - .property("RightEngine", &ACDropShip::GetRThruster) - .property("LeftEngine", &ACDropShip::GetLThruster) - .property("RightThruster", &ACDropShip::GetURThruster) - .property("LeftThruster", &ACDropShip::GetULThruster) - .property("LeftHatch", &ACDropShip::GetLHatch) - .property("RightHatch", &ACDropShip::GetRHatch) + .property("RightEngine", &ACDropShip::GetRightThruster, &ACDropShip::SetRightThruster, detail::null_type(), adopt(_2)) + .property("LeftEngine", &ACDropShip::GetLeftThruster, &ACDropShip::SetLeftThruster, detail::null_type(), adopt(_2)) + .property("RightThruster", &ACDropShip::GetURightThruster, &ACDropShip::SetURightThruster, detail::null_type(), adopt(_2)) + .property("LeftThruster", &ACDropShip::GetULeftThruster, &ACDropShip::SetULeftThruster, detail::null_type(), adopt(_2)) + .property("RightHatch", &ACDropShip::GetRightHatch, &ACDropShip::SetRightHatch, detail::null_type(), adopt(_2)) + .property("LeftHatch", &ACDropShip::GetLeftHatch, &ACDropShip::SetLeftHatch, detail::null_type(), adopt(_2)) .property("MaxEngineAngle", &ACDropShip::GetMaxEngineAngle, &ACDropShip::SetMaxEngineAngle) .property("LateralControlSpeed", &ACDropShip::GetLateralControlSpeed, &ACDropShip::SetLateralControlSpeed) .property("LateralControl", &ACDropShip::GetLateralControl) @@ -1272,11 +1328,13 @@ int LuaMan::Create() { value("RAISING", 3 /*ACRocket::LandingGearState::RAISING*/), value("GearStateCount", 4 /*ACRocket::LandingGearState::GearStateCount*/) ] - .property("MainEngine", &ACRocket::GetMThruster) - .property("LeftEngine", &ACRocket::GetLThruster) - .property("RightEngine", &ACRocket::GetRThruster) - .property("LeftThruster", &ACRocket::GetULThruster) - .property("RightThruster", &ACRocket::GetURThruster) + .property("RightLeg", &ACRocket::GetRightLeg, &ACRocket::SetRightLeg, detail::null_type(), adopt(_2)) + .property("LeftLeg", &ACRocket::GetLeftLeg, &ACRocket::SetLeftLeg, detail::null_type(), adopt(_2)) + .property("MainEngine", &ACRocket::GetMainThruster, &ACRocket::SetMainThruster, detail::null_type(), adopt(_2)) + .property("LeftEngine", &ACRocket::GetLeftThruster, &ACRocket::SetLeftThruster, detail::null_type(), adopt(_2)) + .property("RightEngine", &ACRocket::GetRightThruster, &ACRocket::SetRightThruster, detail::null_type(), adopt(_2)) + .property("LeftThruster", &ACRocket::GetULeftThruster, &ACRocket::SetULeftThruster, detail::null_type(), adopt(_2)) + .property("RightThruster", &ACRocket::GetURightThruster, &ACRocket::SetURightThruster, detail::null_type(), adopt(_2)) .property("GearState", &ACRocket::GetGearState), CONCRETELUABINDING(HeldDevice, Attachable) @@ -1304,6 +1362,12 @@ int LuaMan::Create() { .def("IsFull", &HeldDevice::IsFull) .property("SharpLength", &HeldDevice::GetSharpLength, &HeldDevice::SetSharpLength) .property("SupportOffset", &HeldDevice::GetSupportOffset, &HeldDevice::SetSupportOffset) + .property("HasPickupLimitations", &HeldDevice::HasPickupLimitations) + .property("UnPickupable", &HeldDevice::IsUnPickupable, &HeldDevice::SetUnPickupable) + .def("IsPickupableBy", &HeldDevice::IsPickupableBy) + .def("AddPickupableByPresetName", &HeldDevice::AddPickupableByPresetName) + .def("RemovePickupableByPresetName", &HeldDevice::RemovePickupableByPresetName) + .property("GripStrengthMultiplier", &HeldDevice::GetGripStrengthMultiplier, &HeldDevice::SetGripStrengthMultiplier) .def("SetSupported", &HeldDevice::SetSupported), CONCRETELUABINDING(Magazine, Attachable) @@ -1330,7 +1394,8 @@ int LuaMan::Create() { .property("RateOfFire", &HDFirearm::GetRateOfFire, &HDFirearm::SetRateOfFire) .property("FullAuto", &HDFirearm::IsFullAuto, &HDFirearm::SetFullAuto) .property("RoundInMagCount", &HDFirearm::GetRoundInMagCount) - .property("Magazine", &HDFirearm::GetMagazine) + .property("Magazine", &HDFirearm::GetMagazine, &HDFirearm::SetMagazine, detail::null_type(), adopt(_2)) + .property("Flash", &HDFirearm::GetFlash, &HDFirearm::SetFlash, detail::null_type(), adopt(_2)) .property("ActivationDelay", &HDFirearm::GetActivationDelay, &HDFirearm::SetActivationDelay) .property("DeactivationDelay", &HDFirearm::GetDeactivationDelay, &HDFirearm::SetDeactivationDelay) .property("ReloadTime", &HDFirearm::GetReloadTime, &HDFirearm::SetReloadTime) @@ -1349,7 +1414,7 @@ int LuaMan::Create() { .def("CompareTrajectories", &HDFirearm::CompareTrajectories) .def("SetNextMagazineName", &HDFirearm::SetNextMagazineName) .property("IsAnimatedManually", &HDFirearm::IsAnimatedManually, &HDFirearm::SetAnimatedManually) - .property("RecoilTransmission", &HDFirearm::GetRecoilTransmission, &HDFirearm::SetRecoilTransmission), + .property("RecoilTransmission", &HDFirearm::GetJointStiffness, &HDFirearm::SetJointStiffness), CONCRETELUABINDING(ThrownDevice, HeldDevice) .property("MinThrowVel", &ThrownDevice::GetMinThrowVel, &ThrownDevice::SetMinThrowVel) diff --git a/Managers/MovableMan.cpp b/Managers/MovableMan.cpp index 6e54a5048..29c5a1443 100644 --- a/Managers/MovableMan.cpp +++ b/Managers/MovableMan.cpp @@ -17,7 +17,7 @@ #include "PresetMan.h" #include "AHuman.h" #include "MOPixel.h" -#include "Attachable.h" +#include "HeldDevice.h" #include "SLTerrain.h" #include "Controller.h" #include "AtomGroup.h" @@ -781,21 +781,21 @@ Actor * MovableMan::GetUnassignedBrain(int team) const // best way to add it is. E.g. if it's an Actor, it will be added as such. // Ownership IS transferred! -bool MovableMan::AddMO(MovableObject *pMOToAdd) -{ - if (!pMOToAdd) +bool MovableMan::AddMO(MovableObject *pMOToAdd) { + if (!pMOToAdd) { return false; + } pMOToAdd->SetAsAddedToMovableMan(); // Find out what kind it is and apply accordingly - if (Actor *pActor = dynamic_cast(pMOToAdd)) - { + if (Actor *pActor = dynamic_cast(pMOToAdd)) { AddActor(pActor); return true; - } - else - { + } else if (HeldDevice *pHeldDevice = dynamic_cast(pMOToAdd)) { + AddItem(pHeldDevice); + return true; + } else { AddParticle(pMOToAdd); return true; } @@ -2116,7 +2116,7 @@ void MovableMan::UpdateDrawMOIDs(BITMAP *pTargetBitmap) currentMOID = m_MOIDIndex.size(); } else - m_Actors[i]->SetID(g_NoMOID); + m_Actors[i]->SetAsNoID(); } for (i = 0; i < iCount; ++i) { @@ -2127,7 +2127,7 @@ void MovableMan::UpdateDrawMOIDs(BITMAP *pTargetBitmap) currentMOID = m_MOIDIndex.size(); } else - m_Items[i]->SetID(g_NoMOID); + m_Items[i]->SetAsNoID(); } for (i = 0; i < parCount; ++i) { @@ -2138,7 +2138,7 @@ void MovableMan::UpdateDrawMOIDs(BITMAP *pTargetBitmap) currentMOID = m_MOIDIndex.size(); } else - m_Particles[i]->SetID(g_NoMOID); + m_Particles[i]->SetAsNoID(); } } diff --git a/System/ContentFile.cpp b/System/ContentFile.cpp index 530f1eb8a..8b89ba9fd 100644 --- a/System/ContentFile.cpp +++ b/System/ContentFile.cpp @@ -248,7 +248,7 @@ namespace RTE { FMOD_RESULT result = g_AudioMan.GetAudioSystem()->createSound(m_DataPath.c_str(), fmodFlags, nullptr, &returnSample); if (result != FMOD_OK) { - const std::string errorMessage = "Failed to create sound because of FMOD error: " + std::string(FMOD_ErrorString(result)) + " The path and name were: "; + const std::string errorMessage = "Failed to create sound because of FMOD error:\n" + std::string(FMOD_ErrorString(result)) + "\nThe path and name were: "; RTEAssert(!abortGameForInvalidSound, errorMessage + "\n\n" + m_DataPathAndReaderPosition); g_ConsoleMan.PrintString("ERROR: " + errorMessage + m_DataPath); return returnSample; diff --git a/System/RTETools.cpp b/System/RTETools.cpp index 6d88afcbb..72c14e3ff 100644 --- a/System/RTETools.cpp +++ b/System/RTETools.cpp @@ -134,6 +134,14 @@ namespace RTE { float GetAllegroAngle(float angleDegrees) { return (angleDegrees / 360) * 256; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + float DegreesToRadians(float angleInDegrees) { return angleInDegrees / 180.0F * c_PI; } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + float RadiansToDegrees(float angleInRadians) { return angleInRadians / c_PI * 180.0F; } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void OpenBrowserToURL(std::string goToURL) { diff --git a/System/RTETools.h b/System/RTETools.h index c13d4a782..d95fc8351 100644 --- a/System/RTETools.h +++ b/System/RTETools.h @@ -202,6 +202,20 @@ namespace RTE { /// The angle value to correct. In degrees. /// A float with the represented angle as full rotations being 256. float GetAllegroAngle(float angleDegrees); + + /// + /// Returns the given angle converted from degrees to radians. + /// + /// The angle in degrees to be converted. + /// The converted angle in radians. + float DegreesToRadians(float angleInDegrees); + + /// + /// Returns the given angle converted from radians to degrees. + /// + /// The angle in radians to be converted. + /// The converted angle in degrees. + float RadiansToDegrees(float angleInRadians); #pragma endregion #pragma region Misc diff --git a/System/Vector.cpp b/System/Vector.cpp index 018d28c58..e12f63fea 100644 --- a/System/Vector.cpp +++ b/System/Vector.cpp @@ -51,6 +51,20 @@ namespace RTE { return *this; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Vector & Vector::ClampMagnitude(float upperLimit, float lowerLimit) { + if (upperLimit < lowerLimit) { std::swap(upperLimit, lowerLimit); } + if (upperLimit == 0 && lowerLimit == 0) { + Reset(); + } else if (GetMagnitude() > upperLimit) { + SetMagnitude(upperLimit); + } else if (GetMagnitude() < lowerLimit) { + SetMagnitude(lowerLimit); + } + return *this; + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float Vector::GetAbsRadAngle() const { diff --git a/System/Vector.h b/System/Vector.h index 05934311c..622df919b 100644 --- a/System/Vector.h +++ b/System/Vector.h @@ -163,6 +163,14 @@ namespace RTE { /// Vector reference to this after the operation. Vector & CapMagnitude(const float capMag); + /// + /// Clamps the magnitude of this Vector between the upper and lower limits, and keeps its angle intact. + /// + /// A float value that defines the upper limit for the magnitude. + /// A float value that defines the lower limit for the magnitude. + /// A reference to this after the change. + Vector & ClampMagnitude(float upperMagnitudeLimit, float lowerMagnitudeLimit); + /// /// Returns a Vector that has the same direction as this but with a magnitude of 1.0. ///