if($Stomach::CurrentStomachID $= "") $Stomach::CurrentStomachID = 0;
if($Stomach::DefaultScale $= "") $Stomach::DefaultScale = "10 10 10";
if(!isObject(StomachObjectGroup)) new SimGroup(StomachObjectGroup);

// Datablocks
if(!isObject(stomachShape)) datablock staticShapeData(stomachShape) { shapeFile = "add-ons/server_vore/shapes/stomach.dts"; };
if(!isObject(esoRingsShape)) datablock staticShapeData(esoRingsShape) { shapeFile = "add-ons/server_vore/shapes/EsoRings.dts"; };

if(!isObject(ArrowProjectile)) exec("Add-Ons/Weapon_Bow/weapon_bow.cs");

if(!isObject(stomachSweatParticle)) datablock ParticleData(stomachSweatParticle : arrowTrailParticle) {
	// Interpolation variables
	colors[0]	= "0.549020 0.551402 0 0.981308 0.2";
	colors[1]	= "0.549020 0.551402 0 0.981308 0.0";
};
if(!isObject(stomachSweatTrailEmitter)) datablock ParticleEmitterData(stomachSweatTrailEmitter : arrowTrailEmitter) {
   particles = stomachSweatParticle;
   uiName = "Stomach Sweat Trail";
};
if(!isObject(StomachSweatProjectile)) datablock ProjectileData(StomachSweatProjectile : ArrowProjectile) {
	scale = "0 0 0";
	bounceElasticity = 0;
	particleEmitter = stomachSweatTrailEmitter;
	uiName = "Stomach Sweat Droplet";
};

function StomachSweatProjectile::onCollision(%this, %projectile, %target, %a, %b, %c) {
	if(!isObject(%target)) return;
	
	if(%target.getClassName() $= "Player" || %target.getClassName() $= "AIPlayer") {
		%target.Damage((%target.barOff ? 3.2 : 0.5));
	}
}

function Stomach_offset() {
	return vectorScale(vectorSub(findLocalClient().getControlObject().getPosition(), _e.hbot.phystomach.getPosition()), 0.1);
}
$Stomach::use = 1;

function getRaycastPosition(%start, %fVector) {
	%vector   = VectorScale(%fVector, 100);
	%end      = VectorAdd(%start, %vector);
	%mask     = $TypeMasks::StaticObjectType;
	%scanTarg = containerRayCast(%start, %end, %mask);
	%pos      = posFromRaycast(%scanTarg);
	
	return %pos;
}

function getContainerPlayers(%centerPos, %radius) {
	%mask = ($TypeMasks::PlayerObjectType);
	if((%list = containerFindFirst(%mask, %centerPos, %radius, %radius, %radius)) == 0) return "";
	
	while(%object = containerFindNext()) %list = trim(%list SPC %object.getId());
	
	return %list;
}

function Stomach_reset() {
	StomachObjectGroup.deleteAll();
	$Stomach::CurrentStomachID = 0;
	
	for(%i=0;%i<ClientGroup.getCount();%i++) {
		serverCmdResetBots(ClientGroup.getObject(%i));
	}
}

