Hypergammaspaces' assorted ASM

I decided to make a topic for smaller hacks that don’t really need a full post.

Battle Palette Rework
Summon Rework

Draw Only Used Weapon Ranks (compatible with Modular Stat Screens)

MSS wasn’t compatible with the “display only weapon ranks your unit has” hack, and the original source seems to be lost to time. I had a go at recreating that functionality for MSS.

MSS Page 3 with only active ranks (source)
.thumb
.include "mss_defs.s"

page_start

mov r0, r8
push {r5-r7}
mov r5, #0x0 	@counter for bar id 
mov r7, #0x28 	@weapon rank offset (starts at sword)

LoopWeapons:
mov r6, r8 		@unit
ldrb r6, [r6, r7]
cmp r6, #0x0    @does unit have rank?
ble NoRank

mov     r0, r5        @bar id
SetX:
mov     r1, r5        @tile_x = even 1 odd 9
mov     r2, #0x1
and     r1, r2
cmp     r1, #0x1
beq     OddRank
mov     r1, #0x1
b SetY
OddRank:
mov		r1, #0x9

SetY:
mov     r2, r5        @tile_y = 1 1 3 3 5 5 7 7
lsr     r2, r2, #0x1
lsl     r2, r2, #0x1  @clear last bit and add 1
add     r2, #0x1      
mov     r3, r7        @weapon id - calculate from currentOffset
sub     r3, r3, #0x28
blh     DrawWeaponRank, r4        @08087864

add 	r5, #0x1 @increment bar counter
  
NoRank:
add r7, #0x1
cmp r7, #0x2F
ble LoopWeapons
b EndRanks

.ltorg

EndRanks:
pop {r5-r7}

blh      DrawSupports

page_end

21 Likes

Summon Rework

This hack expands/repoints the summon table and allows different characters to summon different classes. Installer and source included. You can define your new summon table in the provided installer.

Currently only supports physical classes but it shouldn’t be hard to update for magic and monster classes. This hack saves 200 bytes and a RNG call over the original, so you should be free to expand it however you like.

12 Likes

It’d be cool if having items in your inventory, i.e. “summon charms”, allowed you to pick from various summon options. Like how the dancing rings work.

1 Like

Wow, that’s awesome. this is so helpful. thanks so much for sharing!

Updated Summon Rework to include a fix for the hardcoded check for the “Phantom” class when drawing the item menu. Some basic support for magic users and monsters has been added to the assembly file as well.

Battle Palette Rework

This reworks the “character-based custom battle palettes” to use a list of any length (rather than a hard limit of 7 palettes per character). The palette associations are indexed by class to save a bit of space, and also share palettes between male and female classes where possible.

You could use this for reclassing or randomizers, or just to remove that annoying 7-palette limit.

20 Likes

Dynamic Equip

This hack allows units to counterattack with a compatible weapon in their inventory depending on the range they are attacked from. Say you have an enemy Warrior carrying an Iron Axe and a Hand Axe, if you 1-range him he keeps the iron axe equipped, if you 2-range him he counters you with the hand axe. This also works for player units.

I’ve included a version of the patch that works with @Teraspark’s range fixes as well as a vanilla-compatible.

18 Likes

FillAIDangerMap Efficiency Fix

When generating AI units’ movement, the game uses a nested for loop to iterate through each red unit, and then check the strength of every ally unit that could potentially reach it in the next turn. This causes a small amount of lag in the vanilla games, but for hacks using the Modular Stat Getters (especially with skill system), the strength getter increases in complexity from a single function call to dozens of nested function calls, ballooning the load on the CPU. With this function located inside this nested loop, it can potentially get called hundreds of times each time the AI moves a unit, including multiple calls for the same ally unit, causing noticeable loading times of several seconds.

This hack rewrites FillAIDangerMap to cache the result of GetUnitPower so that it only runs once per ally unit per calculation. This improves AI decision speed by 50% or more. It also fits in the same space of the vanilla function, so no repointing needed.

tl;dr If you have MSG and lots of deploy slots on your maps, install this

Before (thanks Pik for the gifs):

After:

36 Likes

Even vanilla seems to improve considerably.
On a map with 50 enemies, your routine were able to optimize for about 7 seconds per turn.

