#pragma require "MLLE-Weapons.asc"
#pragma require "SEweapon.asc"
#include "SEweapon.asc"
shared interface MLLEWeaponApply { bool Apply(uint, se::WeaponHook@ = null, jjSTREAM@ = null, uint8 = 0); }
namespace MLLEWeapons {
class ObjectTemplate {
int Age;
int AnimSpeed;
HANDLING::Bullet BulletHandling;
bool CausesRicochet;
int Counter;
uint8 CounterEnd;
int16 CurAnim;
uint CurFrame;
bool Deactivates;
uint8 DoesHurt;
int8 Energy;
int8 FrameID;
uint8 Freeze;
bool IsBlastable;
bool IsFreezable;
bool IsTarget;
uint8 JustHit;
int16 KillAnim;
int8 Light;
LIGHT::Type LightType;
STATE::State OldState;
HANDLING::Player PlayerHandling;
uint16 Points;
bool ScriptedCollisions;
int Special;
STATE::State State;
bool TriggersTNT;
array<int> Var;
float XAcc;
float XSpeed;
float YAcc;
float YSpeed;
ObjectTemplate(
int age = 0,
int animSpeed = 1,
HANDLING::Bullet bulletHandling = HANDLING::HURTBYBULLET,
bool causesRicochet = false,
int counter = 0,
uint8 counterEnd = 0,
int16 curAnim = 0,
bool deactivates = true,
uint8 doesHurt = 0,
int8 energy = 0,
int8 frameID = 0,
uint8 freeze = 0,
bool isBlastable = true,
bool isFreezable = false,
bool isTarget = false,
uint8 justHit = 0,
int16 killAnim = 0,
int8 light = 0,
LIGHT::Type lightType = LIGHT::NONE,
STATE::State oldState = STATE::START,
HANDLING::Player playerHandling = HANDLING::PLAYERBULLET,
uint16 points = 0,
bool scriptedCollisions = false,
int special = 0,
STATE::State state = STATE::START,
bool triggersTNT = false,
array<int> var = array<int>(0),
float xAcc = 0,
float yAcc = 0,
float xSpeed = 0,
float ySpeed = 0
) {
Age = age;
AnimSpeed = animSpeed;
BulletHandling = bulletHandling;
CausesRicochet = causesRicochet;
Counter = counter;
CounterEnd = counterEnd;
CurAnim = curAnim;
Deactivates = deactivates;
DoesHurt = doesHurt;
Energy = energy;
FrameID = frameID;
Freeze = freeze;
IsBlastable = isBlastable;
IsFreezable = isFreezable;
IsTarget = isTarget;
JustHit = justHit;
KillAnim = killAnim;
Light = light;
LightType = lightType;
OldState = oldState;
PlayerHandling = playerHandling;
Points = points;
ScriptedCollisions = scriptedCollisions;
Special = special;
State = state;
TriggersTNT = triggersTNT;
for (uint i = 0; i < 11; ++i)
Var.insertLast(i < var.length ? var[i] : 0);
XAcc = xAcc;
XSpeed = xSpeed;
YAcc = yAcc;
YSpeed = ySpeed;
}
void Apply(jjOBJ@ obj, uint firstAnim, bool objIsPreset) const {
obj.age = Age;
obj.animSpeed = AnimSpeed;
obj.bulletHandling = BulletHandling;
obj.causesRicochet = CausesRicochet;
obj.counter = Counter;
obj.counterEnd = CounterEnd;
obj.curAnim = (CurAnim < 200) ? firstAnim + CurAnim : CurAnim;
obj.deactivates = Deactivates;
obj.doesHurt = DoesHurt;
obj.energy = Energy;
obj.frameID = FrameID;
obj.determineCurFrame();
obj.freeze = Freeze;
obj.isBlastable = IsBlastable;
obj.isFreezable = IsFreezable;
obj.isTarget = IsTarget;
obj.justHit = JustHit;
obj.killAnim = (KillAnim < 200) ? firstAnim + KillAnim : KillAnim;
obj.light = Light;
obj.lightType = LightType;
obj.oldState = OldState;
obj.playerHandling = PlayerHandling;
obj.points = Points;
obj.scriptedCollisions = ScriptedCollisions;
obj.special =
(Special >= 0) ?
(Special != 0 && Special < 200) ?
firstAnim + Special - 1 :
Special :
obj.curAnim
;
obj.state = State;
obj.triggersTNT = TriggersTNT;
for (uint i = 0; i < 11; ++i)
if (/*objIsPreset || */(i != 7 && i != 3)) //pxSpeed, bullet type
obj.var[i] = Var[i];
if (objIsPreset) {
obj.xAcc = XAcc;
obj.xSpeed = XSpeed;
obj.yAcc = YAcc;
obj.ySpeed = YSpeed;
} else
ApplyTNTSpeeds(obj);
}
void ApplyTNTSpeeds(jjOBJ@ obj) const {
const auto x = obj.xSpeed, y = obj.ySpeed; //radius of 1
obj.xAcc = x * XAcc;
obj.xSpeed = x * XSpeed;
obj.yAcc = abs(x) * YAcc + y * XAcc; //was abs(x * Ything)
obj.ySpeed = abs(x) * YSpeed + y * XSpeed;
}
}
enum AnimationIndices { PickupIndex, PoweredUpPickupIndex, PowerupIndex, AmmoCrateIndex, _COUNT };
funcdef void behaviorFunction(jjOBJ@, bool);
funcdef bool applyFunction(uint, se::WeaponHook@, jjSTREAM@);
funcdef void presetFunction(jjOBJ@, int);
abstract class WeaponInterface : se::WeaponInterface, MLLEWeaponApply, jjBEHAVIORINTERFACE {
protected ObjectTemplate@ RegularObjectTemplate, PowerupObjectTemplate;
private behaviorFunction@ AssignBehavior;
private applyFunction@ ExtraApply;
private presetFunction@ CustomAmmo3, CustomAmmo15, CustomPowerup;
private se::MainCallback@ OnMain;
private se::PlayerCallback@ OnPlayer;
private se::PlayerCallback@ OnPlayerInput;
private se::DrawingCallback@ OnDrawAmmo;
private se::PacketCallback@ OnReceive;
private array<string> SampleFilenames(0);
private string AnimSetFilename;
private uint AnimSetID;
private bool GenerateSupplementalAnimations;
protected int PickupFrameIDToUseForAmmoCrate;
private array<int> Animations(AnimationIndices::_COUNT);
protected int get_PickupAnimation() const { return Animations[AnimationIndices::PickupIndex]; }
protected int get_PoweredUpPickupAnimation() const { return Animations[AnimationIndices::PoweredUpPickupIndex]; }
protected int get_PowerupAnimation() const { return Animations[AnimationIndices::PowerupIndex]; }
protected int get_AmmoCrateAnimation() const { return Animations[AnimationIndices::AmmoCrateIndex]; }
protected bool IsRFMissile;
private uint Traits;
private uint PoweredUpTraits;
bool WeaponHookRequired;
protected uint RoundsPerPickup;
protected SOUND::Sample PickupSample;
protected int Multiplier;
protected bool GradualAim;
protected bool ReplacedByBubbles;
protected SPREAD::Spread Spread;
protected WEAPON::Style Style;
protected jjANIMSET@ AnimSet;
protected uint SetID;
protected array<SOUND::Sample> Samples(0);
protected array<bool> SamplesLoaded(0);
protected se::PacketConstructor@ ConstructPacket;
WeaponInterface(
ObjectTemplate regularObjectTemplate,
behaviorFunction@ behavior,
ObjectTemplate@ powerupObjectTemplate = null,
array<string> sampleFilenames = array<string> = {},
array<ANIM::Set> otherAnimSetsToLoad = array<ANIM::Set> = {},
const string &in animSetFilename = "",
uint animSetID = 0,
int pickupAnimation = -1,
int poweredUpPickupAnimation = -1,
int powerupAnimation = -1,
int ammoCrateAnimation = -1,
bool generateSupplementalAnimations = true,
int pickupFrameIDToUseForAmmoCrate = 0,
bool isRFMissile = false,
uint traits = se::WeaponTrait::weapon_default_traits,
int poweredUpTraits = -1,
bool weaponHookRequired = false,
uint roundsPerPickup = 3,
SOUND::Sample pickupSample = SOUND::COMMON_PICKUPW1,
int multiplier = 1,
bool gradualAim = false,
bool replacedByBubbles = false,
SPREAD::Spread spread = SPREAD::NORMAL,
WEAPON::Style style = WEAPON::NORMAL,
applyFunction@ apply = null,
presetFunction@ customAmmo3 = null,
presetFunction@ customAmmo15 = null,
presetFunction@ customPowerup = null,
se::MainCallback@ onMain = null,
se::PlayerCallback@ onPlayer = null,
se::PlayerCallback@ onPlayerInput = null,
se::DrawingCallback@ onDrawAmmo = null,
se::PacketCallback@ onReceive = null
) {
@RegularObjectTemplate = @regularObjectTemplate;
@AssignBehavior = @behavior;
@PowerupObjectTemplate = @((powerupObjectTemplate !is null) ? powerupObjectTemplate : regularObjectTemplate);
PowerupObjectTemplate.Var[6] = PowerupObjectTemplate.Var[6] | 8;
RegularObjectTemplate.Var[6] = RegularObjectTemplate.Var[6] & ~8;
SampleFilenames = sampleFilenames;
for (uint i = 0; i < otherAnimSetsToLoad.length; ++i)
if (jjAnimSets[otherAnimSetsToLoad[i]] == 0)
jjAnimSets[otherAnimSetsToLoad[i]].load();
AnimSetFilename = animSetFilename;
AnimSetID = animSetID;
Animations[AnimationIndices::PickupIndex] = pickupAnimation;
Animations[AnimationIndices::PoweredUpPickupIndex] = poweredUpPickupAnimation;
Animations[AnimationIndices::PowerupIndex] = powerupAnimation;
Animations[AnimationIndices::AmmoCrateIndex] = ammoCrateAnimation;
GenerateSupplementalAnimations = generateSupplementalAnimations;
PickupFrameIDToUseForAmmoCrate = pickupFrameIDToUseForAmmoCrate;
IsRFMissile = isRFMissile;
Traits = traits;
PoweredUpTraits = (poweredUpTraits == -1) ? traits : uint(poweredUpTraits);
WeaponHookRequired = weaponHookRequired;
RoundsPerPickup = roundsPerPickup;
PickupSample = pickupSample;
Multiplier = multiplier;
GradualAim = gradualAim;
ReplacedByBubbles = replacedByBubbles;
Spread = spread;
Style = style;
@ExtraApply = @apply;
@CustomAmmo3 = @customAmmo3;
@CustomAmmo15 = @customAmmo15;
@CustomPowerup = @customPowerup;
@OnMain = @onMain;
@OnPlayer = @onPlayer;
@OnPlayerInput = @onPlayerInput;
@OnDrawAmmo = @onDrawAmmo;
@OnReceive = @onReceive;
}
array<bool>@ loadSamples(const array<SOUND::Sample>& samples) {
if (SamplesLoaded.length < getSampleCount()){
Samples = samples;
SamplesLoaded.resize(samples.length);
if (samples.length == SampleFilenames.length) {
for (uint i = 0; i < samples.length; ++i)
SamplesLoaded[i] = samples[i] != 0 && jjSampleLoad(samples[i], SampleFilenames[i]);
}
}
return SamplesLoaded;
}
uint getSampleCount() const { return SampleFilenames.length; }
jjANIMSET@ loadAnims(jjANIMSET@ animSet) {
if (AnimSet is null) {
if (AnimSetFilename.length > 0) {
if (AnimSetFilename.findFirst('.') < 0)
AnimSetFilename += ".j2a";
animSet.load(AnimSetID, AnimSetFilename);
}
@AnimSet = @animSet;
}
return @animSet;
}
uint getTraits(bool powerup) const { return powerup ? PoweredUpTraits : Traits; }
bool onIsRFBullet(jjOBJ@) { return IsRFMissile; }
bool setAsWeapon(uint number, se::WeaponHook@ weaponHook) {
if (se::isValidWeapon(number)) {
jjWEAPON@ weapon = jjWeapons[number];
weapon.defaultSample = false;
weapon.multiplier = Multiplier;
weapon.gradualAim = GradualAim;
weapon.replacedByBubbles = ReplacedByBubbles;
weapon.spread = Spread;
weapon.style = Style;
for (int i = 0; i < AnimationIndices::_COUNT; ++i)
if (Animations[i] >= 0 && Animations[i] < 200) //in case Apply wasn't called for whatever reason
Animations[i] += AnimSet.firstAnim;
const auto combinedTraits = Traits | PoweredUpTraits;
jjOBJ@ ammo3;
{
const int pa = Animations[AnimationIndices::PickupIndex], ppa = Animations[AnimationIndices::PoweredUpPickupIndex];
if (weaponHook !is null) {
weaponHook.resetCallbacks(number);
if (pa >= 0 || ppa >= 0) {
weaponHook.setWeaponSprite(number, false, jjAnimations[pa >= 0 ? pa : ppa]);
weaponHook.setWeaponSprite(number, true, jjAnimations[ppa >= 0 ? ppa : pa]);
}
if (OnMain !is null)
weaponHook.setMainCallback(number, OnMain);
if (OnPlayer !is null)
weaponHook.setPlayerCallback(number, OnPlayer);
if (OnPlayerInput !is null)
weaponHook.setPlayerInputCallback(number, OnPlayerInput);
if (OnDrawAmmo !is null)
weaponHook.setDrawingCallback(number, OnDrawAmmo);
if (OnReceive !is null)
@ConstructPacket = weaponHook.addPacketCallback(OnReceive);
} else if (WeaponHookRequired)
return false;
if ((combinedTraits & se::weapon_has_ammo_pickups) != 0 && (pa >= 0 || ppa >= 0)) {
const uint ammo3EventID = se::getAmmoPickupOfWeapon(number);
if (ammo3EventID != 0) {
@ammo3 = jjObjectPresets[ammo3EventID];
if (CustomAmmo3 !is null)
CustomAmmo3(ammo3, number);
else {
if (PickupSample < 0)
PickupSample = SOUND::Sample(Samples[1-PickupSample]);
ammo3.behavior = AmmoPickupWithCrateAndCorpseSupport(
jjAnimations[pa >= 0 ? pa : ppa],
jjAnimations[ppa >= 0 ? ppa : pa],
RoundsPerPickup,
PickupSample
);
ammo3.state = STATE::START;
ammo3.curAnim = pa >= 0 ? pa : ppa;
ammo3.frameID = 0;
ammo3.determineCurFrame();
ammo3.killAnim = jjAnimSets[ANIM::PICKUPS] + 86;
ammo3.objType = HANDLING::PICKUP;
ammo3.isBlastable = true;
ammo3.var[3] = number - 1;
ammo3.points = 100;
}
if (jjGameConnection != GAME::LOCAL && jjObjectPresets[OBJECT::FLICKERGEM].behavior == BEHAVIOR::FLICKERGEM)
jjObjectPresets[OBJECT::FLICKERGEM].behavior = FlickerGemAmmoPickupAssigner;
}
}
} {
const uint ammo15EventID = Ammo15s !is null ? Ammo15s[number-1] : se::getAmmoCrateOfWeapon(number);
if (ammo15EventID != 0) {
jjOBJ@ ammo15 = @jjObjectPresets[ammo15EventID];
if ((combinedTraits & se::weapon_has_ammo_crates) != 0 && Animations[AnimationIndices::AmmoCrateIndex] >= 0) {
if (CustomAmmo15 !is null)
CustomAmmo15(ammo15, number);
else {
ammo15.curAnim = Animations[AnimationIndices::AmmoCrateIndex];
ammo15.frameID = 0;
ammo15.determineCurFrame();
ammo15.killAnim = jjAnimSets[ANIM::AMMO] + 71;
ammo15.state = STATE::START;
ammo15.objType = HANDLING::SPECIAL;
ammo15.noHit = HANDLING::HURTBYBULLET;
ammo15.direction = 1;
ammo15.energy = 1;
ammo15.var[3] = number - 1;
ammo15.var[2] = se::getAmmoPickupOfWeapon(number);
ammo15.points = 300;
if (se::getAmmoCrateOfWeapon(number) != 0 && number != 2) { //3,4,5,6
ammo15.behavior = BEHAVIOR::AMMO15;
ammo15.eventID = ammo15EventID;
} else { //no normal ammo15 object available (or in the case of bouncers, can't be trusted to use the right curAnim)
ammo15.behavior = function(obj) { if (obj.state == STATE::DEACTIVATE) obj.eventID = obj.counterEnd; obj.behave(BEHAVIOR::AMMO15); };
ammo15.eventID = OBJECT::ICEAMMO15;
ammo15.counterEnd = ammo15EventID;
}
}
} else if (ammo3 !is null)
CopyObject(ammo15, ammo3);
}
const uint powerupEventID = se::getPowerupMonitorOfWeapon(number);
jjOBJ@ powerup = @jjObjectPresets[powerupEventID];
if ((combinedTraits & se::weapon_has_powerup_monitors) != 0 && (combinedTraits & se::weapon_has_distinct_powerup) != 0 && Animations[AnimationIndices::PowerupIndex] >= 0) {
if (CustomPowerup !is null)
CustomPowerup(powerup, number);
else {
powerup.curAnim = Animations[AnimationIndices::PowerupIndex];
powerup.frameID = 0;
powerup.determineCurFrame();
powerup.killAnim = jjAnimSets[ANIM::AMMO] + 71;
powerup.state = STATE::START;
powerup.objType = HANDLING::SPECIAL;
powerup.noHit = HANDLING::HURTBYBULLET;
powerup.energy = 1;
powerup.var[3] = number - 1;
powerup.points = 1000;
if (number != WEAPON::BLASTER) {
powerup.behavior = BEHAVIOR::MONITOR;
powerup.eventID = powerupEventID;
} else { //gun1 powerup shouldn't use player-dependent animations if it's been replaced by some other weapon
powerup.behavior = function(obj) { if (obj.state == STATE::DEACTIVATE) obj.eventID = obj.counterEnd; obj.behave(BEHAVIOR::MONITOR); };
powerup.counterEnd = powerupEventID;
powerup.eventID = OBJECT::BOUNCERPOWERUP;
}
}
} else if (ammo3 !is null)
CopyObject(powerup, ammo3);
}
jjOBJ@ Preset1 = jjObjectPresets[se::getBasicBulletOfWeapon(number)];
jjOBJ@ Preset2 = jjObjectPresets[se::getPoweredBulletOfWeapon(number)];
RegularObjectTemplate.Apply(Preset1, AnimSet.firstAnim, true);
if (number != WEAPON::TNT)
PowerupObjectTemplate.Apply(Preset2, AnimSet.firstAnim, true);
else if (Preset1.xSpeed != 0) {
Preset1.xSpeed = 1; //for determining the angle
Preset1.ySpeed = 0;
}
Preset1.var[3] = Preset2.var[3] = number;
Preset1.behavior = Preset2.behavior = this; //onBehave, below
return true;
}
return false;
}
bool Apply(uint number, se::WeaponHook@ weaponHook = null, jjSTREAM@ parameters = null, uint8 ammo15EventID = 0) {
if (se::isValidWeapon(number)) {
LoadResources();
if (ExtraApply !is null && !ExtraApply(
number,
weaponHook,
parameters
))
return false;
if (Ammo15s is null)
@Ammo15s = array<uint8>(9, 0);
if (Ammo15s[number-1] == 0) {
if (ammo15EventID != 0)
Ammo15s[number-1] = ammo15EventID;
else if (number < WEAPON::TNT && number != WEAPON::BLASTER)
Ammo15s[number-1] = se::getAmmoCrateOfWeapon(number);
}
for (int i = 0; i < AnimationIndices::_COUNT; ++i)
if (Animations[i] >= 0 && Animations[i] < 200)
Animations[i] += AnimSet.firstAnim;
if (GenerateSupplementalAnimations)
GenerateSupplementaryWeaponSprites(Animations, Ammo15s[number-1], PickupFrameIDToUseForAmmoCrate);
return setAsWeapon(number, weaponHook);
}
return false;
}
void onBehave(jjOBJ@ obj) {
bool powerup;
if (obj.eventID == OBJECT::TNT) {
float RFYSpeed = 0;
if (IsRFMissile && abs(obj.xSpeed) == jjObjectPresets[OBJECT::TNT].xSpeed) {
RFYSpeed = obj.ySpeed;
obj.ySpeed = 0;
}
if (powerup = (obj.creatorType == CREATOR::PLAYER && jjPlayers[obj.creator].powerup[WEAPON::TNT])) {
const bool firingUp = obj.counterEnd < jjObjectPresets[OBJECT::TNT].counterEnd;
PowerupObjectTemplate.Apply(obj, AnimSet.firstAnim, false);
if (firingUp) obj.counterEnd = obj.counterEnd * 2 / 3;
} else
RegularObjectTemplate.ApplyTNTSpeeds(obj);
if (RFYSpeed != 0) obj.ySpeed = RFYSpeed;
obj.eventID = OBJECT::BULLET; //don't use /tntdamage unless AssignBehavior requests it specifically
} else
powerup = obj.eventID >= OBJECT::BLASTERBULLETPU;
if (PowerupObjectTemplate is RegularObjectTemplate) {
if (powerup)
obj.var[6] = obj.var[6] | 8;
else
obj.var[6] = obj.var[6] & ~8;
}
AssignBehavior(obj, powerup);
obj.behave();
}
array<bool>@ LoadFirstAvailableSamples() {
if (SamplesLoaded.length == getSampleCount())
return SamplesLoaded;
array<SOUND::Sample> samples;
const uint sampleCount = getSampleCount();
array<SOUND::Sample>@ prospectiveSamples = SOUND::GetAllSampleConstantsInRoughOrderOfLikelihoodToBeUsed();
while (samples.length < sampleCount) {
if (prospectiveSamples.length != 0) {
SOUND::Sample prospectiveSample = prospectiveSamples[0];
prospectiveSamples.removeAt(0);
if (!jjSampleIsLoaded(prospectiveSample))
samples.insertLast(prospectiveSample);
} else
samples.insertLast(SOUND::Sample(0));
}
return loadSamples(samples);
}
void LoadResources() {
if (SetID == 0)
loadAnims(jjAnimSets[SetID = GetFirstAvailableCustomAnimsetID()]);
LoadFirstAvailableSamples();
}
uint getMaxDamage(bool powerup) const { return powerup ? 2 : 1; }
array<int> FireBullet(float xPos, float yPos, int angle, bool powerup, uint16 creatorID, CREATOR::Type creatorType = CREATOR::PLAYER, uint number = WEAPON::BLASTER, SPREAD::Spread spread = SPREAD::Spread(-1), float additionalXSpeed = 0) const {
//LoadResources();
int monsterDirection; float monsterXSpeed;
if (creatorType != CREATOR::PLAYER) { //avoid issues with some startup code in some native bullet behaviors
jjOBJ@ monster = jjObjects[creatorID];
monsterDirection = monster.direction; monster.direction = 1;
monsterXSpeed = monster.xSpeed; monster.xSpeed = 0;
}
const uint8 eventID = se::isValidWeapon(number) ? (!powerup ? se::getBasicBulletOfWeapon(number) : se::getPoweredBulletOfWeapon(number)) : OBJECT::BULLET;
angle &= 1023;
array<int> angles(1, angle);
array<int> bulletObjectIDs;
if (spread == -1) spread = Spread;
if (spread == SPREAD::RF)
spread = powerup ? SPREAD::RFPU : SPREAD::RFNORMAL;
else if (spread == SPREAD::ICE)
spread = powerup ? SPREAD::ICEPU : SPREAD::NORMAL;
else if (spread == SPREAD::GUN8)
spread = !jjAllowsFireball ? SPREAD::PEPPERSPRAY : SPREAD::NORMAL;
switch (spread) {
case SPREAD::ICEPU:
angles.insertLast((creatorType == CREATOR::PLAYER ? jjPlayers[creatorID].direction : jjObjects[creatorID].direction) >= 0 ? 0x000 : 0x200);
break;
case SPREAD::RFPU:
angles.insertLast(angle);
//fall through:
case SPREAD::RFNORMAL:
angles[0] -= 33;
angles.insertLast(angle + 33);
break;
case SPREAD::PEPPERSPRAY: {
angles.resize(0);
for (int pepperSprayBulletCount = 0; pepperSprayBulletCount < 2; ++pepperSprayBulletCount)
angles.insertLast(angle + int(jjRandom() & 31) - 16);
if (creatorType == CREATOR::PLAYER)
jjSample(xPos, yPos, SOUND::Sample(SOUND::AMMO_LASER2 + (jjRandom() & 1)));
break; }
case SPREAD::NORMAL:
case SPREAD::TOASTER:
default:
//already fine
break;
}
auto@ template = spread != SPREAD::PEPPERSPRAY ? (powerup ? PowerupObjectTemplate : RegularObjectTemplate) : MLLEWeapons::ObjectTemplate(
xSpeed: 3 + (jjRandom() & 31) / 16.f + additionalXSpeed,
animSpeed: powerup ? 2 : 1,
curAnim: jjAnimSets[ANIM::AMMO].firstAnim + 10,
lightType: (powerup && jjStrongPowerups) ? LIGHT::POINT2 : LIGHT::NONE,
state: (powerup && jjStrongPowerups) ? STATE::FLY : STATE::START,
var: array<int> = {0,0,0,0,0,0, (powerup && jjStrongPowerups) ? 8 : 0}
);
for (uint i = 0; i < angles.length; ++i) {
int bulletObjectID = jjAddObject(eventID, xPos, yPos, creatorID, creatorType, BEHAVIOR::INACTIVE);
jjOBJ@ obj = jjObjects[bulletObjectID];
obj.xSpeed = jjCos(angles[i]);
obj.ySpeed = jjSin(angles[i]);
template.Apply(obj, AnimSet.firstAnim, false);
obj.direction = (obj.xAcc < 0) ? -1 : 0;
if (obj.special != 0 && abs(obj.ySpeed) > abs(obj.xSpeed)) { obj.curAnim = obj.special; obj.determineCurFrame(); }
else if (obj.direction == 0) obj.direction = 1;
obj.var[3] = number;
if (IsRFMissile && (angle == 0 || angle == 0x200)) {
const int dir = angle == 0 ? 1 : -1;
obj.xSpeed = template.XSpeed * dir + additionalXSpeed;
obj.xAcc = template.XAcc * dir;
obj.ySpeed = (angles[i] - angle) / 33;
obj.yAcc = 0;
obj.var[7] = 0;
} else {
if (additionalXSpeed > 8) additionalXSpeed = 8;
else if (additionalXSpeed < -8) additionalXSpeed = -8;
obj.var[7] = int(additionalXSpeed * 65536);
if (angle == 0x300 && !IsRFMissile) { //straight up
obj.counterEnd = obj.counterEnd * 2 / 3;
}
}
if (spread == SPREAD::TOASTER) {
if (angle == 0x300) //straight up
obj.yPos = (obj.yOrg -= 8);
if (creatorType == CREATOR::PLAYER) {
const float mult = (99 - jjPlayers[creatorID].fastfire) / 32.f;
obj.xSpeed *= mult;
obj.ySpeed *= mult;
}
}
if (spread != SPREAD::PEPPERSPRAY) {
if (obj.eventID == OBJECT::TNT) obj.eventID = OBJECT::BULLET;
AssignBehavior(obj, powerup);
} else {
obj.behavior = BEHAVIOR::PEPPERBULLET;
template.XSpeed = 3 + (jjRandom() & 31) / 16.f + additionalXSpeed;
}
if (obj.creatorType != CREATOR::PLAYER && obj.playerHandling == HANDLING::PLAYERBULLET) {
if (obj.freeze == 0)
obj.playerHandling = HANDLING::ENEMYBULLET;
else {
obj.playerHandling = HANDLING::SPECIAL;
obj.scriptedCollisions = true;
obj.behavior = FreezingEnemyBullet(obj.behavior);
}
}
bulletObjectIDs.insertLast(bulletObjectID);
}
for (uint i = 0; i < bulletObjectIDs.length; ++i)
jjObjects[bulletObjectIDs[i]].behave();
if (creatorType != CREATOR::PLAYER) {
jjOBJ@ monster = jjObjects[creatorID];
monster.direction = monsterDirection;
monster.xSpeed = monsterXSpeed;
}
return bulletObjectIDs;
}
}
class FreezingEnemyBullet : jjBEHAVIORINTERFACE {
private jjBEHAVIOR nativeBehavior;
FreezingEnemyBullet(const jjBEHAVIOR &in nb) { nativeBehavior = nb; }
void onBehave(jjOBJ@ obj) { obj.behave(nativeBehavior); }
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int) {
if (bullet is null) {
player.frozen = obj.freeze;
obj.state = STATE::EXPLODE;
}
return true;
}
}
class AmmoPickupWithCrateAndCorpseSupport : se::AmmoPickup {
AmmoPickupWithCrateAndCorpseSupport(const ::jjANIMATION@ a, const ::jjANIMATION@ b, uint c = 3, SOUND::Sample d = SOUND::COMMON_PICKUPW1) { super(a,b,c,d); }
void onBehave(::jjOBJ@ obj) override {
if (obj.playerHandling == HANDLING::DELAYEDPICKUP) {
if (obj.var[2] == 1) { //about to be available to collect
obj.playerHandling = HANDLING::PICKUP; //native code also sets .scriptedCollisions to false, and that's not good
obj.scriptedCollisions = true;
obj.var[2] = 0; //probably not needed
} else
obj.scriptedCollisions = false; //so the native code knows to decrement var[2]
}
if ((obj.state == STATE::FLOAT || (obj.state == STATE::FLOATFALL && obj.ySpeed == 0)) && obj.counter > 0) { //temporary pickup (in MP)
if (--obj.counter < 210) {
if (obj.counter == 0) {
obj.delete();
return;
}
if (jjGameTicks & 1 == 0) //flickering
return;
}
}
se::AmmoPickup::onBehave(obj);
}
}
void FlickerGemAmmoPickupAssigner(jjOBJ@ obj) {
jjBEHAVIORINTERFACE@ intermediateCast;
if (
obj.eventID >= OBJECT::ICEAMMO3 &&
obj.eventID <= OBJECT::GUN9AMMO3 &&
(@intermediateCast = cast<jjBEHAVIORINTERFACE@>(jjObjectPresets[obj.eventID].behavior)) !is null &&
cast<AmmoPickupWithCrateAndCorpseSupport@>(@intermediateCast) !is null
)
obj.behavior = jjObjectPresets[obj.eventID].behavior;
else
obj.behavior = BEHAVIOR::FLICKERGEM;
obj.behave();
}
void _fitInto(jjPIXELMAP &inout dest, const jjPIXELMAP &in source) {
const float xRatio = float(source.width) / float(dest.width);
const float yRatio = float(source.height) / float(dest.height);
for (uint xD = 0; xD < dest.width; ++xD)
for (uint yD = 0; yD < dest.height; ++yD) {
const uint8 color = source[uint(xD * xRatio), uint(yD * yRatio)];
if (color != 0)
dest[xD,yD] = color;
}
}
uint GetFirstAvailableCustomAnimsetID() {
uint setID;
for (uint customAnimSetID = 0; customAnimSetID < 256; ++customAnimSetID)
if (jjAnimSets[setID = ANIM::CUSTOM[customAnimSetID]] == 0)
return setID;
return ANIM::JAZZ;
}
void _recolorAnimation(const jjANIMATION@ dest, const jjANIMATION@ src, const array<uint8>@ lut) {
for (uint i = 0; i < src.frameCount; ++i) {
jjANIMFRAME@ destFrame = jjAnimFrames[dest + i], srcFrame = jjAnimFrames[src + i];
jjPIXELMAP image(srcFrame);
for (uint x = 0; x < image.width; ++x)
for (uint y = 0; y < image.height; ++y)
image[x,y] = lut[image[x,y]];
image.save(destFrame);
destFrame.hotSpotX = srcFrame.hotSpotX;
destFrame.hotSpotY = srcFrame.hotSpotY;
}
}
array<uint8>@ Ammo15s;
void GenerateSupplementaryWeaponSprites(array<int>@ animations, uint8 ammoCrateEventID = 0, int pickupFrameIDToUseForAmmoCrate = 0) {
if (animations[AnimationIndices::PickupIndex] >= 0 || animations[AnimationIndices::PoweredUpPickupIndex] >= 0) {
array<bool> animationsToGenerate(4);
array<uint> numberOfFramesToGenerate(0);
if (animationsToGenerate[AnimationIndices::PickupIndex] = (animations[AnimationIndices::PickupIndex] < 0 && animations[AnimationIndices::PoweredUpPickupIndex] >= 0))
numberOfFramesToGenerate.insertLast(jjAnimations[animations[AnimationIndices::PoweredUpPickupIndex]].frameCount);
else if (animationsToGenerate[AnimationIndices::PoweredUpPickupIndex] = (animations[AnimationIndices::PoweredUpPickupIndex] < 0 && animations[AnimationIndices::PickupIndex] >= 0))
numberOfFramesToGenerate.insertLast(jjAnimations[animations[AnimationIndices::PickupIndex]].frameCount);
if (animationsToGenerate[AnimationIndices::AmmoCrateIndex] = (animations[AnimationIndices::AmmoCrateIndex] < 0 && ammoCrateEventID != 0))
numberOfFramesToGenerate.insertLast(1);
if (animationsToGenerate[AnimationIndices::PowerupIndex] = (animations[AnimationIndices::PowerupIndex] < 0))
numberOfFramesToGenerate.insertLast(10);
if (numberOfFramesToGenerate.length == 0) //nothing needs generating
return;
jjANIMSET@ animSet = jjAnimSets[GetFirstAvailableCustomAnimsetID()];
animSet.allocate(numberOfFramesToGenerate);
uint animsetAnimationIndex = 0;
if ((animationsToGenerate[AnimationIndices::PickupIndex] || animationsToGenerate[AnimationIndices::PoweredUpPickupIndex]) && _static::PowerupColorConversionRandomizer is null)
@_static::PowerupColorConversionRandomizer = jjRNG(jjLevelFileName[0] * jjLevelFileName[1] * jjLevelFileName[2] * jjLevelFileName[3] * jjLevelFileName[4]);
if (animationsToGenerate[AnimationIndices::PickupIndex]) { //generate pickup anim from powerup pickup anim
if (_static::PowerupToNormal is null) { //setup
const array<uint8> OrangeTargets = { 32, 72, 88 };
@_static::PowerupToNormal = array<uint8> = {
48,
(_static::PowerupColorConversionRandomizer() & 1 == 1) ? 16 : 88,
(_static::PowerupColorConversionRandomizer() & 1 == 1) ? 24 : 40,
OrangeTargets[_static::PowerupColorConversionRandomizer() % 3],
80,
0,
72,
64,
32,
(_static::PowerupColorConversionRandomizer() & 1 == 1) ? 24 : 32
};
_static::PowerupToNormal[5] = _static::PowerupToNormal[3];
@_static::PowerupToNormalLUT = array<uint8>(256);
for (uint i = 1; i < 256; ++i)
_static::PowerupToNormalLUT[i] = (i < 16 || i >= 96) ? i : (_static::PowerupToNormal[(i >> 3) - 2] | (i & 7));
}
_recolorAnimation(jjAnimations[animations[AnimationIndices::PickupIndex] = (animSet + animsetAnimationIndex++)], jjAnimations[animations[AnimationIndices::PoweredUpPickupIndex]],_static::PowerupToNormalLUT);
} else if (animationsToGenerate[AnimationIndices::PoweredUpPickupIndex]) { //generate powerup pickup anim from pickup anim
if (_static::NormalToPowerup is null) { //setup
@_static::NormalToPowerup = array<uint8> = {
24,
(_static::PowerupColorConversionRandomizer() & 1 == 1) ? 32 : 88,
(_static::PowerupColorConversionRandomizer() & 1 == 1) ? 40 : 88,
32,
16,
32,
72,
40,
48,
40
};
@_static::NormalToPowerupLUT = array<uint8>(256);
for (uint i = 1; i < 256; ++i)
_static::NormalToPowerupLUT[i] = (i < 16 || i >= 96) ? i : (_static::NormalToPowerup[(i >> 3) - 2] | (i & 7));
}
_recolorAnimation(jjAnimations[animations[AnimationIndices::PoweredUpPickupIndex] = (animSet + animsetAnimationIndex++)], jjAnimations[animations[AnimationIndices::PickupIndex]], _static::NormalToPowerupLUT);
}
if (animationsToGenerate[AnimationIndices::AmmoCrateIndex]) { //generate crate anim from pickup anim
if (_static::CrateImage is null) { //setup
@_static::CrateImage = jjPIXELMAP(jjAnimFrames[jjAnimations[jjAnimSets[ANIM::PICKUPS] + 59]]); //TNT crate--unlikely to have been replaced
for (uint x = 4; x < 20; ++x)
for (uint y = 8; y < 18; ++y)
if (_static::CrateImage[x,y] >= 88) //get rid of existing purple
_static::CrateImage[x,y] = 41;
}
jjPIXELMAP destImage(_static::CrateImage.width, _static::CrateImage.height);
for (uint x = 0; x < destImage.width; ++x)
for (uint y = 0; y < destImage.height; ++y)
destImage[x,y] = _static::CrateImage[x,y];
jjPIXELMAP resizedSourceImage(13, 13);
jjPIXELMAP pickupImage(jjAnimFrames[jjAnimations[animations[AnimationIndices::PickupIndex]].firstFrame + pickupFrameIDToUseForAmmoCrate]);
_fitInto(resizedSourceImage, pickupImage);
for (uint x = 0; x < resizedSourceImage.width; ++x)
for (uint y = 0; y < resizedSourceImage.height; ++y) {
uint8 color = resizedSourceImage[x, y];
if (color != 0) {
if (color == 15) //white
color = 0; //bright
else
color &= 7;
destImage[5+x,7+y] = 95 - (color >> 1);
}
}
jjANIMFRAME@ destFrame = jjAnimFrames[jjAnimations[animations[AnimationIndices::AmmoCrateIndex] = (animSet + animsetAnimationIndex++)]];
destImage.save(destFrame);
destFrame.hotSpotX = -13;
destFrame.hotSpotY = -14;
}
if (animationsToGenerate[AnimationIndices::PowerupIndex]) { //generate powerup monitor anim from powerup pickup anim
if (_static::PowerupImages is null) { //setup
@_static::PowerupImages = array<jjPIXELMAP@>();
const jjANIMATION@ powerupTemplate = jjAnimations[jjAnimSets[ANIM::PICKUPS] + 61]; //bouncer powerup has easy colors to work with
jjPIXELMAP powerupFrameBlankCorner(jjAnimFrames[powerupTemplate + 2]);
for (uint x = 3; x <= 13; ++x)
for (uint y = 8; y <= 18; ++y) {
const uint8 oldColor = powerupFrameBlankCorner[x,y];
if (oldColor >= 88 || (oldColor >= 59 && oldColor <= 63)) //purple or yellow
powerupFrameBlankCorner[x,y] = 71; //monitor black
}
for (uint i = 0; i < 10; ++i) { //powerupTemplate.length
jjANIMFRAME@ srcFrame = jjAnimFrames[powerupTemplate + i];
_static::PowerupImages.insertLast(jjPIXELMAP(srcFrame));
jjPIXELMAP@ destImage = @_static::PowerupImages[_static::PowerupImages.length-1];
for (uint x = 0; x < 17; ++x)
for (uint y = 0; y < 21; ++y)
destImage[x,y] = powerupFrameBlankCorner[x,y];
}
}
const jjANIMATION@ poweredUpPickupAnimation = jjAnimations[animations[AnimationIndices::PoweredUpPickupIndex]];
const jjANIMATION@ newAnim = jjAnimations[animations[AnimationIndices::PowerupIndex] = (animSet + animsetAnimationIndex++)];
int minX = 0, minY = 0;
int maxRight = 0, maxBottom = 0;
array<jjPIXELMAP@> expandedPickupImages(poweredUpPickupAnimation.frameCount, null);
for (uint i = 0; i < poweredUpPickupAnimation.frameCount; ++i) {
const jjANIMFRAME@ frame = jjAnimFrames[poweredUpPickupAnimation + i];
if (frame.hotSpotX < minX) minX = frame.hotSpotX;
if (frame.hotSpotY < minY) minY = frame.hotSpotY;
const int right = frame.width + frame.hotSpotX, bottom = frame.height + frame.hotSpotY;
if (right > maxRight) maxRight = right;
if (bottom > maxBottom) maxBottom = bottom;
}
for (uint i = 0; i < poweredUpPickupAnimation.frameCount; ++i) {
@expandedPickupImages[i] = jjPIXELMAP(maxRight - minX, maxBottom - minY);
const jjANIMFRAME@ sourceFrame = jjAnimFrames[poweredUpPickupAnimation + i];
const jjPIXELMAP sourceImage(sourceFrame);
for (uint xS = 0; xS < sourceFrame.width; ++xS)
for (uint yS = 0; yS < sourceFrame.height; ++yS)
expandedPickupImages[i][xS - minX + sourceFrame.hotSpotX, yS - minY + sourceFrame.hotSpotY] = sourceImage[xS, yS];
}
for (uint i = 0; i < 10; ++i) {
const jjPIXELMAP@ templateImage = _static::PowerupImages[i];
jjPIXELMAP destImage(templateImage.width, templateImage.height);
for (uint x = 0; x < destImage.width; ++x)
for (uint y = 0; y < destImage.height; ++y)
destImage[x,y] = templateImage[x,y];
jjPIXELMAP resizedPickupImage(12, 14); //something like that
_fitInto(resizedPickupImage, expandedPickupImages[i % expandedPickupImages.length]);
for (uint x = 0; x < resizedPickupImage.width; ++x)
for (uint y = 0; y < resizedPickupImage.height; ++y) {
const uint8 color = resizedPickupImage[x,y];
if (color != 0)
destImage[3+x, 4+y] = color;
}
jjANIMFRAME@ destFrame = jjAnimFrames[newAnim + i];
destImage.save(destFrame);
destFrame.hotSpotX = destFrame.hotSpotY = -14;
}
}
}
}
void CopyObject(jjOBJ@ dest, const jjOBJ@ src) {
dest.frameID = src.frameID;
dest.curFrame = src.curFrame;
dest.curAnim = src.curAnim;
dest.killAnim = src.killAnim;
dest.behavior = src.behavior;
dest.noHit = src.noHit;
dest.objType = src.objType;
dest.energy = src.energy;
dest.points = src.points;
dest.counterEnd=src.counterEnd;
dest.state = src.state;
for (int i = 0; i < 11; ++i)
dest.var[i] = src.var[i];
}
void UpdateActiveObjectAnimations(uint number) {
if (se::isValidWeapon(number)) {
array<uint8> eventIDs = {se::getAmmoPickupOfWeapon(number), (Ammo15s !is null && Ammo15s[number-1] != 0) ? Ammo15s[number-1] : se::getAmmoCrateOfWeapon(number), se::getPowerupMonitorOfWeapon(number)};
for (uint objectID = jjObjectCount; --objectID != 0;) {
jjOBJ@ obj = jjObjects[objectID];
if (obj.isActive && eventIDs.find(obj.eventID) >= 0)
CopyObject(obj, jjObjectPresets[obj.eventID]);
}
}
}
bool WeaponHasBeenReplaced(uint number) {
if (!se::isValidWeapon(number))
return false;
const array<BEHAVIOR::Behavior> behaviors = {
BEHAVIOR::BULLET, BEHAVIOR::BULLET,
BEHAVIOR::BOUNCERBULLET, BEHAVIOR::BOUNCERBULLETPU,
BEHAVIOR::BULLET, BEHAVIOR::ICEBULLETPU,
BEHAVIOR::SEEKERBULLET, BEHAVIOR::SEEKERBULLET,
BEHAVIOR::RFBULLET, BEHAVIOR::RFBULLET,
BEHAVIOR::TOASTERBULLET, BEHAVIOR::TOASTERBULLET,
BEHAVIOR::TNT, BEHAVIOR::TNT,
BEHAVIOR::BULLET, BEHAVIOR::BULLET,
BEHAVIOR::ELECTROBULLET, BEHAVIOR::ELECTROBULLET,
};
const int idx = (number - 1) * 2;
return !(
jjObjectPresets[se::getBasicBulletOfWeapon(number)].behavior == behaviors[idx] &&
jjObjectPresets[se::getPoweredBulletOfWeapon(number)].behavior == behaviors[idx + 1]);
}
namespace HelpfulBulletFunctions {
int GetAngle(const jjOBJ@ obj) {
return int(atan2(
(obj.xSpeed >= 0) ? obj.ySpeed : -obj.ySpeed,
abs(obj.xSpeed)
) * -512.0 * 0.318309886142228);
}
int GetDirection(const jjOBJ@ obj) {
if (obj.xSpeed >= 0)
return 1;
return -1;
}
void DrawAngled(const jjOBJ@ obj, float xScale = 1, float yScale = 1, SPRITE::Mode mode = SPRITE::NORMAL, uint8 param = 0, int layerZ = 4, int layerXY = 4, int8 playerID = -1) {
jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, GetAngle(obj), GetDirection(obj) * xScale, yScale, mode, param, layerZ, layerXY, playerID);
}
bool MaskedPixel(const jjOBJ@ obj) {
return jjMaskedPixel(int(obj.xPos), int(obj.yPos));
}
bool IsPowerup(const jjOBJ@ obj) {
return (obj.var[6] & 8) == 8;
}
void Blast(jjOBJ@ obj, uint animID) {
obj.killAnim = animID;
obj.state = STATE::EXPLODE;
obj.behavior = BEHAVIOR::RFBULLET;
obj.behave(BEHAVIOR::DEFAULT, false);
}
bool PlayerIsEnemy(const jjOBJ@ obj, const jjPLAYER@ other) {
if (obj.creatorType != CREATOR::PLAYER)
return true;
else
return jjPlayers[obj.creatorID].isEnemy(other);
}
int GetNearestEnemyPlayer(const jjOBJ@ obj, uint maxDistance, bool canBeFlickering = false) {
if (jjGameMode <= GAME::COOP && obj.creatorType == CREATOR::PLAYER)
return -1;
if (!canBeFlickering && jjGameState != GAME::STARTED && jjGameState != GAME::OVERTIME)
return -1;
int nearestPlayerID = -1;
uint minDistance = maxDistance * maxDistance;
for (uint i = 0; i < 32; ++i) {
const jjPLAYER@ potentialEnemy = jjPlayers[i];
if (!potentialEnemy.isInGame)
continue;
if (!canBeFlickering && potentialEnemy.blink != 0)
continue;
const float dx = potentialEnemy.xPos - obj.xPos, dy = potentialEnemy.yPos - obj.yPos;
const uint distance = uint(dx * dx + dy * dy);
if (distance < minDistance && PlayerIsEnemy(obj, potentialEnemy)) {
minDistance = distance;
nearestPlayerID = i;
}
}
return nearestPlayerID;
}
int GetNearestEnemyObject(const jjOBJ@ obj, uint maxDistance) {
if (obj.creatorType != CREATOR::PLAYER)
return -1;
int nearestObjectID = -1;
uint minDistance = maxDistance * maxDistance;
for (int i = 1; i < jjObjectCount; ++i) {
const jjOBJ@ potentialEnemy = jjObjects[i];
if (!potentialEnemy.isActive || !potentialEnemy.isTarget)
continue;
const float dx = potentialEnemy.xPos - obj.xPos, dy = potentialEnemy.yPos - obj.yPos;
const uint distance = uint(dx * dx + dy * dy);
if (distance < minDistance) {
minDistance = distance;
nearestObjectID = i;
}
}
return nearestObjectID;
}
array<float>@ GetNearestEnemyPosition(const jjOBJ@ obj, uint maxDistance, bool canBeFlickering = false) {
int nearestEnemyID = GetNearestEnemyPlayer(obj, maxDistance, canBeFlickering);
if (nearestEnemyID >= 0) {
const jjPLAYER@ target = jjPlayers[nearestEnemyID];
return array<float> = {target.xPos, target.yPos};
}
nearestEnemyID = GetNearestEnemyObject(obj, maxDistance);
if (nearestEnemyID >= 0) {
const jjOBJ@ target = jjObjects[nearestEnemyID];
return array<float> = {target.xPos, target.yPos};
}
return null;
}
}
const float LevelRight = float(jjLayerWidth[4] * 32);
const float LevelBottom = float(jjLayerHeight[4] * 32);
}
namespace _static {
jjRNG@ PowerupColorConversionRandomizer;
array<uint8>@ NormalToPowerupLUT, NormalToPowerup, PowerupToNormalLUT, PowerupToNormal;
jjPIXELMAP@ CrateImage;
array<jjPIXELMAP@>@ PowerupImages;
}
namespace DefaultWeapons {
void DefaultSample(const jjOBJ@ obj, WEAPON::Weapon number, bool definitelyPlaySample = false) {
const bool powerup = MLLEWeapons::HelpfulBulletFunctions::IsPowerup(obj);
if (obj.creatorType == CREATOR::PLAYER || definitelyPlaySample)
switch (number) {
case WEAPON::BLASTER: {
const bool jazz = obj.creatorType != CREATOR::PLAYER || jjPlayers[obj.creator].charCurr == CHAR::JAZZ;
int sample;
int frequency = 22050;
if (!powerup) {
if (jazz)
sample = SOUND::AMMO_GUNJAZZ;
else
sample = SOUND::AMMO_GUN1;
} else {
if (jazz)
sample = SOUND::AMMO_FUMP;
else {
sample = SOUND::AMMO_FUMP + ((jjRandom() & 1) * 3);
frequency = 0;
}
}
jjSample(obj.xPos,obj.yPos, SOUND::Sample(sample), 0, frequency);
break;
}
case WEAPON::BOUNCER:
jjSample(obj.xPos, obj.yPos, SOUND::Sample((powerup ? SOUND::AMMO_BMP1 : SOUND::AMMO_GUNFLP) + (jjRandom() % 6)), 0, (obj.creatorType != CREATOR::PLAYER || jjPlayers[obj.creator].charCurr == CHAR::JAZZ) ? 11025 : 22050);
break;
case WEAPON::ICE:
if (powerup)
jjSample(obj.xPos, obj.yPos, SOUND::Sample(SOUND::AMMO_ICEPU1 + (jjRandom() & 3)));
else
jjSample(obj.xPos, obj.yPos, SOUND::Sample(SOUND::AMMO_ICEGUN + (jjRandom() % 3)));
break;
case WEAPON::SEEKER:
jjSample(obj.xPos, obj.yPos, SOUND::AMMO_MISSILE);
break;
case WEAPON::RF:
jjSample(obj.xPos, obj.yPos, SOUND::AMMO_LAZRAYS);
break;
//TNT makes no noise
case WEAPON::GUN8:
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_RINGGUN); //fireball
break;
case WEAPON::GUN9:
jjSample(obj.xPos, obj.yPos, SOUND::Sample(SOUND::AMMO_LASER2 + (jjRandom() & 1)));
break;
}
}
}
namespace SOUND {
array<Sample>@ _allSampleConstantsInRoughOrderOfLikelihoodToBeUsed;
array<Sample>@ GetAllSampleConstantsInRoughOrderOfLikelihoodToBeUsed() {
if (_allSampleConstantsInRoughOrderOfLikelihoodToBeUsed is null)
@_allSampleConstantsInRoughOrderOfLikelihoodToBeUsed = array<Sample> = {
BONUS_BONUSBLUB, ENDING_OHTHANK, EPICLOGO_EPIC1, EPICLOGO_EPIC2, FAN_FAN, GLOVE_HIT, STONED_STONED,
INTRO_BOEM1, INTRO_BOEM2, INTRO_BRAKE, INTRO_END, INTRO_GRAB, INTRO_GREN1, INTRO_GREN2, INTRO_GREN3, INTRO_GUNM0, INTRO_GUNM1, INTRO_GUNM2, INTRO_HELI, INTRO_HITSPAZ, INTRO_HITTURT, INTRO_IFEEL, INTRO_INHALE, INTRO_INSECT, INTRO_KATROL, INTRO_LAND, INTRO_MONSTER, INTRO_MONSTER2, INTRO_ROCK, INTRO_ROPE1, INTRO_ROPE2, INTRO_RUN, INTRO_SHOT1, INTRO_SHOTGRN, INTRO_SKI, INTRO_STRING, INTRO_SWISH1, INTRO_SWISH2, INTRO_SWISH3, INTRO_SWISH4, INTRO_UHTURT, INTRO_UP1, INTRO_UP2, INTRO_WIND_01,
ORANGE_BOEML, ORANGE_BOEMR, ORANGE_BUBBELSL, ORANGE_BUBBELSR, ORANGE_GLAS1L, ORANGE_GLAS1R, ORANGE_GLAS2L, ORANGE_GLAS2R, ORANGE_MERGE, ORANGE_SWEEP0L, ORANGE_SWEEP0R, ORANGE_SWEEP1L, ORANGE_SWEEP1R, ORANGE_SWEEP2L, ORANGE_SWEEP2R,
P2_CRUNCH, P2_FART, P2_FOEW1, P2_FOEW4, P2_FOEW5, P2_FROG1, P2_FROG2, P2_FROG3, P2_FROG4, P2_FROG5, P2_KISS4, P2_OPEN, P2_PINCH1, P2_PINCH2, P2_PLOPSEQ1, P2_PLOPSEQ2, P2_PLOPSEQ3, P2_PLOPSEQ4, P2_POEP, P2_PTOEI, P2_SPLUT, P2_THROW, P2_TONG,
BILSBOSS_BILLAPPEAR, BILSBOSS_FINGERSNAP, BILSBOSS_FIRE, BILSBOSS_FIRESTART, BILSBOSS_SCARY3, BILSBOSS_THUNDER, BILSBOSS_ZIP, BUBBA_BUBBABOUNCE1, BUBBA_BUBBABOUNCE2, BUBBA_BUBBAEXPLO, BUBBA_FROG2, BUBBA_FROG3, BUBBA_FROG4, BUBBA_FROG5, BUBBA_SNEEZE2, BUBBA_TORNADOATTACK2, DEVILDEVAN_DRAGONFIRE, DEVILDEVAN_FLAP, DEVILDEVAN_FROG4, DEVILDEVAN_JUMPUP, DEVILDEVAN_LAUGH, DEVILDEVAN_PHASER2, DEVILDEVAN_STRECH2, DEVILDEVAN_STRECHTAIL, DEVILDEVAN_STRETCH1, DEVILDEVAN_STRETCH3, DEVILDEVAN_VANISH1, DEVILDEVAN_WHISTLEDESCENDING2, DEVILDEVAN_WINGSOUT, QUEEN_LADYUP, QUEEN_SCREAM, ROBOT_BIG1, ROBOT_BIG2, ROBOT_CAN1, ROBOT_CAN2, ROBOT_HYDRO, ROBOT_HYDRO2, ROBOT_HYDROFIL, ROBOT_HYDROPUF, ROBOT_IDLE1, ROBOT_IDLE2, ROBOT_JMPCAN1, ROBOT_JMPCAN10, ROBOT_JMPCAN2, ROBOT_JMPCAN3, ROBOT_JMPCAN4, ROBOT_JMPCAN5, ROBOT_JMPCAN6, ROBOT_JMPCAN7, ROBOT_JMPCAN8, ROBOT_JMPCAN9, ROBOT_METAL1, ROBOT_METAL2, ROBOT_METAL3, ROBOT_METAL4, ROBOT_METAL5, ROBOT_OPEN, ROBOT_OUT, ROBOT_POEP, ROBOT_POLE, ROBOT_SHOOT, ROBOT_STEP1, ROBOT_STEP2, ROBOT_STEP3, SONCSHIP_METAL1, SONCSHIP_MISSILE2, SONCSHIP_SCRAPE, SONCSHIP_SHIPLOOP, SONCSHIP_TARGETLOCK, TUFBOSS_CATCH, TUFBOSS_RELEASE, TUFBOSS_SWING, UTERUS_CRABCLOSE, UTERUS_CRABOPEN2, UTERUS_SCISSORS1, UTERUS_SCISSORS2, UTERUS_SCISSORS3, UTERUS_SCISSORS4, UTERUS_SCISSORS5, UTERUS_SCISSORS6, UTERUS_SCISSORS7, UTERUS_SCISSORS8, UTERUS_SCREAM1, UTERUS_STEP1, UTERUS_STEP2,
WITCH_LAUGH, WITCH_MAGIC, BAT_BATFLY1, BUMBEE_BEELOOP, CATERPIL_RIDOE, DEMON_RUN, DOG_AGRESSIV, DOG_SNIF1, DOG_WAF1, DOG_WAF2, DOG_WAF3, DRAGFLY_BEELOOP, EVA_KISS1, EVA_KISS2, EVA_KISS3, EVA_KISS4, FATCHK_HIT1, FATCHK_HIT2, FATCHK_HIT3, FENCER_FENCE1, HATTER_CUP, HATTER_HAT, HATTER_PTOEI, HATTER_SPLIN, HATTER_SPLOUT, LABRAT_BITE, LABRAT_EYE2, LABRAT_EYE3, LABRAT_MOUSE1, LABRAT_MOUSE2, LABRAT_MOUSE3, LIZARD_LIZ1, LIZARD_LIZ2, LIZARD_LIZ4, LIZARD_LIZ6, MONKEY_SPLUT, MONKEY_THROW, MOTH_FLAPMOTH, PINBALL_BELL, PINBALL_FLIP1, PINBALL_FLIP2, PINBALL_FLIP3, PINBALL_FLIP4, RAPIER_GOSTDIE, RAPIER_GOSTLOOP, RAPIER_GOSTOOOH, RAPIER_GOSTRIP, RAPIER_HITCHAR, ROCK_ROCK1, SCIENCE_PLOPKAOS, SKELETON_BONE1, SKELETON_BONE2, SKELETON_BONE3, SKELETON_BONE5, SKELETON_BONE6, SKELETON_BONE7, SMALTREE_FALL, SMALTREE_GROUND, SMALTREE_HEAD, STEAM_STEAM, SUCKER_FART, SUCKER_PINCH1, SUCKER_PINCH2, SUCKER_PINCH3, SUCKER_PLOPSEQ1, SUCKER_PLOPSEQ2, SUCKER_PLOPSEQ3, SUCKER_PLOPSEQ4, SUCKER_UP, TURTLE_BITE3, TURTLE_HIDE, TURTLE_HITSHELL, TURTLE_IDLE1, TURTLE_IDLE2, TURTLE_NECK, TURTLE_SPK1TURT, TURTLE_SPK2TURT, TURTLE_SPK3TURT, TURTLE_SPK4TURT, TURTLE_TURN, WIND_WIND2A, BONUS_BONUS1, P2_SPLOUT, INTRO_BLOW
};
return @_allSampleConstantsInRoughOrderOfLikelihoodToBeUsed;
}
}
Jazz2Online © 1999-INFINITY (Site Credits). We have a Privacy Policy. Jazz Jackrabbit, Jazz Jackrabbit 2, Jazz Jackrabbit Advance and all related trademarks and media are ™ and © Epic Games. Lori Jackrabbit is © Dean Dodrill. J2O development powered by Loops of Fury and Chemical Beats.
Eat your lima beans, Johnny.