function Stomach_update(%stomObj) {
	if(!isObject(%stomObj)) return;
	
	%zoneName  = "StomachAcidZone_" @ %stomObj.getId();
	%planeName = "StomachAcidPlane_" @ %stomObj.getId();
	
	if(!isObject(%plane = %planeName)) {
		%plane = new fxPlane(%planeName) {
			position	= "0 0 0";
			blend		= 0;
			useShader	= 1;
			isSolid		= 0;
			loopsPerUnit	= 1;
			topTexture	= "Add-Ons/Water_White/White.png";
			bottomTexture	= "Add-Ons/Water_White/White.png";
			color		= "140 151 0 240";
			colorMultiply	= "1";
		};
		
		%stomObj.plane = %plane;
		StomachObjectGroup.add(%plane);
	}
	
	if(!isObject(%zone = %zoneName)) {
		%zone = new PhysicalZone(%zoneName) {
			isWater        = 1;
			waterViscosity = 40;
			waterDensity   = 1;
			waterColor     = "0.549020 0.551402 0 0.981308";
			position       = "-500000 500000 -100";
			polyhedron     = "0 0 0 1 0 0 0 -1 0 0 0 1";
			position       = "0 0 0";
			waterType      = %waterType;
		};
		
		%zone.setScale("1000000 1000000 100");
		%stomObj.zone = %zone;
		StomachObjectGroup.add(%zone);
	}
	
	// Get stomach dimensions
	%wlBox = %stomObj.getWorldBox();
	%xSize = mAbs(getWord(%wlBox, 0) - getWord(%wlBox, 3));
	%ySize = mAbs(getWord(%wlBox, 1) - getWord(%wlBox, 4));
	%zSize = mAbs(getWord(%wlBox, 2) - getWord(%wlBox, 5));
	
	// Get stomach acid height
	%AIObj   = %stomObj.ai;
	%height  = (2.34998 * getWord(%stomObj.getScale(), 2));
	%sHeight = mClampF((mClamp(%AIObj.digestionAmt - 90, 0, 300) / 300) * %height, 0, %height);
	
	// Check if we actually need to update
	if(%plane.lastHeight $= %sHeight) return;
	
	// Update plane first
	%pos = getWords(%stomObj.getTransform(), 0, 2);
	%pos = VectorAdd(%pos, "0 0 " @ %sHeight);
	%pos = vectorSub(%pos, "0 0 0.3");
	%plane.lastHeight = %sHeight;
	%plane.setTransform(%pos SPC "0 0 1 0");
	%plane.sendUpdate();
	
	// Update the zone next
	%pos = getWords(%plane.getTransform(), 0, 2);
	%pos = VectorSub(%pos, "0 0 100");
	%pos = VectorAdd(%pos, "0 0 0.5");
	%pos = VectorSub(%pos, "500000 -500000 0");
	%zone.setTransform(%pos SPC "0 0 1 0");
}

function Stomach_report(%AIobj) {
	%stm = %AIobj.phystomach;
	echo("Stomach Mouth Count: " @ %stm.mouthCount);
	echo("Stomach Esophogus Count: " @ %stm.esoCount);
	echo("Stomach Stomach Count: " @ %stm.stomCount);
}

function Stomach_getOffset(%stom, %off) {
	if(!isObject(%stom)) return %off;
	
	%xScale = getWord(%s = %stom.getScale(), 0);
	%yScale = getWord(%s, 1);
	%zScale = getWord(%s, 2);
	
	%off = (getWord(%off, 0) * %xScale) SPC (getWord(%off, 1) * %yScale) SPC (getWord(%off, 2) * %zScale);
	%off = vectorAdd(%stom.getPosition(), %off);
	return %off;
}