I made the FE8J version.

12 Likes

Casual Mode 2.0

Ever wanted to use FE8 Casual Mode but with, like, an actual interface and retreat quotes that are separate from death quotes?

I’ve built a menu interface, ASMCs, and additional graphics to allow you to select Casual or Classic from the New Game menu after selecting a difficulty! This implementation uses options byte 0x42&40 so it won’t replace or remove any game options. It also includes a hack to the death quote functions to allow for a separate table of retreat quotes (if the unit is allied and is not a GameOver unit).
You can also use the CheckCasualMode ASMC to check it within the chapter.

cg3.emulator-0
cg3.emulator-1

Credit to Circles for the original CasualMode hack using EIDs and 7743 for initially porting it as a GameOption.

28 Likes

ayo this is cool, but there’s an empty slot. wanna tell us where phoenix mode is?

1 Like

I mean, if you wanna make it, the code/assets are F2U/E :^)

1 Like

Iron Man mode!! There is no soft reset, only suspend >:)

Real talk though this looks cool! Nice work as usual.

1 Like

I would like to make a suggestion to CasualModeMenu.
You did the implementation by rewriting Proc_SaveMenu_Main.
However, Proc_SaveMenu_Main is too much of an influence.
I implemented the implementation by not rewriting Proc_SaveMenu_Main.

My method is as follows.
At the end of Procs_NewGameDiffilcultySelect, there is a nullstab.
Since it’s a nullstab, it’s an empty function that does nothing. It’s probably a debugging hook.
We’ll reuse this area.

In this area, write “06 00 01 00 // Call new Child Procs [POINTER_PROCS:Child Procs] And Yield” to invoke the casual mode selection Procs.

	//Hijack the last null stub of NewGameDiffilcultySelect and move it to Casual Mode Select Procs.
	ORG 0xA20A88	//{U}
//	ORG 0xA9D9F0	//{J}
	BYTE $06 $00 $01 $00; POIN CasualModeSelectProc	//Call new Child Procs [POINTER_PROCS:Child Procs] And Yield
	POP

This creates a ModeSelect as ChildProcs for NewGameDiffilcultySelect.
ModeSelect blocks the behavior, so the parent does not die until the Child dies.
Thus, we can implement what we want to do without having to rewrite Proc_SaveMenu_Main at all.

Code
https://cdn.discordapp.com/attachments/179027738454261760/758441060753735700/CasualModeMenu2.7z

I also included a version that I ported to FE8J.

3 Likes

Sick. When I implemented casualmode into my enhancementpatch, I planned to fix any issues and the quotes in a roundabout way using flagchecks for the casualmode flag, but this is so much better. This will be fun once remakes give us retreatquotes. :heart_eyes:

I made a lot of changes.

Code
https://cdn.discordapp.com/attachments/179027738454261760/758648745633644584/CasualModeMenu2.7z

1.The number of hooks has been reduced.

ALL DELETE.

	//Setup new save to recognize casual mode
	ORG $30D18
	  SHORT $2D01 //cmp r5, #0x1
	  SHORT $DD03 //ble 30D24
	ORG $30D24
	  //leave r6 for later, it will set casual mode.
	  SHORT $2001 //mov r0, #0x1
	  SHORT $2D01 //cmp r5, #0x1
	  SHORT $DA00 //bge 30D2C
	  SHORT $2000 //mov r0, #0x0
	  SHORT $2142 //mov r1, #0x42
	  SHORT $19C9 //add r1, r1, r7
	  SHORT $468C //mov r12, r1
	  SHORT $2101 //mov r1, #0x1
	  SHORT $4008 //and r0, r1
	  SHORT $0140 //lsl r0, r0, #0x5
	  //r1 and r2 are scratch now
	  jumpToHack(CheckCasualModeInNewSave)
	
	//In SaveNewGame (tested with ExModularSave)
	ORG $A4E98
	  B($A4EA4)
	ORG $A4EF8
	  SHORT $2102 //sets mode byte for new game, change to 2101 if you need FE8's "prologue-ch8" mode
	  
	//Makes sure both difficulty level and casual mode data are passed from menu to new savefile
	ORG $A9250
	  replaceWithHack(New_SetOptionsFromDifficultySelect)

