If you have not already been watching the forums lately, you’ll see that there a couple of interested members who want to improve Amulets & Armor. I think this is great and would like to encourage more people to also pitch in. The help has been so inspiring, I’ve officially started making the Community Edition of A&A (as previously promised) and with Lua scripting support. Working the last days on adding Lua has been eye opening … wow … I wish we had this back in 1994 when the project got started.
Everybody Needs a Little Help
Let me first start by thanking PeeWee_RotA on the Amulets & Armor Forum for getting me motivated. PeeWee_RotA has been deligently changing the A&A code in a fork of his own and adding a Ninja and Barbarian class. He’s got throwing daggers working and generally is making everything work just that much better — like making back stabbing worth it. He’s been logging his progress on the forum in the PeeWee’s Mod Fork thread. Feel free to give feedback on the good and the bad.
But PeeWee_RotA is not the only member helping out. Yokai has been looking at ways to spruce up the levels to make everything look much better. His first screen shots look promising, but unfortunately, problems with the Doom Builder texture alignment system is sabotaging his work. My first two attempts to fix the texture alignment (a system different than Doom used) have not solved the problem. Hang in there, Yokai, I’m still working on the problem.
Now You’re Speaking My Language!
It is inspiring to see people working on the game — so I took a break from the OpenGL to start adding something I think will make the modding community very happy — Lua scripting.
I chose Lua because it works. Plain and simple. The makers of Lua made Lua not simple simple to script, but simple to integrate into existing code. And strangely, since Amulets & Armor was written in C and not the most object oriented program in the world, it maps very well to the Lua constructs.
To prove that this could work at all, I started by compiling Lua into a library, adding it to A&A, and running some basic scripts that only printed to the console “Hello, World!” Yep, check, that’s done. But let’s roll this further a bit. At first, I thought it would be really cool to do the monster AI logic in the scripting or maybe some the item effects. Nope. I decided my first step would be — the title screen.
Okay, not the most exciting but it happens at the very beginning of the game while still doing a fair amount of steps. It has to load in graphics, draw them on the screen, do some flashing effects, play a sound or two, check to see if the mouse or keyboard is being pressed, and generally step through the slides until it ends and goes to the game. It is hard to believe this took several hours to implement, but I also had to organize where the Lua scripts are placed and what style to put everything in. I decided to make everything a two step process. Here is what it kind of looks like to play a sound:
(lua) titlescreen: sound.play(3510) -> (lua) sound: aasound.PlayByNumber(3510) -> (C) aasound : SoundPlayByNumber(3510)
To summarize, the title screen routine calls to play the sound to the Lua sound subsystem which calls the internal A&A C code (called via the aasound subsystem). I’m doing this so there is a layer of abstraction that can take things like sound.play(“lightning”) and change “lighting” to the appropriate sound number or resource. By using this format, it also allows the modding community to intercept commands to play sounds within Lua without ever having to modify the C code or do a search and replace throughout the Lua code. It gives a natural place to “hook” into the code.
After several hours of repeating the above and creating Lua translation scripts, I take C code that looks like this:
static E_Boolean IShowScreen( T_byte8 *picName, T_viewPalette pal, T_word32 timeout, E_Boolean showTag, E_Boolean doFlash) { T_bitmap *p_bitmap = NULL; T_resource res; T_word32 time; E_Boolean bypassed = FALSE; static E_Boolean wasGamma = FALSE; DebugRoutine("IShowScreen"); MousePushEventHandler(IShowScreenNextPage); time = TickerGet() + timeout; p_bitmap = (T_bitmap *)PictureLockData(picName, &res); DebugCheck(p_bitmap!=NULL); if (p_bitmap) { ViewSetPalette(pal); ColorStoreDefaultPalette(); if (doFlash) ColorAddGlobal(64, 64, 64); GrDrawBitmap(p_bitmap, 0, 0); PictureUnlockAndUnfind(res); if (showTag) { /* first time show version number */ GrSetCursorPosition(5, 188); GrDrawShadowedText(VERSION_TEXT, 210, 0); } KeyboardBufferOn(); while (time > TickerGet()) { if (KeyboardBufferGet() != 0) { bypassed = TRUE; break; } MouseUpdateEvents(); if (G_mouseClicked == TRUE) { bypassed = TRUE; break; } ColorUpdate(1); if (KeyboardGetScanCode(KEY_SCAN_CODE_ALT) == TRUE) { if (KeyMapGetScan(KEYMAP_GAMMA_CORRECT) == TRUE) { if (wasGamma == FALSE) { MessagePrintf("Gamma level %d", ColorGammaAdjust()); ColorUpdate(1); wasGamma = TRUE; } } else { wasGamma = FALSE; } } else { wasGamma = FALSE; } } } MousePopEventHandler(); DebugEnd(); return bypassed; } void titlescreen(void) { ColorUpdate(1); SoundPlayByNumber(3501, 255); //if (IShowScreen("UI/SCREENS/USA1", VIEW_PALETTE_MAIN_TITLE, 400, FALSE, TRUE) == FALSE) { if (IShowScreen("UI/SCREENS/COMPANY", VIEW_PALETTE_STANDARD, 400, TRUE, TRUE) == FALSE) { ColorFadeTo(0, 0, 0); GrDrawRectangle(0, 0, SCREEN_SIZE_X - 1, SCREEN_SIZE_Y - 1, 0); ColorUpdate(1); ViewSetPalette(VIEW_PALETTE_STANDARD); //if (IShowScreen("UI/SCREENS/USA2", VIEW_PALETTE_MAIN_TITLE, 250, FALSE, FALSE) == FALSE) { if (IShowScreen("UI/SCREENS/PRESENTS", VIEW_PALETTE_STANDARD, 250, TRUE, FALSE) == FALSE) { ColorFadeTo(0, 0, 0); GrDrawRectangle(0, 0, SCREEN_SIZE_X - 1, SCREEN_SIZE_Y - 1, 0); ColorUpdate(1); ViewSetPalette(VIEW_PALETTE_STANDARD); delay(350); if (IShowScreen("UI/SCREENS/BEGIN1", VIEW_PALETTE_STANDARD, 1000, TRUE, FALSE) == FALSE) { ColorFadeTo(0, 0, 0); GrDrawRectangle(0, 0, SCREEN_SIZE_X - 1, SCREEN_SIZE_Y - 1, 0); ColorUpdate(1); } } } }
And turn it into this (Lua code):
function showScreen(picName, pal, timeout, showTag, doFlash) local bypassed = false local wasGamma = false local showScreen_clicked = false stoptime = ticker.get() + timeout bitmap = pics.lockBitmap(picName) if (not bitmap) then print("Missing bitmap "..picName.."!") return; end mouse.pushEventHandler(function (event, x, y, buttons) if (buttons) then showScreen_clicked = true end end ) view.setPalette(pal) color.storeDefaultPalette() if (doFlash) then color.addGlobal({255, 255, 255}) end graphics.drawPic(bitmap, 0, 0) pics.unlockAndUnfind(bitmap) if (showTag) then graphics.setCursor(5, 188) graphics.drawShadowedText(VERSION_TEXT, 210, 0) end -- Check if a key was pressed on the keyboard and stop if pressed keyboard.bufferOn() while (ticker.get() < stoptime) do if (keyboard.bufferGet()) then bypassed = true break end -- Check if a mouse button was pressed and stop if pressed mouse.updateEvents() if (showScreen_clicked) then bypassed = true; break end color.update(1); -- At this point, the darkness can be adjusted with ALT-F11 -- Check if the gamma key is pressed and adjust -- but don't keep adjusting until the key is released if (keyboard.getScanCode(keyboard.scankeys.KEY_SCAN_CODE_ALT)) then if (keymap.getScanCode(keymap.mapping.KEYMAP_GAMMA_CORRECT)) then if (wasGamma ~= true) then color.gammaAdjust() color.update(1) wasGamma = true end else wasGamma = false; end else wasGamma = false; end end mouse.popEventHandler() if (not bypassed) then color.fadeto({0, 0, 0}) end graphics.fillRect(0, 0, display.width()-1, display.height()-1, 0) color.update(1) return bypassed end function titlescreen() print("Time is now "..ticker.get()); sound.play(3501, 1, 1); -- Show the company screen if (showScreen("UI/SCREENS/COMPANY", "standard", 400, true, true)) then return end; -- Show "presents" if (showScreen("UI/SCREENS/PRESENTS", "standard", 250, false, false)) then return end; time.delayMS(350) -- Show A&A Logo screen if (showScreen("UI/SCREENS/BEGIN1", "standard", 1000, true, false)) then return end; end
Not too shabby. And the best part is a person can come along and change the text, run A&A, and no compiling or tools are needed.
A Verison To Rule Them All
And so we return to the concept of a Community Edition of A&A. With the above work in Lua being started and showing me that it can be done, I will declare this the start of the Community Edition. The branch of GitHub is called the Community Edition. The OpenGL version will be merged with this, so don’t think that branch is forgotten.
In the next weeks, I hope to get more of the Lua scripts created to recreate what is in the A&A code. But there is more to come in the form of expanding the Lua scripting. For instance, many of the .FRM (Form) and QUEST.INI files can be changed into Lua scripts and that will make them easier to read and modify. I’m sure many players would love to make different UI layouts of the banner — something that takes little to no programming knowledge.
As usual, let me know what you think and stay tuned for more.
Good to hear you’ve been busy with stuff! Sounds like you’ve been making a lot more progress. I even noticed that Peewee_RotA has been adding some stuff to the wiki since I finished up my historical research into A&A for it. I was impressed by the amount of lore you guys generated for the game. There was a lot of thought put into Elmore. It was fun piecing a lot of it back together. But also a pity so much has been lost and forgotten.
I also spent some time in doom builder. I wrote up a few ideas for a mission, and started learning the editor, but I have so many projects I’ve started, I’d had to refocus. Hopefully I’ll get back to it someday.
Hello, how can I run faster in the new edition? I remember pressing SHIFT would make you run faster, but now it let you go slower… Why? Is it a bug?
I changed it so you are running all the time and the shift key slows you down — inverting the function. If you don’t care for it, let me know.
Oh, ok. But I remember that I could do some really long jumps in the old version while pressing SHIFT, but now I can’t… Isn’t there a version with the same upgrades as this latest one but in which you can still run?
I’ll have to look at it, but you should still be able to do long jumps. As usual, it depends on your character’s jumping ability and if the character is a rogue who has a bonus.
Why all the switch animations stop working if I edit the original maps?
The reason is that switches were created using the scripting. It was a rather inelegant solution, but the script command to change texture looks like this:
ChangeSideTexture(1574,”SWIT1D”);
The number is the sidedef’s number. When you make edits, the sidedef numbers change and the script no longer refers to the correct side.
Currently, the only solution is to edit the level, change the sidedef numbers, then recompile the script.
It’s april 2015 and I only just found out about the renewed work on A&A. I must say, I am greatful from the bottom of my heart. This has been one of my most fond chindhood memories and one of my favourite games ever. THANK YOU!
Isn’t there a way to add mouselook into this game
Mouselook was added. Just download the latest and when in 3D, press Tab to toggle in/out of mouse look mode.
Fuckin A man. Now all this game needs is some special abilities added to some of the more lackluster classes, like the citizen. There is literally no reason to make one of those atm. I am definitely going to spend some quality time with this game now.
Yo, what’s up with the wiki? It’s all broken and stuff.
Got it fixed. Check it out now.
Awesome, thanks. That thing is invaluable.
I sure wish I could look down farther.