/// @description COPY FROM oBALL
//global.debugValue2++;
if (other == owner) // quick out for self because you're constantly doing that
return;
global.debugValue1 = "computing...";
global.debugValue2++;
if (!visible)
{
global.debugValue1 = "Inactive";
return; // Don't collide if swallowed by a fish
}
if (nonColliding || other.nonColliding)
{
global.debugValue1 = "Non-Colliding";
return;
}
if (other.shotSide == shotSide)
{
global.debugValue1 = "Same shot side";
return; // Don't collide with friendlies
}
if (other.framesPhysicalInvuln > 0 && other.physicalInvulnTo == self) // DIFFERENT
{
global.debugValue1 = "Physically invulnerable";
return;
}
//if (framesPhysicalInvuln > 0) // No damage
// return;
//if (framesPhysicalInvuln > 0) // No damage
// return;
//if (dodging && dodgeAttack == 0) // Dodge
// return;
// Grace
// -2/((x+1.7)^1.3) + 1
// At x=0 y=0; x=1 y=0.48; x=2 y=0.65; x=3 y=.73
//var graceChance = -2/(power(grace+1.7, 1.3)) + 1;
// (Didn't increase fast enough)
// -7/((x+2)^2.7) + 1
// (0,0), (1,0.64), (2,0.84), (3,0.93), (4,0.95), (5,0.97)
//var graceChance = -7/(power(grace+2, 2.7)) + 1;
//if (!dodging && random(1) < graceChance)
//{
// return;
//}
//oVibrationManager.collision = true;
//if (owner.spikes > 0 && framesSinceSpike >= spikeDelay)
//{
// var totalSpikes = round(2 + owner.spikes);
// var angleBetweenShots = 360 / totalSpikes;
// var initialAngle = image_angle;
// var spikeRotateDirection = choose(-1, 1);
// var spikeWispType = OtherWispType(other.wispType); // Make it the color that will hurt whatever touched
// for (var i=0; i<totalSpikes; i++)
// {
// var dirShoot = initialAngle + (angleBetweenShots * i);
// if (dirShoot > 360)
// dirShoot -= 360;
// if (dirShoot < 0)
// dirShoot += 360;
// with (instance_create_layer(x, y, "Blasts", oSpike))
// {
// x += lengthdir_x(other.owner.spikes, dirShoot);
// y += lengthdir_y(other.owner.spikes, dirShoot);
// dmg = 1.618 * other.owner.gun.dmg * power(GRUPHALF, other.spikes); // 1.3
// spd = 0;
// hsp = 0;
// vsp = 0;
// image_angle = dirShoot;
// turnTargetAngle = image_angle;
// ttl = other.owner.gun.ttl * 3 * power(GRUPHALF, other.spikes); // 1.3
// size = other.owner.gun.size * 1.4;
// image_xscale = size * power(GRUPHALF, other.spikes); // 1.3 1.2
// image_yscale = size * power(1.1, other.owner.spikes);
// spikes = other.owner.spikes;
// spikesRotate = other.owner.spikesRotate;
// rotateDirection = spikeRotateDirection;
// maxHits = max(1, other.spikes/2);
// wispType = spikeWispType;
// shotDrag = other.owner.gun.shotDrag;
// poison = other.owner.gun.poison;
// slow = other.owner.gun.slow;
// //wave = other.gun.wave;
// //waveUp = other.gun.waveUp;
// //wavesAreRandom = other.gun.wavesAreRandom;
// //penetration = other.gun.penetration;
// //tracking = other.gun.tracking;
// //blastPower = other.gun.blastPower;
// //blastRadius = other.gun.blastRadius;
// //blastCrackle = other.gun.blastCrackle;
// //blastAttract = other.gun.blastAttract;
// lightning = other.owner.gun.lightning;
// lightningChains = other.owner.gun.lightningChains;
// lightningExplodes = other.owner.gun.lightningExplodes;
// //shatterChains = other.gun.shatterChains;
// //shatterSplits = other.gun.shatterSplits;
// //shatterRetention = other.gun.shatterRetention;
// grow = other.owner.gun.grow; // Mines growing too much is OP - if Update, update grow in mine too
// vampire = other.owner.gun.vampire;
// vampireOwner = other;
// shotSide = other.owner.gun.shotSide;
// }
// }
//}
/////////////
///////////////////
////// THIS IS DIFFERENT FROM oPLAYER
//////////////////////////////////////////////
var collisionDamage = baseDamage * power(1.2, owner.screw) * power(global.playerLevelUpDamage, global.level);
targetDamage = collisionDamage;
global.debugValue1 = "damaging: " + string(targetDamage);
other.hp -= targetDamage;
other.flashTimeToLive = global.standardFlashTime;
other.flashWispType = OtherWispType(other.wispType);
with (instance_create_layer(other.x + random_range(-5, 5), other.y + random_range(-5, 5), "Damage", oDamageNum))
{
value = other.targetDamage;
shotSide = ShotSide.Player;
}
//// TODO: change enemyPhysicalAttack to read from the shootable and customize for every enemy?
////var enemyPhysicalAttack = 15;
//var enemyPhysicalAttack = 30; // 40 // nov 30 made much stronger
//// playerDamage is damage that the player is going to receive
//playerDamage = enemyPhysicalAttack * power(global.enemyWaveScaling, global.wave) * global.enemyDamageScaling;
//targetDamage = physicalAttackDmg * power(global.playerLevelUpDamage, global.level);
//targetDamage *= power(1.2, bodyStrength);
//playerDamage /= power(1.2, bodyStrength);
//playerDamage /= power(1.2, damageResist);
//if (screwing)
//{
// playerDamage = 0;
// targetDamage /= 2;// 1.618; // 2; // 3
// targetDamage *= power(1.2, screw);
//}
// NEW COLLISIONS CODE
// https://www.plasmaphysics.org.uk/programs/coll2d_cpp.htm
var m21 = other.mass / mass;
var x21 = other.x - x;
var y21 = other.y - y;
var vx21 = other.hsp - hsp;
var vy21 = other.vsp - vsp;
var vx_cm = (mass * hsp + other.mass * other.hsp) / (mass + other.mass);
var vy_cm = (mass * vsp + other.mass * other.vsp) / (mass + other.mass);
var fy21 = 0.000001 * abs(y21);
if (abs(x21) < fy21)
{
var signVal = 1;
if (x21 < 0)
signVal = -1;
x21 = fy21 * signVal;
}
// Update velocities
var a = y21/x21;
var dvx2 = -2*(vx21 + a * vy21)/((1 + a * a)*(1 + m21));
var vx2 = other.hsp + dvx2;
var vy2 = other.vsp + a * dvx2;
var vx1 = hsp - m21 * dvx2;
var vy1 = vsp - a * m21 * dvx2;
var R = 0.98; // (restitution coefficient) between 0 and 1 (1=perfectly elastic collision)
// *** velocity correction for inelastic collisions ***
vx1=(vx1-vx_cm)*R + vx_cm;
vy1=(vy1-vy_cm)*R + vy_cm;
vx2=(vx2-vx_cm)*R + vx_cm;
vy2=(vy2-vy_cm)*R + vy_cm;
//var collisionDir = point_direction(other.x, other.y, x, y);
//var playerMoveMagnitude = 20; //5; //25;
//var enemyMoveMagnitude = playerMoveMagnitude;
//vx2 *= power(1.3, bodyStrength);
//vy2 *= power(1.3, bodyStrength);
//vx1 /= power(1.3, bodyStrength);
//vy1 /= power(1.3, bodyStrength);
//noControlFrames = 7 * power(0.7, bodyStrength);
// Move player (if not dodging)
//if (!dodging)
//{
// NOTE: This part is the only uncommented part in oBall
// But we don't want to move the storm
//hsp = vx1;
//vsp = vy1;
//hsp += lengthdir_x(playerMoveMagnitude, collisionDir);
//vsp += lengthdir_y(playerMoveMagnitude, collisionDir);
//framesPhysicalInvuln = framesPhysicalInvulnAmount;
//}
// NOTE: The wrecking ball hits every frame if it can.
// Move target
other.framesPhysicalInvuln = other.framesPhysicalInvulnAmount;
other.physicalInvulnTo = self;
if (!other.unPushasble)
{
//collisionDir = point_direction(x, y, other.x, other.y);
//other.hsp += lengthdir_x(enemyMoveMagnitude, collisionDir);
//other.vsp += lengthdir_y(enemyMoveMagnitude, collisionDir);
other.hsp = vx2;
other.vsp = vy2;
}
ScreenShake(4, 4); // ball collision screenshake
//PlaySound(Explosion_18, global.currentSfxVolume, 50, false);
audio_sound_pitch(dBodyHit1, random_range(0.93, 1.07));
PlaySound(dBodyHit1, global.currentSfxVolume, 60, false);
// Copied from oPlayer... (which was copied from oProjectile)
////////////////////////////////////////////////////
// Physical lightning, explosions, poison, ...
////////////////////////////////////////////////////
if (owner.gun.slowPhysical > 0)
{
other.frameModToCheckSlow = (global.frame mod 60) + 1; // Start feeling the SLOW IMMEDIATELY if you don't have slow yet
if (other.frameModToCheckSlow > 59)
other.frameModToCheckSlow -= 60;
var slowStack = instance_create_layer(0, 0, "Logic", dsStack);
with (slowStack)
{
slowStack.startFrame = global.frame;
slowStack.stacks = 1.5 * power(other.owner.gun.slowPhysical, 1.2);
}
ds_list_insert(other.slowStackList, 0, slowStack);
}
if (owner.gun.poisonPhysical > 0)
{
//other.frameModToCheckPoison = (global.frame mod 60) + 30; // Start feeling the POISON SOON (+ half a second) if you don't have poison yet
//if (other.frameModToCheckPoison > 59)
// other.frameModToCheckPoison -= 60;
var poisonStack = instance_create_layer(0, 0, "Logic", dsStack);
with (poisonStack)
{
poisonStack.startFrame = global.frame;
poisonStack.stacks = 0.15 * other.targetDamage * power(other.owner.gun.poisonPhysical, 1.2); // Becomes accumulated poison damage
}
ds_list_insert(other.poisonStackList, 0, poisonStack);
}
//if (parasite > 0)
//{
// var parasiteStack = instance_create_layer(0, 0, "Logic", dsStack);
// with (parasiteStack)
// {
// parasiteStack.startFrame = global.frame;
// parasiteStack.stacks = other.parasite;
// }
// ds_list_insert(other.parasiteStackList, 0, parasiteStack);
//}
if (owner.gun.vampirePhysical > 0)
{
owner.vampireHp += targetDamage * 0.0618 * power(1.3, owner.gun.vampirePhysical-1);
}
// Blast
if (owner.gun.blastPhysical > 0 && owner.wispType != other.wispType)
{
if (random(1) < 0.3 * power(1.3, owner.gun.blastPhysical) * power(1.3, owner.gun.blastRadius))
{
with (instance_create_layer(x, y, "Blasts", oBlast))
{
//dmg = (other.targetDamage * 0.2) * power(1.2, owner.gun.blastPhysical);
//size = (other.owner.gun.size * 0.3) * power(1.3, other.blastRadius);
dmg = other.targetDamage * 0.5 * power(1.2, other.owner.gun.blastPhysical);
size = 0.25 * power(1.2, other.owner.gun.blastRadius) * power(1.1, other.owner.gun.blastPhysical);
spd = 0;
hsp = 0;
vsp = 0;
wispType = other.owner.wispType;
shotSide = other.owner.shotSide;
blastPower = other.owner.gun.blastPower;
blastRadius = other.owner.gun.blastRadius;
blastCrackle = other.owner.gun.blastCrackle;
//blastAttract = other.blastAttract; // not needed - only mines
}
}
}
/// NEW STUFF (not copied, but added)
// TODO: Move below, convert to only blow up on the opposite color
//if (explodes > 0)
//{
// with (instance_create_layer(x, y, "Blasts", oBlast))
// {
// dmg = collisionDamage * 0.3 * power(1.2, other.owner.gun.blastPower) * power(1.2, other.explodes);
// size = 0.25 * power(1.3, other.owner.gun.blastRadius) * power(1.2, other.explodes);
// spd = 0;
// hsp = 0;
// vsp = 0;
// wispType = other.owner.wispType;
// shotSide = other.owner.shotSide;
// blastPower = other.owner.gun.blastPower;
// blastRadius = other.owner.gun.blastRadius;
// blastCrackle = other.owner.gun.blastCrackle;
// //blastAttract = other.blastAttract; // not needed - only mines
// }
//}
// Chain lightning
if (owner.gun.lightningPhysical > 0 && owner.wispType != other.wispType)
{
dude💩💩💩💩[0] = other;
lTarget = other;
lChains = 0;
var lStartX = other.x; // The starting point of the lightning,
var lStartY = other.y; // the enemy getting hit.
// While we can still make another chain...
while (true)
{
if (lChains >= owner.gun.lightningChains)
{
//global.debugValue = "Too many chains, correct";
break;
}
if (random(1) > 0.3 * power(1.2, owner.gun.lightningChains) * power(1.2, owner.gun.lightningPhysical))
{
// Failed the dice roll to keep chaining lightning
break;
}
//lTarget = InstanceNthNearest(lStartX, lStartY, oShootableEnemy, 2);
var maxAttemptsToTrack = 20;
// Create a list of the 20 closest enemies
maxAttemptsToTrack = min(max(1, maxAttemptsToTrack), instance_number(oShootableEnemy));
var nearestList = ds_priority_create();
//nearest = noone;
// Make the priority queue and sort by distance
with (oShootableEnemy) ds_priority_add(nearestList, id, distance_to_point(lStartX, lStartY));
// Burn the first result, as it's the enemy getting hit by the projectile
dude💩💩💩💩[0] = ds_priority_delete_min(nearestList);;
var validTarget = false;
for (var i=0; i<maxAttemptsToTrack-1; i++)
{
// Grab the next closest enemy from the priority queue
lTarget = ds_priority_delete_min(nearestList);
//lTarget = InstanceNthNearest(lStartX, lStartY, oShootableEnemy, 2+i); // Start 2 away so you don't target the same enemy right away
if (lTarget == noone)
break;
if (distance_to_object(lTarget) > 250 * power(1.2, owner.gun.lightningPhysical))
break;
if (lTarget.shotSide == owner.shotSide)
continue;
if (lTarget.wispType == owner.wispType)
continue;
if (lTarget.nonColliding || !lTarget.visible || !lTarget.shootable)
continue;
if (lTarget.bbox_left > RES_W || lTarget.bbox_right < 0 || lTarget.bbox_bottom < 0 || lTarget.bbox_top > RES_H)
continue;
var alreadyHitThisGuy = false;
for (var dh=0; dh<=lChains; dh++)
{
if (dude💩💩💩💩[dh] == lTarget)
{
// We've already hit this guy
alreadyHitThisGuy = true;
break;
}
}
if (alreadyHitThisGuy)
continue;
// If we make it here, we have a valid new target in lTarget
validTarget = true;
break;
}
ds_priority_destroy(nearestList);
if (!validTarget)
{
break;
}
dude💩💩💩💩[lChains+1] = lTarget;
with (instance_create_layer(lStartX, lStartY, "Lightning", oLightning))
{
target = other.lTarget;
wispType = other.owner.wispType;
shotSide = other.owner.shotSide;
dmg = other.targetDamage * 0.4 * power(1.2, other.owner.gun.lightningPhysical) * power(0.8, other.lChains);
var width = bbox_right - bbox_left;
var dist = distance_to_point(target.x, target.y);
var ratio = dist * (1/width);
image_xscale = ratio;
// NOTE: there's a small bug in the lightning code.
// Sometimes the image doesn't stretch all the way it needs to
// Like image_xscale is limited to integers... or something...
// Some of this code is trying to figure that out
image_angle = point_direction(x, y, target.x, target.y);
poison = other.owner.gun.poisonPhysical;
slow = other.owner.gun.slowPhysical;
}
if (owner.gun.lightningExplodes > 0)
{
with (instance_create_layer(lTarget.x, lTarget.y, "Blasts", oBlast))
{
dmg = (other.targetDamage * 0.5) * power(1.2, other.owner.gun.blastPower) * power(1.2, other.owner.gun.lightningExplodes);
size = (other.owner.gun.size * 0.6) * power(1.2, other.owner.gun.blastRadius) * power(1.2, other.owner.gun.lightningExplodes);
spd = 0;
hsp = 0;
vsp = 0;
wispType = other.owner.wispType;
shotSide = other.owner.shotSide;
blastPower = other.owner.gun.blastPower;
blastRadius = other.owner.gun.blastRadius;
blastCrackle = other.owner.gun.blastCrackle;
//blastAttract = other.blastAttract; // not needed - only mines
}
}
lStartX = lTarget.x;
lStartY = lTarget.y;
lChains++;
} // while
} // if
////////////////////////////////////////////////////
// (end) Physical lightning, explosions, poison, ...
////////////////////////////////////////////////////