–>

	//Setup new save to recognize casual mode
	ORG $30DAC	//{U}
//	ORG $30CF8	//{J}
	  jumpToHack(CasualMode_SetFlag)

I stopped increasing the argument of “08030CF4 InitClearChapterState” and changed it to refer directly to the procs data.
This reduces the number of hooks and prevents them from colliding with other patches.

That’s all the code you need.

@Call 08030DAC {U}
@Call 08030CF8 {J}
CasualMode_SetFlag:
	@Retransmission of breaking code.
	mov r0, #0x11
	neg r0 ,r0
	and r1 ,r0
	mov r2, r10
	strb r1, [r2, #0x0]

	@Set judgment of casual mode.
	ldr  r0, =0x080AA4F0	@Proc_SaveMenu_Main	Pointer @{U}
@	ldr  r0, =0x080AEFD4	@Proc_SaveMenu_Main	Pointer @{J}
	ldr  r0, [r0]
	blh  0x08002e9c   @Find6C	@{U}
@	blh  0x08002DEC   @Find6C	@{J}

	@Check for errors just in case.
	cmp  r0,#0x0
	beq  CasualMode_SetFlag_Exit

	add  r0, #0x50 @ casual mode
	ldrb r2, [r0, #0x0]
	cmp  r2, #0x00   @if r2==1 then casual. r2==0 then classic
	beq  CasualMode_SetFlag_Exit

	@Set Casual mode flag
	ldr  r1, =0x0202BCF0 @gChapterData	@{U}
@	ldr  r1, =0x0202BCEC @gChapterData	@{J}
	add  r1, #0x42
	ldrb r0, [r1] @bit 40 = casual mode
	mov  r2, #0x40
	orr  r0, r2
	strb r0, [r1]

CasualMode_SetFlag_Exit:
	ldr r3, =0x08030DB6|1	@{U}
@	ldr r3, =0x08030D02|1	@{J}
	bx r3

2.Fixed MAP 0xFF not working.

I created it because the check for MAP was not created in the CallRetreatQuote function.

Also, the hook position of CallRetreatQuote has been moved slightly forward.
This is because there is a BGM 0x3F specification.

    08083620 21C0   mov r1, #0xc0 <--MOVE HERE
    08083622 4008   and r0 ,r1
    08083624 2800   cmp r0, #0x0
    08083626 D103   bne #0x8083630
        08083628 203F   mov r0, #0x3f   //MUSIC_悲しみの中で@ADDRESS <-OLD HOOK
        0808362A 2100   mov r1, #0x0
        0808362C F77E FF52   bl 0x080024d4   //BGMを切り替える(最上位) r0=BGM番号:MUSIC r1=不明

By referring to the BGM as follows, 0x3F can be obtained from the vanilla ROM address.
This is also to avoid conflicts with other patches.

	ldr  r0, =0x08083628	@{U}
@	ldr  r0, =0x08085960	@{J}
	ldrb r0 ,[r0]      @Get BGM 0x3F Death songID
	mov  r1, #0x0
	blh  0x080024d4 @SwitchBGM	@{U}
@	blh  0x08002424 @SwitchBGM	@{J}
3 Likes

I have developed a routine to display information about CasualModeMenu patch.
https://cdn.discordapp.com/attachments/179027738454261760/759293387891015690/ShowDifficulty.7z

1

2

11 Likes

Thanks for all your ports and improvements, 7743! Looking forward to updating my local version with these fixes.

5 Likes

Ariadna found this bug for me related to DynamicEquip (RangeFix version).


Attack Pulse, Leaf Flurry, and Bubble are 2 range weapons. Headbutt is 1 range.

If I have an equippable weapon in the 3rd slot, it is ignored.

Works:

Headbutt in 2nd slot

l7tcprC1Lw

(4th and 5th slots also seem to work fine.)

Broken:

Headbutt in 3rd slot

WFRv8zNzbL

If you can offer any help, that would be appreciated. :slight_smile:

4 Likes

Hello, HyperGammaSpaces.

I used the event editor on Battle Palette Rework but got an error saying it didn’t recognize all the characters. Do I need to something manual to use the event file?

Sorry for the newbie question. Just trying to learn all I can