function Stomach_churn(%AIobj, %oldID, %timer) {
	if(!(%isObj = isObject(%AIobj)) && %oldID $= "") return;
	
	%stomName = "StomachObj_" @ (%oldID $= "" ? %AIobj.getId() : %oldID);
	%scalar   = 3;
	%scale    = %scalar * getWord(%AIobj.getScale(), 0) SPC %scalar * getWord(%AIobj.getScale(), 1) SPC %scalar * getWord(%AIobj.getScale(), 2);
	
	// Delete and return if the AI no longer exists.
	if(!%isObj) {
		if(isObject(%stomName)) {
			if(isObject(%o = %stomName.zone)) %o.delete();
			if(isObject(%o = %stomName.plane)) %o.delete();
			if(isObject(%o = %stomName.esoRings)) %o.delete();
			%stomName.delete();
		}
		
		return;
	}
	
	// Create the stomach if it does not exist
	if(!isObject(%stomObj = %stomName)) {
		%stomObj = new StaticShape(%stomName) {
			dataBlock     = stomachShape;
			position      = vectorSub("0 0 -200", "0 0" SPC (200 * $Stomach::CurrentStomachID));
			scale         = %scale;
			ai            = %AIobj;
			lastChurning  = getSimTime();
			isChurning    = 0;
			nextAmtTamper = 2;
		};
		
		%stomObj.setScale(%scale);
		%stomObj.hideNode("ALL");
		%stomObj.unHideNode("stomach");
		%stomObj.unHideNode("Col0");
		%stomObj.unHideNode("Col1");
		%stomObj.unHideNode("Col2");
		%stomObj.unHideNode("Col3");
		%stomObj.unHideNode("Col4");
		%stomObj.unHideNode("Col5");
		StomachObjectGroup.add(%stomObj);
		Stomach_update(%stomObj);
		
		%stomObj.esoRings = new StaticShape() {
			dataBlock = esoRingsShape;
			ai        = %AIobj;
			stomach   = %stomObj;
			position  = Stomach_getOffset(%stomObj, "1.15841 0.949592 2.94231");
			scale     = %scale;
		};
		StomachObjectGroup.add(%stomObj.esoRings);
		
		%AIobj.phyStomach = %stomObj;
		$Stomach::CurrentStomachID++;
	}
	
	// Process all mouth players
	%esophogusStart = getWord(%stomObj.getPosition(), 2) + (6.9619 * getWord(%stomObj.getScale(), 2));
	for(%i=0;%i<%stomObj.mouthCount;%i++) {
		%plr = %stomObj.mouthPlayer[%i];
		
		if(!isObject(%plr) || !%plr.inMouth) {
			for(%j=%i + 1;%j<%stomObj.mouthCount + 1;%j++) %stomObj.mouthPlayer[%j - 1] = %stomObj.mouthPlayer[%j];
			%stomObj.mouthCount--;
			%i--;

			if(%stomObj.esoCount == 0 && %stomObj.mouthCount == 0) {
 				%stomObj.hideNode("esophogus");
				%playersInEsophogus = false;
				break;
			}
			continue;
		}
		
		if(getWord(%plr.getPosition(), 2) < %esophogusStart) {
 			for(%j=%i + 1;%j<%stomObj.mouthCount + 1;%j++) %stomObj.mouthPlayer[%j - 1] = %stomObj.mouthPlayer[%j];
			%stomObj.mouthCount--;
			%i--;
			
			// Force the player to swallow them
			if(%AIobj.becomeNotVorist) {
				%this.becomeNotVorist = 0;
				%this.isNotVorist = 1;
				%obj.stomach_struggle = $CACHE::Marc::StomachStruggle::Max/1.4;
			}
			cancel(%AIobj.wantToEat);
			%AIobj.holding  = "";
			%AIobj.eatDelay = -1;
			%AIobj.loopMarcAI(%AIobj.firstName SPC %AIobj.lastName, %AIobj.getScale(), %AIobj.personality, %AIobj.isNotVorist);
			
			%plr.inMouth = 0;
			AIPlayer::Stomach_Add(%AIobj, %plr);
			continue;
		}
	}
	
	// Process esophogus players
	%LESPos             = getWord(%stomObj.getPosition(), 2) + (2.34998 * getWord(%stomObj.getScale(), 2));
	%LESPlayerPos       = getWord(%stomObj.getPosition(), 2) + (2.35998 * getWord(%stomObj.getScale(), 2));
	%playersInEsophogus = false;
	if(%stomObj.esoCount > 0) {
		%playersInEsophogus = true;
		
		// Process them!
		for(%i=0;%i<%stomObj.esoCount;%i++) {
			%plr = %stomObj.esoPlayer[%i];
			
			if((%timer % 2) == 0) {
				%speed = %stomObj.getScale() / 3;
				%stomObj.esoRings.setTransform(vectorSub(%stomObj.esoRings.getTransform(), "0 0" SPC %speed));
				//%plr.setVelocity("0 0 -200");
				%plr.setTransform(vectorSub(%plr.getTransform(), "0 0" SPC %speed));
			}
			
			if(!isObject(%plr) || getWord(%stomObj.esoRings.getPosition(), 2) < %LESPos) {
				%spawnPos = vectorAdd(%stm.getTransform(), vectorScale("0.00381989 1.38222 6.90997", getWord(%stm.getScale(), 0)));
				%stomObj.esoRings.setTransform(Stomach_getOffset(%stomObj, "0.00381989 7.38222 6.90997"));
				%stomObj.esoRings.hideNode("Top");
				%stomObj.esoRings.hideNode("Col1");
				%plr.setVelocity("0 0 -10");
			}
			
			if(!isObject(%plr) || getWord(%plr.getPosition(), 2) < %LESPlayerPos) {
				// Re-sort array
				for(%j=%i + 1;%j<%stomObj.esoCount + 1;%j++) %stomObj.esoPlayer[%j - 1] = %stomObj.esoPlayer[%j];
				%stomObj.esoCount--;
				%i--;
				
				%plr.setVelocity("0 0 -10");
				%plr.inStomach = 1;
				%plr.heldBy    = %AIobj;
				%stomObj.stomPlayer[-1 + %stomObj.stomCount++] = %plr;
				
				if(%stomObj.esoCount == 0 && %stomObj.mouthCount == 0) {
 					%stomObj.hideNode("esophogus");
					%playersInEsophogus = false;
					break;
				}
				
				continue;
			}
		}
	}
	
	// Process all players
	if(%stomObj.stomCount) {
		for(%i=0;%i<%stomObj.stomCount;%i++) {
			%plr = %stomObj.stomPlayer[%i];
			
			if(!isObject(%plr) || !%plr.inStomach || %plr.getState() $= "Dead") {
				for(%j=%i + 1;%j<%stomObj.stomCount + 1;%j++) %stomObj.stomPlayer[%j - 1] = %stomObj.stomPlayer[%j];
				%stomObj.stomCount--;
				%i--;
				
				if(%stomObj.stomCount == 0) %stomObj.hideNode("ALL");
				continue;
			}
			
			%dmg  = getRandom() * (%plr.barOff ? getRandom(8, 16) : getRandom(2, 4));
			%mult = ((getWord(%stomObj.getPosition(), 2) + %stomObj.plane.lastHeight) - getWord(%plr.getPosition(), 2)) / (2.31198 * getWord(%stomObj.getScale(), 2));
			%dmg += %mult * 6;
			if(getWord(%plr.getPosition(), 2) < getWord(%stomObj.getPosition(), 2) + %stomObj.plane.lastHeight) {
				if((%timer % 32) == 0 && getRandom(0, 10000) >= 7000) {
					if(%plr.getDamageLevel() + %dmg >= %plr.dataBlock.maxDamage) {
						// Player will die next turn
						
						if(%plr.invulnerable) {
							%plr.setDamageLevel(%plr.dataBlock.maxDamage - 1);
							continue;
						}
						
						%plr.hideNode("ALL");
						%plr.setTransform(%AIobj.getTransform());
						%plr.kill();
						continue;
					}
					%plr.setDamageLevel(%plr.getDamageLevel() + %dmg);
				}
			}
		}
		
		// Churn the stomach
		if(%AIobj.digestionAmt >= 60 && getSimTime() - %stomObj.lastChurning >= 5000) {
			if(!%stomObj.isChurning) {
				if((%timer % 4) == 0 && getRandom(0, 10000) < 100) {
					%stomObj.churnState    = -1;
					%stomObj.nextChurnTime = 0;
					%stomObj.isChurning    = 1;
					%stomObj.lastChurnTime = getSimTime();
				}
			} else {
				if(getSimTime() - %stomObj.lastChurnTime >= %stomObj.nextChurnTime) {
					%stomObj.lastChurnTime = getSimTime();
					%stomObj.nextChurnTime = getRandom(1000, 1200);
					%stomObj.unHideNode("churn" @ %stomObj.churnState++);
					
					%churnPos[-1 + %churnCnt++] = "0.0212744 1.7371 1.62582	0.0421658 2.06769 0.821997";
					%churnPos[-1 + %churnCnt++] = "0.10027 1.01686 0.767786	-0.115046 0.641561 0.144";
					%churnPos[-1 + %churnCnt++] = "-0.0575948 0.125799 0.509491	-0.182669 -0.106218 0.0669983";
					%churnPos[-1 + %churnCnt++] = "0.0097357 -0.33785 0.476001	-0.184393 -0.689604 0.14002";
					%churnPos[-1 + %churnCnt++] = "-0.024301 -1.01128 0.618787	0.111135 -1.21956 0.278998";
					
					if((%cp = %churnPos[%stomObj.churnState]) !$= "") {
						%scanPos  = Stomach_getOffset(%stomObj, getField(%cp, 0));
						%displace = Stomach_getOffset(%stomObj, getField(%cp, 1));
						%players  = getContainerPlayers(%scanPos, 2.7);
						
						for(%i=0;%i<getWordCount(%players);%i++) {
							%plr = getWord(%players, %i);
							//%plr.setTransform(%displace SPC getWords(%plr.getTransform(), 3, 6));
							%plr.addVelocity("0 -20 0");
						}
					}
				} else if(getSimTime() - %stomObj.lastChurnTime >= (%stomObj.nextChurnTime / 5)) {
					%stomObj.hideNode("churn" @ %stomObj.churnState - 1);
					
					if(%stomObj.churnState >= 5) {
						%stomObj.isChurning    = 0;
						%stomObj.lastChurnTime = "";
						%stomObj.churnState    = "";
						%stomObj.nextChurnTime = "";
						%stomObj.lastChurning  = getSimTime();
					}
				}
			}
			
		}
		
		// Make the stomach "sweat" acid
		if(%AIobj.digestionAmt >= 50 && (%timer % 6) == 0 && getRandom(0, 4) == 2) {
			%wBox   = Stomach_getOffset(%stomObj, "0.835725 -1.71024 2.78763") SPC Stomach_getOffset(%stomObj, "-0.626866 2.12323 0.0663513");
			%xSize  = mAbs(getWord(%wBox, 3) - getWord(%wBox, 0));
			%ySize  = mAbs(getWord(%wBox, 4) - getWord(%wBox, 1));
			%zSize  = mAbs(getWord(%wBox, 5) - getWord(%wBox, 2));
			
			%start = vectorAdd(%stomObj.getPosition(), (%xSize / 2) SPC (%ySize / 2) SPC %zSize);
			%start = vectorSub(%start, getRandom() * %xSize SPC getRandom() * %ySize SPC getRandom() * %zSize);
			%start = getRaycastPosition(%start, "0 0 -1");
			
			if(%start !$= "") {
				%start = vectorSub(%start, "0 0 1");
				
				new Projectile() {
					dataBlock       = StomachSweatProjectile;
					initialVelocity = "0 0 0";
					initialPosition = %start;
					position        = %start;
					stomach         = %stomObj;
				};
			}
		}
		
		// Update the stomach every few ticks
		if((%timer % 2) == 0) Stomach_update(%stomObj);
	}
	
	// If there are no more players in the stomach, simply stop looping this function.
	if(%stomObj.mouthCount == 0 && %stomObj.esoCount == 0 && %stomObj.stomCount == 0 && %AIobj.digestionAmt <= 0) {
		if(%AIobj.settingUp) {
			%AIobj.settingUp = "";
			
			cancel(%AIobj.phystomach_churnSchedule);
			%AIobj.phystomach_churnSchedule = schedule(1, 0, Stomach_churn, %AIobj, (%oldID $= "" ? %AIobj.getId() : %oldID), %timer++);
			return;
		}
		if(isObject(%o = %stomObj.plane)) %o.delete();
		if(isObject(%o = %stomObj.zone)) %o.delete();
		if(isObject(%o = %stomObj.esoRings)) %o.delete();
		%stomObj.delete();
		return;
	}
	
	if(%AIobj.settingUp) %AIobj.settingUp = "";
	
	// Digest the energy drink
	if((%timer % 4) == 0 && %AIobj.energyDrinkAmt > 0) {
		%AIobj.energyDrinkAmt = mClampF(%AIobj.energyDrinkAmt - 1, 0, 5000);
	}
	
	// Calculate digestion amount
	if((%timer % %stomObj.nextAmtTamper) == 0) {
		%stomObj.nextAmtTamper = (%AIobj.energyDrinkAmt == 0 ? getRandom(128, 256) : getRandom(32, 64));
		
		if(%stomObj.stomCount == 0) {
			%AIobj.digestionAmt--;
			if(%AIobj.digestionAmt < 0) %AIobj.digestionAmt = 0;
		} else {
			%AIobj.digestionAmt += 1 + ((%AIobj.energyDrinkAmt / 5000) * 4);
		}
	}
	
	// Make a sound
	%AIobj.makeStomachSound();
	
	cancel(%AIobj.phystomach_churnSchedule);
	%AIobj.phystomach_churnSchedule = schedule(1, 0, Stomach_churn, %AIobj, (%oldID $= "" ? %AIobj.getId() : %oldID), %timer++);
}

