Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 21-installer-nodd
  • 2210-pre1
  • 2210-pre2
  • 2210-rc1
  • 2210-rc2
  • 2210-rc3
  • 2211-pre1
  • 2211-pre2
  • 2211-rc1
  • 2212-pre1
  • 2212-pre2
  • 2212-pre3
  • 2212-rc1
  • 2213
  • 2214-pre1
  • 2214-pre2
  • 2214-pre3
  • 2214-pre4
  • 2_2_12
  • 64-gl-log
  • COM_ImmedExecute-lua
  • DJGPP
  • accel-momentum
  • acs
  • action-args
  • alpha-fixes
  • any-resolution
  • appveyor
  • better-distance-math
  • blend-locking
  • blentran
  • blua-unary-not-fix
  • boost-tickrate
  • bustablesoundz
  • cleanup-opengl
  • cleanupmusic
  • clipmidtex
  • cmake-valgrind
  • crawlacommander-sprites
  • custom-map-names
  • custom-teams
  • cutscene-cleanup
  • dd-music-bypass
  • dd-music-fix
  • delfile2
  • deprecate-lua-dedicated-server
  • dpl-2
  • dropshadows-spawning
  • dynabsp
  • emblem-drawing
  • exchndl-xp-fix
  • extra-textures
  • few-kart-lua-changes
  • ffloorclip
  • fix-167
  • fix-cvar-conflicts
  • fix-opengl-parameter-crash
  • fix-opengl-shear-roll
  • flipfuncpointers
  • fof-lightlist-fixes
  • font-FUCK
  • frictionrefactor
  • fuck-macros-1
  • gamepad-luakeydown
  • gamepad-morefixes
  • gamepad_experiments
  • gametype-refactor
  • gametype-refactor-1
  • gametype-refactor-player-spawns
  • ghost-networking
  • gif-splitting
  • grr-lj
  • hitboxviewer
  • hwr-texture-cache-refactor
  • hwrender2
  • improve-439
  • increase-maxconditionsets
  • increase-packet-tics
  • input-display
  • input-display-translucency
  • io
  • joystick-juggling-maz
  • just-in-case
  • keycodes-only
  • ksf-wadfiles
  • ld413-mp-fix
  • levelstruct
  • libpng-version-support
  • linedef-actions
  • lj-test
  • lol-states
  • loopedsounds
  • lower-unpegged-fix
  • lua-change-gametype
  • lua-command-netids
  • lua-gfx-2
  • lua-gfx-sprites
  • lua-local
  • makefile-auto-mingw-gcc
  • makefile-tinkering
  • SRB2_release_2.1
  • SRB2_release_2.1.1
  • SRB2_release_2.1.10
  • SRB2_release_2.1.11
  • SRB2_release_2.1.12
  • SRB2_release_2.1.14
  • SRB2_release_2.1.15
  • SRB2_release_2.1.16
  • SRB2_release_2.1.16a
  • SRB2_release_2.1.17
  • SRB2_release_2.1.18
  • SRB2_release_2.1.19
  • SRB2_release_2.1.2
  • SRB2_release_2.1.20
  • SRB2_release_2.1.21
  • SRB2_release_2.1.22
  • SRB2_release_2.1.23
  • SRB2_release_2.1.24
  • SRB2_release_2.1.25
  • SRB2_release_2.1.3
  • SRB2_release_2.1.4
  • SRB2_release_2.1.5
  • SRB2_release_2.1.6
  • SRB2_release_2.1.7
  • SRB2_release_2.1.8
  • SRB2_release_2.1.9
  • SRB2_release_2.2.0
  • SRB2_release_2.2.1
  • SRB2_release_2.2.10
  • SRB2_release_2.2.11
  • SRB2_release_2.2.12
  • SRB2_release_2.2.13
  • SRB2_release_2.2.15
  • SRB2_release_2.2.2
  • SRB2_release_2.2.3
  • SRB2_release_2.2.4
  • SRB2_release_2.2.5
  • SRB2_release_2.2.6
  • SRB2_release_2.2.7
  • SRB2_release_2.2.8
  • SRB2_release_2.2.9
  • td-release-v1.0.0
142 results

Target

Select target project
  • STJr/SRB2
  • Sryder/SRB2
  • wolfy852/SRB2
  • Alpha2244/SRB2
  • Inuyasha/SRB2
  • yoshibot/SRB2
  • TehRealSalt/SRB2
  • PrisimaTF/SRB2
  • Hatninja/SRB2
  • SteelT/SRB2
  • james/SRB2
  • ShaderWraith/SRB2
  • SinnamonLat/SRB2
  • mazmazz_/SRB2
  • filpAM/SRB2
  • chaoloveicemdboy/SRB2
  • Whooa21/SRB2
  • Machturne/SRB2
  • Golden/SRB2
  • Tatsuru/SRB2
  • Snu/SRB2
  • Zwip-Zwap_Zapony/SRB2
  • fickleheart/SRB2
  • alphaRexJames/SRB2
  • JJK/SRB2
  • diskpoppy/SRB2
  • Hannu_Hanhi/SRB2
  • ZipperQR/SRB2
  • kays/SRB2
  • spherallic/SRB2
  • Zippy_Zolton/SRB2
  • namiishere/SRB2
  • Ors/SRB2
  • SMS_Alfredo/SRB2
  • sonic_edge/SRB2
  • lavla/SRB2
  • ashi/SRB2
  • X.organic/SRB2
  • Fafabis/SRB2
  • Meziu/SRB2
  • v-rob/SRB2
  • tertu/SRB2
  • bitten2up/SRB2
  • flarn2006/SRB2
  • Krabs/SRB2
  • clairebun/SRB2
  • Lactozilla/SRB2
  • thehackstack/SRB2
  • Spice/SRB2
  • win8linux/SRB2
  • JohnFrostFox/SRB2
  • talktoneon726/SRB2
  • Wane/SRB2
  • Lamibe/SRB2
  • spectrumuk2/srb-2
  • nerdyminer18/srb-2
  • 256nil/SRB2
  • ARJr/SRB2
  • Alam/SRB2
  • Zenya/srb-2-marathon-demos
  • Acelite/srb-2-archivedmodifications
  • MIDIMan/SRB2
  • Lach/SRB2
  • Frostiikin/bounce-tweaks
  • Jaden/SRB2
  • Tyron/SRB2
  • Astronight/SRB2
  • Mari0shi06/SRB2
  • aiire/SRB2
  • Galactice/SRB2
  • srb2-ports/srb2-dreamcast
  • sdasdas/SRB2
  • chreas/srb-2-vr
  • StarManiaKG/the-story-of-sinically-rocketing-and-botching-the-2nd
  • LoganAir/SRB2
  • NepDisk/srb-2
  • alufolie91/SRB2
  • Felicia.iso/SRB2
  • twi/SRB2
  • BarrelsOFun/SRB2
  • Speed2411/SRB2
  • Leather_Realms/SRB2
  • Ayemar/SRB2
  • Acelite/SRB2
  • VladDoc/SRB2
  • kaldrum/model-features
  • strawberryfox417/SRB2
  • Lugent/SRB2
  • Rem/SRB2
  • Refrag/SRB2
  • Henry_3230/srb-3230
  • TehPuertoRicanSpartan2/tprs-srb2
  • Leminn/srb-2-marathon-stuff
  • chromaticpipe2/SRB2
  • MiguelGustavo15/SRB2
  • Maru/srb-2-tests
  • SilicDev/SRB2
  • UnmatchedBracket/SRB2
  • HybridDog/SRB2
  • xordspar0/SRB2
  • jsjhbewfhh/SRB2
  • Fancy2209/SRB2
  • Lorsoen/SRB2
  • shindoukin/SRB2
  • GamerOfDays/SRB2
  • Craftyawesome/SRB2
  • tenshi-tensai-tennoji/SRB2
  • Scarfdudebalder/SRB2
  • luigi-budd/srb-2-fix-interplag-lockon
  • mskluesner/SRB2
  • johnpetersa19/SRB2
  • Pheazant/SRB2
  • chromaticpipe2/srb2classic
  • romoney5/SRB2
  • PAS/SRB2Classic
  • BlueStaggo/SRB2
  • Jisk/srb-2-beef-jerky
  • voltybystorm/SRB2
  • ZenithNeko/srb-2-xp
  • Nep2Disk/SRB2
  • Cloudeon/SRB2
  • mushe/srb-2-ps-b
122 results
Select Git revision
  • 21-installer-nodd
  • 2210-pre1
  • 2210-pre2
  • 2210-rc1
  • 2210-rc2
  • 2210-rc3
  • 2211-pre1
  • 2211-pre2
  • 2211-rc1
  • 2212-pre1
  • 2212-pre2
  • 2212-pre3
  • 2212-rc1
  • 2213
  • 2214-pre1
  • 2214-pre2
  • 2214-pre3
  • 2_2_12
  • 64-gl-log
  • COM_ImmedExecute-lua
  • DJGPP
  • PS3
  • accel-momentum
  • acs
  • action-args
  • alpha-fixes
  • any-resolution
  • appveyor
  • better-player-states
  • blend-locking
  • blentran
  • blua-unary-not-fix
  • boost-tickrate
  • bustablesoundz
  • cleanup-opengl
  • cleanupmusic
  • clipmidtex
  • cmake-valgrind
  • crawlacommander-sprites
  • custom-map-names
  • custom-teams
  • cutscene-cleanup
  • dd-music-bypass
  • dd-music-fix
  • delfile2
  • deprecate-lua-dedicated-server
  • dpl-2
  • dropshadows-spawning
  • dynabsp
  • emblem-drawing
  • exchndl-xp-fix
  • extra-textures
  • few-kart-lua-changes
  • ffloorclip
  • fix-167
  • fix-cvar-conflicts
  • fix-opengl-parameter-crash
  • fix-opengl-shear-roll
  • flipfuncpointers
  • fof-lightlist-fixes
  • font-FUCK
  • frictionrefactor
  • fuck-macros-1
  • gamepad-luakeydown
  • gamepad-morefixes
  • gamepad_experiments
  • gametype-refactor
  • gametype-refactor-1
  • gametype-refactor-player-spawns
  • ghost-networking
  • gif-splitting
  • gitlab-ci
  • grr-lj
  • hitboxviewer
  • hwr-texture-cache-refactor
  • hwrender2
  • improve-439
  • increase-maxconditionsets
  • increase-packet-tics
  • input-display
  • input-display-translucency
  • io
  • joystick-juggling-maz
  • just-in-case
  • keycodes-only
  • ksf-wadfiles
  • ld413-mp-fix
  • levelstruct
  • libpng-version-support
  • linedef-actions
  • lj-test
  • lol-states
  • loopedsounds
  • lower-unpegged-fix
  • lua-change-gametype
  • lua-command-netids
  • lua-debug-library
  • lua-gfx-2
  • lua-gfx-sprites
  • lua-local
  • SRB2_release_2.1
  • SRB2_release_2.1.1
  • SRB2_release_2.1.10
  • SRB2_release_2.1.11
  • SRB2_release_2.1.12
  • SRB2_release_2.1.14
  • SRB2_release_2.1.15
  • SRB2_release_2.1.16
  • SRB2_release_2.1.16a
  • SRB2_release_2.1.17
  • SRB2_release_2.1.18
  • SRB2_release_2.1.19
  • SRB2_release_2.1.2
  • SRB2_release_2.1.20
  • SRB2_release_2.1.21
  • SRB2_release_2.1.22
  • SRB2_release_2.1.23
  • SRB2_release_2.1.24
  • SRB2_release_2.1.25
  • SRB2_release_2.1.3
  • SRB2_release_2.1.4
  • SRB2_release_2.1.5
  • SRB2_release_2.1.6
  • SRB2_release_2.1.7
  • SRB2_release_2.1.8
  • SRB2_release_2.1.9
  • SRB2_release_2.2.0
  • SRB2_release_2.2.1
  • SRB2_release_2.2.10
  • SRB2_release_2.2.11
  • SRB2_release_2.2.12
  • SRB2_release_2.2.13
  • SRB2_release_2.2.2
  • SRB2_release_2.2.3
  • SRB2_release_2.2.4
  • SRB2_release_2.2.5
  • SRB2_release_2.2.6
  • SRB2_release_2.2.7
  • SRB2_release_2.2.8
  • SRB2_release_2.2.9
  • td-release-v1.0.0
