Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Facilis descensus Averno | Loon | Battle | 8.5 |
namespace se {
funcdef void MainCallback(int number);
funcdef void PlayerCallback(::jjPLAYER@ player, int number);
funcdef bool DrawingCallback(::jjPLAYER@ player, ::jjCANVAS@ canvas, const ::jjANIMATION@ anim);
funcdef void PacketCallback(::jjSTREAM& packet, int clientID);
funcdef ::jjSTREAM PacketConstructor();
shared enum WeaponTrait {
weapon_no_trait = 0,
weapon_deals_damage = 1 << 0,
weapon_causes_splash_damage = 1 << 1,
weapon_may_harm_user = 1 << 2,
weapon_may_heal_players = 1 << 3,
weapon_increases_mobility = 1 << 4,
weapon_inflicts_status_condition = 1 << 5,
weapon_melts_ice = 1 << 6,
weapon_freezes_objects = 1 << 7,
weapon_fails_underwater = 1 << 8,
weapon_fails_in_interiors = 1 << 9,
weapon_goes_through_thin_walls = 1 << 10,
weapon_goes_through_thick_walls = 1 << 11,
weapon_is_super_weapon = 1 << 12,
weapon_is_effective_against_players = 1 << 13,
weapon_is_effective_against_sp_enemies = 1 << 14,
weapon_destroys_crates = 1 << 15,
weapon_supports_mouse_aim = 1 << 16,
weapon_requires_mouse_use = 1 << 17,
weapon_works_in_mp = 1 << 18,
weapon_works_in_sp = 1 << 19,
weapon_works_in_coop = 1 << 20,
weapon_works_in_splitscreen = 1 << 21,
weapon_works_online = 1 << 22,
weapon_has_ammo_pickups = 1 << 23,
weapon_has_ammo_crates = 1 << 24,
weapon_has_powerup_monitors = 1 << 25,
weapon_has_distinct_powerup = 1 << 26,
weapon_requires_no_ammo_pickups = 1 << 27,
weapon_cannot_be_fired_by_key_press = 1 << 28,
weapon_requires_certain_events_in_level = 1 << 29,
weapon_goes_through_walls = weapon_goes_through_thin_walls | weapon_goes_through_thick_walls,
weapon_is_effective_against_all_targets = weapon_is_effective_against_players | weapon_is_effective_against_sp_enemies | weapon_destroys_crates,
weapon_works_in_all_modes = weapon_works_in_mp | weapon_works_in_sp | weapon_works_in_coop | weapon_works_in_splitscreen | weapon_works_online,
weapon_has_all_pickups = weapon_has_ammo_pickups | weapon_has_ammo_crates | weapon_has_powerup_monitors | weapon_has_distinct_powerup,
weapon_default_traits = weapon_deals_damage | weapon_is_effective_against_all_targets | weapon_supports_mouse_aim |
weapon_works_in_all_modes | weapon_has_all_pickups
}
shared interface UserData {}
shared interface WeaponHook {
void setUserData(uint number, UserData@ data);
UserData@ getUserData(uint number) const;
void setWeaponSprite(uint number, bool powerup, const ::jjANIMATION@ anim);
void resetCallbacks(uint number);
void setMainCallback(uint number, MainCallback@ callback);
void setPlayerCallback(uint number, PlayerCallback@ callback);
void setPlayerInputCallback(uint number, PlayerCallback@ callback);
void setDrawingCallback(uint number, DrawingCallback@ callback);
PacketConstructor@ addPacketCallback(PacketCallback@ callback);
bool drawAmmo(::jjPLAYER@ player, ::jjCANVAS@ canvas);
void processMain();
void processPlayer(::jjPLAYER@ player);
void processPlayerInput(::jjPLAYER@ player);
bool processPacket(::jjSTREAM &in packet, int clientID);
}
shared interface WeaponInterface {
::jjANIMSET@ loadAnims(::jjANIMSET@ animSet);
::array<bool>@ loadSamples(const ::array<SOUND::Sample>& samples);
uint getSampleCount() const;
uint getTraits(bool powerup) const;
uint getMaxDamage(bool powerup) const;
bool setAsWeapon(uint number, WeaponHook@ weaponHook);
}
shared bool isValidWeapon(uint number) {
return number != 0 && number <= WEAPON::GUN9;
}
shared uint getBasicBulletOfWeapon(uint number) {
if (isValidWeapon(number)) {
if (number < WEAPON::TNT)
return OBJECT::BLASTERBULLET + number - WEAPON::BLASTER;
if (number > WEAPON::TNT)
return OBJECT::FIREBALLBULLET + number - WEAPON::GUN8;
return OBJECT::TNT;
}
return 0;
}
shared uint getPoweredBulletOfWeapon(uint number) {
if (isValidWeapon(number)) {
if (number < WEAPON::TNT)
return OBJECT::BLASTERBULLETPU + number - WEAPON::BLASTER;
if (number > WEAPON::TNT)
return OBJECT::FIREBALLBULLETPU + number - WEAPON::GUN8;
return OBJECT::TNT;
}
return 0;
}
shared uint getAmmoPickupOfWeapon(uint number) {
if (isValidWeapon(number) && number != WEAPON::BLASTER) {
if (number == WEAPON::BOUNCER)
return OBJECT::BOUNCERAMMO3;
if (number == WEAPON::ICE)
return OBJECT::ICEAMMO3;
return OBJECT::SEEKERAMMO3 + number - WEAPON::SEEKER;
}
return 0;
}
shared uint getAmmoCrateOfWeapon(uint number) {
if (number > WEAPON::BLASTER && number < WEAPON::TNT) {
if (number == WEAPON::BOUNCER)
return OBJECT::BOUNCERAMMO15;
if (number == WEAPON::ICE)
return OBJECT::ICEAMMO15;
return OBJECT::SEEKERAMMO15 + number - WEAPON::SEEKER;
}
return 0;
}
shared uint getPowerupMonitorOfWeapon(uint number) {
if (isValidWeapon(number)) {
if (number < WEAPON::TNT)
return OBJECT::BLASTERPOWERUP + number - WEAPON::BLASTER;
return OBJECT::TNTPOWERUP + number - WEAPON::TNT;
}
return 0;
}
shared class DefaultPacketConstructor {
private ::jjSTREAM m_result;
DefaultPacketConstructor(const ::jjSTREAM &in result) {
m_result = result;
}
::jjSTREAM opCall() const {
return m_result;
}
}
shared class DefaultWeaponHook : WeaponHook {
private ::array<::array<const ::jjANIMATION@>> m_anims(9, ::array<const ::jjANIMATION@>(2));
private ::array<UserData@> m_userData(9);
private ::array<MainCallback@> m_mainCallbacks(9);
private ::array<PlayerCallback@> m_playerCallbacks(9), m_playerInputCallbacks(9);
private ::array<DrawingCallback@> m_drawingCallbacks(9);
private ::array<PacketCallback@> m_packetCallbacks;
private bool m_usePacketID;
private uint8 m_packetID;
DefaultWeaponHook() {
m_usePacketID = false;
}
DefaultWeaponHook(uint8 packetID) {
m_packetID = packetID;
m_usePacketID = true;
}
void setUserData(uint number, UserData@ data) override {
if (isValidWeapon(number))
@m_userData[number - 1] = @data;
}
UserData@ getUserData(uint number) const override {
return isValidWeapon(number) ? @m_userData[number - 1] : null;
}
void setWeaponSprite(uint number, bool powerup, const ::jjANIMATION@ anim) override {
if (isValidWeapon(number))
@m_anims[number - 1][powerup ? 1 : 0] = @anim;
}
void resetCallbacks(uint number) override {
if (isValidWeapon(number)) {
number--;
@m_mainCallbacks[number] = null;
@m_playerCallbacks[number] = null;
@m_playerInputCallbacks[number] = null;
@m_drawingCallbacks[number] = null;
}
}
void setMainCallback(uint number, MainCallback@ callback) override {
if (isValidWeapon(number))
@m_mainCallbacks[number - 1] = @callback;
}
void setPlayerCallback(uint number, PlayerCallback@ callback) override {
if (isValidWeapon(number))
@m_playerCallbacks[number - 1] = @callback;
}
void setPlayerInputCallback(uint number, PlayerCallback@ callback) override {
if (isValidWeapon(number))
@m_playerInputCallbacks[number - 1] = @callback;
}
void setDrawingCallback(uint number, DrawingCallback@ callback) override {
if (isValidWeapon(number))
@m_drawingCallbacks[number - 1] = @callback;
}
PacketConstructor@ addPacketCallback(PacketCallback@ callback) override {
::jjSTREAM packet;
uint index = m_packetCallbacks.length();
m_packetCallbacks.insertLast(@callback);
if (m_usePacketID)
packet.push(m_packetID);
if (index >= 0x80) {
packet.push(uint8(index >> 24) | 0x80);
packet.push(uint8(index >> 16));
packet.push(uint8(index >> 8));
}
packet.push(uint8(index));
return @PacketConstructor(DefaultPacketConstructor(packet).opCall);
}
protected void drawAmmoSprite(::jjCANVAS@ canvas, int x, int y, const ::jjANIMATION@ anim) const {
canvas.drawSpriteFromCurFrame(x, y, anim + (::jjGameTicks >> 2) % anim.frameCount);
}
bool drawAmmo(::jjPLAYER@ player, ::jjCANVAS@ canvas) override {
if (player !is null) {
uint number = player.currWeapon;
if (isValidWeapon(number)) {
const ::jjANIMATION@ anim = @m_anims[number - 1][player.powerup[number] ? 1 : 0];
DrawingCallback@ callback = @m_drawingCallbacks[number - 1];
if (callback !is null && callback(@player, @canvas, @anim))
return true;
if (anim !is null) {
if (canvas !is null && !player.noFire && player.charCurr != CHAR::FROG && player.charCurr != CHAR::BIRD) {
int x, y;
STRING::Size font;
::string text;
const ::jjWEAPON@ weapon = ::jjWeapons[number];
if (::jjSubscreenWidth > 400) {
x = ::jjSubscreenWidth - 88;
y = ::jjSubscreenHeight - 14;
font = STRING::MEDIUM;
} else {
x = ::jjSubscreenWidth - 48;
y = ::jjSubscreenHeight - 9;
font = STRING::SMALL;
}
if (weapon.infinite || weapon.replacedByShield && player.shieldTime > 0) {
text = "x^";
} else {
int ammo = player.ammo[number];
text = "x" + (ammo > 0 ? ammo : 0);
}
drawAmmoSprite(@canvas, x, y, @anim);
canvas.drawString(x + 8, y, text, font);
}
return true;
}
}
}
return false;
}
void processMain() override {
for (int i = 0, size = m_mainCallbacks.length(); i < size; i++) {
if (m_mainCallbacks[i] !is null)
m_mainCallbacks[i](i + 1);
}
}
void processPlayer(::jjPLAYER@ player) override {
for (int i = 0, size = m_playerCallbacks.length(); i < size; i++) {
if (m_playerCallbacks[i] !is null)
m_playerCallbacks[i](@player, i + 1);
}
}
void processPlayerInput(::jjPLAYER@ player) override {
for (int i = 0, size = m_playerInputCallbacks.length(); i < size; i++) {
if (m_playerInputCallbacks[i] !is null)
m_playerInputCallbacks[i](@player, i + 1);
}
}
bool processPacket(::jjSTREAM &in packet, int clientID) override {
uint8 id, index;
if ((!m_usePacketID || packet.pop(id) && id == m_packetID) && packet.pop(index)) {
uint fullIndex;
if (index & 0x80 != 0) {
index &= ~0x80;
uint8 u1, u2, u3;
if (packet.pop(u1) && packet.pop(u2) && packet.pop(u3))
fullIndex = uint(index) << 24 | uint(u1) << 16 | uint(u2) << 8 | u3;
else
fullIndex = m_packetCallbacks.length();
} else {
fullIndex = index;
}
if (fullIndex < m_packetCallbacks.length()) {
PacketCallback@ callback = @m_packetCallbacks[fullIndex];
if (callback !is null)
callback(packet, clientID);
return true;
}
}
return false;
}
}
shared class AmmoPickup : ::jjBEHAVIORINTERFACE {
private const ::jjANIMATION@ m_basic, m_powered;
private uint m_count;
private SOUND::Sample m_sample;
AmmoPickup(const ::jjANIMATION@ basic, const ::jjANIMATION@ powered, uint count = 3, SOUND::Sample sample = SOUND::COMMON_PICKUPW1) {
@m_basic = @basic;
@m_powered = @powered;
m_count = count;
m_sample = sample;
}
protected void draw(float x, float y, uint sprite, int direction, bool translucent, int playerID) const {
::jjDrawSpriteFromCurFrame(x, y, sprite, direction, translucent ? SPRITE::TRANSLUCENT : SPRITE::NORMAL, 0, 4, 4, playerID);
}
protected float getY(const ::jjOBJ@ obj) const {
int arg = (((obj.objectID << 3) + ::jjGameTicks) << (obj.yPos > ::jjWaterLevel ? 1 : 4)) + (int(obj.xPos) << 4);
return obj.yPos + ::jjSin(arg) * 4.f;
}
void setAnimationBasic(const ::jjANIMATION@ basic) {
@m_basic = @basic;
}
const ::jjANIMATION@ getAnimationBasic() const {
return @m_basic;
}
void setAnimationPowered(const ::jjANIMATION@ powered) {
@m_powered = @powered;
}
const ::jjANIMATION@ getAnimationPowered() const {
return @m_powered;
}
void setCount(uint count) {
m_count = count;
}
uint getCount() const {
return m_count;
}
void setSample(SOUND::Sample sample) {
m_sample = sample;
}
SOUND::Sample getSample() const {
return m_sample;
}
void onBehave(::jjOBJ@ obj) override {
if (obj.state == STATE::START)
obj.scriptedCollisions = true;
obj.behave(BEHAVIOR::PICKUP, false);
for (int i = 0; i < ::jjLocalPlayerCount; i++) {
const ::jjPLAYER@ player = @::jjLocalPlayers[i];
const ::jjANIMATION@ anim = @(player.powerup[obj.var[3] + 1] ? @m_powered : @m_basic);
uint sprite = anim + obj.frameID % anim.frameCount;
draw(obj.xPos, getY(obj), sprite, obj.direction, ::jjAnimFrames[sprite].transparent, player.playerID);
}
}
bool onObjectHit(::jjOBJ@ obj, ::jjOBJ@ bullet, ::jjPLAYER@ player, int) {
if (bullet is null) {
bool sp = ::jjGameMode == GAME::SP || ::jjGameMode == GAME::COOP;
int type = obj.var[3] + 1;
int multiplier = ::jjWeapons[type].multiplier;
::jjWeapons[type].multiplier = 1;
if (::jjAutoWeaponChange && player.ammo[type] == 0 && (player.shieldType == 0 || !::jjWeapons[player.currWeapon].replacedByShield) && m_count != 0)
player.currWeapon = type;
int maximum = ::jjWeapons[type].maximum;
if (maximum == -1)
maximum = sp ? 99 : 50;
maximum *= multiplier;
if (player.ammo[type] >= maximum) {
player.ammo[type] = maximum;
} else {
player.ammo[type] = player.ammo[type] + m_count * multiplier;
if (player.ammo[type] >= maximum)
player.ammo[type] = maximum;
obj.frameID = 0;
obj.behavior = BEHAVIOR::EXPLOSION2;
::jjSample(obj.xPos, obj.yPos, m_sample);
if (sp && obj.points != 0) {
player.score += obj.points;
::jjPARTICLE@ part = ::jjAddParticle(PARTICLE::STRING);
if (part !is null) {
part.xPos = obj.xPos;
part.yPos = obj.yPos;
part.xSpeed = -0.5f - (jjRandom() & 0x3FFF) / 65536.f;
part.ySpeed = -1.f - (jjRandom() & 0x7FFF) / 65536.f;
part.string.text = ::formatInt(obj.points);
}
obj.points = 0;
}
}
::jjWeapons[type].multiplier = multiplier;
return true;
}
return false;
}
}
}
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.