function AIPlayer::Stomach_add(%this, %player) {
	if(!isObject(%player)) return;
	
	if(!isEventPending(%this.phystomach_churnSchedule)) { %this.settingUp = 1; Stomach_churn(%this); }

	%this.Mouth_remove(%player);
	
	// Get the spawn position
	%stm         = %this.phyStomach;
	%esoSpawnPos = vectorAdd(%stm.getTransform(), vectorScale("0.00381989 1.38222 6.90997", getWord(%stm.getScale(), 0)));
	%esoSpawnPos = vectorAdd(%esoSpawnPos, "0 0" SPC (getWord(%stm.esoRings.getWorldBox(), 2) - getWord(%stm.esoRings.getWorldBox(), 5)));
	%spawnPos    = Stomach_getOffset(%stm, "0.000743851 1.3466 6.208");
	%stm.esoRings.setTransform(%esoSpawnPos);
	%player.setTransform(%spawnPos);
	
	// Make sure it's all visible
	%stm.unHideNode("esophogus");
	%stm.unHideNode("stomach");
	%stm.unHideNode("Col0");
	%stm.unHideNode("Col1");
	%stm.unHideNode("Col2");
	%stm.unHideNode("Col3");
	%stm.unHideNode("Col4");
	%stm.unHideNode("Col5");
	%stm.esoRings.unHideNode("ALL");
	
	// Teleport them
	%stm.esoPlayer[-1 + %stm.esoCount++] = %player;
	%player.setTransform(%spawnPos SPC getWords(%player.getTransform(), 3, 6));
	%player.setVelocity("0 0 -20");
}