141 results
Show changes
Showing with 2948 additions and 996 deletions
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior.
// Copyright (C) 1999-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -298,276 +298,278 @@ enum actionnum
NUMACTIONS
};
struct mobj_s;
// IMPORTANT NOTE: If you add/remove from this list of action
// functions, don't forget to update them in deh_tables.c!
void A_Explode();
void A_Pain();
void A_Fall();
void A_MonitorPop();
void A_GoldMonitorPop();
void A_GoldMonitorRestore();
void A_GoldMonitorSparkle();
void A_Look();
void A_Chase();
void A_FaceStabChase();
void A_FaceStabRev();
void A_FaceStabHurl();
void A_FaceStabMiss();
void A_StatueBurst();
void A_FaceTarget();
void A_FaceTracer();
void A_Scream();
void A_BossDeath();
void A_SetShadowScale();
void A_ShadowScream(); // MARIA!!!!!!
void A_CustomPower(); // Use this for a custom power
void A_GiveWeapon(); // Gives the player weapon(s)
void A_RingBox(); // Obtained Ring Box Tails
void A_Invincibility(); // Obtained Invincibility Box
void A_SuperSneakers(); // Obtained Super Sneakers Box
void A_BunnyHop(); // have bunny hop tails
void A_BubbleSpawn(); // Randomly spawn bubbles
void A_FanBubbleSpawn();
void A_BubbleRise(); // Bubbles float to surface
void A_BubbleCheck(); // Don't draw if not underwater
void A_AwardScore();
void A_ExtraLife(); // Extra Life
void A_GiveShield(); // Obtained Shield
void A_GravityBox();
void A_ScoreRise(); // Rise the score logo
void A_AttractChase(); // Ring Chase
void A_DropMine(); // Drop Mine from Skim or Jetty-Syn Bomber
void A_FishJump(); // Fish Jump
void A_ThrownRing(); // Sparkle trail for red ring
void A_SetSolidSteam();
void A_UnsetSolidSteam();
void A_SignSpin();
void A_SignPlayer();
void A_OverlayThink();
void A_JetChase();
void A_JetbThink(); // Jetty-Syn Bomber Thinker
void A_JetgThink(); // Jetty-Syn Gunner Thinker
void A_JetgShoot(); // Jetty-Syn Shoot Function
void A_ShootBullet(); // JetgShoot without reactiontime setting
void A_MinusDigging();
void A_MinusPopup();
void A_MinusCheck();
void A_ChickenCheck();
void A_MouseThink(); // Mouse Thinker
void A_DetonChase(); // Deton Chaser
void A_CapeChase(); // Fake little Super Sonic cape
void A_RotateSpikeBall(); // Spike ball rotation
void A_SlingAppear();
void A_UnidusBall();
void A_RockSpawn();
void A_SetFuse();
void A_CrawlaCommanderThink(); // Crawla Commander
void A_SmokeTrailer();
void A_RingExplode();
void A_OldRingExplode();
void A_MixUp();
void A_RecyclePowers();
void A_BossScream();
void A_Boss2TakeDamage();
void A_GoopSplat();
void A_Boss2PogoSFX();
void A_Boss2PogoTarget();
void A_EggmanBox();
void A_TurretFire();
void A_SuperTurretFire();
void A_TurretStop();
void A_JetJawRoam();
void A_JetJawChomp();
void A_PointyThink();
void A_CheckBuddy();
void A_HoodFire();
void A_HoodThink();
void A_HoodFall();
void A_ArrowBonks();
void A_SnailerThink();
void A_SharpChase();
void A_SharpSpin();
void A_SharpDecel();
void A_CrushstaceanWalk();
void A_CrushstaceanPunch();
void A_CrushclawAim();
void A_CrushclawLaunch();
void A_VultureVtol();
void A_VultureCheck();
void A_VultureHover();
void A_VultureBlast();
void A_VultureFly();
void A_SkimChase();
void A_SkullAttack();
void A_LobShot();
void A_FireShot();
void A_SuperFireShot();
void A_BossFireShot();
void A_Boss7FireMissiles();
void A_Boss1Laser();
void A_FocusTarget();
void A_Boss4Reverse();
void A_Boss4SpeedUp();
void A_Boss4Raise();
void A_SparkFollow();
void A_BuzzFly();
void A_GuardChase();
void A_EggShield();
void A_SetReactionTime();
void A_Boss1Spikeballs();
void A_Boss3TakeDamage();
void A_Boss3Path();
void A_Boss3ShockThink();
void A_Shockwave();
void A_LinedefExecute();
void A_LinedefExecuteFromArg();
void A_PlaySeeSound();
void A_PlayAttackSound();
void A_PlayActiveSound();
void A_1upThinker();
void A_BossZoom(); //Unused
void A_Boss1Chase();
void A_Boss2Chase();
void A_Boss2Pogo();
void A_Boss7Chase();
void A_BossJetFume();
void A_SpawnObjectAbsolute();
void A_SpawnObjectRelative();
void A_ChangeAngleRelative();
void A_ChangeAngleAbsolute();
void A_RollAngle();
void A_ChangeRollAngleRelative();
void A_ChangeRollAngleAbsolute();
void A_PlaySound();
void A_FindTarget();
void A_FindTracer();
void A_SetTics();
void A_SetRandomTics();
void A_ChangeColorRelative();
void A_ChangeColorAbsolute();
void A_Dye();
void A_SetTranslation();
void A_MoveRelative();
void A_MoveAbsolute();
void A_Thrust();
void A_ZThrust();
void A_SetTargetsTarget();
void A_SetObjectFlags();
void A_SetObjectFlags2();
void A_RandomState();
void A_RandomStateRange();
void A_StateRangeByAngle();
void A_StateRangeByParameter();
void A_DualAction();
void A_RemoteAction();
void A_ToggleFlameJet();
void A_OrbitNights();
void A_GhostMe();
void A_SetObjectState();
void A_SetObjectTypeState();
void A_KnockBack();
void A_PushAway();
void A_RingDrain();
void A_SplitShot();
void A_MissileSplit();
void A_MultiShot();
void A_InstaLoop();
void A_Custom3DRotate();
void A_SearchForPlayers();
void A_CheckRandom();
void A_CheckTargetRings();
void A_CheckRings();
void A_CheckTotalRings();
void A_CheckHealth();
void A_CheckRange();
void A_CheckHeight();
void A_CheckTrueRange();
void A_CheckThingCount();
void A_CheckAmbush();
void A_CheckCustomValue();
void A_CheckCusValMemo();
void A_SetCustomValue();
void A_UseCusValMemo();
void A_RelayCustomValue();
void A_CusValAction();
void A_ForceStop();
void A_ForceWin();
void A_SpikeRetract();
void A_InfoState();
void A_Repeat();
void A_SetScale();
void A_RemoteDamage();
void A_HomingChase();
void A_TrapShot();
void A_VileTarget();
void A_VileAttack();
void A_VileFire();
void A_BrakChase();
void A_BrakFireShot();
void A_BrakLobShot();
void A_NapalmScatter();
void A_SpawnFreshCopy();
void A_FlickySpawn();
void A_FlickyCenter();
void A_FlickyAim();
void A_FlickyFly();
void A_FlickySoar();
void A_FlickyCoast();
void A_FlickyHop();
void A_FlickyFlounder();
void A_FlickyCheck();
void A_FlickyHeightCheck();
void A_FlickyFlutter();
void A_FlameParticle();
void A_FadeOverlay();
void A_Boss5Jump();
void A_LightBeamReset();
void A_MineExplode();
void A_MineRange();
void A_ConnectToGround();
void A_SpawnParticleRelative();
void A_MultiShotDist();
void A_WhoCaresIfYourSonIsABee();
void A_ParentTriesToSleep();
void A_CryingToMomma();
void A_CheckFlags2();
void A_Boss5FindWaypoint();
void A_DoNPCSkid();
void A_DoNPCPain();
void A_PrepareRepeat();
void A_Boss5ExtraRepeat();
void A_Boss5Calm();
void A_Boss5CheckOnGround();
void A_Boss5CheckFalling();
void A_Boss5PinchShot();
void A_Boss5MakeItRain();
void A_Boss5MakeJunk();
void A_LookForBetter();
void A_Boss5BombExplode();
void A_DustDevilThink();
void A_TNTExplode();
void A_DebrisRandom();
void A_TrainCameo();
void A_TrainCameo2();
void A_CanarivoreGas();
void A_KillSegments();
void A_SnapperSpawn();
void A_SnapperThinker();
void A_SaloonDoorSpawn();
void A_MinecartSparkThink();
void A_ModuloToState();
void A_LavafallRocks();
void A_LavafallLava();
void A_FallingLavaCheck();
void A_FireShrink();
void A_SpawnPterabytes();
void A_PterabyteHover();
void A_RolloutSpawn();
void A_RolloutRock();
void A_DragonbomberSpawn();
void A_DragonWing();
void A_DragonSegment();
void A_ChangeHeight();
void A_Explode(void *actor);
void A_Pain(void *actor);
void A_Fall(void *actor);
void A_MonitorPop(void *actor);
void A_GoldMonitorPop(void *actor);
void A_GoldMonitorRestore(void *actor);
void A_GoldMonitorSparkle(void *actor);
void A_Look(void *actor);
void A_Chase(void *actor);
void A_FaceStabChase(void *actor);
void A_FaceStabRev(void *actor);
void A_FaceStabHurl(void *actor);
void A_FaceStabMiss(void *actor);
void A_StatueBurst(void *actor);
void A_FaceTarget(void *actor);
void A_FaceTracer(void *actor);
void A_Scream(void *actor);
void A_BossDeath(void *actor);
void A_SetShadowScale(void *actor);
void A_ShadowScream(void *actor); // MARIA!!!!!!
void A_CustomPower(void *actor); // Use this for a custom power
void A_GiveWeapon(void *actor); // Gives the player weapon(s)
void A_RingBox(void *actor); // Obtained Ring Box Tails
void A_Invincibility(void *actor); // Obtained Invincibility Box
void A_SuperSneakers(void *actor); // Obtained Super Sneakers Box
void A_BunnyHop(void *actor); // have bunny hop tails
void A_BubbleSpawn(void *actor); // Randomly spawn bubbles
void A_FanBubbleSpawn(void *actor);
void A_BubbleRise(void *actor); // Bubbles float to surface
void A_BubbleCheck(void *actor); // Don't draw if not underwater
void A_AwardScore(void *actor);
void A_ExtraLife(void *actor); // Extra Life
void A_GiveShield(void *actor); // Obtained Shield
void A_GravityBox(void *actor);
void A_ScoreRise(void *actor); // Rise the score logo
void A_AttractChase(void *actor); // Ring Chase
void A_DropMine(void *actor); // Drop Mine from Skim or Jetty-Syn Bomber
void A_FishJump(void *actor); // Fish Jump
void A_ThrownRing(void *actor); // Sparkle trail for red ring
void A_SetSolidSteam(void *actor);
void A_UnsetSolidSteam(void *actor);
void A_SignSpin(void *actor);
void A_SignPlayer(void *actor);
void A_OverlayThink(void *actor);
void A_JetChase(void *actor);
void A_JetbThink(void *actor); // Jetty-Syn Bomber Thinker
void A_JetgThink(void *actor); // Jetty-Syn Gunner Thinker
void A_JetgShoot(void *actor); // Jetty-Syn Shoot Function
void A_ShootBullet(void *actor); // JetgShoot without reactiontime setting
void A_MinusDigging(void *actor);
void A_MinusPopup(void *actor);
void A_MinusCheck(void *actor);
void A_ChickenCheck(void *actor);
void A_MouseThink(void *actor); // Mouse Thinker
void A_DetonChase(void *actor); // Deton Chaser
void A_CapeChase(void *actor); // Fake little Super Sonic cape
void A_RotateSpikeBall(void *actor); // Spike ball rotation
void A_SlingAppear(void *actor);
void A_UnidusBall(void *actor);
void A_RockSpawn(void *actor);
void A_SetFuse(void *actor);
void A_CrawlaCommanderThink(void *actor); // Crawla Commander
void A_SmokeTrailer(void *actor);
void A_RingExplode(void *actor);
void A_OldRingExplode(void *actor);
void A_MixUp(void *actor);
void A_RecyclePowers(void *actor);
void A_BossScream(void *actor);
void A_Boss2TakeDamage(void *actor);
void A_GoopSplat(void *actor);
void A_Boss2PogoSFX(void *actor);
void A_Boss2PogoTarget(void *actor);
void A_EggmanBox(void *actor);
void A_TurretFire(void *actor);
void A_SuperTurretFire(void *actor);
void A_TurretStop(void *actor);
void A_JetJawRoam(void *actor);
void A_JetJawChomp(void *actor);
void A_PointyThink(void *actor);
void A_CheckBuddy(void *actor);
void A_HoodFire(void *actor);
void A_HoodThink(void *actor);
void A_HoodFall(void *actor);
void A_ArrowBonks(void *actor);
void A_SnailerThink(void *actor);
void A_SharpChase(void *actor);
void A_SharpSpin(void *actor);
void A_SharpDecel(void *actor);
void A_CrushstaceanWalk(void *actor);
void A_CrushstaceanPunch(void *actor);
void A_CrushclawAim(void *actor);
void A_CrushclawLaunch(void *actor);
void A_VultureVtol(void *actor);
void A_VultureCheck(void *actor);
void A_VultureHover(void *actor);
void A_VultureBlast(void *actor);
void A_VultureFly(void *actor);
void A_SkimChase(void *actor);
void A_SkullAttack(void *actor);
void A_LobShot(void *actor);
void A_FireShot(void *actor);
void A_SuperFireShot(void *actor);
void A_BossFireShot(void *actor);
void A_Boss7FireMissiles(void *actor);
void A_Boss1Laser(void *actor);
void A_FocusTarget(void *actor);
void A_Boss4Reverse(void *actor);
void A_Boss4SpeedUp(void *actor);
void A_Boss4Raise(void *actor);
void A_SparkFollow(void *actor);
void A_BuzzFly(void *actor);
void A_GuardChase(void *actor);
void A_EggShield(void *actor);
void A_SetReactionTime(void *actor);
void A_Boss1Spikeballs(void *actor);
void A_Boss3TakeDamage(void *actor);
void A_Boss3Path(void *actor);
void A_Boss3ShockThink(void *actor);
void A_Shockwave(void *actor);
void A_LinedefExecute(void *actor);
void A_LinedefExecuteFromArg(void *actor);
void A_PlaySeeSound(void *actor);
void A_PlayAttackSound(void *actor);
void A_PlayActiveSound(void *actor);
void A_1upThinker(void *actor);
void A_BossZoom(void *actor); //Unused
void A_Boss1Chase(void *actor);
void A_Boss2Chase(void *actor);
void A_Boss2Pogo(void *actor);
void A_Boss7Chase(void *actor);
void A_BossJetFume(void *actor);
void A_SpawnObjectAbsolute(void *actor);
void A_SpawnObjectRelative(void *actor);
void A_ChangeAngleRelative(void *actor);
void A_ChangeAngleAbsolute(void *actor);
void A_RollAngle(void *actor);
void A_ChangeRollAngleRelative(void *actor);
void A_ChangeRollAngleAbsolute(void *actor);
void A_PlaySound(void *actor);
void A_FindTarget(void *actor);
void A_FindTracer(void *actor);
void A_SetTics(void *actor);
void A_SetRandomTics(void *actor);
void A_ChangeColorRelative(void *actor);
void A_ChangeColorAbsolute(void *actor);
void A_Dye(void *actor);
void A_SetTranslation(void *actor);
void A_MoveRelative(void *actor);
void A_MoveAbsolute(void *actor);
void A_Thrust(void *actor);
void A_ZThrust(void *actor);
void A_SetTargetsTarget(void *actor);
void A_SetObjectFlags(void *actor);
void A_SetObjectFlags2(void *actor);
void A_RandomState(void *actor);
void A_RandomStateRange(void *actor);
void A_StateRangeByAngle(void *actor);
void A_StateRangeByParameter(void *actor);
void A_DualAction(void *actor);
void A_RemoteAction(void *actor);
void A_ToggleFlameJet(void *actor);
void A_OrbitNights(void *actor);
void A_GhostMe(void *actor);
void A_SetObjectState(void *actor);
void A_SetObjectTypeState(void *actor);
void A_KnockBack(void *actor);
void A_PushAway(void *actor);
void A_RingDrain(void *actor);
void A_SplitShot(void *actor);
void A_MissileSplit(void *actor);
void A_MultiShot(void *actor);
void A_InstaLoop(void *actor);
void A_Custom3DRotate(void *actor);
void A_SearchForPlayers(void *actor);
void A_CheckRandom(void *actor);
void A_CheckTargetRings(void *actor);
void A_CheckRings(void *actor);
void A_CheckTotalRings(void *actor);
void A_CheckHealth(void *actor);
void A_CheckRange(void *actor);
void A_CheckHeight(void *actor);
void A_CheckTrueRange(void *actor);
void A_CheckThingCount(void *actor);
void A_CheckAmbush(void *actor);
void A_CheckCustomValue(void *actor);
void A_CheckCusValMemo(void *actor);
void A_SetCustomValue(void *actor);
void A_UseCusValMemo(void *actor);
void A_RelayCustomValue(void *actor);
void A_CusValAction(void *actor);
void A_ForceStop(void *actor);
void A_ForceWin(void *actor);
void A_SpikeRetract(void *actor);
void A_InfoState(void *actor);
void A_Repeat(void *actor);
void A_SetScale(void *actor);
void A_RemoteDamage(void *actor);
void A_HomingChase(void *actor);
void A_TrapShot(void *actor);
void A_VileTarget(void *actor);
void A_VileAttack(void *actor);
void A_VileFire(void *actor);
void A_BrakChase(void *actor);
void A_BrakFireShot(void *actor);
void A_BrakLobShot(void *actor);
void A_NapalmScatter(void *actor);
void A_SpawnFreshCopy(void *actor);
void A_FlickySpawn(void *actor);
void A_FlickyCenter(void *actor);
void A_FlickyAim(void *actor);
void A_FlickyFly(void *actor);
void A_FlickySoar(void *actor);
void A_FlickyCoast(void *actor);
void A_FlickyHop(void *actor);
void A_FlickyFlounder(void *actor);
void A_FlickyCheck(void *actor);
void A_FlickyHeightCheck(void *actor);
void A_FlickyFlutter(void *actor);
void A_FlameParticle(void *actor);
void A_FadeOverlay(void *actor);
void A_Boss5Jump(void *actor);
void A_LightBeamReset(void *actor);
void A_MineExplode(void *actor);
void A_MineRange(void *actor);
void A_ConnectToGround(void *actor);
void A_SpawnParticleRelative(void *actor);
void A_MultiShotDist(void *actor);
void A_WhoCaresIfYourSonIsABee(void *actor);
void A_ParentTriesToSleep(void *actor);
void A_CryingToMomma(void *actor);
void A_CheckFlags2(void *actor);
void A_Boss5FindWaypoint(void *actor);
void A_DoNPCSkid(void *actor);
void A_DoNPCPain(void *actor);
void A_PrepareRepeat(void *actor);
void A_Boss5ExtraRepeat(void *actor);
void A_Boss5Calm(void *actor);
void A_Boss5CheckOnGround(void *actor);
void A_Boss5CheckFalling(void *actor);
void A_Boss5PinchShot(void *actor);
void A_Boss5MakeItRain(void *actor);
void A_Boss5MakeJunk(void *actor);
void A_LookForBetter(void *actor);
void A_Boss5BombExplode(void *actor);
void A_DustDevilThink(void *actor);
void A_TNTExplode(void *actor);
void A_DebrisRandom(void *actor);
void A_TrainCameo(void *actor);
void A_TrainCameo2(void *actor);
void A_CanarivoreGas(void *actor);
void A_KillSegments(void *actor);
void A_SnapperSpawn(void *actor);
void A_SnapperThinker(void *actor);
void A_SaloonDoorSpawn(void *actor);
void A_MinecartSparkThink(void *actor);
void A_ModuloToState(void *actor);
void A_LavafallRocks(void *actor);
void A_LavafallLava(void *actor);
void A_FallingLavaCheck(void *actor);
void A_FireShrink(void *actor);
void A_SpawnPterabytes(void *actor);
void A_PterabyteHover(void *actor);
void A_RolloutSpawn(void *actor);
void A_RolloutRock(void *actor);
void A_DragonbomberSpawn(void *actor);
void A_DragonWing(void *actor);
void A_DragonSegment(void *actor);
void A_ChangeHeight(void *actor);
extern int actionsoverridden[NUMACTIONS][MAX_ACTION_RECURSION];
......@@ -855,6 +857,8 @@ typedef enum sprite
SPR_XMS4, // Lamppost
SPR_XMS5, // Hanging Star
SPR_XMS6, // Mistletoe
SPR_SNTT, // Silver Shiver tree
SPR_SSTT, // Silver Shiver tree with snow
SPR_FHZI, // FHZ Ice
SPR_ROSY,
......@@ -888,6 +892,8 @@ typedef enum sprite
// Misc Scenery
SPR_STLG, // Stalagmites
SPR_DBAL, // Disco
SPR_GINE, // Crystalline Heights tree
SPR_PPAL, // Pristine Shores palm trees
// Powerup Indicators
SPR_ARMA, // Armageddon Shield Orb
......@@ -1074,6 +1080,9 @@ typedef enum sprite
SPR_GWLG,
SPR_GWLR,
// LJ Knuckles
SPR_OLDK,
SPR_FIRSTFREESLOT,
SPR_LASTFREESLOT = SPR_FIRSTFREESLOT + NUMSPRITEFREESLOTS - 1,
NUMSPRITES
......@@ -1147,6 +1156,18 @@ typedef enum playersprite
SPR2_TALB,
SPR2_TALC,
// Misc slots
SPR2_MSC0,
SPR2_MSC1,
SPR2_MSC2,
SPR2_MSC3,
SPR2_MSC4,
SPR2_MSC5,
SPR2_MSC6,
SPR2_MSC7,
SPR2_MSC8,
SPR2_MSC9,
SPR2_CNT1, // continue disappointment
SPR2_CNT2, // continue lift
SPR2_CNT3, // continue spin
......@@ -1894,11 +1915,11 @@ typedef enum state
S_FANG_FIRE1,
S_FANG_FIRE2,
S_FANG_FIRE3,
S_FANG_FIRE4,
S_FANG_FIREREPEAT,
S_FANG_LOBSHOT0,
S_FANG_LOBSHOT1,
S_FANG_LOBSHOT2,
S_FANG_LOBSHOT3,
S_FANG_WAIT1,
S_FANG_WAIT2,
S_FANG_WALLHIT,
......@@ -1920,6 +1941,7 @@ typedef enum state
S_FANG_PINCHLOBSHOT2,
S_FANG_PINCHLOBSHOT3,
S_FANG_PINCHLOBSHOT4,
S_FANG_PINCHLOBSHOT5,
S_FANG_DIE1,
S_FANG_DIE2,
S_FANG_DIE3,
......@@ -3058,6 +3080,10 @@ typedef enum state
S_LAMPPOST2, // with snow
S_HANGSTAR,
S_MISTLETOE,
S_SSZTREE,
S_SSZTREE_BRANCH,
S_SSZTREE2,
S_SSZTREE2_BRANCH,
// Xmas GFZ bushes
S_XMASBLUEBERRYBUSH,
S_XMASBERRYBUSH,
......@@ -3065,11 +3091,9 @@ typedef enum state
// FHZ
S_FHZICE1,
S_FHZICE2,
S_ROSY_IDLE1,
S_ROSY_IDLE2,
S_ROSY_IDLE3,
S_ROSY_IDLE4,
S_ROSY_IDLE,
S_ROSY_JUMP,
S_ROSY_FALL,
S_ROSY_WALK,
S_ROSY_HUG,
S_ROSY_PAIN,
......@@ -3178,6 +3202,9 @@ typedef enum state
S_DBALL5,
S_DBALL6,
S_EGGSTATUE2,
S_GINE,
S_PPAL,
S_PPEL,
// Shield Orb
S_ARMA1,
......@@ -4062,6 +4089,7 @@ typedef enum state
S_MARIOBUSH2,
S_TOAD,
// Nights-specific stuff
S_NIGHTSDRONE_MAN1,
S_NIGHTSDRONE_MAN2,
......@@ -4366,6 +4394,12 @@ typedef enum state
S_NAMECHECK,
// LJ Knuckles
S_OLDK_STND,
S_OLDK_DIE0,
S_OLDK_DIE1,
S_OLDK_DIE2,
S_FIRSTFREESLOT,
S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1,
NUMSTATES
......@@ -4376,7 +4410,7 @@ typedef struct
spritenum_t sprite;
UINT32 frame; // we use the upper 16 bits for translucency and other shade effects
INT32 tics;
actionf_t action;
actionf_p1 action;
INT32 var1;
INT32 var2;
statenum_t nextstate;
......@@ -4857,6 +4891,10 @@ typedef enum mobj_type
MT_LAMPPOST2, // with snow
MT_HANGSTAR,
MT_MISTLETOE,
MT_SSZTREE,
MT_SSZTREE_BRANCH,
MT_SSZTREE2,
MT_SSZTREE2_BRANCH,
// Xmas GFZ bushes
MT_XMASBLUEBERRYBUSH,
MT_XMASBERRYBUSH,
......@@ -4936,6 +4974,9 @@ typedef enum mobj_type
// Misc scenery
MT_DBALL,
MT_EGGSTATUE2,
MT_GINE,
MT_PPAL,
MT_PPEL,
// Powerup Indicators
MT_ELEMENTAL_ORB, // Elemental shield mobj
......@@ -5164,6 +5205,8 @@ typedef enum mobj_type
MT_NAMECHECK,
MT_RAY, // General purpose mobj
MT_OLDK,
MT_FIRSTFREESLOT,
MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
NUMMOBJTYPES
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file lua_archive.c
/// \brief Lua gamestate archival
#include "lua_archive.h"
#include "doomtype.h"
#include "fastcmp.h"
#include "g_game.h"
#include "g_input.h"
#include "lua_script.h"
#include "lua_libs.h"
#include "matrix.h"
#include "p_saveg.h"
#include "p_setup.h"
#include "p_slopes.h"
#include "p_spec.h"
#include "quaternion.h"
#include "r_skins.h"
#include "r_state.h"
enum
{
ARCH_NULL=0,
ARCH_TRUE,
ARCH_FALSE,
ARCH_INT8,
ARCH_INT16,
ARCH_INT32,
ARCH_SMALLSTRING,
ARCH_LARGESTRING,
ARCH_TABLE,
ARCH_MOBJINFO,
ARCH_STATE,
ARCH_MOBJ,
ARCH_PLAYER,
ARCH_MAPTHING,
ARCH_VERTEX,
ARCH_LINE,
ARCH_SIDE,
ARCH_SUBSECTOR,
ARCH_SECTOR,
ARCH_FFLOOR,
ARCH_POLYOBJ,
ARCH_SLOPE,
ARCH_MAPHEADER,
ARCH_SKINCOLOR,
ARCH_MOUSE,
ARCH_SKIN,
ARCH_VECTOR2,
ARCH_VECTOR3,
ARCH_MATRIX,
ARCH_QUATERNION,
ARCH_TEND=0xFF,
};
static const struct {
const char *meta;
UINT8 arch;
} meta2arch[] = {
{META_MOBJINFO, ARCH_MOBJINFO},
{META_STATE, ARCH_STATE},
{META_MOBJ, ARCH_MOBJ},
{META_PLAYER, ARCH_PLAYER},
{META_MAPTHING, ARCH_MAPTHING},
{META_VERTEX, ARCH_VERTEX},
{META_LINE, ARCH_LINE},
{META_SIDE, ARCH_SIDE},
{META_SUBSECTOR,ARCH_SUBSECTOR},
{META_SECTOR, ARCH_SECTOR},
{META_FFLOOR, ARCH_FFLOOR},
{META_POLYOBJ, ARCH_POLYOBJ},
{META_SLOPE, ARCH_SLOPE},
{META_MAPHEADER, ARCH_MAPHEADER},
{META_SKINCOLOR, ARCH_SKINCOLOR},
{META_MOUSE, ARCH_MOUSE},
{META_SKIN, ARCH_SKIN},
{META_VECTOR2, ARCH_VECTOR2},
{META_VECTOR3, ARCH_VECTOR3},
{META_MATRIX, ARCH_MATRIX},
{META_QUATERNION, ARCH_QUATERNION},
{NULL, ARCH_NULL}
};
static UINT8 GetUserdataArchType(int index)
{
UINT8 i;
lua_getmetatable(gL, index);
for (i = 0; meta2arch[i].meta; i++)
{
luaL_getmetatable(gL, meta2arch[i].meta);
if (lua_rawequal(gL, -1, -2))
{
lua_pop(gL, 2);
return meta2arch[i].arch;
}
lua_pop(gL, 1);
}
lua_pop(gL, 1);
return ARCH_NULL;
}
static void *PrepareArchiveLuaUserdata(lua_State *L, save_t *save_p, int myindex, int archtype, int USERDATAINDEX)
{
boolean found = false;
INT32 i;
UINT16 t = (UINT16)lua_objlen(gL, USERDATAINDEX);
for (i = 1; i <= t && !found; i++)
{
lua_rawgeti(gL, USERDATAINDEX, i);
if (lua_rawequal(gL, myindex, -1))
{
t = i;
found = true;
}
lua_pop(gL, 1);
}
if (!found)
{
t++;
if (t == 0)
{
CONS_Alert(CONS_ERROR, "Too much userdata to archive!\n");
P_WriteUINT8(save_p, ARCH_NULL);
return NULL;
}
}
P_WriteUINT8(save_p, archtype);
P_WriteUINT16(save_p, t);
if (found)
{
return NULL;
}
else
{
lua_pushvalue(gL, myindex);
lua_rawseti(gL, USERDATAINDEX, t);
return lua_touserdata(L, myindex);
}
}
static UINT8 ArchiveValue(save_t *save_p, int TABLESINDEX, int USERDATAINDEX, int myindex)
{
if (myindex < 0)
myindex = lua_gettop(gL)+1+myindex;
switch (lua_type(gL, myindex))
{
case LUA_TNONE:
case LUA_TNIL:
P_WriteUINT8(save_p, ARCH_NULL);
break;
// This might be a problem. D:
case LUA_TLIGHTUSERDATA:
case LUA_TTHREAD:
case LUA_TFUNCTION:
P_WriteUINT8(save_p, ARCH_NULL);
return 2;
case LUA_TBOOLEAN:
P_WriteUINT8(save_p, lua_toboolean(gL, myindex) ? ARCH_TRUE : ARCH_FALSE);
break;
case LUA_TNUMBER:
{
lua_Integer number = lua_tointeger(gL, myindex);
if (number >= INT8_MIN && number <= INT8_MAX)
{
P_WriteUINT8(save_p, ARCH_INT8);
P_WriteSINT8(save_p, number);
}
else if (number >= INT16_MIN && number <= INT16_MAX)
{
P_WriteUINT8(save_p, ARCH_INT16);
P_WriteINT16(save_p, number);
}
else
{
P_WriteUINT8(save_p, ARCH_INT32);
P_WriteFixed(save_p, number);
}
break;
}
case LUA_TSTRING:
{
UINT32 len = (UINT32)lua_objlen(gL, myindex); // get length of string, including embedded zeros
const char *s = lua_tostring(gL, myindex);
UINT32 i = 0;
// if you're wondering why we're writing a string to save_p this way,
// it turns out that Lua can have embedded zeros ('\0') in the strings,
// so we can't use P_WriteString as that cuts off when it finds a '\0'.
// Saving the size of the string also allows us to get the size of the string on the other end,
// fixing the awful crashes previously encountered for reading strings longer than 1024
// (yes I know that's kind of a stupid thing to care about, but it'd be evil to trim or ignore them?)
// -- Monster Iestyn 05/08/18
if (len < 255)
{
P_WriteUINT8(save_p, ARCH_SMALLSTRING);
P_WriteUINT8(save_p, len); // save size of string
}
else
{
P_WriteUINT8(save_p, ARCH_LARGESTRING);
P_WriteUINT32(save_p, len); // save size of string
}
while (i < len)
P_WriteChar(save_p, s[i++]); // write chars individually, including the embedded zeros
break;
}
case LUA_TTABLE:
{
boolean found = false;
INT32 i;
UINT16 t = (UINT16)lua_objlen(gL, TABLESINDEX);
for (i = 1; i <= t && !found; i++)
{
lua_rawgeti(gL, TABLESINDEX, i);
if (lua_rawequal(gL, myindex, -1))
{
t = i;
found = true;
}
lua_pop(gL, 1);
}
if (!found)
{
t++;
if (t == 0)
{
CONS_Alert(CONS_ERROR, "Too many tables to archive!\n");
P_WriteUINT8(save_p, ARCH_NULL);
return 0;
}
}
P_WriteUINT8(save_p, ARCH_TABLE);
P_WriteUINT16(save_p, t);
if (!found)
{
lua_pushvalue(gL, myindex);
lua_rawseti(gL, TABLESINDEX, t);
return 1;
}
break;
}
case LUA_TUSERDATA:
switch (GetUserdataArchType(myindex))
{
case ARCH_MOBJINFO:
{
mobjinfo_t *info = *((mobjinfo_t **)lua_touserdata(gL, myindex));
P_WriteUINT8(save_p, ARCH_MOBJINFO);
P_WriteUINT16(save_p, info - mobjinfo);
break;
}
case ARCH_STATE:
{
state_t *state = *((state_t **)lua_touserdata(gL, myindex));
P_WriteUINT8(save_p, ARCH_STATE);
P_WriteUINT16(save_p, state - states);
break;
}
case ARCH_MOBJ:
{
mobj_t *mobj = *((mobj_t **)lua_touserdata(gL, myindex));
if (!mobj)
P_WriteUINT8(save_p, ARCH_NULL);
else {
P_WriteUINT8(save_p, ARCH_MOBJ);
P_WriteUINT32(save_p, mobj->mobjnum);
}
break;
}
case ARCH_PLAYER:
{
player_t *player = *((player_t **)lua_touserdata(gL, myindex));
if (!player)
P_WriteUINT8(save_p, ARCH_NULL);
else {
P_WriteUINT8(save_p, ARCH_PLAYER);
P_WriteUINT8(save_p, player - players);
}
break;
}
case ARCH_MAPTHING:
{
mapthing_t *mapthing = *((mapthing_t **)lua_touserdata(gL, myindex));
if (!mapthing)
P_WriteUINT8(save_p, ARCH_NULL);
else {
P_WriteUINT8(save_p, ARCH_MAPTHING);
P_WriteUINT16(save_p, mapthing - mapthings);
}
break;
}
case ARCH_VERTEX:
{
vertex_t *vertex = *((vertex_t **)lua_touserdata(gL, myindex));
if (!vertex)
P_WriteUINT8(save_p, ARCH_NULL);
else {
P_WriteUINT8(save_p, ARCH_VERTEX);
P_WriteUINT16(save_p, vertex - vertexes);
}
break;
}
case ARCH_LINE:
{
line_t *line = *((line_t **)lua_touserdata(gL, myindex));
if (!line)
P_WriteUINT8(save_p, ARCH_NULL);
else {
P_WriteUINT8(save_p, ARCH_LINE);
P_WriteUINT16(save_p, line - lines);
}
break;
}
case ARCH_SIDE:
{
side_t *side = *((side_t **)lua_touserdata(gL, myindex));
if (!side)
P_WriteUINT8(save_p, ARCH_NULL);
else {
P_WriteUINT8(save_p, ARCH_SIDE);
P_WriteUINT16(save_p, side - sides);
}
break;
}
case ARCH_SUBSECTOR:
{
subsector_t *subsector = *((subsector_t **)lua_touserdata(gL, myindex));
if (!subsector)
P_WriteUINT8(save_p, ARCH_NULL);
else {
P_WriteUINT8(save_p, ARCH_SUBSECTOR);
P_WriteUINT16(save_p, subsector - subsectors);
}
break;
}
case ARCH_SECTOR:
{
sector_t *sector = *((sector_t **)lua_touserdata(gL, myindex));
if (!sector)
P_WriteUINT8(save_p, ARCH_NULL);
else {
P_WriteUINT8(save_p, ARCH_SECTOR);
P_WriteUINT16(save_p, sector - sectors);
}
break;
}
case ARCH_FFLOOR:
{
ffloor_t *rover = *((ffloor_t **)lua_touserdata(gL, myindex));
if (!rover)
P_WriteUINT8(save_p, ARCH_NULL);
else {
UINT16 i = P_GetFFloorID(rover);
if (i == UINT16_MAX) // invalid ID
P_WriteUINT8(save_p, ARCH_NULL);
else
{
P_WriteUINT8(save_p, ARCH_FFLOOR);
P_WriteUINT16(save_p, rover->target - sectors);
P_WriteUINT16(save_p, i);
}
}
break;
}
case ARCH_POLYOBJ:
{
polyobj_t *polyobj = *((polyobj_t **)lua_touserdata(gL, myindex));
if (!polyobj)
P_WriteUINT8(save_p, ARCH_NULL);
else {
P_WriteUINT8(save_p, ARCH_POLYOBJ);
P_WriteUINT16(save_p, polyobj-PolyObjects);
}
break;
}
case ARCH_SLOPE:
{
pslope_t *slope = *((pslope_t **)lua_touserdata(gL, myindex));
if (!slope)
P_WriteUINT8(save_p, ARCH_NULL);
else {
P_WriteUINT8(save_p, ARCH_SLOPE);
P_WriteUINT16(save_p, slope->id);
}
break;
}
case ARCH_MAPHEADER:
{
mapheader_t *header = *((mapheader_t **)lua_touserdata(gL, myindex));
if (!header)
P_WriteUINT8(save_p, ARCH_NULL);
else {
P_WriteUINT8(save_p, ARCH_MAPHEADER);
P_WriteUINT16(save_p, header - *mapheaderinfo);
}
break;
}
case ARCH_SKINCOLOR:
{
skincolor_t *info = *((skincolor_t **)lua_touserdata(gL, myindex));
P_WriteUINT8(save_p, ARCH_SKINCOLOR);
P_WriteUINT16(save_p, info - skincolors);
break;
}
case ARCH_MOUSE:
{
mouse_t *m = *((mouse_t **)lua_touserdata(gL, myindex));
P_WriteUINT8(save_p, ARCH_MOUSE);
P_WriteUINT8(save_p, m == &mouse ? 1 : 2);
break;
}
case ARCH_SKIN:
{
skin_t *skin = *((skin_t **)lua_touserdata(gL, myindex));
P_WriteUINT8(save_p, ARCH_SKIN);
P_WriteUINT8(save_p, skin->skinnum); // UINT8 because MAXSKINS must be <= 256
break;
}
case ARCH_VECTOR2:
{
vector2_t *vector = PrepareArchiveLuaUserdata(gL, save_p, myindex, ARCH_VECTOR2, USERDATAINDEX);
if (vector)
{
P_WriteFixed(save_p, vector->x);
P_WriteFixed(save_p, vector->y);
}
break;
}
case ARCH_VECTOR3:
{
vector3_t *vector = PrepareArchiveLuaUserdata(gL, save_p, myindex, ARCH_VECTOR3, USERDATAINDEX);
if (vector)
{
P_WriteFixed(save_p, vector->x);
P_WriteFixed(save_p, vector->y);
P_WriteFixed(save_p, vector->z);
}
break;
}
case ARCH_MATRIX:
{
matrix_t *matrix = PrepareArchiveLuaUserdata(gL, save_p, myindex, ARCH_MATRIX, USERDATAINDEX);
if (matrix)
{
for (size_t r = 0; r < 4; r++)
for (size_t c = 0; c < 4; c++)
P_WriteFixed(save_p, matrix->matrix[r][c]);
}
break;
}
case ARCH_QUATERNION:
{
quaternion_t *quat = PrepareArchiveLuaUserdata(gL, save_p, myindex, ARCH_QUATERNION, USERDATAINDEX);
if (quat)
{
P_WriteFixed(save_p, quat->x);
P_WriteFixed(save_p, quat->y);
P_WriteFixed(save_p, quat->z);
P_WriteFixed(save_p, quat->w);
}
break;
}
default:
P_WriteUINT8(save_p, ARCH_NULL);
return 2;
}
break;
}
return 0;
}
static void ArchiveExtVars(save_t *save_p, void *pointer, const char *ptype)
{
int TABLESINDEX;
int USERDATAINDEX;
UINT16 i;
if (!gL) {
if (fastcmp(ptype,"player")) // players must always be included, even if no vars
P_WriteUINT16(save_p, 0);
return;
}
TABLESINDEX = lua_gettop(gL) - 1;
USERDATAINDEX = lua_gettop(gL);
lua_getfield(gL, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(gL, -1));
lua_pushlightuserdata(gL, pointer);
lua_rawget(gL, -2);
lua_remove(gL, -2); // pop LREG_EXTVARS
if (!lua_istable(gL, -1))
{ // no extra values table
lua_pop(gL, 1);
if (fastcmp(ptype,"player")) // players must always be included, even if no vars
P_WriteUINT16(save_p, 0);
return;
}
lua_pushnil(gL);
for (i = 0; lua_next(gL, -2); i++)
lua_pop(gL, 1);
// skip anything that has an empty table and isn't a player.
if (i == 0)
{
if (fastcmp(ptype,"player")) // always include players even if they have no extra variables
P_WriteUINT16(save_p, 0);
lua_pop(gL, 1);
return;
}
if (fastcmp(ptype,"mobj")) // mobjs must write their mobjnum as a header
P_WriteUINT32(save_p, ((mobj_t *)pointer)->mobjnum);
P_WriteUINT16(save_p, i);
lua_pushnil(gL);
while (lua_next(gL, -2))
{
I_Assert(lua_type(gL, -2) == LUA_TSTRING);
P_WriteString(save_p, lua_tostring(gL, -2));
if (ArchiveValue(save_p, TABLESINDEX, USERDATAINDEX, -1) == 2)
CONS_Alert(CONS_ERROR, "Type of value for %s entry '%s' (%s) could not be archived!\n", ptype, lua_tostring(gL, -2), luaL_typename(gL, -1));
lua_pop(gL, 1);
}
lua_pop(gL, 1);
}
// FIXME: remove and pass as local variable
static save_t *lua_save_p;
static int NetArchive(lua_State *L)
{
int TABLESINDEX = lua_upvalueindex(1);
int USERDATAINDEX = lua_upvalueindex(2);
int i, n = lua_gettop(L);
for (i = 1; i <= n; i++)
ArchiveValue(lua_save_p, TABLESINDEX, USERDATAINDEX, i);
return n;
}
static void ArchiveTables(save_t *save_p)
{
int TABLESINDEX;
int USERDATAINDEX;
UINT16 i, n;
UINT8 e;
if (!gL)
return;
TABLESINDEX = lua_gettop(gL) - 1;
USERDATAINDEX = lua_gettop(gL);
n = (UINT16)lua_objlen(gL, TABLESINDEX);
for (i = 1; i <= n; i++)
{
lua_rawgeti(gL, TABLESINDEX, i);
lua_pushnil(gL);
while (lua_next(gL, -2))
{
// Write key
e = ArchiveValue(save_p, TABLESINDEX, USERDATAINDEX, -2); // key should be either a number or a string, ArchiveValue can handle this.
if (e == 1)
n++; // the table contained a new table we'll have to archive. :(
else if (e == 2) // invalid key type (function, thread, lightuserdata, or anything we don't recognise)
CONS_Alert(CONS_ERROR, "Index '%s' (%s) of table %d could not be archived!\n", lua_tostring(gL, -2), luaL_typename(gL, -2), i);
// Write value
e = ArchiveValue(save_p, TABLESINDEX, USERDATAINDEX, -1);
if (e == 1)
n++; // the table contained a new table we'll have to archive. :(
else if (e == 2) // invalid value type
CONS_Alert(CONS_ERROR, "Type of value for table %d entry '%s' (%s) could not be archived!\n", i, lua_tostring(gL, -2), luaL_typename(gL, -1));
lua_pop(gL, 1);
}
P_WriteUINT8(save_p, ARCH_TEND);
// Write metatable ID
if (lua_getmetatable(gL, -1))
{
// registry.metatables[metatable]
lua_getfield(gL, LUA_REGISTRYINDEX, LREG_METATABLES);
lua_pushvalue(gL, -2);
lua_gettable(gL, -2);
P_WriteUINT16(save_p, lua_isnil(gL, -1) ? 0 : lua_tointeger(gL, -1));
lua_pop(gL, 3);
}
else
P_WriteUINT16(save_p, 0);
lua_pop(gL, 1);
}
}
static void *PrepareUnarchiveLuaUserdata(lua_State *L, save_t *save_p, const char *meta, size_t size, int USERDATAINDEX)
{
UINT16 tid = P_ReadUINT16(save_p);
lua_rawgeti(L, USERDATAINDEX, tid);
if (lua_isnil(L, -1))
{
lua_pop(L, 1);
void *ud = lua_newuserdata(L, size);
luaL_getmetatable(L, meta);
lua_setmetatable(L, -2);
lua_pushvalue(L, -1);
lua_rawseti(L, USERDATAINDEX, tid);
return ud;
}
else
{
return NULL;
}
}
static UINT8 UnArchiveValue(save_t *save_p, int TABLESINDEX, int USERDATAINDEX)
{
UINT8 type = P_ReadUINT8(save_p);
switch (type)
{
case ARCH_NULL:
lua_pushnil(gL);
break;
case ARCH_TRUE:
lua_pushboolean(gL, true);
break;
case ARCH_FALSE:
lua_pushboolean(gL, false);
break;
case ARCH_INT8:
lua_pushinteger(gL, P_ReadSINT8(save_p));
break;
case ARCH_INT16:
lua_pushinteger(gL, P_ReadINT16(save_p));
break;
case ARCH_INT32:
lua_pushinteger(gL, P_ReadFixed(save_p));
break;
case ARCH_SMALLSTRING:
case ARCH_LARGESTRING:
{
UINT32 len;
char *value;
UINT32 i = 0;
// See my comments in the ArchiveValue function;
// it's much the same for reading strings as writing them!
// (i.e. we can't use P_ReadString either)
// -- Monster Iestyn 05/08/18
if (type == ARCH_SMALLSTRING)
len = P_ReadUINT8(save_p); // length of string, including embedded zeros
else
len = P_ReadUINT32(save_p); // length of string, including embedded zeros
value = malloc(len); // make temp buffer of size len
// now read the actual string
while (i < len)
value[i++] = P_ReadChar(save_p); // read chars individually, including the embedded zeros
lua_pushlstring(gL, value, len); // push the string (note: this function supports embedded zeros)
free(value); // free the buffer
break;
}
case ARCH_TABLE:
{
UINT16 tid = P_ReadUINT16(save_p);
lua_rawgeti(gL, TABLESINDEX, tid);
if (lua_isnil(gL, -1))
{
lua_pop(gL, 1);
lua_newtable(gL);
lua_pushvalue(gL, -1);
lua_rawseti(gL, TABLESINDEX, tid);
return 2;
}
break;
}
case ARCH_MOBJINFO:
LUA_PushUserdata(gL, &mobjinfo[P_ReadUINT16(save_p)], META_MOBJINFO);
break;
case ARCH_STATE:
LUA_PushUserdata(gL, &states[P_ReadUINT16(save_p)], META_STATE);
break;
case ARCH_MOBJ:
LUA_PushUserdata(gL, P_FindNewPosition(P_ReadUINT32(save_p)), META_MOBJ);
break;
case ARCH_PLAYER:
LUA_PushUserdata(gL, &players[P_ReadUINT8(save_p)], META_PLAYER);
break;
case ARCH_MAPTHING:
LUA_PushUserdata(gL, &mapthings[P_ReadUINT16(save_p)], META_MAPTHING);
break;
case ARCH_VERTEX:
LUA_PushUserdata(gL, &vertexes[P_ReadUINT16(save_p)], META_VERTEX);
break;
case ARCH_LINE:
LUA_PushUserdata(gL, &lines[P_ReadUINT16(save_p)], META_LINE);
break;
case ARCH_SIDE:
LUA_PushUserdata(gL, &sides[P_ReadUINT16(save_p)], META_SIDE);
break;
case ARCH_SUBSECTOR:
LUA_PushUserdata(gL, &subsectors[P_ReadUINT16(save_p)], META_SUBSECTOR);
break;
case ARCH_SECTOR:
LUA_PushUserdata(gL, &sectors[P_ReadUINT16(save_p)], META_SECTOR);
break;
case ARCH_FFLOOR:
{
sector_t *sector = &sectors[P_ReadUINT16(save_p)];
UINT16 id = P_ReadUINT16(save_p);
ffloor_t *rover = P_GetFFloorByID(sector, id);
if (rover)
LUA_PushUserdata(gL, rover, META_FFLOOR);
break;
}
case ARCH_POLYOBJ:
LUA_PushUserdata(gL, &PolyObjects[P_ReadUINT16(save_p)], META_POLYOBJ);
break;
case ARCH_SLOPE:
LUA_PushUserdata(gL, P_SlopeById(P_ReadUINT16(save_p)), META_SLOPE);
break;
case ARCH_MAPHEADER:
LUA_PushUserdata(gL, mapheaderinfo[P_ReadUINT16(save_p)], META_MAPHEADER);
break;
case ARCH_SKINCOLOR:
LUA_PushUserdata(gL, &skincolors[P_ReadUINT16(save_p)], META_SKINCOLOR);
break;
case ARCH_MOUSE:
LUA_PushUserdata(gL, P_ReadUINT16(save_p) == 1 ? &mouse : &mouse2, META_MOUSE);
break;
case ARCH_SKIN:
LUA_PushUserdata(gL, skins[P_ReadUINT8(save_p)], META_SKIN);
break;
case ARCH_VECTOR2:
{
vector2_t *vector = PrepareUnarchiveLuaUserdata(gL, save_p, META_VECTOR2, sizeof(vector2_t), USERDATAINDEX);
if (vector)
{
vector->x = P_ReadFixed(save_p);
vector->y = P_ReadFixed(save_p);
}
break;
}
case ARCH_VECTOR3:
{
vector3_t *vector = PrepareUnarchiveLuaUserdata(gL, save_p, META_VECTOR3, sizeof(vector3_t), USERDATAINDEX);
if (vector)
{
vector->x = P_ReadFixed(save_p);
vector->y = P_ReadFixed(save_p);
vector->z = P_ReadFixed(save_p);
}
break;
}
case ARCH_MATRIX:
{
matrix_t *matrix = PrepareUnarchiveLuaUserdata(gL, save_p, META_MATRIX, sizeof(matrix_t), USERDATAINDEX);
if (matrix)
{
for (size_t r = 0; r < 4; r++)
for (size_t c = 0; c < 4; c++)
matrix->matrix[r][c] = P_ReadFixed(save_p);
}
break;
}
case ARCH_QUATERNION:
{
quaternion_t *quat = PrepareUnarchiveLuaUserdata(gL, save_p, META_QUATERNION, sizeof(quaternion_t), USERDATAINDEX);
if (quat)
{
quat->x = P_ReadFixed(save_p);
quat->y = P_ReadFixed(save_p);
quat->z = P_ReadFixed(save_p);
quat->w = P_ReadFixed(save_p);
}
break;
}
case ARCH_TEND:
return 1;
}
return 0;
}
static void UnArchiveExtVars(save_t *save_p, void *pointer)
{
int TABLESINDEX;
int USERDATAINDEX;
UINT16 field_count = P_ReadUINT16(save_p);
UINT16 i;
char field[1024];
if (field_count == 0)
return;
I_Assert(gL != NULL);
TABLESINDEX = lua_gettop(gL) - 1;
USERDATAINDEX = lua_gettop(gL);
lua_createtable(gL, 0, field_count); // pointer's ext vars subtable
for (i = 0; i < field_count; i++)
{
P_ReadString(save_p, field);
UnArchiveValue(save_p, TABLESINDEX, USERDATAINDEX);
lua_setfield(gL, -2, field);
}
lua_getfield(gL, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(gL, -1));
lua_pushlightuserdata(gL, pointer);
lua_pushvalue(gL, -3); // pointer's ext vars subtable
lua_rawset(gL, -3);
lua_pop(gL, 2); // pop LREG_EXTVARS and pointer's subtable
}
static int NetUnArchive(lua_State *L)
{
int TABLESINDEX = lua_upvalueindex(1);
int USERDATAINDEX = lua_upvalueindex(2);
int i, n = lua_gettop(L);
for (i = 1; i <= n; i++)
UnArchiveValue(lua_save_p, TABLESINDEX, USERDATAINDEX);
return n;
}
static void UnArchiveTables(save_t *save_p)
{
int TABLESINDEX;
int USERDATAINDEX;
UINT16 i, n;
UINT16 metatableid;
if (!gL)
return;
TABLESINDEX = lua_gettop(gL) - 1;
USERDATAINDEX = lua_gettop(gL);
n = (UINT16)lua_objlen(gL, TABLESINDEX);
for (i = 1; i <= n; i++)
{
lua_rawgeti(gL, TABLESINDEX, i);
while (true)
{
UINT8 e = UnArchiveValue(save_p, TABLESINDEX, USERDATAINDEX); // read key
if (e == 1) // End of table
break;
else if (e == 2) // Key contains a new table
n++;
if (UnArchiveValue(save_p, TABLESINDEX, USERDATAINDEX) == 2) // read value
n++;
if (lua_isnil(gL, -2)) // if key is nil (if a function etc was accidentally saved)
{
CONS_Alert(CONS_ERROR, "A nil key in table %d was found! (Invalid key type or corrupted save?)\n", i);
lua_pop(gL, 2); // pop key and value instead of setting them in the table, to prevent Lua panic errors
}
else
lua_rawset(gL, -3);
}
metatableid = P_ReadUINT16(save_p);
if (metatableid)
{
// setmetatable(table, registry.metatables[metatableid])
lua_getfield(gL, LUA_REGISTRYINDEX, LREG_METATABLES);
lua_rawgeti(gL, -1, metatableid);
if (lua_isnil(gL, -1))
I_Error("Unknown metatable ID %d\n", metatableid);
lua_setmetatable(gL, -3);
lua_pop(gL, 1);
}
lua_pop(gL, 1);
}
}
void LUA_Archive(save_t *save_p)
{
INT32 i;
thinker_t *th;
if (gL)
{
lua_newtable(gL); // tables to be archived.
lua_newtable(gL); // userdata to be archived.
}
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] && i > 0) // dedicated servers...
continue;
// all players in game will be archived, even if they just add a 0.
ArchiveExtVars(save_p, &players[i], "player");
}
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->removing)
continue;
// archive function will determine when to skip mobjs,
// and write mobjnum in otherwise.
ArchiveExtVars(save_p, th, "mobj");
}
P_WriteUINT32(save_p, UINT32_MAX); // end of mobjs marker, replaces mobjnum.
lua_save_p = save_p;
LUA_HookNetArchive(NetArchive); // call the NetArchive hook in archive mode
ArchiveTables(save_p);
if (gL)
lua_pop(gL, 2); // pop tables
}
void LUA_UnArchive(save_t *save_p)
{
UINT32 mobjnum;
INT32 i;
thinker_t *th;
if (gL)
{
lua_newtable(gL); // tables to be read
lua_newtable(gL); // userdata to be read
}
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] && i > 0) // dedicated servers...
continue;
UnArchiveExtVars(save_p, &players[i]);
}
do {
mobjnum = P_ReadUINT32(save_p); // read a mobjnum
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->removing)
continue;
if (((mobj_t *)th)->mobjnum != mobjnum) // find matching mobj
continue;
UnArchiveExtVars(save_p, th); // apply variables
}
} while(mobjnum != UINT32_MAX); // repeat until end of mobjs marker.
lua_save_p = save_p;
LUA_HookNetArchive(NetUnArchive); // call the NetArchive hook in unarchive mode
UnArchiveTables(save_p);
if (gL)
lua_pop(gL, 2); // pop tables
}
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file lua_archive.h
/// \brief Lua gamestate archival
#ifndef LUA_ARCHIVE_H
#define LUA_ARCHIVE_H
#include "lua_script.h"
#include "p_saveg.h"
void LUA_Archive(save_t *save_p);
void LUA_UnArchive(save_t *save_p);
void LUA_HookNetArchive(lua_CFunction archFunc);
#endif/*LUA_ARCHIVE_H*/
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2023 by Sonic Team Junior.
// Copyright (C) 2012-2025 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -171,6 +171,8 @@ static const struct {
{META_SKINCOLOR, "skincolor_t"},
{META_COLORRAMP, "skincolor_t.ramp"},
{META_SPRITEINFO, "spriteinfo_t"},
{META_SPRITEINFOFRAMELIST,"spriteinfoframe_t[]"},
{META_SPRITEINFOFRAME,"spriteinfoframe_t"},
{META_PIVOTLIST, "spriteframepivot_t[]"},
{META_FRAMEPIVOT, "spriteframepivot_t"},
......@@ -184,8 +186,12 @@ static const struct {
{META_SKIN, "skin_t"},
{META_POWERS, "player_t.powers"},
{META_SOUNDSID, "skin_t.soundsid"},
{META_SKINSPRITES, "skin_t.sprites"},
{META_SKINSPRITESLIST, "skin_t.sprites[]"},
{META_SKINSPRITES, "skin_t.skinsprites"},
{META_SKINSPRITESLIST, "skin_t.skinsprites[]"},
{META_SKINSPRITESCOMPAT, "skin_t.sprites"}, // TODO: 2.3: Delete
{META_MUSICDEF, "musicdef_t"},
{META_VERTEX, "vertex_t"},
{META_LINE, "line_t"},
......@@ -193,13 +199,7 @@ static const struct {
{META_SUBSECTOR, "subsector_t"},
{META_SECTOR, "sector_t"},
{META_FFLOOR, "ffloor_t"},
#ifdef HAVE_LUA_SEGS
{META_SEG, "seg_t"},
{META_NODE, "node_t"},
#endif
{META_SLOPE, "slope_t"},
{META_VECTOR2, "vector2_t"},
{META_VECTOR3, "vector3_t"},
{META_MAPHEADER, "mapheader_t"},
{META_POLYOBJ, "polyobj_t"},
......@@ -218,10 +218,6 @@ static const struct {
{META_THINGARGS, "mapthing.args"},
{META_THINGSTRINGARGS, "mapthing.stringargs"},
#ifdef HAVE_LUA_SEGS
{META_NODEBBOX, "node_t.bbox"},
{META_NODECHILDREN, "node_t.children"},
#endif
{META_BBOX, "bbox"},
......@@ -236,7 +232,16 @@ static const struct {
{META_LUABANKS, "luabanks[]"},
{META_KEYEVENT, "keyevent_t"},
{META_TEXTEVENT, "textevent_t"},
{META_MOUSE, "mouse_t"},
{META_INTERCEPT, "intercept_t"},
{META_VECTOR2, "vector2_t"},
{META_VECTOR3, "vector3_t"},
{META_MATRIX, "matrix_t"},
{META_QUATERNION, "quaternion_t"},
{NULL, NULL}
};
......@@ -345,6 +350,18 @@ static int lib_reserveLuabanks(lua_State *L)
return 1;
}
static int lib_tofixed(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
char *end;
float f = strtof(arg, &end);
if (*end != '\0')
lua_pushnil(L);
else
lua_pushnumber(L, FLOAT_TO_FIXED(f));
return 1;
}
// M_MENU
//////////////
......@@ -641,7 +658,7 @@ static int lib_pSpawnMobj(lua_State *L)
NOHUD
INLEVEL
NOSPAWNNULL
LUA_PushUserdata(L, P_SpawnMobj(x, y, z, type), META_MOBJ);
LUA_PushUserdata(L, P_SpawnMobj(x, y, z, type, NULL), META_MOBJ);
return 1;
}
......@@ -1716,12 +1733,11 @@ static int lib_pResetCamera(lua_State *L)
static int lib_pSuperReady(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
boolean transform = (boolean)lua_opttrueboolean(L, 2);
//HUDSAFE
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
lua_pushboolean(L, P_SuperReady(player, transform));
lua_pushboolean(L, P_SuperReady(player));
return 1;
}
......@@ -2912,6 +2928,121 @@ static int lib_evStartCrumble(lua_State *L)
return 0;
}
static int lib_pStartMoveFloor(lua_State *L)
{
NOHUD
INLEVEL
sector_t* sec = NULL;
if (!lua_isnone(L, 1) && lua_isuserdata(L, 1))
sec = *((sector_t**)luaL_checkudata(L, 1, META_SECTOR));
if (!sec)
return LUA_ErrInvalid(L, "sector_t");
fixed_t destheight = luaL_checkfixed(L, 2);
fixed_t speed = luaL_checkfixed(L, 3);
if (speed <= 0)
return luaL_error(L, "argument #3 must be greater than 0");
floormove_t* floor;
floor = Z_Calloc(sizeof(*floor), PU_LEVSPEC, NULL);
if (sec->floordata != NULL)
{
P_RemoveThinker(sec->floordata);
sec->floordata = NULL;
sec->floorspeed = 0;
}
sec->floordata = floor;
P_AddThinker(THINK_MAIN, &floor->thinker);
floor->thinker.function = (actionf_p1)T_MoveFloor;
floor->sector = sec;
floor->speed = speed;
floor->texture = -1;
floor->type = -1;
floor->floordestheight = destheight;
floor->direction = destheight >= sec->floorheight ? 1 : -1;
R_CreateInterpolator_SectorPlane(&floor->thinker, sec, true);
return 0;
}
static int lib_pStopMoveFloor(lua_State *L)
{
NOHUD
INLEVEL
sector_t* sec = NULL;
if (!lua_isnone(L, 1) && lua_isuserdata(L, 1))
sec = *((sector_t**)luaL_checkudata(L, 1, META_SECTOR));
if (!sec)
return LUA_ErrInvalid(L, "sector_t");
if (sec->floordata != NULL) // Override movement on new lua call.
{
P_RemoveThinker(sec->floordata);
sec->floorspeed = 0;
sec->floordata = NULL;
}
return 0;
}
static int lib_pStartMoveCeiling(lua_State *L)
{
NOHUD
INLEVEL
sector_t* sec = NULL;
if (!lua_isnone(L, 1) && lua_isuserdata(L, 1))
sec = *((sector_t**)luaL_checkudata(L, 1, META_SECTOR));
if (!sec)
return LUA_ErrInvalid(L, "sector_t");
fixed_t destheight = luaL_checkfixed(L, 2);
fixed_t speed = luaL_checkfixed(L, 3);
if (speed <= 0)
return luaL_error(L, "argument #3 must be greater than 0");
ceiling_t* ceiling;
ceiling = Z_Calloc(sizeof(*ceiling), PU_LEVSPEC, NULL);
if (sec->ceilingdata != NULL)
{
P_RemoveThinker(sec->ceilingdata);
sec->ceilingdata = NULL;
sec->ceilspeed = 0;
}
sec->ceilingdata = ceiling;
P_AddThinker(THINK_MAIN, &ceiling->thinker);
ceiling->thinker.function = (actionf_p1)T_MoveCeiling;
ceiling->sector = sec;
ceiling->speed = speed;
ceiling->texture = -1;
ceiling->type = -1;
ceiling->direction = destheight >= sec->ceilingheight ? 1 : -1;
ceiling->topheight = ceiling->bottomheight = destheight;
R_CreateInterpolator_SectorPlane(&ceiling->thinker, sec, true);
return 0;
}
static int lib_pStopMoveCeiling(lua_State *L)
{
NOHUD
INLEVEL
sector_t* sec = NULL;
if (!lua_isnone(L, 1) && lua_isuserdata(L, 1))
sec = *((sector_t**)luaL_checkudata(L, 1, META_SECTOR));
if (!sec)
return LUA_ErrInvalid(L, "sector_t");
if (sec->ceilingdata != NULL)
{
P_RemoveThinker(sec->ceilingdata);
sec->ceilspeed = 0;
sec->ceilingdata = NULL;
}
return 0;
}
// P_SLOPES
////////////
......@@ -3153,7 +3284,10 @@ static int lib_rCheckTextureNumForName(lua_State *L)
{
const char *name = luaL_checkstring(L, 1);
//HUDSAFE
lua_pushinteger(L, R_CheckTextureNumForName(name));
INT32 num = R_CheckTextureNumForName(name, TEXTURETYPE_TEXTURE);
if (num == -1)
num = R_CheckTextureNumForName(name, TEXTURETYPE_FLAT);
lua_pushinteger(L, num);
return 1;
}
......@@ -3167,17 +3301,25 @@ static int lib_rTextureNumForName(lua_State *L)
static int lib_rCheckTextureNameForNum(lua_State *L)
{
char s[9];
INT32 num = (INT32)luaL_checkinteger(L, 1);
//HUDSAFE
lua_pushstring(L, R_CheckTextureNameForNum(num));
M_Memcpy(s, R_CheckTextureNameForNum(num), 8);
s[8] = '\0';
lua_pushstring(L, s);
return 1;
}
static int lib_rTextureNameForNum(lua_State *L)
{
char s[9];
INT32 num = (INT32)luaL_checkinteger(L, 1);
//HUDSAFE
lua_pushstring(L, R_TextureNameForNum(num));
M_Memcpy(s, R_TextureNameForNum(num), 8);
s[8] = '\0';
lua_pushstring(L, s);
return 1;
}
......@@ -3233,10 +3375,10 @@ static int GetValidSoundOrigin(lua_State *L, void **origin)
return LUA_ErrInvalid(L, "sector_t");
*origin = &((sector_t *)(*origin))->soundorg;
return 1;
return 2;
}
return LUA_ErrInvalid(L, "mobj_t/sector_t");
return 0;
}
static int lib_sStartSound(lua_State *L)
......@@ -3255,15 +3397,23 @@ static int lib_sStartSound(lua_State *L)
if (!player)
return LUA_ErrInvalid(L, "player_t");
}
if (!lua_isnil(L, 1))
if (!GetValidSoundOrigin(L, &origin))
return 0;
if (!player || P_IsLocalPlayer(player))
if (player == NULL || P_IsLocalPlayer(player))
{
if (hud_running || hook_cmd_running)
origin = NULL; // HUD rendering and CMD building startsound shouldn't have an origin, just remove it instead of having a retarded error.
S_StartSound(origin, sound_id);
if (lua_isnil(L, 1))
S_StartSoundFromEverywhere(sound_id);
else
{
int origintype = GetValidSoundOrigin(L, &origin);
if (origintype == 1)
S_StartSoundFromMobj(origin, sound_id);
else if (origintype == 2)
{
origin = *((sector_t**)luaL_checkudata(L, 1, META_SECTOR)); // Restore this (other functions still use soundorg outright)
S_StartSoundFromSector(origin, sound_id);
}
else
return LUA_ErrInvalid(L, "mobj_t/sector_t");
}
}
return 0;
}
......@@ -3284,12 +3434,25 @@ static int lib_sStartSoundAtVolume(lua_State *L)
if (!player)
return LUA_ErrInvalid(L, "player_t");
}
if (!lua_isnil(L, 1))
if (!GetValidSoundOrigin(L, &origin))
if (player == NULL || P_IsLocalPlayer(player))
{
if (lua_isnil(L, 1))
S_StartSoundFromEverywhereVol(sound_id, volume);
else
{
int origintype = GetValidSoundOrigin(L, &origin);
if (origintype == 1)
S_StartSoundFromMobjVol(origin, sound_id, volume);
else if (origintype == 2)
{
origin = *((sector_t**)luaL_checkudata(L, 1, META_SECTOR)); // Restore this (other functions still use soundorg outright)
S_StartSoundFromSectorVol(origin, sound_id, volume);
}
else
return LUA_ErrInvalid(L, "mobj_t/sector_t");
}
if (!player || P_IsLocalPlayer(player))
S_StartSoundAtVolume(origin, sound_id, volume);
}
return 0;
}
......@@ -3714,6 +3877,73 @@ static int lib_sResumeMusic(lua_State *L)
return 1;
}
enum musicdef_e
{
musicdef_name,
musicdef_title,
musicdef_alttitle,
musicdef_authors
};
static const char *const musicdef_opt[] = {
"name",
"title",
"alttitle",
"authors",
NULL,
};
static int musicdef_fields_ref = LUA_NOREF;
static int musicdef_get(lua_State *L)
{
musicdef_t *musicdef = *((musicdef_t **)luaL_checkudata(L, 1, META_MUSICDEF));
enum musicdef_e field = Lua_optoption(L, 2, -1, musicdef_fields_ref);
lua_settop(L, 2);
if (!musicdef)
return LUA_ErrInvalid(L, "musicdef_t");
switch (field)
{
case musicdef_name:
lua_pushstring(L, musicdef->name);
break;
case musicdef_title:
lua_pushstring(L, musicdef->title);
break;
case musicdef_alttitle:
lua_pushstring(L, musicdef->alttitle);
break;
case musicdef_authors:
lua_pushstring(L, musicdef->authors);
break;
default:
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(L, -1));
lua_pushlightuserdata(L, musicdef);
lua_rawget(L, -2);
if (!lua_istable(L, -1)) { // no extra values table
CONS_Debug(DBG_LUA, M_GetText("'%s' has no extvars table or field named '%s'; returning nil.\n"), "musicdef_t", lua_tostring(L, 2));
return 0;
}
lua_pushvalue(L, 2); // field name
lua_gettable(L, -2);
if (lua_isnil(L, -1)) // no value for this field
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "musicdef_t", lua_tostring(L, 2));
break;
}
return 1;
}
static int lib_sMusicInfo(lua_State *L)
{
const char *music_name = lua_tolstring(L, 1, NULL);
LUA_PushUserdata(L, S_MusicInfo(music_name), META_MUSICDEF);
return 1;
}
// G_GAME
////////////
......@@ -3856,6 +4086,34 @@ static int lib_gAddGametype(lua_State *L)
return 0;
}
// Lua exclusive function to unlock Lua Conditions
// Up to Lua scripter
static int lib_gUnlockCondition(lua_State* L)
{
int id = luaL_checkinteger(L, 1) - 1;
boolean global = luaL_checkboolean(L, 2);
if (id <= 0 || id > MAXLUACONDITIONS)
{
luaL_error(L, "Lua condition %d out of range (1 - %d)", id + 1, MAXLUACONDITIONS);
return 0;
}
if (global)
{
serverGamedata->lua[id] = true;
M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
}
clientGamedata->lua[id] = true;
if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
{
S_StartSoundFromEverywhere(sfx_s3k68);
}
return 0;
}
// Bot adding function!
// Partly lifted from Got_AddPlayer
static int lib_gAddPlayer(lua_State *L)
......@@ -3864,7 +4122,7 @@ static int lib_gAddPlayer(lua_State *L)
player_t *newplayer;
SINT8 skinnum = 0, bot;
for (i = 0; i < MAXPLAYERS; i++)
for (i = 1; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
break;
......@@ -4155,16 +4413,21 @@ static int lib_gSetCustomExitVars(lua_State *L)
// G_SetCustomExitVars(nil, int) [skipstats only]
// G_SetCustomExitVars(int, int) [both of the above]
// G_SetCustomExitVars(int, int, int) [nextmapoverride, skipstats and nextgametype]
// G_SetCustomExitVars(int, int, int, int) [nextmapoverride, skipstats, nextgametype and keepcutscenes]
nextmapoverride = 0;
skipstats = 0;
nextgametype = -1;
keepcutscene = false;
if (n >= 1)
{
nextmapoverride = (INT16)luaL_optinteger(L, 1, 0);
skipstats = (INT16)luaL_optinteger(L, 2, 0);
nextgametype = (INT16)luaL_optinteger(L, 3, -1);
if (!lua_isnil(L, 4))
keepcutscene = luaL_checkboolean(L, 4);
}
return 0;
......@@ -4333,6 +4596,7 @@ static luaL_Reg lib[] = {
{"userdataMetatable", lib_userdataMetatable},
{"IsPlayerAdmin", lib_isPlayerAdmin},
{"reserveLuabanks", lib_reserveLuabanks},
{"tofixed", lib_tofixed},
// m_menu
{"M_MoveColorAfter",lib_pMoveColorAfter},
......@@ -4534,6 +4798,10 @@ static luaL_Reg lib[] = {
{"P_StartQuake",lib_pStartQuake},
{"EV_CrumbleChain",lib_evCrumbleChain},
{"EV_StartCrumble",lib_evStartCrumble},
{"P_StartMoveFloor",lib_pStartMoveFloor},
{"P_StopMoveFloor",lib_pStopMoveFloor},
{"P_StartMoveCeiling",lib_pStartMoveCeiling},
{"P_StopMoveCeiling",lib_pStopMoveCeiling},
// p_slopes
{"P_GetZAt",lib_pGetZAt},
......@@ -4598,11 +4866,13 @@ static luaL_Reg lib[] = {
{"S_GetMusicLoopPoint",lib_sGetMusicLoopPoint},
{"S_PauseMusic",lib_sPauseMusic},
{"S_ResumeMusic", lib_sResumeMusic},
{"S_MusicInfo", lib_sMusicInfo},
// g_game
{"G_AddGametype", lib_gAddGametype},
{"G_AddPlayer", lib_gAddPlayer},
{"G_RemovePlayer", lib_gRemovePlayer},
{"G_UnlockCondition", lib_gUnlockCondition},
{"G_SetUsedCheats", lib_gSetUsedCheats},
{"G_BuildMapName",lib_gBuildMapName},
{"G_BuildMapTitle",lib_gBuildMapTitle},
......@@ -4636,6 +4906,11 @@ static luaL_Reg lib[] = {
int LUA_BaseLib(lua_State *L)
{
// musicdef_t
// Sound should have its whole own file for Lua, but this will do for now.
LUA_RegisterUserdataMetatable(L, META_MUSICDEF, musicdef_get, NULL, NULL);
musicdef_fields_ref = Lua_CreateFieldTable(L, musicdef_opt);
// Set metatable for string
lua_pushliteral(L, ""); // dummy string
lua_getmetatable(L, -1); // get string metatable
......
......@@ -254,11 +254,10 @@ static int lib_searchBlockmap(lua_State *L)
}
else // mobj and function only - search around mobj's radius by default
{
fixed_t radius = mobj->radius + MAXRADIUS;
x1 = mobj->x - radius;
x2 = mobj->x + radius;
y1 = mobj->y - radius;
y2 = mobj->y + radius;
x1 = mobj->x - mobj->radius;
x2 = mobj->x + mobj->radius;
y1 = mobj->y - mobj->radius;
y2 = mobj->y + mobj->radius;
}
lua_settop(L, 2); // pop everything except function, mobj
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2021-2022 by "Lactozilla".
// Copyright (C) 2014-2023 by Sonic Team Junior.
// Copyright (C) 2021-2024 by Lactozilla.
// Copyright (C) 2014-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -593,7 +593,7 @@ static int extracolormap_set(lua_State *L)
|| exc->fadergba != old_fade_rgba
|| exc->fadestart != old_fade_start
|| exc->fadeend != old_fade_end)
R_GenerateLightTable(exc, true);
R_UpdateLightTable(exc, true);
return 0;
}
......
......@@ -22,6 +22,11 @@
#include "lua_libs.h"
#include "lua_hud.h" // hud_running errors
// Included for the custom options menu
#include "netcode/d_netfil.h"
#include "m_menu.h"
#include "w_wad.h"
// for functions not allowed in hud.add hooks
#define NOHUD if (hud_running)\
return luaL_error(L, "HUD rendering code should not call this function!");
......@@ -31,11 +36,27 @@ return luaL_error(L, "HUD rendering code should not call this function!");
static consvar_t *this_cvar;
static void clear_lua_stack(void)
{
if (gL) // check if Lua is actually turned on first, you dummmy -- Monster Iestyn 04/07/18
lua_settop(gL, 0); // clear stack
}
void Got_Luacmd(UINT8 **cp, INT32 playernum)
{
UINT8 i, argc, flags;
const char *argv[256];
char buf[256];
argc = READUINT8(*cp);
argv[0] = (const char*)*cp;
SKIPSTRINGN(*cp, 255);
for (i = 1; i < argc; i++)
{
argv[i] = (const char*)*cp;
SKIPSTRINGN(*cp, 255);
}
// don't use I_Assert here, goto the deny code below
// to clean up and kick people who try nefarious exploits
// like sending random junk lua commands to crash the server
......@@ -48,8 +69,7 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum)
lua_getfield(gL, LUA_REGISTRYINDEX, "COM_Command"); // push COM_Command
if (!lua_istable(gL, -1)) goto deny;
argc = READUINT8(*cp);
READSTRINGN(*cp, buf, 255);
strlcpy(buf, argv[0], 255);
strlwr(buf); // must lowercase buffer
lua_getfield(gL, -1, buf); // push command info table
if (!lua_istable(gL, -1)) goto deny;
......@@ -75,10 +95,17 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum)
lua_remove(gL, -2); // pop command info table
if (!lua_checkstack(gL, argc)) // player + command arguments
{
clear_lua_stack();
CONS_Alert(CONS_WARNING, "lua command stack overflow from %s (%d, need %d more)\n", player_names[playernum], lua_gettop(gL), argc);
return;
}
LUA_PushUserdata(gL, &players[playernum], META_PLAYER);
for (i = 1; i < argc; i++)
{
READSTRINGN(*cp, buf, 255);
strlcpy(buf, argv[i], 255);
lua_pushstring(gL, buf);
}
LUA_Call(gL, (int)argc, 0, 1); // argc is 1-based, so this will cover the player we passed too.
......@@ -86,8 +113,7 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum)
deny:
//must be hacked/buggy client
if (gL) // check if Lua is actually turned on first, you dummmy -- Monster Iestyn 04/07/18
lua_settop(gL, 0); // clear stack
clear_lua_stack();
CONS_Alert(CONS_WARNING, M_GetText("Illegal lua command received from %s\n"), player_names[playernum]);
if (server)
......@@ -173,6 +199,11 @@ void COM_Lua_f(void)
I_Assert(lua_isfunction(gL, -1));
lua_remove(gL, -2); // pop command info table
if (!lua_checkstack(gL, COM_Argc() + 1))
{
CONS_Alert(CONS_WARNING, "lua command stack overflow (%d, need %s more)\n", lua_gettop(gL), sizeu1(COM_Argc() + 1));
return;
}
LUA_PushUserdata(gL, &players[playernum], META_PLAYER);
for (i = 1; i < COM_Argc(); i++)
lua_pushstring(gL, COM_Argv(i));
......@@ -482,7 +513,7 @@ static int lib_cvRegisterVar(lua_State *L)
lua_pop(L, 1);
cvar->func = Lua_OnChange;
}
else if (cvar->flags & CV_CALL && (k && fasticmp(k, "can_change")))
else if (cvar->flags & CV_CALL && (i == 6 || (k && fasticmp(k, "can_change"))))
{
if (!lua_isfunction(L, 4))
{
......@@ -496,6 +527,29 @@ static int lib_cvRegisterVar(lua_State *L)
lua_pop(L, 1);
cvar->can_change = Lua_CanChange;
}
else if (((i == 5 && !(cvar->flags & CV_CALL))
|| (cvar->flags & CV_CALL && i == 7))
|| (k && fasticmp(k, "category")))
{
if (!lua_isstring(L, 4))
{
TYPEERROR("category", LUA_TSTRING)
}
cvar->category = Z_StrDup(lua_tostring(L, 4));
}
else if (((i == 6 && !(cvar->flags & CV_CALL))
|| (cvar->flags & CV_CALL && i == 8))
|| (k && fasticmp(k, "displayname")))
{
if (!lua_isstring(L, 4))
{
TYPEERROR("displayname", LUA_TSTRING)
}
cvar->displayname = Z_StrDup(lua_tostring(L, 4));
}
lua_pop(L, 1);
}
......@@ -522,6 +576,19 @@ static int lib_cvRegisterVar(lua_State *L)
return luaL_error(L, M_GetText("Variable %s has CV_CALL without any callbacks"), cvar->name);
}
if (!cvar->displayname || cvar->displayname[0] == '\0')
{
cvar->displayname = cvar->name;
}
if (!cvar->category || cvar->category[0] == '\0')
{
char* temp = wadfiles[numwadfiles - 1]->filename;
temp += strlen(temp) - nameonlylength(temp);
cvar->category = temp;
}
cvar->flags |= CV_ALLOWLUA;
// actually time to register it to the console now! Finally!
cvar->flags |= CV_MODIFIED;
......@@ -532,6 +599,9 @@ static int lib_cvRegisterVar(lua_State *L)
return luaL_error(L, "failed to register cvar (probable conflict with internal variable/command names)");
}
if (cvar->flags & CV_MENU)
M_RegisterCustomCVOption(cvar);
// return cvar userdata
return 1;
}
......@@ -646,6 +716,8 @@ enum cvar_e
cvar_value,
cvar_string,
cvar_changed,
cvar_displayname,
cvar_category,
};
static const char *const cvar_opt[] = {
......@@ -655,6 +727,8 @@ static const char *const cvar_opt[] = {
"value",
"string",
"changed",
"displayname",
"category",
NULL,
};
......@@ -685,6 +759,12 @@ static int cvar_get(lua_State *L)
case cvar_changed:
lua_pushboolean(L, cvar->changed);
break;
case cvar_displayname:
lua_pushstring(L, cvar->displayname);
break;
case cvar_category:
lua_pushstring(L, cvar->category);
break;
default:
if (devparm)
return luaL_error(L, LUA_QL("consvar_t") " has no field named " LUA_QS ".", lua_tostring(L, 2));
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2023 by Sonic Team Junior.
// Copyright (C) 2012-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -62,18 +62,24 @@ automatically.
X (ShieldSpecial),/* shield abilities */\
X (PlayerCanDamage),/* P_PlayerCanDamage */\
X (PlayerQuit),\
X (NameChange),\
X (IntermissionThinker),/* Y_Ticker */\
X (TeamSwitch),/* team switching in... uh... *what* speak, spit it the fuck out */\
X (ViewpointSwitch),/* spy mode (no trickstabs) */\
X (SeenPlayer),/* MT_NAMECHECK */\
X (PlayerThink),/* P_PlayerThink */\
X (GameStart),\
X (GameQuit),\
X (GameEnd),\
X (PlayerCmd),/* building the player's ticcmd struct (Ported from SRB2Kart) */\
X (MusicChange),\
X (SoundPlay),\
X (PlayerHeight),/* override player height */\
X (PlayerCanEnterSpinGaps),\
X (AddonLoaded),\
X (KeyDown),\
X (KeyUp),\
X (TextInput),\
#define STRING_HOOK_LIST(X) \
X (BotAI),/* B_BuildTailsTiccmd by skin name */\
......@@ -85,6 +91,7 @@ automatically.
X (scores),/* emblems/multiplayer list */\
X (title),/* titlescreen */\
X (titlecard),\
X (escpanel) /* rings/time/score/emblem panel in pause menu */,\
X (intermission),\
X (continue),\
X (playersetup),\
......@@ -126,6 +133,10 @@ int LUA_HookCharacterHUD
INT32 skinIndex, UINT8 sprite2, UINT8 frame, UINT8 rotation, skincolornum_t color,
INT32 ticker, boolean mode
);
boolean LUA_HookEscapePanel(
int hook, huddrawlist_h drawlist,
int x, int y, int width, int height
);
int LUA_HookMobj(mobj_t *, int hook);
int LUA_Hook2Mobj(mobj_t *, mobj_t *, int hook);
......@@ -134,6 +145,7 @@ void LUA_HookBool(boolean value, int hook);
int LUA_HookPlayer(player_t *, int hook);
int LUA_HookTiccmd(player_t *, ticcmd_t *, int hook);
int LUA_HookKey(event_t *event, int hook); // Hooks for key events
int LUA_HookText(event_t *event, int hook); // Hooks for text events
void LUA_HookPreThinkFrame(void);
void LUA_HookThinkFrame(void);
......@@ -152,11 +164,13 @@ int LUA_HookMapThingSpawn(mobj_t *, mapthing_t *);
int LUA_HookFollowMobj(player_t *, mobj_t *);
int LUA_HookPlayerCanDamage(player_t *, mobj_t *);
void LUA_HookPlayerQuit(player_t *, kickreason_t);
int LUA_HookNameChange(player_t *plr, const char *name);
int LUA_HookTeamSwitch(player_t *, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble);
int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced);
int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend);
int LUA_HookShouldJingleContinue(player_t *, const char *musname);
int LUA_HookPlayerCmd(player_t *, ticcmd_t *);
int LUA_HookMusicChange(const char *oldname, struct MusicChange *);
int LUA_HookSoundPlay(sfxenum_t sfx_id, void *origin, const int origintype);
fixed_t LUA_HookPlayerHeight(player_t *player);
int LUA_HookPlayerCanEnterSpinGaps(player_t *player);
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2023 by Sonic Team Junior.
// Copyright (C) 2012-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -22,6 +22,7 @@
#include "lua_libs.h"
#include "lua_hook.h"
#include "lua_hud.h" // hud_running errors
#include "lua_archive.h"
#include "m_perfstats.h"
#include "netcode/d_netcmd.h" // for cv_perfstats
......@@ -498,7 +499,25 @@ static int call_string_hooks(Hook_State *hook)
static int call_mobj_type_hooks(Hook_State *hook, mobjtype_t mobj_type)
{
return call_mapped(hook, &mobjHookIds[mobj_type][hook->hook_type]);
int numCalls = call_mapped(hook, &mobjHookIds[mobj_type][hook->hook_type]);
if (numCalls > 0 && mobj_type == MT_NULL && (
hook->hook_type == MOBJ_HOOK(MobjThinker )
|| hook->hook_type == MOBJ_HOOK(MobjCollide )
|| hook->hook_type == MOBJ_HOOK(MobjLineCollide)
|| hook->hook_type == MOBJ_HOOK(MobjMoveCollide)
|| hook->hook_type == MOBJ_HOOK(MobjFuse )
|| hook->hook_type == MOBJ_HOOK(MobjThinker )
|| hook->hook_type == MOBJ_HOOK(BossThinker )
|| hook->hook_type == MOBJ_HOOK(MobjMoveBlocked)
|| hook->hook_type == MOBJ_HOOK(FollowMobj )
))
LUA_UsageWarning(L, va(
"%s hooks not attached to a specific mobj type are deprecated and will be removed.",
mobjHookNames[hook->hook_type])
);
return numCalls;
}
static void call_hud_hooks
......@@ -672,6 +691,17 @@ int LUA_HookKey(event_t *event, int hook_type)
return hook.status;
}
int LUA_HookText(event_t *event, int hook_type)
{
Hook_State hook;
if (prepare_hook(&hook, false, hook_type))
{
LUA_PushUserdata(gL, event, META_TEXTEVENT);
call_hooks(&hook, 1, res_true);
}
return hook.status;
}
void LUA_HookHUD(int hook_type, huddrawlist_h list)
{
Hook_State hook;
......@@ -709,6 +739,22 @@ int LUA_HookCharacterHUD
return hook.status;
}
boolean LUA_HookEscapePanel(int hook, huddrawlist_h drawlist, int x, int y, int width, int height)
{
Hook_State hookstate;
if (prepare_hud_hook(&hookstate, false, hook))
{
LUA_SetHudHook(hook, drawlist);
lua_pushinteger(gL, x);
lua_pushinteger(gL, y);
lua_pushinteger(gL, width);
lua_pushinteger(gL, height);
call_hud_hooks(&hookstate, 1, res_true);
}
return hookstate.status;
}
/* =========================================================================
SPECIALIZED HOOKS
========================================================================= */
......@@ -998,9 +1044,10 @@ void LUA_HookNetArchive(lua_CFunction archFunc)
begin_hook_values(&hook);
// tables becomes an upvalue of archFunc
lua_pushvalue(gL, -1);
lua_pushcclosure(gL, archFunc, 1);
// tables and userdata becomes an upvalue of archFunc
lua_pushvalue(gL, -2);
lua_pushvalue(gL, -2);
lua_pushcclosure(gL, archFunc, 2);
// stack: tables, archFunc
init_hook_call(&hook, 0, res_none);
......@@ -1059,6 +1106,18 @@ void LUA_HookPlayerQuit(player_t *plr, kickreason_t reason)
}
}
int LUA_HookNameChange(player_t *plr, const char *name)
{
Hook_State hook;
if (prepare_hook(&hook, true, HOOK(NameChange)))
{
LUA_PushUserdata(gL, plr, META_PLAYER); // Player that changed name
lua_pushstring(gL, name); // New player name
call_hooks(&hook, 1, res_false);
}
return hook.status;
}
int LUA_HookTeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble)
{
Hook_State hook;
......@@ -1209,6 +1268,47 @@ int LUA_HookMusicChange(const char *oldname, struct MusicChange *param)
return hook.status;
}
static void res_soundplay(Hook_State* hook)
{
if (!lua_isnil(gL, -1))
{
UINT32 sfx_id = lua_tonumber(gL, -1);
if (lua_isboolean(gL, -1) && lua_toboolean(gL, -1))
hook->status = 0; //sfx_None if returning True
//Make sure number is in range
else if (!lua_isboolean(gL, -1)
&& (sfx_id < NUMSFX))
hook->status = sfx_id;
if (sfx_id >= NUMSFX)
CONS_Alert(CONS_WARNING, "sfx %d out of range (0 - %d)\n", sfx_id, NUMSFX-1);
}
}
int LUA_HookSoundPlay(sfxenum_t sfx_id, void *origin, const int origintype)
{
Hook_State hook;
if (prepare_hook(&hook, sfx_id, HOOK(SoundPlay)))
{
lua_pushinteger(gL, sfx_id);
if (origin != NULL)
{
if (origintype == 0)
LUA_PushUserdata(gL, (mobj_t*)origin, META_MOBJ);
else if (origintype == 1)
LUA_PushUserdata(gL, (sector_t*)origin, META_SECTOR);
lua_pushinteger(gL, origintype);
}
hud_running = true; // local hook
call_hooks(&hook, 1, res_soundplay);
hud_running = false;
}
return hook.status;
}
static void res_playerheight(Hook_State *hook)
{
if (!lua_isnil(gL, -1))
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2014-2016 by John "JTE" Muniz.
// Copyright (C) 2014-2023 by Sonic Team Junior.
// Copyright (C) 2014-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -20,6 +20,10 @@ enum hud {
hud_textspectator,
hud_crosshair,
hud_powerups,
hud_gameover,
hud_pause,
hud_cecho,
hud_chat,
// Singleplayer / Co-op
hud_score,
hud_time,
......@@ -30,6 +34,7 @@ enum hud {
hud_weaponrings,
hud_powerstones,
hud_teamscores,
hud_itemhunt,
// NiGHTS mode
hud_nightslink,
hud_nightsdrill,
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2014-2016 by John "JTE" Muniz.
// Copyright (C) 2014-2023 by Sonic Team Junior.
// Copyright (C) 2014-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -42,6 +42,10 @@ static const char *const hud_disable_options[] = {
"textspectator",
"crosshair",
"powerups",
"gameover",
"pause",
"cecho",
"chat",
"score",
"time",
......@@ -52,6 +56,7 @@ static const char *const hud_disable_options[] = {
"weaponrings",
"powerstones",
"teamscores",
"itemhunt",
"nightslink",
"nightsdrill",
......@@ -461,15 +466,29 @@ static int camera_set(lua_State *L)
static int libd_patchExists(lua_State *L)
{
HUDONLY
lua_pushboolean(L, W_LumpExists(luaL_checkstring(L, 1)));
return 1;
}
static int libd_cachePatch(lua_State *L)
{
HUDONLY
LUA_PushUserdata(L, W_CachePatchLongName(luaL_checkstring(L, 1), PU_PATCH), META_PATCH);
const char *name = luaL_checkstring(L, 1);
patch_t *patch = W_CachePatchLongName(name, PU_PATCH);
#ifdef ROTSPRITE
if (lua_isnumber(L, 2))
{
angle_t rollangle = luaL_checkangle(L, 2);
INT32 rot = R_GetRollAngle(rollangle);
if (rot) {
patch_t *rotpatch = Patch_GetRotated(patch, rot, false);
LUA_PushUserdata(L, rotpatch, META_PATCH);
return 1;
}
}
#endif
LUA_PushUserdata(L, patch, META_PATCH);
return 1;
}
......@@ -481,7 +500,6 @@ static int libd_getSpritePatch(lua_State *L)
UINT8 angle = 0;
spritedef_t *sprdef;
spriteframe_t *sprframe;
HUDONLY
if (lua_isnumber(L, 1)) // sprite number given, e.g. SPR_THOK
{
......@@ -555,7 +573,6 @@ static int libd_getSprite2Patch(lua_State *L)
spritedef_t *sprdef;
spriteframe_t *sprframe;
boolean super = false; // add SPR2F_SUPER to sprite2 if true
HUDONLY
// get skin first!
if (lua_isnumber(L, 1)) // find skin by number
......@@ -613,6 +630,10 @@ static int libd_getSprite2Patch(lua_State *L)
if (super)
j |= SPR2F_SUPER;
// If there is no "super" variation of this sprite, try with the normal one.
if (!P_IsValidSprite2(skins[i], j))
j &= ~SPR2F_SUPER;
sprdef = P_GetSkinSpritedef(skins[i], j);
// set frame number
......@@ -871,6 +892,28 @@ static int libd_drawFill(lua_State *L)
return 0;
}
static int libd_drawFixedFill(lua_State *L)
{
huddrawlist_h list;
INT32 x = luaL_optinteger(L, 1, 0);
INT32 y = luaL_optinteger(L, 2, 0);
INT32 w = luaL_optinteger(L, 3, BASEVIDWIDTH << FRACBITS);
INT32 h = luaL_optinteger(L, 4, BASEVIDHEIGHT << FRACBITS);
INT32 c = luaL_optinteger(L, 5, 31);
HUDONLY
lua_getfield(L, LUA_REGISTRYINDEX, "HUD_DRAW_LIST");
list = (huddrawlist_h) lua_touserdata(L, -1);
lua_pop(L, 1);
if (LUA_HUD_IsDrawListValid(list))
LUA_HUD_AddDrawFixedFill(list, x, y, w, h, c);
else
V_DrawFixedFill(x, y, w, h, c);
return 0;
}
static int libd_drawString(lua_State *L)
{
huddrawlist_h list;
......@@ -1126,13 +1169,12 @@ static int libd_levelTitleHeight(lua_State *L)
static int libd_getColormap(lua_State *L)
{
HUDONLY
INT32 skinnum = TC_DEFAULT;
skincolornum_t color = luaL_optinteger(L, 2, 0);
UINT8* colormap = NULL;
int translation_id = -1;
HUDONLY
if (lua_isnoneornil(L, 1))
; // defaults to TC_DEFAULT
else if (lua_type(L, 1) == LUA_TNUMBER) // skin number
......@@ -1172,9 +1214,10 @@ static int libd_getColormap(lua_State *L)
static int libd_getStringColormap(lua_State *L)
{
HUDONLY
INT32 flags = luaL_checkinteger(L, 1);
UINT8* colormap = NULL;
HUDONLY
colormap = V_GetStringColormap(flags & V_CHARCOLORMASK);
if (colormap) {
LUA_PushUserdata(L, colormap, META_COLORMAP); // push as META_COLORMAP userdata, specifically for patches to use!
......@@ -1185,6 +1228,7 @@ static int libd_getStringColormap(lua_State *L)
static int libd_getSectorColormap(lua_State *L)
{
HUDONLY
boolean has_sector = false;
sector_t *sector = NULL;
if (!lua_isnoneornil(L, 1))
......@@ -1260,21 +1304,18 @@ static int libd_fadeScreen(lua_State *L)
static int libd_width(lua_State *L)
{
HUDONLY
lua_pushinteger(L, vid.width); // push screen width
return 1;
}
static int libd_height(lua_State *L)
{
HUDONLY
lua_pushinteger(L, vid.height); // push screen height
return 1;
}
static int libd_dup(lua_State *L)
{
HUDONLY
lua_pushinteger(L, vid.dup); // push integral scale (patch scale)
lua_pushfixed(L, vid.fdup); // push fixed point scale (position scale)
return 2;
......@@ -1282,7 +1323,6 @@ static int libd_dup(lua_State *L)
static int libd_renderer(lua_State *L)
{
HUDONLY
switch (rendermode) {
case render_opengl: lua_pushliteral(L, "opengl"); break; // OpenGL renderer
case render_soft: lua_pushliteral(L, "software"); break; // Software renderer
......@@ -1296,14 +1336,12 @@ static int libd_renderer(lua_State *L)
static int libd_RandomFixed(lua_State *L)
{
HUDONLY
lua_pushfixed(L, M_RandomFixed());
return 1;
}
static int libd_RandomByte(lua_State *L)
{
HUDONLY
lua_pushinteger(L, M_RandomByte());
return 1;
}
......@@ -1312,7 +1350,6 @@ static int libd_RandomKey(lua_State *L)
{
INT32 a = (INT32)luaL_checkinteger(L, 1);
HUDONLY
lua_pushinteger(L, M_RandomKey(a));
return 1;
}
......@@ -1322,7 +1359,6 @@ static int libd_RandomRange(lua_State *L)
INT32 a = (INT32)luaL_checkinteger(L, 1);
INT32 b = (INT32)luaL_checkinteger(L, 2);
HUDONLY
lua_pushinteger(L, M_RandomRange(a, b));
return 1;
}
......@@ -1330,7 +1366,6 @@ static int libd_RandomRange(lua_State *L)
// Macros.
static int libd_SignedRandom(lua_State *L)
{
HUDONLY
lua_pushinteger(L, M_SignedRandom());
return 1;
}
......@@ -1338,7 +1373,6 @@ static int libd_SignedRandom(lua_State *L)
static int libd_RandomChance(lua_State *L)
{
fixed_t p = luaL_checkfixed(L, 1);
HUDONLY
lua_pushboolean(L, M_RandomChance(p));
return 1;
}
......@@ -1347,7 +1381,6 @@ static int libd_RandomChance(lua_State *L)
// Could as well be thrown in global vars for ease of access but I guess it makes sense for it to be a HUD fn
static int libd_getlocaltransflag(lua_State *L)
{
HUDONLY
lua_pushinteger(L, (10-st_translucency)*V_10TRANS);
return 1;
}
......@@ -1355,7 +1388,6 @@ static int libd_getlocaltransflag(lua_State *L)
// Get cv_translucenthud's value for HUD rendering as a normal V_xxTRANS int
static int libd_getusertransflag(lua_State *L)
{
HUDONLY
lua_pushinteger(L, (10-cv_translucenthud.value)*V_10TRANS); // A bit weird that it's called "translucenthud" yet 10 is fully opaque :V
return 1;
}
......@@ -1377,6 +1409,7 @@ static luaL_Reg lib_draw[] = {
{"drawNum", libd_drawNum},
{"drawPaddedNum", libd_drawPaddedNum},
{"drawFill", libd_drawFill},
{"drawFixedFill", libd_drawFixedFill},
{"drawString", libd_drawString},
{"drawNameTag", libd_drawNameTag},
{"drawScaledNameTag", libd_drawScaledNameTag},
......@@ -1448,6 +1481,26 @@ static luaL_Reg lib_hud[] = {
{"disable", lib_huddisable},
{"enabled", lib_hudenabled},
{"add", lib_hudadd},
{"patchExists", libd_patchExists},
{"cachePatch", libd_cachePatch},
{"getSpritePatch", libd_getSpritePatch},
{"getSprite2Patch", libd_getSprite2Patch},
{"width", libd_width},
{"height", libd_height},
{"scale", libd_dup},
{"renderer", libd_renderer},
{NULL, NULL}
};
// globalized client_side random functions.
static luaL_Reg lib_randomclient[] = {
// m_random
{"localFixed",libd_RandomFixed},
{"localByte",libd_RandomByte},
{"localKey",libd_RandomKey},
{"localRange",libd_RandomRange},
{"localSignedRandom",libd_SignedRandom}, // MACRO
{"localChance",libd_RandomChance}, // MACRO
{NULL, NULL}
};
......@@ -1474,6 +1527,7 @@ int LUA_HudLib(lua_State *L)
LUA_RegisterGlobalUserdata(L, "hudinfo", lib_getHudInfo, NULL, lib_hudinfolen);
luaL_register(L, "hud", lib_hud);
luaL_register(L, "random", lib_randomclient);
return 0;
}
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2014-2016 by John "JTE" Muniz.
// Copyright (C) 2014-2023 by Sonic Team Junior.
// Copyright (C) 2014-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -25,6 +25,7 @@ enum drawitem_e {
DI_DrawNum,
DI_DrawPaddedNum,
DI_DrawFill,
DI_DrawFixedFill,
DI_DrawString,
DI_DrawNameTag,
DI_DrawScaledNameTag,
......@@ -180,7 +181,8 @@ static const char *CopyString(huddrawlist_h list, const char* str)
const char *old_offset = list->strbuf;
size_t i;
if (list->strbuf_capacity == 0) list->strbuf_capacity = 256;
else list->strbuf_capacity *= 2;
while (list->strbuf_capacity <= list->strbuf_len + lenstr + 1)
list->strbuf_capacity *= 2;
list->strbuf = (char*) Z_Realloc(list->strbuf, sizeof(char) * list->strbuf_capacity, PU_STATIC, NULL);
// align the string pointers to make sure old pointers don't point towards invalid addresses
......@@ -345,6 +347,25 @@ void LUA_HUD_AddDrawFill(
item->c = c;
}
void LUA_HUD_AddDrawFixedFill(
huddrawlist_h list,
fixed_t x,
fixed_t y,
fixed_t w,
fixed_t h,
INT32 c
)
{
size_t i = AllocateDrawItem(list);
drawitem_t *item = &list->items[i];
item->type = DI_DrawFixedFill;
item->x = x;
item->y = y;
item->w = w;
item->h = h;
item->c = c;
}
void LUA_HUD_AddDrawString(
huddrawlist_h list,
fixed_t x,
......@@ -481,6 +502,9 @@ void LUA_HUD_DrawList(huddrawlist_h list)
case DI_DrawFill:
V_DrawFill(item->x, item->y, item->w, item->h, item->c);
break;
case DI_DrawFixedFill:
V_DrawFixedFill(item->x, item->y, item->w, item->h, item->c);
break;
case DI_DrawString:
switch(item->align)
{
......
......@@ -98,6 +98,14 @@ void LUA_HUD_AddDrawFill(
INT32 h,
INT32 c
);
void LUA_HUD_AddDrawFixedFill(
huddrawlist_h list,
fixed_t x,
fixed_t y,
fixed_t w,
fixed_t h,
INT32 c
);
void LUA_HUD_AddDrawString(
huddrawlist_h list,
fixed_t x,
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2024 by Kart Krew.
// Copyright (C) 2012-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2023 by Sonic Team Junior.
// Copyright (C) 2012-2025 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -82,7 +83,7 @@ static int lib_getSprname(lua_State *L)
i = lua_tonumber(L, 1);
if (i > NUMSPRITES)
return 0;
lua_pushlstring(L, sprnames[i], 4);
lua_pushstring(L, sprnames[i]);
return 1;
}
else if (lua_isstring(L, 1))
......@@ -236,23 +237,50 @@ static int lib_spr2namelen(lua_State *L)
// SPRITE INFO //
/////////////////
struct SpriteInfoFrame {
spriteinfo_t *sprinfo;
UINT16 frame;
};
static UINT16 GetSpriteInfoFrameIndex(lua_State *L, int idx)
{
if (lua_type(L, idx) == LUA_TSTRING)
{
const char *field = luaL_checkstring(L, idx);
if (fastcmp("default", field))
{
return SPRINFO_DEFAULT_FRAME;
}
else
{
UINT8 frame = R_Char2Frame(field[0]);
if (frame == 255)
return luaL_error(L, "invalid frame %s", field);
return (UINT16)frame;
}
}
else
{
int frameID = luaL_checknumber(L, idx);
if (frameID < 0 || frameID >= MAXFRAMENUM)
return luaL_error(L, "frame %d out of range (0 - %d)", frameID, MAXFRAMENUM - 1);
return (UINT16)frameID;
}
}
// spriteinfo[]
static int lib_getSpriteInfo(lua_State *L)
{
UINT32 i = NUMSPRITES;
lua_remove(L, 1);
if (lua_isstring(L, 1))
if (lua_type(L, 1) == LUA_TSTRING)
{
const char *name = lua_tostring(L, 1);
INT32 spr = R_GetSpriteNumByName(name);
if (spr == NUMSPRITES)
{
char *check;
i = strtol(name, &check, 10);
if (check == name || *check != '\0')
return luaL_error(L, "unknown sprite name %s", name);
}
i = spr;
}
else
......@@ -268,9 +296,72 @@ static int lib_getSpriteInfo(lua_State *L)
#define FIELDERROR(f, e) luaL_error(L, "bad value for " LUA_QL(f) " in table passed to spriteinfo[] (%s)", e);
#define TYPEERROR(f, t1, t2) FIELDERROR(f, va("%s expected, got %s", lua_typename(L, t1), lua_typename(L, t2)))
static int PopPivotSubTable(spriteframepivot_t *pivot, lua_State *L, int stk, int idx)
static int PopPivotSubTable(spriteinfoframe_t *frame, lua_State *L, int stk);
static int PopFrameSubTable(spriteinfoframe_t *frame, lua_State *L, int stk)
{
int okcool = 0;
switch (lua_type(L, stk))
{
case LUA_TTABLE:
lua_pushnil(L);
while (lua_next(L, stk))
{
const char *key = luaL_checkstring(L, stk+1);
if (fastcmp(key, "pivot"))
{
if (PopPivotSubTable(frame, L, stk+2))
okcool = 1;
}
else
{
FIELDERROR("sprite info frame key", va("invalid option %s", key));
}
okcool = 1;
lua_pop(L, 1);
}
break;
default:
TYPEERROR("sprite info frame", LUA_TTABLE, lua_type(L, stk))
}
return okcool;
}
static int PopFrameTable(spriteinfo_t *info, lua_State *L, int stk)
{
lua_pushnil(L);
while (lua_next(L, stk))
{
int idx = 0;
switch (lua_type(L, stk+1))
{
case LUA_TSTRING:
case LUA_TNUMBER:
idx = GetSpriteInfoFrameIndex(L, stk+1);
break;
default:
TYPEERROR("sprite info frame", LUA_TNUMBER, lua_type(L, stk+1));
}
// the values in frames[] are also tables
if (PopFrameSubTable(&info->frames[idx], L, stk+2))
set_bit_array(info->available, idx);
lua_pop(L, 1);
}
return 0;
}
static int PopPivotSubTable(spriteinfoframe_t *frame, lua_State *L, int stk)
{
int okcool = 0;
switch (lua_type(L, stk))
{
case LUA_TTABLE:
......@@ -280,6 +371,7 @@ static int PopPivotSubTable(spriteframepivot_t *pivot, lua_State *L, int stk, in
const char *key = NULL;
lua_Integer ikey = -1;
lua_Integer value = 0;
// x or y?
switch (lua_type(L, stk+1))
{
......@@ -292,6 +384,7 @@ static int PopPivotSubTable(spriteframepivot_t *pivot, lua_State *L, int stk, in
default:
FIELDERROR("pivot key", va("string or number expected, got %s", luaL_typename(L, stk+1)))
}
// then get value
switch (lua_type(L, stk+2))
{
......@@ -304,59 +397,60 @@ static int PopPivotSubTable(spriteframepivot_t *pivot, lua_State *L, int stk, in
default:
TYPEERROR("pivot value", LUA_TNUMBER, lua_type(L, stk+2))
}
// finally set omg!!!!!!!!!!!!!!!!!!
// Set it
if (ikey == 1 || (key && fastcmp(key, "x")))
pivot[idx].x = (INT32)value;
frame->pivot.x = (INT32)value;
else if (ikey == 2 || (key && fastcmp(key, "y")))
pivot[idx].y = (INT32)value;
frame->pivot.y = (INT32)value;
// TODO: 2.3: Delete
else if (ikey == 3 || (key && fastcmp(key, "rotaxis")))
LUA_UsageWarning(L, "\"rotaxis\" is deprecated and will be removed.")
else if (ikey == -1 && (key != NULL))
FIELDERROR("pivot key", va("invalid option %s", key));
okcool = 1;
lua_pop(L, 1);
}
break;
default:
TYPEERROR("sprite pivot", LUA_TTABLE, lua_type(L, stk))
}
if (okcool)
frame->pivot.available = true;
return okcool;
}
static int PopPivotTable(spriteinfo_t *info, lua_State *L, int stk)
{
// Just in case?
if (!lua_istable(L, stk))
TYPEERROR("pivot table", LUA_TTABLE, lua_type(L, stk));
lua_pushnil(L);
// stk = 0 has the pivot table
// stk = 1 has the frame key
// stk = 2 has the frame table
// stk = 3 has either a string or a number as key
// stk = 4 has the value for the key mentioned above
while (lua_next(L, stk))
{
int idx = 0;
const char *framestr = NULL;
switch (lua_type(L, stk+1))
{
case LUA_TSTRING:
framestr = lua_tostring(L, stk+1);
idx = R_Char2Frame(framestr[0]);
break;
case LUA_TNUMBER:
idx = lua_tonumber(L, stk+1);
idx = GetSpriteInfoFrameIndex(L, stk+1);
break;
default:
TYPEERROR("pivot frame", LUA_TNUMBER, lua_type(L, stk+1));
}
if ((idx < 0) || (idx >= MAXFRAMENUM))
return luaL_error(L, "pivot frame %d out of range (0 - %d)", idx, MAXFRAMENUM - 1);
// the values in pivot[] are also tables
if (PopPivotSubTable(info->pivot, L, stk+2, idx))
info->available = true;
if (PopPivotSubTable(&info->frames[idx], L, stk+2))
{
set_bit_array(info->available, idx);
// TODO: 2.3: Delete
info->frames[SPRINFO_DEFAULT_FRAME].pivot.available = true;
set_bit_array(info->available, SPRINFO_DEFAULT_FRAME);
}
lua_pop(L, 1);
}
......@@ -402,6 +496,14 @@ static int lib_setSpriteInfo(lua_State *L)
else
FIELDERROR("pivot", va("%s expected, got %s", lua_typename(L, LUA_TTABLE), luaL_typename(L, -1)))
}
else if (str && fastcmp(str, "frames"))
{
// frames[] is a table
if (lua_istable(L, 3))
return PopFrameTable(info, L, 3);
else
FIELDERROR("frames", va("%s expected, got %s", lua_typename(L, LUA_TTABLE), luaL_typename(L, -1)))
}
lua_pop(L, 1);
}
......@@ -427,17 +529,26 @@ static int spriteinfo_get(lua_State *L)
I_Assert(sprinfo != NULL);
// push spriteframepivot_t userdata
// TODO: 2.3: delete
if (fastcmp(field, "pivot"))
{
// bypass LUA_PushUserdata
void **userdata = lua_newuserdata(L, sizeof(void *));
*userdata = &sprinfo->pivot;
*userdata = sprinfo;
luaL_getmetatable(L, META_PIVOTLIST);
lua_setmetatable(L, -2);
// stack is left with the userdata on top, as if getting it had originally succeeded.
return 1;
}
else if (fastcmp(field, "frames"))
{
void **userdata = lua_newuserdata(L, sizeof(void *));
*userdata = sprinfo;
luaL_getmetatable(L, META_SPRITEINFOFRAMELIST);
lua_setmetatable(L, -2);
return 1;
}
else
return luaL_error(L, LUA_QL("spriteinfo_t") " has no field named " LUA_QS, field);
......@@ -470,9 +581,28 @@ static int spriteinfo_set(lua_State *L)
// pivot[] is userdata
else if (lua_isuserdata(L, 1))
{
spriteframepivot_t *pivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_PIVOTLIST));
memcpy(&sprinfo->pivot, pivot, sizeof(spriteframepivot_t));
sprinfo->available = true; // Just in case?
spriteinfo_t *copyinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_PIVOTLIST));
for (UINT16 i = 0; i <= MAXFRAMENUM; i++)
{
if (in_bit_array(copyinfo->available, i))
memcpy(&sprinfo->frames[i].pivot, &copyinfo->frames[i].pivot, sizeof(spriteframepivot_t));
}
}
}
else if (fastcmp(field, "frames"))
{
// frames[] is a table
if (lua_istable(L, 1))
return PopFrameTable(sprinfo, L, 1);
// frames[] is userdata
else if (lua_isuserdata(L, 1))
{
spriteinfo_t *copyinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_SPRITEINFOFRAMELIST));
for (UINT16 i = 0; i <= MAXFRAMENUM; i++)
{
if (in_bit_array(copyinfo->available, i))
memcpy(&sprinfo->frames[i], &copyinfo->frames[i], sizeof(spriteinfoframe_t));
}
}
}
else
......@@ -492,21 +622,138 @@ static int spriteinfo_num(lua_State *L)
return 1;
}
// framepivot_t
static int pivotlist_get(lua_State *L)
// spriteinfoframe_t
static int framelist_get(lua_State *L)
{
struct SpriteInfoFrame *container;
spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_SPRITEINFOFRAMELIST));
UINT16 frame = GetSpriteInfoFrameIndex(L, 2);
container = lua_newuserdata(L, sizeof *container);
container->sprinfo = sprinfo;
container->frame = frame;
luaL_getmetatable(L, META_SPRITEINFOFRAME);
lua_setmetatable(L, -2);
// stack is left with the userdata on top, as if getting it had originally succeeded.
return 1;
}
static int framelist_set(lua_State *L)
{
spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_SPRITEINFOFRAMELIST));
UINT16 frame;
int okcool = 0;
if (!lua_lumploading)
return luaL_error(L, "Do not alter spriteinfoframe_t from within a hook or coroutine!");
if (hud_running)
return luaL_error(L, "Do not alter spriteinfoframe_t in HUD rendering code!");
if (hook_cmd_running)
return luaL_error(L, "Do not alter spriteinfoframe_t in CMD building code!");
frame = GetSpriteInfoFrameIndex(L, 2);
// frames[] is a table
if (lua_istable(L, 3))
okcool = PopFrameSubTable(&sprinfo->frames[frame], L, 3);
// frames[] is userdata
else if (lua_isuserdata(L, 3))
{
void **userdata;
spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_PIVOTLIST));
struct SpriteInfoFrame *container = luaL_checkudata(L, 3, META_SPRITEINFOFRAME);
memcpy(&sprinfo->frames[frame],
&container->sprinfo->frames[container->frame],
sizeof(spriteinfoframe_t));
okcool = 1;
}
if (okcool)
set_bit_array(sprinfo->available, frame);
return 0;
}
static int framelist_num(lua_State *L)
{
lua_pushinteger(L, MAXFRAMENUM);
return 1;
}
// spriteinfoframe_t
static int sprinfoframe_get(lua_State *L)
{
struct SpriteInfoFrame *container = luaL_checkudata(L, 1, META_SPRITEINFOFRAME);
const char *field = luaL_checkstring(L, 2);
UINT8 frame;
frame = R_Char2Frame(field[0]);
if (frame == 255)
luaL_error(L, "invalid frame %s", field);
if (fastcmp("pivot", field))
{
struct SpriteInfoFrame *other_container = lua_newuserdata(L, sizeof *container);
memcpy(other_container, container, sizeof *container);
luaL_getmetatable(L, META_FRAMEPIVOT);
lua_setmetatable(L, -2);
}
else
return luaL_error(L, "Field %s does not exist in spriteinfoframe_t", field);
return 1;
}
// bypass LUA_PushUserdata
userdata = lua_newuserdata(L, sizeof(void *));
*userdata = &framepivot[frame];
static int sprinfoframe_set(lua_State *L)
{
struct SpriteInfoFrame *container = luaL_checkudata(L, 1, META_SPRITEINFOFRAME);
spriteinfoframe_t *frame = &container->sprinfo->frames[container->frame];
UINT8 *available = container->sprinfo->available;
const char *field = luaL_checkstring(L, 2);
boolean is_available = false;
if (!lua_lumploading)
return luaL_error(L, "Do not alter spriteinfoframe_t from within a hook or coroutine!");
if (hud_running)
return luaL_error(L, "Do not alter spriteinfoframe_t in HUD rendering code!");
if (hook_cmd_running)
return luaL_error(L, "Do not alter spriteinfoframe_t in CMD building code!");
I_Assert(frame != NULL);
if (fastcmp("pivot", field))
{
// pivot[] is a table
if (lua_istable(L, 3))
{
if (PopPivotSubTable(frame, L, 3))
is_available = true;
}
// pivot[] is userdata
else if (lua_isuserdata(L, 3))
{
struct SpriteInfoFrame *other_container = luaL_checkudata(L, 3, META_FRAMEPIVOT);
memcpy(&frame->pivot,
&other_container->sprinfo->frames[other_container->frame].pivot,
sizeof(spriteframepivot_t));
is_available = true;
}
}
else
return luaL_error(L, "Field %s does not exist in spriteframepivot_t", field);
if (is_available)
{
set_bit_array(available, container->frame);
}
return 0;
}
// framepivot_t
static int pivotlist_get(lua_State *L)
{
struct SpriteInfoFrame *container;
spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_PIVOTLIST));
UINT16 frame = GetSpriteInfoFrameIndex(L, 2);
container = lua_newuserdata(L, sizeof *container);
container->sprinfo = sprinfo;
container->frame = frame;
luaL_getmetatable(L, META_FRAMEPIVOT);
lua_setmetatable(L, -2);
......@@ -516,11 +763,9 @@ static int pivotlist_get(lua_State *L)
static int pivotlist_set(lua_State *L)
{
// Because I already know it's a spriteframepivot_t anyway
spriteframepivot_t *pivotlist = *((spriteframepivot_t **)lua_touserdata(L, 1));
//spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT));
const char *field = luaL_checkstring(L, 2);
UINT8 frame;
spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_PIVOTLIST));
UINT16 frame;
int okcool = 0;
if (!lua_lumploading)
return luaL_error(L, "Do not alter spriteframepivot_t from within a hook or coroutine!");
......@@ -529,20 +774,24 @@ static int pivotlist_set(lua_State *L)
if (hook_cmd_running)
return luaL_error(L, "Do not alter spriteframepivot_t in CMD building code!");
frame = R_Char2Frame(field[0]);
if (frame == 255)
luaL_error(L, "invalid frame %s", field);
frame = GetSpriteInfoFrameIndex(L, 2);
// pivot[] is a table
if (lua_istable(L, 3))
return PopPivotSubTable(pivotlist, L, 3, frame);
okcool = PopPivotSubTable(&sprinfo->frames[frame], L, 3);
// pivot[] is userdata
else if (lua_isuserdata(L, 3))
{
spriteframepivot_t *copypivot = *((spriteframepivot_t **)luaL_checkudata(L, 3, META_FRAMEPIVOT));
memcpy(&pivotlist[frame], copypivot, sizeof(spriteframepivot_t));
struct SpriteInfoFrame *container = luaL_checkudata(L, 3, META_FRAMEPIVOT);
memcpy(&sprinfo->frames[frame].pivot,
&container->sprinfo->frames[container->frame].pivot,
sizeof(spriteframepivot_t));
okcool = 1;
}
if (okcool)
set_bit_array(sprinfo->available, frame);
return 0;
}
......@@ -554,7 +803,8 @@ static int pivotlist_num(lua_State *L)
static int framepivot_get(lua_State *L)
{
spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT));
struct SpriteInfoFrame *container = luaL_checkudata(L, 1, META_FRAMEPIVOT);
spriteframepivot_t *framepivot = &container->sprinfo->frames[container->frame].pivot;
const char *field = luaL_checkstring(L, 2);
I_Assert(framepivot != NULL);
......@@ -577,7 +827,9 @@ static int framepivot_get(lua_State *L)
static int framepivot_set(lua_State *L)
{
spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT));
struct SpriteInfoFrame *container = luaL_checkudata(L, 1, META_FRAMEPIVOT);
spriteframepivot_t *framepivot = &container->sprinfo->frames[container->frame].pivot;
UINT8 *available = container->sprinfo->available;
const char *field = luaL_checkstring(L, 2);
if (!lua_lumploading)
......@@ -590,9 +842,15 @@ static int framepivot_set(lua_State *L)
I_Assert(framepivot != NULL);
if (fastcmp("x", field))
{
framepivot->x = luaL_checkinteger(L, 3);
set_bit_array(available, container->frame);
}
else if (fastcmp("y", field))
{
framepivot->y = luaL_checkinteger(L, 3);
set_bit_array(available, container->frame);
}
// TODO: 2.3: delete
else if (fastcmp("rotaxis", field))
LUA_UsageWarning(L, "\"rotaxis\" is deprecated and will be removed.")
......@@ -719,21 +977,19 @@ static int lib_setState(lua_State *L)
switch(lua_type(L, 3))
{
case LUA_TNIL: // Null? Set the action to nothing, then.
state->action.acp1 = NULL;
state->action = NULL;
break;
case LUA_TSTRING: // It's a string, expect the name of a built-in action
LUA_SetActionByName(state, lua_tostring(L, 3));
break;
case LUA_TUSERDATA: // It's a userdata, expect META_ACTION of a built-in action
{
actionf_t *action = *((actionf_t **)luaL_checkudata(L, 3, META_ACTION));
actionf_p1 *action = *((actionf_p1 **)luaL_checkudata(L, 3, META_ACTION));
if (!action)
return luaL_error(L, "not a valid action?");
state->action = *action;
state->action.acv = action->acv;
state->action.acp1 = action->acp1;
break;
}
case LUA_TFUNCTION: // It's a function (a Lua function or a C function? either way!)
......@@ -743,7 +999,7 @@ static int lib_setState(lua_State *L)
lua_pushvalue(L, 3); // Bring it to the top of the stack
lua_rawset(L, -3); // Set it in the registry
lua_pop(L, 1); // pop LREG_STATEACTION
state->action.acp1 = (actionf_p1)A_Lua; // Set the action for the userdata.
state->action = (actionf_p1)A_Lua; // Set the action for the userdata.
break;
default: // ?!
return luaL_typerror(L, 3, "function");
......@@ -801,7 +1057,7 @@ boolean LUA_SetLuaAction(void *stv, const char *action)
lua_pop(gL, 1); // pop LREG_STATEACTION
lua_pop(gL, 2); // pop the function and LREG_ACTIONS
st->action.acp1 = (actionf_p1)A_Lua; // Set the action for the userdata.
st->action = (actionf_p1)A_Lua; // Set the action for the userdata.
return true; // action successfully set.
}
......@@ -906,9 +1162,9 @@ static int state_get(lua_State *L)
number = st->tics;
else if (fastcmp(field,"action")) {
const char *name;
if (!st->action.acp1) // Action is NULL.
if (!st->action) // Action is NULL.
return 0; // return nil.
if (st->action.acp1 == (actionf_p1)A_Lua) { // This is a Lua function?
if (st->action == (actionf_p1)A_Lua) { // This is a Lua function?
lua_getfield(L, LUA_REGISTRYINDEX, LREG_STATEACTION);
I_Assert(lua_istable(L, -1));
lua_pushlightuserdata(L, st); // Push the state pointer and
......@@ -963,21 +1219,19 @@ static int state_set(lua_State *L)
switch(lua_type(L, 3))
{
case LUA_TNIL: // Null? Set the action to nothing, then.
st->action.acp1 = NULL;
st->action = NULL;
break;
case LUA_TSTRING: // It's a string, expect the name of a built-in action
LUA_SetActionByName(st, lua_tostring(L, 3));
break;
case LUA_TUSERDATA: // It's a userdata, expect META_ACTION of a built-in action
{
actionf_t *action = *((actionf_t **)luaL_checkudata(L, 3, META_ACTION));
actionf_p1 *action = *((actionf_p1 **)luaL_checkudata(L, 3, META_ACTION));
if (!action)
return luaL_error(L, "not a valid action?");
st->action = *action;
st->action.acv = action->acv;
st->action.acp1 = action->acp1;
break;
}
case LUA_TFUNCTION: // It's a function (a Lua function or a C function? either way!)
......@@ -987,7 +1241,7 @@ static int state_set(lua_State *L)
lua_pushvalue(L, 3); // Bring it to the top of the stack
lua_rawset(L, -3); // Set it in the registry
lua_pop(L, 1); // pop LREG_STATEACTION
st->action.acp1 = (actionf_p1)A_Lua; // Set the action for the userdata.
st->action = (actionf_p1)A_Lua; // Set the action for the userdata.
break;
default: // ?!
return luaL_typerror(L, 3, "function");
......@@ -1163,6 +1417,7 @@ enum mobjinfo_e
mobjinfo_activesound,
mobjinfo_flags,
mobjinfo_raisestate,
mobjinfo_name,
};
const char *const mobjinfo_opt[] = {
......@@ -1190,6 +1445,7 @@ const char *const mobjinfo_opt[] = {
"activesound",
"flags",
"raisestate",
"name",
NULL,
};
......@@ -1204,6 +1460,8 @@ static int mobjinfo_get(lua_State *L)
I_Assert(info != NULL);
I_Assert(info >= mobjinfo);
mobjtype_t id = info-mobjinfo;
switch (field)
{
case mobjinfo_doomednum:
......@@ -1278,6 +1536,21 @@ static int mobjinfo_get(lua_State *L)
case mobjinfo_raisestate:
lua_pushinteger(L, info->raisestate);
break;
case mobjinfo_name:
if (id < MT_FIRSTFREESLOT)
{
lua_pushstring(L, MOBJTYPE_LIST[id]+3);
return 1;
}
id -= MT_FIRSTFREESLOT;
if (id < NUMMOBJFREESLOTS && FREE_MOBJS[id])
{
lua_pushstring(L, FREE_MOBJS[id]);
return 1;
}
return 0;
default:
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(L, -1));
......@@ -1740,7 +2013,7 @@ static int lib_setSkinColor(lua_State *L)
else if (i == 6 || (str && fastcmp(str,"accessible"))) {
boolean v = lua_toboolean(L, 3);
if (cnum < FIRSTSUPERCOLOR && v != skincolors[cnum].accessible)
CONS_Alert(CONS_WARNING, "skincolors[] index %d is a standard color; accessibility changes are prohibited.", cnum);
CONS_Alert(CONS_WARNING, "skincolors[] index %d is a standard color; accessibility changes are prohibited.\n", cnum);
else
info->accessible = v;
}
......@@ -1835,7 +2108,7 @@ static int skincolor_set(lua_State *L)
else if (fastcmp(field,"accessible")) {
boolean v = lua_toboolean(L, 3);
if (cnum < FIRSTSUPERCOLOR && v != skincolors[cnum].accessible)
CONS_Alert(CONS_WARNING, "skincolors[] index %d is a standard color; accessibility changes are prohibited.", cnum);
CONS_Alert(CONS_WARNING, "skincolors[] index %d is a standard color; accessibility changes are prohibited.\n", cnum);
else
info->accessible = v;
} else
......@@ -1914,6 +2187,8 @@ int LUA_InfoLib(lua_State *L)
LUA_RegisterUserdataMetatable(L, META_COLORRAMP, colorramp_get, colorramp_set, colorramp_len);
LUA_RegisterUserdataMetatable(L, META_SFXINFO, sfxinfo_get, sfxinfo_set, sfxinfo_num);
LUA_RegisterUserdataMetatable(L, META_SPRITEINFO, spriteinfo_get, spriteinfo_set, spriteinfo_num);
LUA_RegisterUserdataMetatable(L, META_SPRITEINFOFRAMELIST, framelist_get, framelist_set, framelist_num);
LUA_RegisterUserdataMetatable(L, META_SPRITEINFOFRAME, sprinfoframe_get, sprinfoframe_set, NULL);
LUA_RegisterUserdataMetatable(L, META_PIVOTLIST, pivotlist_get, pivotlist_set, pivotlist_num);
LUA_RegisterUserdataMetatable(L, META_FRAMEPIVOT, framepivot_get, framepivot_set, framepivot_num);
LUA_RegisterUserdataMetatable(L, META_LUABANKS, lib_getluabanks, lib_setluabanks, lib_luabankslen);
......
......@@ -10,16 +10,19 @@
/// \brief input library for Lua scripting
#include "doomdef.h"
#include "doomstat.h"
#include "fastcmp.h"
#include "g_input.h"
#include "g_game.h"
#include "hu_stuff.h"
#include "i_system.h"
#include "console.h"
#include "lua_script.h"
#include "lua_libs.h"
boolean mousegrabbedbylua = true;
boolean textinputmodeenabledbylua = false;
boolean ignoregameinputs = false;
///////////////
......@@ -129,6 +132,20 @@ static int lib_getCursorPosition(lua_State *L)
return 2;
}
static int lib_setTextInputMode(lua_State *L)
{
textinputmodeenabledbylua = luaL_checkboolean(L, 1);
if (!(menuactive || CON_Ready() || chat_on))
I_SetTextInputMode(textinputmodeenabledbylua);
return 0;
}
static int lib_getTextInputMode(lua_State *L)
{
lua_pushinteger(L, textinputmodeenabledbylua);
return 1;
}
static luaL_Reg lib[] = {
{"gameControlDown", lib_gameControlDown},
{"gameControl2Down", lib_gameControl2Down},
......@@ -143,6 +160,8 @@ static luaL_Reg lib[] = {
{"getMouseGrab", lib_getMouseGrab},
{"setMouseGrab", lib_setMouseGrab},
{"getCursorPosition", lib_getCursorPosition},
{"setTextInputMode", lib_setTextInputMode},
{"getTextInputMode", lib_getTextInputMode},
{NULL, NULL}
};
......@@ -220,6 +239,28 @@ static int lib_lenGameKeyDown(lua_State *L)
return 1;
}
////////////////
// TEXT EVENT //
////////////////
static int textevent_get(lua_State *L)
{
event_t *event = *((event_t **)luaL_checkudata(L, 1, META_TEXTEVENT));
const char *field = luaL_checkstring(L, 2);
I_Assert(event != NULL);
if (fastcmp(field,"text"))
{
char s[2] = { event->key, 0 };
lua_pushstring(L, s);
}
else
return luaL_error(L, "textevent_t has no field named %s", field);
return 1;
}
///////////////
// KEY EVENT //
///////////////
......@@ -285,6 +326,7 @@ static int mouse_num(lua_State *L)
int LUA_InputLib(lua_State *L)
{
LUA_RegisterUserdataMetatable(L, META_TEXTEVENT, textevent_get, NULL, NULL);
LUA_RegisterUserdataMetatable(L, META_KEYEVENT, keyevent_get, NULL, NULL);
LUA_RegisterUserdataMetatable(L, META_MOUSE, mouse_get, NULL, mouse_num);
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2024-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file lua_interceptlib.c
/// \brief intercept library for Lua scripting
#include "doomdef.h"
#include "fastcmp.h"
#include "p_local.h"
#include "lua_script.h"
#include "lua_libs.h"
#include "lua_hud.h" // hud_running errors
#define NOHUD if (hud_running)\
return luaL_error(L, "HUD rendering code should not call this function!");
enum intercept_e {
intercept_valid = 0,
intercept_frac,
intercept_thing,
intercept_line
};
static const char *const intercept_opt[] = {
"valid",
"frac",
"thing",
"line",
NULL};
static int intercept_fields_ref = LUA_NOREF;
static boolean Lua_PathTraverser(intercept_t *in)
{
boolean traverse = false;
I_Assert(in != NULL);
lua_settop(gL, 6);
lua_pushcfunction(gL, LUA_GetErrorMessage);
I_Assert(lua_isfunction(gL, -2));
lua_pushvalue(gL, -2);
LUA_PushUserdata(gL, in, META_INTERCEPT);
LUA_Call(gL, 1, 1, -3);
traverse = lua_toboolean(gL, -1);
lua_pop(gL, 1);
return !traverse; // Stay consistent with the MobjMoveCollide hook
}
static int intercept_get(lua_State *L)
{
intercept_t *in = *((intercept_t **)luaL_checkudata(L, 1, META_INTERCEPT));
enum intercept_e field = Lua_optoption(L, 2, intercept_valid, intercept_fields_ref);
if (!in)
{
if (field == intercept_valid) {
lua_pushboolean(L, 0);
return 1;
}
return luaL_error(L, "accessed intercept_t doesn't exist anymore.");
}
switch(field)
{
case intercept_valid: // valid
lua_pushboolean(L, 1);
return 1;
case intercept_frac:
lua_pushfixed(L, in->frac);
return 1;
case intercept_thing:
if (in->isaline)
return 0;
LUA_PushUserdata(L, in->d.thing, META_MOBJ);
return 1;
case intercept_line:
if (!in->isaline)
return 0;
LUA_PushUserdata(L, in->d.line, META_LINE);
return 1;
}
return 0;
}
static int lib_pPathTraverse(lua_State *L)
{
fixed_t px1 = luaL_checkfixed(L, 1);
fixed_t py1 = luaL_checkfixed(L, 2);
fixed_t px2 = luaL_checkfixed(L, 3);
fixed_t py2 = luaL_checkfixed(L, 4);
INT32 flags = (INT32)luaL_checkinteger(L, 5);
luaL_checktype(L, 6, LUA_TFUNCTION);
NOHUD
INLEVEL
lua_pushboolean(L, P_PathTraverse(px1, py1, px2, py2, flags, Lua_PathTraverser));
return 1;
}
int LUA_InterceptLib(lua_State *L)
{
LUA_RegisterUserdataMetatable(L, META_INTERCEPT, intercept_get, NULL, NULL);
intercept_fields_ref = Lua_CreateFieldTable(L, intercept_opt);
lua_register(L, "P_PathTraverse", lib_pPathTraverse);
return 0;
}
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2023 by Sonic Team Junior.
// Copyright (C) 2012-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -13,6 +13,7 @@
extern lua_State *gL;
extern boolean mousegrabbedbylua;
extern boolean textinputmodeenabledbylua;
extern boolean ignoregameinputs;
#define MUTABLE_TAGS
......@@ -29,6 +30,8 @@ extern boolean ignoregameinputs;
#define META_SKINCOLOR "SKINCOLOR_T*"
#define META_COLORRAMP "SKINCOLOR_T*RAMP"
#define META_SPRITEINFO "SPRITEINFO_T*"
#define META_SPRITEINFOFRAMELIST "SPRITEINFOFRAME_T[]"
#define META_SPRITEINFOFRAME "SPRITEINFOFRAME_T*"
#define META_PIVOTLIST "SPRITEFRAMEPIVOT_T[]"
#define META_FRAMEPIVOT "SPRITEFRAMEPIVOT_T*"
......@@ -42,8 +45,11 @@ extern boolean ignoregameinputs;
#define META_SKIN "SKIN_T*"
#define META_POWERS "PLAYER_T*POWERS"
#define META_SOUNDSID "SKIN_T*SOUNDSID"
#define META_SKINSPRITES "SKIN_T*SPRITES"
#define META_SKINSPRITESLIST "SKIN_T*SPRITES[]"
#define META_SKINSPRITES "SKIN_T*SKINSPRITES"
#define META_SKINSPRITESLIST "SKIN_T*SKINSPRITES[]"
#define META_SKINSPRITESCOMPAT "SKIN_T*SPRITES" // TODO: 2.3: Delete
#define META_MUSICDEF "MUSICDEF_T*"
#define META_VERTEX "VERTEX_T*"
#define META_LINE "LINE_T*"
......@@ -51,13 +57,7 @@ extern boolean ignoregameinputs;
#define META_SUBSECTOR "SUBSECTOR_T*"
#define META_SECTOR "SECTOR_T*"
#define META_FFLOOR "FFLOOR_T*"
#ifdef HAVE_LUA_SEGS
#define META_SEG "SEG_T*"
#define META_NODE "NODE_T*"
#endif
#define META_SLOPE "PSLOPE_T*"
#define META_VECTOR2 "VECTOR2_T"
#define META_VECTOR3 "VECTOR3_T"
#define META_MAPHEADER "MAPHEADER_T*"
#define META_POLYOBJ "POLYOBJ_T*"
......@@ -68,17 +68,17 @@ extern boolean ignoregameinputs;
#ifdef MUTABLE_TAGS
#define META_SECTORTAGLIST "sector_t.taglist"
#endif
#define META_SECTORCUSTOMARGS "SECTOR_T*CUSTOMARGS"
#define META_SIDENUM "LINE_T*SIDENUM"
#define META_LINEARGS "LINE_T*ARGS"
#define META_LINESTRINGARGS "LINE_T*STRINGARGS"
#define META_LINECUSTOMARGS "LINE_T*CUSTOMARGS"
#define META_SIDECUSTOMARGS "SIDE_T*CUSTOMARGS"
#define META_THINGARGS "MAPTHING_T*ARGS"
#define META_THINGSTRINGARGS "MAPTHING_T*STRINGARGS"
#define META_THINGCUSTOMARGS "MAPTHING_T*CUSTOMARGS"
#define META_POLYOBJVERTICES "POLYOBJ_T*VERTICES"
#define META_POLYOBJLINES "POLYOBJ_T*LINES"
#ifdef HAVE_LUA_SEGS
#define META_NODEBBOX "NODE_T*BBOX"
#define META_NODECHILDREN "NODE_T*CHILDREN"
#endif
#define META_BBOX "BOUNDING_BOX"
......@@ -92,9 +92,17 @@ extern boolean ignoregameinputs;
#define META_LUABANKS "LUABANKS[]*"
#define META_TEXTEVENT "TEXTEVENT_T*"
#define META_KEYEVENT "KEYEVENT_T*"
#define META_MOUSE "MOUSE_T*"
#define META_INTERCEPT "INTERCEPT_T*"
#define META_VECTOR2 "VECTOR2_T"
#define META_VECTOR3 "VECTOR3_T"
#define META_MATRIX "MATRIX_T"
#define META_QUATERNION "QUATERNION_T"
boolean luaL_checkboolean(lua_State *L, int narg);
int LUA_EnumLib(lua_State *L);
......@@ -115,3 +123,7 @@ int LUA_BlockmapLib(lua_State *L);
int LUA_HudLib(lua_State *L);
int LUA_ColorLib(lua_State *L);
int LUA_InputLib(lua_State *L);
int LUA_InterceptLib(lua_State *L);
int LUA_MatrixLib(lua_State *L);
int LUA_QuaternionLib(lua_State *L);
int LUA_VectorLib(lua_State *L);
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2023 by Sonic Team Junior.
// Copyright (C) 2012-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -69,6 +69,7 @@ enum sector_e {
sector_triggerer,
sector_friction,
sector_gravity,
sector_customargs
};
static const char *const sector_opt[] = {
......@@ -112,6 +113,7 @@ static const char *const sector_opt[] = {
"triggerer",
"friction",
"gravity",
"customargs",
NULL};
static int sector_fields_ref = LUA_NOREF;
......@@ -147,6 +149,7 @@ enum line_e {
line_taglist,
line_args,
line_stringargs,
line_customargs,
line_sidenum,
line_frontside,
line_backside,
......@@ -173,6 +176,7 @@ static const char *const line_opt[] = {
"taglist",
"args",
"stringargs",
"customargs",
"sidenum",
"frontside",
"backside",
......@@ -213,7 +217,16 @@ enum side_e {
side_sector,
side_special,
side_repeatcnt,
side_text
side_light,
side_light_top,
side_light_mid,
side_light_bottom,
side_lightabsolute,
side_lightabsolute_top,
side_lightabsolute_mid,
side_lightabsolute_bottom,
side_text,
side_customargs
};
static const char *const side_opt[] = {
......@@ -241,7 +254,16 @@ static const char *const side_opt[] = {
"sector",
"special",
"repeatcnt",
"light",
"light_top",
"light_mid",
"light_bottom",
"lightabsolute",
"lightabsolute_top",
"lightabsolute_mid",
"lightabsolute_bottom",
"text",
"customargs",
NULL};
static int side_fields_ref = LUA_NOREF;
......@@ -338,72 +360,6 @@ static const char *const ffloor_opt[] = {
static int ffloor_fields_ref = LUA_NOREF;
#ifdef HAVE_LUA_SEGS
enum seg_e {
seg_valid = 0,
seg_v1,
seg_v2,
seg_side,
seg_offset,
seg_angle,
seg_sidedef,
seg_linedef,
seg_frontsector,
seg_backsector,
seg_polyseg
};
static const char *const seg_opt[] = {
"valid",
"v1",
"v2",
"side",
"offset",
"angle",
"sidedef",
"linedef",
"frontsector",
"backsector",
"polyseg",
NULL};
static int seg_fields_ref = LUA_NOREF;
enum node_e {
node_valid = 0,
node_x,
node_y,
node_dx,
node_dy,
node_bbox,
node_children,
};
static const char *const node_opt[] = {
"valid",
"x",
"y",
"dx",
"dy",
"bbox",
"children",
NULL};
static int node_fields_ref = LUA_NOREF;
enum nodechild_e {
nodechild_valid = 0,
nodechild_right,
nodechild_left,
};
static const char *const nodechild_opt[] = {
"valid",
"right",
"left",
NULL};
#endif
enum bbox_e {
bbox_valid = 0,
bbox_top,
......@@ -444,19 +400,6 @@ static const char *const slope_opt[] = {
static int slope_fields_ref = LUA_NOREF;
// shared by both vector2_t and vector3_t
enum vector_e {
vector_x = 0,
vector_y,
vector_z
};
static const char *const vector_opt[] = {
"x",
"y",
"z",
NULL};
static const char *const array_opt[] ={"iterate",NULL};
static const char *const valid_opt[] ={"valid",NULL};
......@@ -658,6 +601,73 @@ static int sectorlines_num(lua_State *L)
return 1;
}
//////////////////
// customargs_t //
//////////////////
FUNCINLINE static ATTRINLINE int customargs_get(lua_State* L, const char* meta)
{
customargs_t *args = *((customargs_t**)luaL_checkudata(L, 1, meta));
const char* field = luaL_checkstring(L, 2);
if (args == NULL) {
lua_pushnil(L);
return 1;
}
customargs_t* current = args;
while (current != NULL)
{
if (!strcmp(current->name, field))
{
switch (current->type)
{
case UDMF_TYPE_STRING:
lua_pushstring(L, current->value.vstring);
break;
case UDMF_TYPE_NUMERIC:
lua_pushinteger(L, current->value.vint);
break;
case UDMF_TYPE_FIXED:
lua_pushfixed(L, current->value.vfloat);
break;
case UDMF_TYPE_BOOLEAN:
lua_pushboolean(L, current->value.vbool);
break;
default:
lua_pushnil(L);
}
return 1;
}
current = current->next;
}
lua_pushnil(L);
return 1;
}
static int sectorcustomargs_get(lua_State* L)
{
return customargs_get(L, META_SECTORCUSTOMARGS);
}
static int sidecustomargs_get(lua_State* L)
{
return customargs_get(L, META_SIDECUSTOMARGS);
}
static int linecustomargs_get(lua_State* L)
{
return customargs_get(L, META_LINECUSTOMARGS);
}
static int thingcustomargs_get(lua_State* L)
{
return customargs_get(L, META_THINGCUSTOMARGS);
}
//////////////
// sector_t //
//////////////
......@@ -819,6 +829,9 @@ static int sector_get(lua_State *L)
case sector_gravity: // gravity
lua_pushfixed(L, sector->gravity);
return 1;
case sector_customargs:
LUA_PushUserdata(L, sector->customargs, META_SECTORCUSTOMARGS);
return 1;
}
return 0;
}
......@@ -1114,6 +1127,9 @@ static int line_get(lua_State *L)
case line_stringargs:
LUA_PushUserdata(L, line->stringargs, META_LINESTRINGARGS);
return 1;
case line_customargs:
LUA_PushUserdata(L, line->customargs, META_LINECUSTOMARGS);
return 1;
case line_sidenum:
LUA_PushUserdata(L, line->sidenum, META_SIDENUM);
return 1;
......@@ -1311,6 +1327,33 @@ static int side_get(lua_State *L)
case side_repeatcnt:
lua_pushinteger(L, side->repeatcnt);
return 1;
case side_light:
lua_pushinteger(L, side->light);
return 1;
case side_light_top:
lua_pushinteger(L, side->light_top);
return 1;
case side_light_mid:
lua_pushinteger(L, side->light_mid);
return 1;
case side_light_bottom:
lua_pushinteger(L, side->light_bottom);
return 1;
case side_lightabsolute:
lua_pushboolean(L, side->lightabsolute);
return 1;
case side_lightabsolute_top:
lua_pushboolean(L, side->lightabsolute_top);
return 1;
case side_lightabsolute_mid:
lua_pushboolean(L, side->lightabsolute_mid);
return 1;
case side_lightabsolute_bottom:
lua_pushboolean(L, side->lightabsolute_bottom);
return 1;
case side_customargs:
LUA_PushUserdata(L, side->customargs, META_SIDECUSTOMARGS);
return 1;
// TODO: 2.3: Delete
case side_text:
{
......@@ -1330,6 +1373,7 @@ static int side_get(lua_State *L)
return 1;
}
}
return 0;
}
......@@ -1413,6 +1457,30 @@ static int side_set(lua_State *L)
case side_repeatcnt:
side->repeatcnt = luaL_checkinteger(L, 3);
break;
case side_light:
side->light = luaL_checkinteger(L, 3);
break;
case side_light_top:
side->light_top = luaL_checkinteger(L, 3);
break;
case side_light_mid:
side->light_mid = luaL_checkinteger(L, 3);
break;
case side_light_bottom:
side->light_bottom = luaL_checkinteger(L, 3);
break;
case side_lightabsolute:
side->lightabsolute = luaL_checkboolean(L, 3);
break;
case side_lightabsolute_top:
side->lightabsolute_top = luaL_checkboolean(L, 3);
break;
case side_lightabsolute_mid:
side->lightabsolute_mid = luaL_checkboolean(L, 3);
break;
case side_lightabsolute_bottom:
side->lightabsolute_bottom = luaL_checkboolean(L, 3);
break;
}
return 0;
}
......@@ -1476,246 +1544,6 @@ static int vertex_num(lua_State *L)
return 1;
}
#ifdef HAVE_LUA_SEGS
///////////
// seg_t //
///////////
static int seg_get(lua_State *L)
{
seg_t *seg = *((seg_t **)luaL_checkudata(L, 1, META_SEG));
enum seg_e field = Lua_optoption(L, 2, seg_valid, seg_fields_ref);
if (!seg)
{
if (field == seg_valid) {
lua_pushboolean(L, 0);
return 1;
}
return luaL_error(L, "accessed seg_t doesn't exist anymore.");
}
switch(field)
{
case seg_valid: // valid
lua_pushboolean(L, 1);
return 1;
case seg_v1:
LUA_PushUserdata(L, seg->v1, META_VERTEX);
return 1;
case seg_v2:
LUA_PushUserdata(L, seg->v2, META_VERTEX);
return 1;
case seg_side:
lua_pushinteger(L, seg->side);
return 1;
case seg_offset:
lua_pushfixed(L, seg->offset);
return 1;
case seg_angle:
lua_pushangle(L, seg->angle);
return 1;
case seg_sidedef:
LUA_PushUserdata(L, seg->sidedef, META_SIDE);
return 1;
case seg_linedef:
LUA_PushUserdata(L, seg->linedef, META_LINE);
return 1;
case seg_frontsector:
LUA_PushUserdata(L, seg->frontsector, META_SECTOR);
return 1;
case seg_backsector:
LUA_PushUserdata(L, seg->backsector, META_SECTOR);
return 1;
case seg_polyseg:
LUA_PushUserdata(L, seg->polyseg, META_POLYOBJ);
return 1;
}
return 0;
}
static int seg_num(lua_State *L)
{
seg_t *seg = *((seg_t **)luaL_checkudata(L, 1, META_SEG));
lua_pushinteger(L, seg-segs);
return 1;
}
////////////
// node_t //
////////////
static int node_get(lua_State *L)
{
node_t *node = *((node_t **)luaL_checkudata(L, 1, META_NODE));
enum node_e field = Lua_optoption(L, 2, node_valid, node_fields_ref);
if (!node)
{
if (field == node_valid) {
lua_pushboolean(L, 0);
return 1;
}
return luaL_error(L, "accessed node_t doesn't exist anymore.");
}
switch(field)
{
case node_valid: // valid
lua_pushboolean(L, 1);
return 1;
case node_x:
lua_pushfixed(L, node->x);
return 1;
case node_y:
lua_pushfixed(L, node->y);
return 1;
case node_dx:
lua_pushfixed(L, node->x);
return 1;
case node_dy:
lua_pushfixed(L, node->x);
return 1;
case node_bbox:
LUA_PushUserdata(L, node->bbox, META_NODEBBOX);
return 1;
case node_children:
LUA_PushUserdata(L, node->children, META_NODECHILDREN);
return 1;
}
return 0;
}
static int node_num(lua_State *L)
{
node_t *node = *((node_t **)luaL_checkudata(L, 1, META_NODE));
lua_pushinteger(L, node-nodes);
return 1;
}
///////////////
// node.bbox //
///////////////
/*
// node.bbox[i][j]: i = 0 or 1, j = 0 1 2 or 3
// NOTE: 2D arrays are NOT double pointers,
// the second bbox will be directly after the first in memory (hence the way the bbox is pushed here)
// this function handles the [i] part, bbox_get handles the [j] part
static int nodebbox_get(lua_State *L)
{
fixed_t *bbox = *((fixed_t **)luaL_checkudata(L, 1, META_NODEBBOX));
int i;
lua_settop(L, 2);
if (!lua_isnumber(L, 2))
{
int field = luaL_checkoption(L, 2, NULL, valid_opt);
if (!bbox)
{
if (field == 0) {
lua_pushboolean(L, 0);
return 1;
}
return luaL_error(L, "accessed node_t doesn't exist anymore.");
} else if (field == 0) {
lua_pushboolean(L, 1);
return 1;
}
}
i = lua_tointeger(L, 2);
if (i < 0 || i > 1)
return 0;
LUA_PushUserdata(L, bbox + i*4*sizeof(fixed_t), META_BBOX);
return 1;
}
*/
static int nodebbox_call(lua_State *L)
{
fixed_t *bbox = *((fixed_t **)luaL_checkudata(L, 1, META_NODEBBOX));
int i, j;
int n = lua_gettop(L);
if (!bbox)
return luaL_error(L, "accessed node bbox doesn't exist anymore.");
if (n < 3)
return luaL_error(L, "arguments 2 and/or 3 not given (expected node.bbox(child, coord))");
// get child
if (!lua_isnumber(L, 2)) {
enum nodechild_e field = luaL_checkoption(L, 2, nodechild_opt[0], nodechild_opt);
switch (field) {
case nodechild_right: i = 0; break;
case nodechild_left: i = 1; break;
default:
return luaL_error(L, "invalid node child \"%s\".", lua_tostring(L, 2));
}
}
else {
i = lua_tointeger(L, 2);
if (i < 0 || i > 1)
return 0;
}
// get bbox coord
if (!lua_isnumber(L, 3)) {
enum bbox_e field = luaL_checkoption(L, 3, bbox_opt[0], bbox_opt);
switch (field) {
case bbox_top: j = BOXTOP; break;
case bbox_bottom: j = BOXBOTTOM; break;
case bbox_left: j = BOXLEFT; break;
case bbox_right: j = BOXRIGHT; break;
default:
return luaL_error(L, "invalid bbox coordinate \"%s\".", lua_tostring(L, 3));
}
}
else {
j = lua_tointeger(L, 3);
if (j < 0 || j > 3)
return 0;
}
lua_pushinteger(L, bbox[i*4 + j]);
return 1;
}
/////////////////////
// node.children[] //
/////////////////////
// node.children[i]: i = 0 or 1
static int nodechildren_get(lua_State *L)
{
UINT16 *children = *((UINT16 **)luaL_checkudata(L, 1, META_NODECHILDREN));
int i;
lua_settop(L, 2);
if (!lua_isnumber(L, 2))
{
enum nodechild_e field = luaL_checkoption(L, 2, nodechild_opt[0], nodechild_opt);
if (!children)
{
if (field == nodechild_valid) {
lua_pushboolean(L, 0);
return 1;
}
return luaL_error(L, "accessed node_t doesn't exist anymore.");
} else if (field == nodechild_valid) {
lua_pushboolean(L, 1);
return 1;
} else switch (field) {
case nodechild_right: i = 0; break;
case nodechild_left: i = 1; break;
default: return 0;
}
}
else {
i = lua_tointeger(L, 2);
if (i < 0 || i > 1)
return 0;
}
lua_pushinteger(L, children[i]);
return 1;
}
#endif
//////////
// bbox //
//////////
......@@ -1997,113 +1825,6 @@ static int lib_numvertexes(lua_State *L)
return 1;
}
#ifdef HAVE_LUA_SEGS
////////////
// segs[] //
////////////
static int lib_iterateSegs(lua_State *L)
{
size_t i = 0;
INLEVEL
if (lua_gettop(L) < 2)
return luaL_error(L, "Don't call segs.iterate() directly, use it as 'for seg in segs.iterate do <block> end'.");
lua_settop(L, 2);
lua_remove(L, 1); // state is unused.
if (!lua_isnil(L, 1))
i = (size_t)(*((seg_t **)luaL_checkudata(L, 1, META_SEG)) - segs)+1;
if (i < numsegs)
{
LUA_PushUserdata(L, &segs[i], META_SEG);
return 1;
}
return 0;
}
static int lib_getSeg(lua_State *L)
{
int field;
INLEVEL
lua_settop(L, 2);
lua_remove(L, 1); // dummy userdata table is unused.
if (lua_isnumber(L, 1))
{
size_t i = lua_tointeger(L, 1);
if (i >= numsegs)
return 0;
LUA_PushUserdata(L, &segs[i], META_SEG);
return 1;
}
field = luaL_checkoption(L, 1, NULL, array_opt);
switch(field)
{
case 0: // iterate
lua_pushcfunction(L, lib_iterateSegs);
return 1;
}
return 0;
}
static int lib_numsegs(lua_State *L)
{
lua_pushinteger(L, numsegs);
return 1;
}
/////////////
// nodes[] //
/////////////
static int lib_iterateNodes(lua_State *L)
{
size_t i = 0;
INLEVEL
if (lua_gettop(L) < 2)
return luaL_error(L, "Don't call nodes.iterate() directly, use it as 'for node in nodes.iterate do <block> end'.");
lua_settop(L, 2);
lua_remove(L, 1); // state is unused.
if (!lua_isnil(L, 1))
i = (size_t)(*((node_t **)luaL_checkudata(L, 1, META_NODE)) - nodes)+1;
if (i < numsegs)
{
LUA_PushUserdata(L, &nodes[i], META_NODE);
return 1;
}
return 0;
}
static int lib_getNode(lua_State *L)
{
int field;
INLEVEL
lua_settop(L, 2);
lua_remove(L, 1); // dummy userdata table is unused.
if (lua_isnumber(L, 1))
{
size_t i = lua_tointeger(L, 1);
if (i >= numnodes)
return 0;
LUA_PushUserdata(L, &nodes[i], META_NODE);
return 1;
}
field = luaL_checkoption(L, 1, NULL, array_opt);
switch(field)
{
case 0: // iterate
lua_pushcfunction(L, lib_iterateNodes);
return 1;
}
return 0;
}
static int lib_numnodes(lua_State *L)
{
lua_pushinteger(L, numnodes);
return 1;
}
#endif
//////////////
// ffloor_t //
//////////////
......@@ -2648,47 +2369,6 @@ static int slope_set(lua_State *L)
return 0;
}
///////////////
// vector*_t //
///////////////
static int vector2_get(lua_State *L)
{
vector2_t *vec = *((vector2_t **)luaL_checkudata(L, 1, META_VECTOR2));
enum vector_e field = luaL_checkoption(L, 2, vector_opt[0], vector_opt);
if (!vec)
return luaL_error(L, "accessed vector2_t doesn't exist anymore.");
switch(field)
{
case vector_x: lua_pushfixed(L, vec->x); return 1;
case vector_y: lua_pushfixed(L, vec->y); return 1;
default: break;
}
return 0;
}
static int vector3_get(lua_State *L)
{
vector3_t *vec = *((vector3_t **)luaL_checkudata(L, 1, META_VECTOR3));
enum vector_e field = luaL_checkoption(L, 2, vector_opt[0], vector_opt);
if (!vec)
return luaL_error(L, "accessed vector3_t doesn't exist anymore.");
switch(field)
{
case vector_x: lua_pushfixed(L, vec->x); return 1;
case vector_y: lua_pushfixed(L, vec->y); return 1;
case vector_z: lua_pushfixed(L, vec->z); return 1;
default: break;
}
return 0;
}
/////////////////////
// mapheaderinfo[] //
/////////////////////
......@@ -2997,18 +2677,20 @@ int LUA_MapLib(lua_State *L)
LUA_RegisterUserdataMetatable(L, META_SECTORLINES, sectorlines_get, NULL, sectorlines_num);
LUA_RegisterUserdataMetatable(L, META_SECTOR, sector_get, sector_set, sector_num);
LUA_RegisterUserdataMetatable(L, META_SUBSECTOR, subsector_get, NULL, subsector_num);
LUA_RegisterUserdataMetatable(L, META_SECTORCUSTOMARGS, sectorcustomargs_get, NULL, NULL);
LUA_RegisterUserdataMetatable(L, META_LINE, line_get, NULL, line_num);
LUA_RegisterUserdataMetatable(L, META_LINEARGS, lineargs_get, NULL, lineargs_len);
LUA_RegisterUserdataMetatable(L, META_LINESTRINGARGS, linestringargs_get, NULL, linestringargs_len);
LUA_RegisterUserdataMetatable(L, META_LINECUSTOMARGS, linecustomargs_get, NULL, NULL);
LUA_RegisterUserdataMetatable(L, META_SIDECUSTOMARGS, sidecustomargs_get, NULL, NULL);
LUA_RegisterUserdataMetatable(L, META_SIDENUM, sidenum_get, NULL, NULL);
LUA_RegisterUserdataMetatable(L, META_SIDE, side_get, side_set, side_num);
LUA_RegisterUserdataMetatable(L, META_VERTEX, vertex_get, NULL, vertex_num);
LUA_RegisterUserdataMetatable(L, META_FFLOOR, ffloor_get, ffloor_set, NULL);
LUA_RegisterUserdataMetatable(L, META_BBOX, bbox_get, NULL, NULL);
LUA_RegisterUserdataMetatable(L, META_SLOPE, slope_get, slope_set, NULL);
LUA_RegisterUserdataMetatable(L, META_VECTOR2, vector2_get, NULL, NULL);
LUA_RegisterUserdataMetatable(L, META_VECTOR3, vector3_get, NULL, NULL);
LUA_RegisterUserdataMetatable(L, META_MAPHEADER, mapheaderinfo_get, NULL, NULL);
LUA_RegisterUserdataMetatable(L, META_THINGCUSTOMARGS, thingcustomargs_get, NULL, NULL);
sector_fields_ref = Lua_CreateFieldTable(L, sector_opt);
subsector_fields_ref = Lua_CreateFieldTable(L, subsector_opt);
......@@ -3040,22 +2722,5 @@ int LUA_MapLib(lua_State *L)
&numlines, &lines,
sizeof (line_t), META_LINE);
#ifdef HAVE_LUA_SEGS
LUA_RegisterUserdataMetatable(L, META_SEG, seg_get, NULL, seg_num);
LUA_RegisterUserdataMetatable(L, META_NODE, node_get, NULL, node_num);
LUA_RegisterUserdataMetatable(L, META_NODECHILDREN, nodechildren_get, NULL, NULL);
seg_fields_ref = Lua_CreateFieldTable(L, seg_opt);
node_fields_ref = Lua_CreateFieldTable(L, node_opt);
luaL_newmetatable(L, META_NODEBBOX);
//LUA_SetCFunctionField(L, "__index", nodebbox_get);
LUA_SetCFunctionField(L, "__call", nodebbox_call);
lua_pop(L, 1);
LUA_RegisterGlobalUserdata(L, "segs", lib_getSeg, NULL, lib_numsegs);
LUA_RegisterGlobalUserdata(L, "nodes", lib_getNode, NULL, lib_numnodes);
#endif
return 0;
}
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2025 by LJ Sonic
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file lua_matrixlib.c
/// \brief matrix library for Lua scripting
#include "matrix.h"
#include "vector3d.h"
#include "lua_script.h"
#include "lua_libs.h"
static vector3_t *GetVector(lua_State *L, int index)
{
vector3_t *vec = lua_touserdata(L, index);
if (!vec)
return NULL;
if (!lua_getmetatable(L, index))
return NULL;
lua_getfield(L, LUA_REGISTRYINDEX, META_VECTOR3);
if (!lua_rawequal(L, -1, -2))
return NULL;
lua_pop(L, 2);
return vec;
}
static void GetVectorOrXYZ(lua_State *L, int index, vector3_t *result)
{
vector3_t *vec = GetVector(L, index);
if (vec)
{
*result = *vec;
}
else
{
result->x = luaL_checkfixed(L, 1);
result->y = luaL_checkfixed(L, 2);
result->z = luaL_checkfixed(L, 3);
}
}
static matrix_t *NewMatrix(lua_State *L)
{
matrix_t *mat = lua_newuserdata(L, sizeof(*mat));
luaL_getmetatable(L, META_MATRIX);
lua_setmetatable(L, -2);
return mat;
}
////////////////////
// STATIC MEMBERS //
////////////////////
static int matrix_new(lua_State *L)
{
Matrix_SetIdentity(NewMatrix(L));
return 1;
}
static int matrix_translation(lua_State *L)
{
vector3_t translation;
GetVectorOrXYZ(L, 1, &translation);
Matrix_SetTranslation(NewMatrix(L), translation.x, translation.y, translation.z);
return 1;
}
static int matrix_scaling(lua_State *L)
{
vector3_t scaling;
GetVectorOrXYZ(L, 1, &scaling);
Matrix_SetScaling(NewMatrix(L), scaling.x, scaling.y, scaling.z);
return 1;
}
static luaL_Reg matrix[] = {
{"new", matrix_new},
{"translation", matrix_translation},
{"scaling", matrix_scaling},
{NULL, NULL}
};
/////////////
// MEMBERS //
/////////////
enum matrixfield_e {
matrixfield_clone = 0,
matrixfield_getvalue,
matrixfield_setvalue,
matrixfield_mulXYZ,
};
static const char *const matrixfield_opt[] = {
"clone",
"get",
"set",
"mulXYZ",
NULL};
static int matrix_clone(lua_State *L)
{
matrix_t *mat = luaL_checkudata(L, 1, META_MATRIX);
Matrix_Copy(NewMatrix(L), mat);
return 1;
}
static int matrix_getvalue(lua_State *L)
{
matrix_t *mat = luaL_checkudata(L, 1, META_MATRIX);
INT32 row = luaL_checkinteger(L, 2);
INT32 col = luaL_checkinteger(L, 3);
if (row < 1 || row > 4)
return luaL_error(L, "matrix row %d out of range (1 - 4)", row);
if (col < 1 || col > 4)
return luaL_error(L, "matrix column %d out of range (1 - 4)", col);
lua_pushfixed(L, mat->matrix[row - 1][col - 1]);
return 1;
}
static int matrix_setvalue(lua_State *L)
{
matrix_t *mat = luaL_checkudata(L, 1, META_MATRIX);
INT32 row = luaL_checkinteger(L, 2);
INT32 col = luaL_checkinteger(L, 3);
fixed_t value = luaL_checkfixed(L, 4);
if (row < 1 || row > 4)
return luaL_error(L, "matrix row %d out of range (1 - 4)", row);
if (col < 1 || col > 4)
return luaL_error(L, "matrix column %d out of range (1 - 4)", col);
mat->matrix[row - 1][col - 1] = value;
return 0;
}
static int matrix_mulXYZ(lua_State *L)
{
matrix_t *mat = luaL_checkudata(L, 1, META_MATRIX);
fixed_t x = luaL_checkfixed(L, 2);
fixed_t y = luaL_checkfixed(L, 3);
fixed_t z = luaL_checkfixed(L, 4);
vector3_t vec;
vector3_t result;
Vector3D_Set(&vec, x, y, z);
Matrix_MulVector(&result, mat, &vec);
lua_pushfixed(L, result.x);
lua_pushfixed(L, result.y);
lua_pushfixed(L, result.z);
return 3;
}
static int matrix_get(lua_State *L)
{
enum matrixfield_e field = luaL_checkoption(L, 2, matrixfield_opt[0], matrixfield_opt);
switch(field)
{
case matrixfield_clone: lua_pushcfunction(L, matrix_clone); return 1;
case matrixfield_getvalue: lua_pushcfunction(L, matrix_getvalue); return 1;
case matrixfield_setvalue: lua_pushcfunction(L, matrix_setvalue); return 1;
case matrixfield_mulXYZ: lua_pushcfunction(L, matrix_mulXYZ); return 1;
default: break;
}
return 0;
}
///////////////
// OPERATORS //
///////////////
static int matrix_mul(lua_State *L)
{
matrix_t *mat1 = luaL_checkudata(L, 1, META_MATRIX);
vector3_t *vec2 = GetVector(L, 2);
if (vec2)
{
vector3_t *result = lua_newuserdata(L, sizeof(*result));
luaL_getmetatable(L, META_VECTOR3);
lua_setmetatable(L, -2);
Matrix_MulVector(result, mat1, vec2);
}
else
{
matrix_t *mat2 = luaL_checkudata(L, 2, META_MATRIX);
Matrix_Mul(NewMatrix(L), mat1, mat2);
}
return 1;
}
int LUA_MatrixLib(lua_State *L)
{
luaL_newmetatable(L, META_MATRIX);
LUA_SetCFunctionField(L, "__index", matrix_get);
LUA_SetCFunctionField(L, "__mul", matrix_mul);
lua_pop(L, 1);
luaL_register(L, "Matrix", matrix);
return 0;
}
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2023 by Sonic Team Junior.
// Copyright (C) 2012-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -69,6 +69,7 @@ enum mobj_e {
mobj_color,
mobj_translation,
mobj_blendmode,
mobj_alpha,
mobj_bnext,
mobj_bprev,
mobj_hnext,
......@@ -150,6 +151,7 @@ static const char *const mobj_opt[] = {
"color",
"translation",
"blendmode",
"alpha",
"bnext",
"bprev",
"hnext",
......@@ -196,12 +198,12 @@ static int mobj_get(lua_State *L)
enum mobj_e field = Lua_optoption(L, 2, -1, mobj_fields_ref);
lua_settop(L, 2);
if (!mo || !ISINLEVEL) {
if (P_MobjWasRemoved(mo) || !ISINLEVEL) {
if (field == mobj_valid) {
lua_pushboolean(L, 0);
return 1;
}
if (!mo) {
if (P_MobjWasRemoved(mo)) {
return LUA_ErrInvalid(L, "mobj_t");
} else
return luaL_error(L, "Do not access an mobj_t field outside a level!");
......@@ -354,6 +356,9 @@ static int mobj_get(lua_State *L)
case mobj_blendmode:
lua_pushinteger(L, mo->blendmode);
break;
case mobj_alpha:
lua_pushfixed(L, mo->alpha);
break;
case mobj_bnext:
if (mo->blocknode && mo->blocknode->bnext) {
LUA_PushUserdata(L, mo->blocknode->bnext->mobj, META_MOBJ);
......@@ -733,6 +738,16 @@ static int mobj_set(lua_State *L)
mo->blendmode = blendmode;
break;
}
case mobj_alpha:
{
fixed_t alpha = luaL_checkfixed(L, 3);
if (alpha < 0)
alpha = 0;
else if (alpha > FRACUNIT)
alpha = FRACUNIT;
mo->alpha = alpha;
break;
}
case mobj_bnext:
return NOSETPOS;
case mobj_bprev:
......@@ -958,6 +973,7 @@ enum mapthing_e {
mapthing_taglist,
mapthing_args,
mapthing_stringargs,
mapthing_customargs,
mapthing_mobj,
};
......@@ -979,6 +995,7 @@ const char *const mapthing_opt[] = {
"taglist",
"args",
"stringargs",
"customargs",
"mobj",
NULL,
};
......@@ -1057,6 +1074,9 @@ static int mapthing_get(lua_State *L)
case mapthing_stringargs:
LUA_PushUserdata(L, mt->stringargs, META_THINGSTRINGARGS);
break;
case mapthing_customargs:
LUA_PushUserdata(L, mt->customargs, META_THINGCUSTOMARGS);
break;
case mapthing_mobj:
LUA_PushUserdata(L, mt->mobj, META_MOBJ);
break;
......