123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 |
- function Player::reddenLimb(%player, %limb, %damage, %emphasis)
- {
- //!rBloodPlayerShapeAllowed(%player.getDataBlock().shapeFile)
- if(!$Pref::RBloodMod::LimbReddening || !rBloodPlayerShapeAllowed(%player.getDataBlock().shapeFile))
- return;
-
- if(%emphasis $= "")
- %emphasis = 0;
-
- %emphasis += $Pref::RBloodMod::LimbReddeningFactor;
-
- // switch chest string to one specifically meant for redding and not dismemberment
- if(%limb == 1)
- %limb = 6;
-
- // find out how much reddening we will do based on damage
- %damageMax = %player.getDatablock().maxDamage * getWord(%player.scale, 2);
- %damage = mCeil((mClampF(%damage, 0, %damageMax) / %damageMax) * 5);
-
- //%emphasis = mClamp(mCeil((%damageMax - %damage) / 40), 1, 100);
-
- for(%i = 0; %i < getWordCount($RBloodLimbString[%limb]); %i++)
- {
- %limbName = getWord($RBloodLimbString[%limb], %i);
-
- // incrementally blend the color
- %newColor = %player.nodeColor[%limbName];
- for(%b = 0; %b < %damage; %b++)
- {
- //%newColor = calcAdditiveRGB(%newColor, "0.4 0 0 1");
-
- if(%emphasis == 0)
- %newColor = calcAdditiveRGB(%newColor, "0.4 0 0 1");
- else
- %newColor = calcAdditiveRGB_emphasis(%newColor, "0.4 0 0 1", %emphasis);
- }
-
- // set new color
- %player.setNodeColor(%limbName, %newColor);
- %player.RBlood_NodeOverrideColor[%limbName] = %newColor;
- }
- }
- function Player::causeBleeding(%player, %duration, %bloodOverDuration, %startTime, %spread)
- {
- cancel(%player.rBloodBleedSchedule);
-
- %elapsed = (getSimTime() - %startTime);
- %stepTime = mClamp(%duration / %bloodOverDuration, 33, %duration);
-
- if(%spread > 0)
- %vector = vectorNormalize(vectorAdd("0 0 -1", ((getRandom() - 0.5) * %spread) SPC ((getRandom() - 0.5) * %spread) SPC ((getRandom() - 0.5) * %spread)));
- else
- %vector = "0 0 -1";
-
- doBloodSplatter(%vector, %player.getHackPosition(), 50);
-
- if(%elapsed > %duration)
- return;
- %player.rBloodBleedSchedule = %player.schedule(%stepTime, causeBleeding, %duration, %bloodOverDuration, %startTime, %spread);
- }
- function rbloodSplashNearbyPlayers(%position, %radius, %source, %intensity)
- {
- %mask = $TypeMasks::PlayerObjectType;
- initContainerRadiusSearch(%position, %radius, %mask);
- while(%player = containerSearchNext())
- {
- //if(%player == %source)
- // continue;
-
- %distance = mCeil(vectorDist(%player.getHackPosition(), %position));
- %iter = getRandom(3, 5);
-
- if(%intensity !$= "")
- %iter *= %intensity;
-
- if(%player == %source)
- %iter *= 0.75;
-
- %iter *= $Pref::RBloodMod::LimbReddeningSplash;
-
- for(%i = 0; %i < %iter; %i++)
- {
- %player.reddenLimb(getRandom(0, 5), 1, %distance);
- }
- }
- }
- // functions for spawning blood effects
- function doBloodExplosion(%position, %scale)
- {
- %bloodExplosionProjectile = new Projectile()
- {
- datablock = bloodBurstFinalExplosionProjectile;
- initialPosition = %position;
- };
- MissionCleanup.add(%bloodExplosionProjectile);
- %bloodExplosionProjectile.setScale(%scale SPC %scale SPC %scale);
- %bloodExplosionProjectile.explode();
- }
- function doBloodDismemberExplosion(%position, %scale)
- {
- %bloodDismemberProjectile = new Projectile()
- {
- datablock = bloodDismemberProjectile;
- initialPosition = %position;
- };
- MissionCleanup.add(%bloodDismemberProjectile);
- %bloodDismemberProjectile.setScale(%scale SPC %scale SPC %scale);
- %bloodDismemberProjectile.explode();
- }
- function doGibLimbsExplosion(%position, %scale)
- {
- for(%i = 0; %i < 3; %i++)
- {
- %datablock = $RBloodGib[%i];
- %bloodGibLimbsProjectile = new Projectile()
- {
- datablock = %datablock;
- initialPosition = %position;
- };
- MissionCleanup.add(%bloodGibLimbsProjectile);
- %bloodGibLimbsProjectile.setScale(%scale SPC %scale SPC %scale);
- %bloodGibLimbsProjectile.explode();
- }
- }
- // function for creating static-shape blood splatters that stick to surfaces
- // these arguments are hot garbage lmao. Sorry in advance to anyone who wants to use this
- function doBloodSplatter(%velocity, %position, %damage, %dataBlock)
- {
- if($Pref::RBloodMod::DisableStatic)
- return;
- if(%dataBlock !$= "")
- {
- %lifetime = %dataBlock.lifetime;
- if(%lifetime < 50)
- {
- %range = ((getRandom() - 0.5) * 6);
- }
- else
- {
- %range = 10 * (%lifetime / 125);
- }
- }
- else
- {
- %range = getRandom(8, 14);
- }
-
- %spread = %range;
-
- %projectileVector = vectorScale(vectorNormalize(%velocity), %range);
- %projectileVector = vectorAdd(%projectileVector, ((getRandom() - 0.5) * %spread) SPC ((getRandom() - 0.5) * %spread) SPC ((getRandom() - 0.5) * %spread));
- %start = %position;
- %end = vectorAdd(%position, %projectileVector);
-
- %mask = $TypeMasks::FxBrickObjectType | $TypeMasks::TerrainObjectType;
- %raycast = containerRayCast(%start, %end, %mask);
- %hit = getWord(%raycast, 0);
-
- //drawLine(%start, %end, "1 1 0 0.5", 0.2);
-
- if(isObject(%hit))
- {
- %end = getWords(%raycast, 1, 3);
- %normal = getWords(%raycast, 4, 6);
- %rotation = Normal2Rotation(%normal);
- %doStatic = true;
- }
- else
- {
- // hit nothing, simulate blood going to the floor
- %start = %end;
- %end = setWord(%start, 2, getWord(%start, 2) - 50);
- %raycast = containerRayCast(%start, %end, %mask);
- %hit = getWord(%raycast, 0);
-
- if(isObject(%hit))
- {
- %end = getWords(%raycast, 1, 3);
- %normal = getWords(%raycast, 4, 6);
- %rotation = Normal2Rotation(%normal);
- %doStatic = true;
- }
- }
-
- if(%doStatic)
- {
- %bloodPuddle = new StaticShape()
- {
- datablock = $RBloodPuddleStatic[getRandom(0, 1)];
- position = %end;
- rotation = %rotation;
- normal = %normal;
- };
- MissionCleanup.add(%bloodPuddle);
- %bloodPuddle.setNodeColor("ALL", $Pref::RBloodMod::BloodColor);
-
- %lifetime = $Pref::RBloodMod::BloodFadeTime;
- %bloodPuddle.schedule(%lifetime, delete);
- %bloodPuddle.bloodFadeLoop(%lifetime, 50, $Pref::RBloodMod::BloodColor);
-
- %scale = (mCeil(%damage / 64) + (2 * getRandom())) / 2;
- %bloodPuddle.setScale(%scale SPC %scale SPC %scale);
-
- if(!$Pref::RBloodMod::DisablePuddleTrigger)
- createBloodPuddleTrigger(%end);
-
- %downwardAngle = mRadtoDeg(mACos(vectorDot(%normal, "0 0 -1")));
- if(%downwardAngle < 80)
- {
- %bloodPuddle.doDripRBlood();
- }
- }
- }
- function createBloodPuddleTrigger(%position)
- {
- %trigger = new Trigger()
- {
- datablock = rbloodPuddleTriggerData;
- position = %position;
- polyhedron = "-0.5 -0.5 -0.5 1 0 0 0 1 0 0 0 1";
- creationTime = getSimTime();
- };
- %trigger.schedule($Pref::RBloodMod::BloodFadeTime * 0.75, delete);
- %trigger.setScale(0.5 SPC 0.5 SPC 0.2);
- return;
- }
- function rbloodPuddleTriggerData::onEnterTrigger(%data, %trigger, %player)
- {
- if(!rBloodPlayerShapeAllowed(%player.getDataBlock().shapeFile))
- return;
-
- %timeDif = getSimTime() - %trigger.creationTime;
- if(%timeDif < 1000)
- return;
- if(!%trigger.triggered)
- {
- %trigger.triggered = true;
- %player.reddenLimb(getRandom(4, 5), 1, 5);
- }
- return;
- }
- // function for fading a static shape out to invisibility over a certain period of time and increments
- function StaticShape::bloodFadeLoop(%shape, %lifetime, %increments, %color)
- {
- cancel(%shape.fadeLoopSchedule);
- %loopTime = %lifetime / %increments;
-
- %RGB = getWords(%color, 0, 2);
- %alpha = getWord(%color, 3);
-
- %alpha = %alpha - (getWord($Pref::RBloodMod::BloodColor, 3) / %increments);
-
- %newColor = %RGB SPC %alpha;
- %shape.setNodeColor("ALL", %newColor);
- %shape.fadeAlpha = %alpha;
- %shape.fadeLoopSchedule = %shape.schedule(%loopTime, bloodFadeLoop, %lifetime, %increments, %newColor);
- }
- function StaticShape::doDripRBlood(%shape)
- {
- cancel(%shape.rbloodDripLoop);
- %scale = mClampF(%shape.fadeAlpha, 0, 0.8);
-
- %vector = vectorNormalize(%shape.normal);
- %position = %shape.getPosition();
-
- %end = vectorAdd(%position, vectorScale(%vector, 0.25));
- //drawLine(%position, %end, "1 0 0 0.2", 0.2);
-
- %bloodDripProjectile = new Projectile()
- {
- datablock = rbloodDripProjectile;
- initialPosition = %end;
- initialVelocity = "0 0 -1";
- fadeAlpha = %shape.fadeAlpha;
- };
- MissionCleanup.add(%bloodDripProjectile);
- %bloodDripProjectile.setScale(%scale SPC %scale SPC %scale);
-
- %shape.rbloodDripLoop = %shape.schedule(getRandom(33, 2000), doDripRBlood);
- }
- function Player::RBloodSimulate(%obj, %position, %velocity, %projectileData, %projectileScale, %dismemberOnKill, %damage) {
- %data = %obj.getDatablock();
- //%damage = (%projectileData.directDamage * %projectileScale);
- %damage = %damage * 100 / %obj.getDatablock().maxDamage; // scale blood effects by HP %
-
- if(!rBloodPlayerShapeAllowed(%data.shapeFile))
- return;
-
- %obj.lastRBloodSimTime = getSimTime();
-
- // Some information about who we just shot
- %maxHP = %data.maxDamage;
- %currentHP = %maxHealth * (1 - %obj.getDamagePercent());
- %scale = getWord(%obj.getScale(), 2);
-
- %limb = %obj.rgetDamageLocation(%position);
-
- // Damage base blood effect
- %effectPosition = %position;
- %bloodAmount = mCeil(%damage / 16);
- %bloodAmountMax = 250;
- if(%bloodAmount>%bloodAmountMax) %bloodAmount = %bloodAmountMax;
- for(%i = 0; %i < %bloodAmount; %i++)
- {
- doBloodExplosion(%effectPosition, %scale);
- }
- schedule(0, 0, rbloodSplashNearbyPlayers, %effectPosition, 4, %obj, 0.6);
- serverPlay3D($RBlood::smallHit[getRandom(0, 3)], %effectPosition);
-
- // Blood splatters (static)
- for(%i = 0; %i < getRandom(2, 6); %i++)
- {
- doBloodSplatter(%velocity, %position, %damage, %projectileData);
- }
-
- // Hencefourth blood splattering caused by bleeding
- if(!$Pref::RBloodMod::DisableBleed)
- {
- %bloodTime = mCeil((%damage * 100) * $Pref::RBloodMod::BloodTimeScale);
- %bleedAmount = mCeil((%damage / 2) * $Pref::RBloodMod::BloodAmountScale);
- %obj.causeBleeding(%bloodTime, %bleedAmount, getSimTime());
- }
-
- // Node reddining on damage
- if($Pref::RBloodMod::LimbReddening)
- {
- %obj.reddenLimb(%limb, %damage);
- }
-
- // Dismemberment
- // Occurs only with high-powered weapons, like shotguns and 1-shot weapons
- // Dismemberment can only occur after death, so we store the limbs that are
- // marked for dismemberment, and then check the values at death for a valid
- // dismemberment condition.
- %obj.limbShotgunStrike = (getSimTime() - %obj.lastLimbHitTime[%limb] < 5); // if the limb was hit more than once within 5ms, we can assume we were shot by a shotgun
- %obj.lastLimbHitTime[%limb] = getSimTime();
- %obj.lastLimbDamage[%limb] = %damage;
- %obj.lastLimbDamagePosition[%limb] = %effectPosition;
-
- if(%damage > %maxHP || %obj.limbShotgunStrike || %damage >= $Pref::RBloodMod::DismemberDamage || %dismemberOnKill)
- {
- %obj.markLimbForDismember[%limb] = true;
- for(%i = 0; %i < getRandom(2, 6); %i++)
- {
- doBloodSplatter(%velocity, %position, %damage, %projectileData);
- }
- schedule(0, 0, rbloodSplashNearbyPlayers, %effectPosition, 6, %obj);
- }
- }
- package RBloodPackage
- {
- function Armor::Damage(%data, %obj, %sourceObject, %position, %damage, %damageType)
- {
- // Armor::Damage hook will provide functionality for weapons that do not use projectiles.
- // Due to the nature of Armor::Damage, this process is very tedious and ambiguous, and uses
- // a lot of hacky assumptive solutions.
-
- // First, determine whether or not RBlood was simulated already. if it has, there's no need to continue.
- %doRBloodSim = (getSimTime() - %obj.lastRBloodSimTime > 5);
- if(!%doRBloodSim)
- return Parent::Damage(%data, %obj, %sourceObject, %position, %damage, %damageType);
-
- if(%damage < $Pref::RBloodMod::MinimumBloodThreshold)
- return Parent::Damage(%data, %obj, %sourceObject, %position, %damage, %damageType);
-
- // We need to exclude certain damages that wouldn't make a lot of sense. You don't bleed in lava nor dor your limbs
- // explode when you commit suicide.
- if(%damageType == $DamageType::Lava || %damageType == $DamageType::Suicide)
- return Parent::Damage(%data, %obj, %sourceObject, %position, %damage, %damageType);
-
- %rbloodPosition = %position;
- %scale = getWord(%obj.scale, 2);
- %hs = 1;
- %vs = 2;
- %randomString = ((getRandom() - 0.5) * %hs * %scale) SPC ((getRandom() - 0.5) * %hs * %scale) SPC ((getRandom() - 0.5) * %vs * %scale);
-
- // Add-on makers don't always put in accurate information for %position. Sometimes this is done on purpose. Either way, we need to account for it.
- if(%rbloodPosition $= "" || %rbloodPosition $= "0 0 0" || vectorDist(%rbloodPosition, %obj.getHackPosition()) > (1.5 * %scale))
- {
- // bogus position. try to comp
- // Will get the center of the player and then randomly offset
- %rbloodPosition = %obj.getHackPosition();
- %rbloodPosition = vectorAdd(%rbloodPosition, %randomString);
- }
-
- // No projectile = no vector. We have no idea where the damage came from, so just bleed in the general downwards direction.
- %vector = vectorNormalize(vectorAdd("0 0 0", %randomString));
-
- // Dismembering.
- %dismember = (%damage >= $Pref::RBloodMod::DismemberDamage);
-
- %damage_old = %obj.getDamageLevel();
- Parent::Damage(%data, %obj, %sourceObject, %position, %damage, %damageType);
- %damage_new = %obj.getDamageLevel();
-
- if(%damage_new > %damage_old) {
- if(%damage_new < %data.maxDamage) %damage = %damage_new - %damage_old;
- %obj.schedule(0, RBloodSimulateArmor, %rbloodPosition, %vector, swordProjectile, 1, %dismember, %damage);
- }
- }
-
- function Player::RBloodSimulateArmor(%obj, %pos, %vec, %proj, %scl, %dis, %dmg) {
- if(getSimTime() - %obj.lastRBloodSimTime < 5) return;
- %obj.RBloodSimulate(%pos, %vec, %proj, %scl, %dis, %dmg);
- }
-
- function ProjectileData::Damage(%this, %projectile, %obj, %fade, %position, %normal)
- {
- %damage = (%this.directDamage * %projectile.scale);
-
- // avoid blood on damageless weapons like pushbrooms
- if(%damage < $Pref::RBloodMod::MinimumBloodThreshold)
- return Parent::Damage(%this, %projectile, %obj, %fade, %position, %normal);
-
- // functionality for developers who want to disable gore on their weapons
- if(%this.disableGore)
- return Parent::Damage(%this, %projectile, %obj, %fade, %position, %normal);
-
- if(%obj.getClassName()!$="Player" && %obj.getClassName()!$="AIPlayer")
- return Parent::Damage(%this, %projectile, %obj, %fade, %position, %normal);
-
- %damage_old = %obj.getDamageLevel();
- Parent::Damage(%this, %projectile, %obj, %fade, %position, %normal);
- %damage_new = %obj.getDamageLevel();
-
- if(%damage_new > %damage_old) {
- if(%damage_new < %data.maxDamage) %damage = %damage_new - %damage_old;
- %obj.RBloodSimulate(%position, %projectile.getVelocity(), %this, %projectile.scale, %this.dismemberOnKill, %damage);
- }
- }
-
- function Armor::onDisabled(%this, %obj, %state)
- {
- %position = %obj.getHackPosition();
-
- // check each limb for a dismemberment flag
- for(%limb = 0; %limb <= 5; %limb++)
- {
- %time = (getSimTime() - %obj.lastLimbHitTime[%limb]) < $Pref::RBloodMod::DismemberTime;
- %dism = %obj.markLimbForDismember[%limb];
- %dama = %obj.lastLimbDamage[%limb];
-
- %effectPosition = %obj.lastLimbDamagePosition[%limb];
-
- if(%time && %dism)
- {
- for(%i = 0; %i < getWordCount($RBloodLimbString[%limb]); %i++)
- {
- %obj.hideNode(getWord($RBloodLimbString[%limb], %i));
- }
-
- doBloodDismemberExplosion(%position, 1);
-
- if(!%obj.markForGibExplosion && isObject($RBlood_Stump[%limb]) && $Pref::RBloodMod::UseLimbStubs)
- %obj.mountimage($RBlood_Stump[%limb], 2);
-
- serverPlay3D($RBlood::HeavyHit[getRandom(0, 3)], %effectPosition);
- }
- }
-
- // were we in the middle of an explosion when we were killed? assume we were killed in an explosion and gib us
- if(%obj.markForGibExplosion)
- {
- %time = (getSimTime() - %obj.markForGibExplosionTime < 5);
- if(%time)
- {
- // hide everything and mount a ribcage to our chest, giving the illusion that it's part of the player model
- %obj.hideNode("ALL");
- %obj.setNodeColor("ALL", "0.4 0 0 1");
- %obj.mountimage(RBloodRibcageImage, 2);
- %obj.unMountImage(0);
-
- for(%i = 0; %i < getRandom(2, 3); %i++)
- {
- doBloodDismemberExplosion(%position, 1.5);
- doBloodExplosion(%position, 2);
- }
-
- %splatterAMT = getRandom(8, 16);
- %splatterAMT *= $Pref::RBloodMod::StaticBloodAmt;
- for(%i = 0; %i < %splatterAMT; %i++)
- {
- %vector = (getRandom() - 0.5) * 2 SPC (getRandom() - 0.5) * 2 SPC (getRandom() - 0.5) * 2;
- doBloodSplatter(%vector, %position, 100);
- }
-
- schedule(0, 0, rbloodSplashNearbyPlayers, %position, 10, %obj, 5);
- doGibLimbsExplosion(%position, getWord(%obj.scale, 2));
- }
- }
- Parent::onDisabled(%this, %obj, %state);
- }
-
- function ProjectileData::radiusDamage(%this, %obj, %col, %distanceFactor, %pos, %damageAmt)
- {
- %damage = %damageAmt * mClampF(%distanceFactor, 0, 1);
-
- if(!rBloodPlayerShapeAllowed(%col.getdataBlock().shapeFile) || %damage < $Pref::RBloodMod::MinimumBloodThreshold || %this.disableGore)
- return Parent::radiusDamage(%this, %obj, %col, %distanceFactor, %pos, %damageAmt);
-
- %hackPosition = %col.getHackPosition();
- %bloodAmount = mCeil(%damage / 24);
- for(%i = 0; %i < %bloodAmount; %i++)
- {
- doBloodExplosion(%hackPosition, 1);
- }
- schedule(0, 0, rbloodSplashNearbyPlayers, %hackPosition, 4, %col);
-
- if(!$Pref::RBloodMod::DisableBleed)
- {
- %bloodTime = mCeil((%damage * 50) * $Pref::RBloodMod::BloodTimeScale);
- %bleedAmount = mCeil((%damage / 2) * $Pref::RBloodMod::BloodAmountScale);
- %col.causeBleeding(%bloodTime, %bleedAmount, getSimTime());
- }
-
- if(%damageAmt < $Pref::RBloodMod::MinimumGibDamage)
- return Parent::radiusDamage(%this, %obj, %col, %distanceFactor, %pos, %damageAmt);
-
- %hackDistance = vectorDist(%pos, %hackPosition);
-
- if((%hackDistance / getWord(%col.scale, 2)) < $Pref::RBloodMod::DistanceToGib)
- {
- %col.markForGibExplosion = true;
- %col.markForGibExplosionTime = getSimTime();
- }
-
- Parent::radiusDamage(%this, %obj, %col, %distanceFactor, %pos, %damageAmt);
- }
-
- function ShapeBase::setNodeColor(%obj, %node, %color)
- {
- Parent::setNodeColor(%obj, %node, %color);
-
- // silly work around for not having a legitimate way to get node colors
- //rBloodPlayerShapeAllowed(%shape)
- %shape = %obj.getDatablock().shapeFile;
- if(rBloodPlayerShapeAllowed(%shape))
- %obj.nodeColor[%node] = %color;
- }
- };
- activatePackage(RBloodPackage);
|