function AIPlayer::Mouth_add(%this, %player) {
	if(!isObject(%player)) return;
	
	if(!isEventPending(%this.phystomach_churnSchedule)) { %this.settingUp = 1; Stomach_churn(%this); }
	
	// Get the spawn position
	%stm      = %this.phyStomach;
	%spawnPos = vectorAdd(%stm.getTransform(), vectorScale("-1.95076 1.3235 7.8873", getWord(%stm.getScale(), 0)));
	
	// Make sure it's all visible
	%stm.unHideNode("esophogus");
	
	// Teleport them
	%stm.mouthPlayer[-1 + %stm.mouthCount++] = %player;
	%player.inMouth = 1;
	%player.heldBy  = %this;
	%player.setTransform(%spawnPos SPC getWords(%player.getTransform(), 3, 6));
	//%player.setVelocity("0 0 -20");
}

function AIPlayer::Mouth_remove(%this, %player) {
	if(!isObject(%player)) return;
	
	// Get the spawn position
	%stm = %this.phyStomach;
	
	for(%i=0;%i<=%stm.mouthCount;%i++) {
		if(%i == %stm.mouthCount) return;
		%plr = %stm.mountPlayer[%i];
		
		if(%plr == %player) {
 			for(%j=%i + 1;%j<%stomObj.mouthCount + 1;%j++) %stomObj.mouthPlayer[%j - 1] = %stomObj.mouthPlayer[%j];
			%stomObj.mouthCount--;
			%i--;
			break;
		}
	}

	if(%stm.esoCount == 0 && %stm.mouthCount == 0) %stm.hideNode("esophogus");
	
	%player.setScale(%player.oldScale);
}