Improving the Swarm System - pret/pokecrystal GitHub Wiki
As was requested, here is a tutorial for reworking the swarm system, and adding extra features to it.
Contents
- Rework Base Swarm System
- Implement System for Increased Shiny Odds
- Create an NPC for Triggering a Daily Swarm
- Remove Old Phone Call Swarm Triggers
- Add New Swarms
- Bonus: Multiple Swarms on Same Route
1. Rework Base Swarm System
First things first, we'll be reworking the base swarm system to be more like GS's- no wram labels for individual swarms, but one shared 'Swarm' label. This will make things much easier for adding any future swarms.
Edit ram/wram.asm:
-wDunsparceMapGroup:: db
-wDunsparceMapNumber:: db
+wSwarmMapGroup:: db
+wSwarmMapNumber:: db
Edit constants/engine_flags.asm:
- const QWILFISH_SWARM
+ const ENGINE_SWARM
Edit constants/wram_constants.asm:
- const DAILYFLAGS1_FISH_SWARM_F
+ const DAILYFLAGS1_SWARM_F
Edit data/events/engine_flags.asm:
- engine_flag wDailyFlags1, DAILYFLAGS1_FISH_SWARM_F
+ engine_flag wDailyFlags1, DAILYFLAGS1_SWARM_F
Edit macros/scripts/events.asm:
swarm: MACRO
db swarm_command
- db \1 ; flag
- map_id \2 ; map
+ map_id \1 ; map
Edit engine/events/fish.asm:
GetFishGroupIndex:
; Return the index of fishgroup d in de.
- push hl
- ld hl, wDailyFlags1
- bit DAILYFLAGS1_FISH_SWARM_F, [hl]
- pop hl
- jr z, .done
Edit engine/events/specials.asm:
ActivateFishingSwarm:
ld a, [wScriptVar]
ld [wFishingSwarmFlag], a
- ret
+ jr SetSwarmFlag
StoreSwarmMapIndices::
- ld a, c
- and a
- jr nz, .yanma
-; swarm dark cave violet entrance
- ld a, d
- ld [wDunsparceMapGroup], a
- ld a, e
- ld [wDunsparceMapNumber], a
- ret
-
-.yanma
- ld a, d
- ld [wYanmaMapGroup], a
- ld a, e
- ld [wYanmaMapNumber], a
- ret
+ ld a, d
+ ld [wSwarmMapGroup], a
+ ld a, e
+ ld [wSwarmMapNumber], a
+
+SetSwarmFlag:
+ ld hl, wDailyFlags1
+ set DAILYFLAGS1_SWARM_F, [hl]
+ ret
+
+CheckSwarmFlag::
+ ld hl, wDailyFlags1
+ bit DAILYFLAGS1_SWARM_F, [hl]
+ jr z, .clear_swarm
+ xor a
+ ld [wScriptVar], a
+ ret
+
+.clear_swarm
+ ld a, 1
+ ld [wScriptVar], a
+ xor a
+ ld [wFishingSwarmFlag], a
+ ld [wSwarmMapGroup], a
+ ld [wSwarmMapNumber], a
+ ret
Edit engine/overworld/scripting.asm:
Script_swarm:
- call GetScriptByte
- ld c, a
Edit engine/overworld/time.asm:
-UnusedSetSwarmFlag: ; unreferenced
- ld hl, wDailyFlags1
- set DAILYFLAGS1_FISH_SWARM_F, [hl]
- ret
-
-UnusedCheckSwarmFlag: ; unreferenced
- and a
- ld hl, wDailyFlags1
- bit DAILYFLAGS1_FISH_SWARM_F, [hl]
- ret nz
- scf
- ret
Edit engine/overworld/wildmons.asm:
_SwarmWildmonCheck:
call CopyCurrMapDE
- push hl
- ld hl, wSwarmFlags
- bit SWARMFLAGS_DUNSPARCE_SWARM_F, [hl]
- pop hl
- jr z, .CheckYanma
- ld a, [wDunsparceMapGroup]
- cp d
- jr nz, .CheckYanma
- ld a, [wDunsparceMapNumber]
- cp e
- jr nz, .CheckYanma
- call LookUpWildmonsForMapDE
- jr nc, _NoSwarmWildmon
- scf
- ret
-
-.CheckYanma:
- push hl
- ld hl, wSwarmFlags
- bit SWARMFLAGS_YANMA_SWARM_F, [hl]
- pop hl
- jr z, _NoSwarmWildmon
- ld a, [wYanmaMapGroup]
- cp d
- jr nz, _NoSwarmWildmon
- ld a, [wYanmaMapNumber]
+ ld a, [wSwarmMapGroup]
+ cp d
+ jr nz, _NoSwarmWildmon
+ ld a, [wSwarmMapNumber]
cp e
jr nz, _NoSwarmWildmon
call LookUpWildmonsForMapDE
jr nc, _NoSwarmWildmon
scf
ret
Lastly, edit data/wild/swarm_water.asm:
SwarmWaterWildMons:
- ; No swarms encountered while surfing in Crystal
+ ; qwilfish
+ map_id ROUTE_32
+ db 6 percent ; encounter rate
+ db 15, QWILFISH
+ db 20, QWILFISH
+ db 20, TENTACRUEL
Note that surfing swarms are possible, so I chose to replace the single fishing swarm and just reuse some of the leftovers of that here.
2. Implement System for Increased Shiny Odds
The existing ATKDEFDV_SHINY constant will almost always result in a male shiny due to gender being tied to dvs as well, so we're just creating a female weighted constant here.
Edit constants/battle_constants.asm:
; shiny dvs
DEF ATKDEFDV_SHINY EQU $EA
+DEF ATKDEFDV_SHINYF EQU $1A
DEF SPDSPCDV_SHINY EQU $AA
Create engine/battle/swarm_shiny.asm:
+GenerateSwarmShiny:
+ ld a, [wSwarmMapGroup]
+ ld b, a
+ ld a, [wSwarmMapNumber]
+ ld c, a
+ call GetWorldMapLocation
+ cp LANDMARK_ROUTE_35
+ jr z, .yanma
+ cp LANDMARK_DARK_CAVE
+ jr z, .dunsparce
+ cp LANDMARK_ROUTE_32
+ jr z, .qwilfish
+ jr .skipshine
+
+.yanma
+ ld a, [wCurPartySpecies]
+ cp YANMA
+ jr nz, .skipshine
+ jr .rollshiny
+.dunsparce
+ ld a, [wCurPartySpecies]
+ cp DUNSPARCE
+ jr nz, .skipshine
+ jr .rollshiny
+.qwilfish
+ ld a, [wCurPartySpecies]
+ cp QWILFISH
+ jr nz, .skipshine
+ ;fallthrough
+.rollshiny
+ call Random
+ cp 7 ; adjust to desired percentage
+ jr nc, .trynext
+ ld b, ATKDEFDV_SHINY
+ ld c, SPDSPCDV_SHINY
+ jr .UpdateDVs
+.trynext:
+ call Random
+ cp 7 ; adjust to desired percentage
+ jr nc, .skipshine
+ ld b, ATKDEFDV_SHINYF
+ ld c, SPDSPCDV_SHINY
+ jr .UpdateDVs
+
+.skipshine:
+; Generate new random DVs
+ call BattleRandom
+ ld b, a
+ call BattleRandom
+ ld c, a
+
+.UpdateDVs:
+; Input DVs in register bc
+ ld hl, wEnemyMonDVs
+ ld a, b
+ ld [hli], a
+ ld [hl], c
+ ret
Edit main.asm:
INCLUDE "engine/battle/link_result.asm"
+
+
+SECTION "Own Section", ROMX
+
+INCLUDE "engine/battle/swarm_shiny.asm"
Edit engine/battle/core.asm:
.GenerateDVs:
+;checkswarm
+ ld hl, wDailyFlags1
+ bit DAILYFLAGS1_SWARM_F, [hl]
+ jr z, .skipshine
+
+ farcall GenerateSwarmShiny
+ jr .next
+
+.skipshine:
; Generate new random DVs
call BattleRandom
ld b, a
call BattleRandom
ld c, a
.UpdateDVs:
; Input DVs in register bc
ld hl, wEnemyMonDVs
ld a, b
ld [hli], a
ld [hl], c
+.next
This code will check for the swarming species, while a swarm is active, and boost the chance of that species being shiny. This can be set to as common or as infrequent as desired.
3. Create an NPC for Triggering a Daily Swarm
Edit maps/[whereveryouwant].asm:
+SwarmGrampsScript:
+ faceplayer
+ opentext
+ checkflag ENGINE_SWARM
+ iftrue .skiprandomswarm
+ random 3
+ ifequal 0, .dunsparce
+ ifequal 1, .yanma
+ ifequal 2, .qwilfish
+
+.dunsparce
+ setflag ENGINE_SWARM
+ swarm DARK_CAVE_VIOLET_ENTRANCE
+ writetext SwarmDunsparceText
+ waitbutton
+ closetext
+ end
+
+.yanma
+ setflag ENGINE_SWARM
+ swarm ROUTE_35
+ writetext SwarmYanmaText
+ waitbutton
+ closetext
+ end
+
+.qwilfish
+ setflag ENGINE_SWARM
+ swarm ROUTE_32
+ writetext SwarmQwilfishText
+ waitbutton
+ closetext
+ end
+
+.skiprandomswarm
+ writetext SkipSwarmText
+ waitbutton
+ closetext
+ end
+
+SwarmDunsparceText:
+ text "Let me see…"
+ line "What did the news"
+ cont "say?"
+
+ para "Oh yes! There's a"
+ line "swarm of DUNSPARCE"
+ cont "at DARK CAVE."
+ done
+
+SwarmYanmaText:
+ text "Let me see…"
+ line "What did the news"
+ cont "say?"
+
+ para "Oh yes! There's a"
+ line "swarm of YANMA"
+ cont "on ROUTE 35."
+ done
+
+SwarmQwilfishText:
+ text "Let me see…"
+ line "What did the news"
+ cont "say?"
+
+ para "Oh yes! There's a"
+ line "swarm of QWILFISH"
+ cont "on ROUTE 32."
+ done
+
+SkipSwarmText:
+ text "Often #MON"
+ line "of unusual colors"
+
+ para "are reported in"
+ line "swarms."
+ done
(...)
def_object_events
+object_event 2, 3, SPRITE_GRAMPS, SPRITEMOVEDATA_STANDING_UP, 0, 0, -1, -1, 0, OBJECTTYPE_SCRIPT, 0, SwarmGrampsScript, -1
Another improvement that has been made here is to create an npc that the player can talk directly with to trigger a swarm, once per day. No more repetitively changing DST, hoping someone will call you.
4. Remove Old Phone Call Swarm Triggers
Edit engine/phone/scripts/ralph.asm:
farscall PhoneScript_AnswerPhone_Male
- checkflag ENGINE_RALPH_WEDNESDAY_MORNING
- iftrue .CheckSwarm
- readvar VAR_WEEKDAY
- ifnotequal WEDNESDAY, .CheckSwarm
checktime MORN
iftrue Ralph_WednesdayMorning
-.CheckSwarm:
- checkflag ENGINE_QWILFISH_SWARM
- iftrue .ReportSwarm
farsjump RalphNoItemScript
(...)
-.ReportSwarm:
- getlandmarkname STRING_BUFFER_5, LANDMARK_ROUTE_32
- farsjump RalphHurryScript
(...)
- checkflag ENGINE_FLYPOINT_GOLDENROD
- iffalse .CheckSwarm
- checkflag ENGINE_RALPH_READY_FOR_REMATCH
- iftrue .CheckSwarm
- checkflag ENGINE_RALPH_WEDNESDAY_MORNING
- iftrue .CheckSwarm
farscall PhoneScript_Random2
ifequal 0, Ralph_FightMe
-.CheckSwarm:
- farscall PhoneScript_Random5
- ifequal 0, Ralph_SetUpSwarm
farsjump Phone_GenericCall_Male
(...)
-Ralph_SetUpSwarm:
- checkflag ENGINE_QWILFISH_SWARM
- iftrue .Generic
- setflag ENGINE_QWILFISH_SWARM
- getmonname STRING_BUFFER_4, QWILFISH
- getlandmarkname STRING_BUFFER_5, LANDMARK_ROUTE_32
- setval FISHSWARM_QWILFISH
- special ActivateFishingSwarm
- farsjump RalphItemScript
-
-.Generic:
- farsjump Phone_GenericCall_Male
Edit engine/phone/scripts/anthony.asm:
- checkflag ENGINE_ANTHONY_FRIDAY_NIGHT
- iftrue .NotFriday
- readvar VAR_WEEKDAY
- ifnotequal FRIDAY, .NotFriday
checktime NITE
iftrue AnthonyFridayNight
-
-.NotFriday:
- checkflag ENGINE_DUNSPARCE_SWARM
- iftrue .AlreadySwarming
farsjump AnthonyHangUpScript
(...)
-.AlreadySwarming:
- getlandmarkname STRING_BUFFER_5, LANDMARK_ROUTE_33
- farsjump AnthonyHurryScript
(...)
- checkflag ENGINE_FLYPOINT_GOLDENROD
- iffalse .TriesSwarm
- checkflag ENGINE_ANTHONY_READY_FOR_REMATCH
- iftrue .TriesSwarm
- checkflag ENGINE_ANTHONY_FRIDAY_NIGHT
- iftrue .TriesSwarm
farscall PhoneScript_Random2
ifequal 0, AnthonyWantsBattle
-
-.TriesSwarm:
- farscall PhoneScript_Random5
- ifequal 0, AnthonyTriesDunsparceSwarm
farsjump Phone_GenericCall_Male
(...)
-AnthonyTriesDunsparceSwarm:
- checkflag ENGINE_DUNSPARCE_SWARM
- iftrue .Generic
- setflag ENGINE_DUNSPARCE_SWARM
- getmonname STRING_BUFFER_4, DUNSPARCE
- swarm SWARM_DUNSPARCE, DARK_CAVE_VIOLET_ENTRANCE
- getlandmarkname STRING_BUFFER_5, LANDMARK_DARK_CAVE
- farsjump AnthonySwarmScript
-
-.Generic:
- farsjump Phone_GenericCall_Male
Edit engine/phone/scripts/arnie.asm:
- checkflag ENGINE_ARNIE_TUESDAY_MORNING
- iftrue .NotTuesday
- readvar VAR_WEEKDAY
- ifnotequal TUESDAY, .NotTuesday
checktime MORN
iftrue ArnieTuesdayMorning
-
-.NotTuesday:
- checkflag ENGINE_YANMA_SWARM
- iftrue .AlreadySwarming
farsjump ArnieHangUpScript
(...)
-.AlreadySwarming:
- getlandmarkname STRING_BUFFER_5, LANDMARK_ROUTE_35
- farsjump ArnieHurryScript
(...)
- checkflag ENGINE_ARNIE_READY_FOR_REMATCH
- iftrue .Swarm
- checkflag ENGINE_ARNIE_TUESDAY_MORNING
- iftrue .Swarm
farscall PhoneScript_Random2
ifequal 0, ArnieWantsBattle
-
-.Swarm:
- farscall PhoneScript_Random5
- ifequal 0, ArnieYanmaSwarm
- farscall PhoneScript_Random3
- ifequal 0, ArnieFoundRare
farsjump Phone_GenericCall_Male
(...)
-ArnieYanmaSwarm: ; start swarm
- checkflag ENGINE_YANMA_SWARM
- iftrue ArnieYanmaAlreadySwarming
- setflag ENGINE_YANMA_SWARM
- getmonname STRING_BUFFER_4, YANMA
- swarm SWARM_YANMA, ROUTE_35
- getlandmarkname STRING_BUFFER_5, LANDMARK_ROUTE_35
- farsjump ArnieSwarmScript
-
-ArnieFoundRare:
- farsjump Phone_CheckIfUnseenRare_Male
-
-ArnieYanmaAlreadySwarming:
- farsjump Phone_GenericCall_Male
Since we're no longer using these, we're just removing them.
5. Add New Swarms
Should you want to add more swarms, it is now as simple as this-
Edit data/wild/swarm_grass.asm:
+; Vulpix swarm
+ map_id ROUTE_37
+ db 10 percent, 10 percent, 10 percent ; encounter rates: morn/day/nite
+ ; morn
+ db 13, LEDYBA
+ db 14, VULPIX
+ db 15, PIDGEY
+ db 16, VULPIX
+ db 15, PIDGEOTTO
+ db 15, VULPIX
+ db 15, LEDIAN
+ ; day
+ db 13, PIDGEY
+ db 14, VULPIX
+ db 15, PIDGEY
+ db 16, VULPIX
+ db 15, PIDGEOTTO
+ db 15, VULPIX
+ db 15, PIDGEY
+ ; nite
+ db 13, SPINARAK
+ db 14, VULPIX
+ db 15, HOOTHOOT
+ db 16, VULPIX
+ db 15, NOCTOWL
+ db 15, VULPIX
+ db 15, ARIADOS
Edit engine/battle/swarm_shiny.asm:
jr z, .qwilfish
+ cp LANDMARK_ROUTE_37
+ jr z, .vulpix
jr .skipshine
+.vulpix
+ ld a, [wCurPartySpecies]
+ cp VULPIX
+ jr nz, .skipshine
+ jr .rollshiny
.yanma
Edit maps/[whereveryouwant].asm:
- random 3
+ random 4
ifequal 0, .dunsparce
ifequal 1, .yanma
ifequal 2, .qwilfish
+ ifequal 3, .vulpix
(...)
+.vulpix
+ setflag ENGINE_SWARM
+ swarm ROUTE_37
+ writetext SwarmVulpixText
+ waitbutton
+ closetext
+ end
(...)
+SwarmVulpixText:
+ text "Let me see…"
+ line "What did the news say?"
+
+ para "Oh yes! There's a"
+ line "swarm of VULPIX"
+ cont "on ROUTE 37."
+ done
And that should be all.
6. Bonus: Multiple Swarms on Same Route
Per request, here we will explore how to add an additional swarm to a route that already has one. Start off by repurposing the now unused Dunsparce swarm flag.
Edit constants/engine_flags.asm:
; wSwarmFlags
- const ENGINE_DUNSPARCE_SWARM
+ const ENGINE_ALT_SWARM
Edit constants/wram_constants.asm:
- const SWARMFLAGS_DUNSPARCE_SWARM_F ; 2
+ const SWARMFLAGS_ALT_SWARM_F ; 2
Edit data/events/engine_flags.asm:
- engine_flag wSwarmFlags, SWARMFLAGS_DUNSPARCE_SWARM_F
+ engine_flag wSwarmFlags, SWARMFLAGS_ALT_SWARM_F
Edit maps/Route33.asm:
- checkflag ENGINE_DUNSPARCE_SWARM
- iftrue .Swarm
Now we're going to add some code to set and check for the ALT_SWARM flag to determine if the swarm is the secondary swarm on the route, and how to handle it.
Create data/wild/swarm_grass_alt.asm:
+; Pokémon swarms in grass
+
+SwarmGrassWildMonsAlt:
+
+; Murkrow swarm
+ map_id ROUTE_37
+ db 10 percent, 10 percent, 10 percent ; encounter rates: morn/day/nite
+ ; morn
+ db 12, MURKROW
+ db 14, MURKROW
+ db 13, MURKROW
+ db 10, ABRA
+ db 12, MURKROW
+ db 10, DITTO
+ db 12, YANMA
+ ; day
+ db 12, MURKROW
+ db 14, MURKROW
+ db 13, MURKROW
+ db 10, ABRA
+ db 12, MURKROW
+ db 10, DITTO
+ db 12, YANMA
+ ; nite
+ db 12, MURKROW
+ db 14, MURKROW
+ db 13, MURKROW
+ db 10, ABRA
+ db 12, MURKROW
+ db 10, DITTO
+ db 12, YANMA
+
+ db -1 ; end
Edit maps/[whereveryouwant].asm:
- random 4
+ random 5
ifequal 0, .dunsparce
ifequal 1, .yanma
ifequal 2, .qwilfish
ifequal 3, .vulpix
+ ifequal 4, .murkrow
(...)
+.murkrow
+ setflag ENGINE_ALT_SWARM
+ setflag ENGINE_SWARM
+ swarm ROUTE_35
+ writetext SwarmMurkrowText
+ waitbutton
+ closetext
+ end
(...)
+SwarmMurkrowText:
+ text "Let me see…"
+ line "What did the news say?"
+
+ para "Oh yes! There's a"
+ line "swarm of MURKROW"
+ cont "on ROUTE 35."
+ done
Edit engine/overworld/wildmons.asm:
_GrassWildmonLookup:
+ ld hl, SwarmGrassWildMonsAlt
+ push hl
+ ld hl, wSwarmFlags
+ bit SWARMFLAGS_ALT_SWARM_F, [hl]
+ pop hl
+ jr nz, .nextgrass
ld hl, SwarmGrassWildMons
+.nextgrass
ld bc, GRASS_WILDDATA_LENGTH
call _SwarmWildmonCheck
ret c
(...)
INCLUDE "data/wild/swarm_grass.asm"
+INCLUDE "data/wild/swarm_grass_alt.asm"
Edit engine/battle/swarm_shiny.asm:
.yanma
+ ld hl, wSwarmFlags
+ bit SWARMFLAGS_ALT_SWARM_F, [hl]
+ jr nz, .murkrow
ld a, [wCurPartySpecies]
(...)
+.murkrow
+ ld a, [wCurPartySpecies]
+ cp MURKROW
+ jr nz, .skipshine
+ jr .rollshiny
And now two swarms on the same route are functional. If for some reason you should want to add a third, you can repurpose the Yanma swarm flag the same way.